@akotliar/sitemap-qa 1.0.0-alpha.2 โ†’ 1.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/index.ts","../src/commands/analyze.ts","../src/config/config-loader.ts","../src/types/config.ts","../src/errors/network-errors.ts","../src/utils/http-client.ts","../src/core/discovery.ts","../src/core/parser.ts","../src/utils/batch-processor.ts","../src/core/extractor.ts","../src/core/consolidator.ts","../src/core/patterns/risk-patterns.ts","../src/core/patterns/domain-patterns.ts","../src/core/patterns/admin-patterns.ts","../src/utils/sanitizer.ts","../src/core/risk-grouper.ts","../src/core/risk-detector.ts","../src/summarizer.ts","../src/reporters/json-reporter.ts","../src/reporters/html-reporter.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport 'dotenv/config';\r\nimport { Command } from 'commander';\r\nimport { analyzeCommand } from '@/commands/analyze';\r\n\r\nconst program = new Command();\r\n\r\nprogram\r\n .name('sitemap-qa')\r\n .version('1.0.0')\r\n .description('sitemap analysis for QA teams');\r\n\r\nprogram.addCommand(analyzeCommand);\r\n\r\n// Global error handler\r\nprocess.on('unhandledRejection', (reason, promise) => {\r\n console.error('Unhandled Rejection at:', promise, 'reason:', reason);\r\n process.exit(1);\r\n});\r\n\r\n// Graceful shutdown handlers\r\nprocess.on('SIGINT', () => {\r\n console.log('\\nGracefully shutting down...');\r\n process.exit(0);\r\n});\r\n\r\nprocess.on('SIGTERM', () => {\r\n console.log('\\nGracefully shutting down...');\r\n process.exit(0);\r\n});\r\n\r\nprogram.parse();\r\n","import { Command } from 'commander';\r\nimport { promises as fs } from 'fs';\r\nimport ora from 'ora';\r\nimport chalk from 'chalk';\r\nimport cliProgress from 'cli-progress';\r\nimport os from 'os';\r\nimport { loadConfig } from '@/config/config-loader';\r\nimport { discoverSitemaps } from '@/core/discovery';\r\nimport { extractAllUrls } from '@/core/extractor';\r\nimport { consolidateUrls } from '@/core/consolidator';\r\nimport { detectRisks } from '@/core/risk-detector';\r\nimport { groupRiskFindings } from '@/core/risk-grouper';\r\nimport { summarizeRisks } from '@/summarizer';\r\nimport { generateJsonReport } from '@/reporters/json-reporter';\r\nimport { writeHtmlReport } from '@/reporters/html-reporter';\r\nimport type { Config } from '@/types/config';\r\nimport type { DiscoveryResult } from '@/core/discovery';\r\nimport type { RiskSummary } from '@/summarizer';\r\nimport type { RiskGroup } from '@/core/risk-grouper';\r\n\r\ninterface PhaseTiming {\r\n name: string;\r\n startTime: number;\r\n endTime: number;\r\n duration: number;\r\n}\r\n\r\ninterface AnalyzeOptions {\r\n timeout: string;\r\n progress: boolean;\r\n output: 'json' | 'html';\r\n outputDir?: string;\r\n outputFile?: string;\r\n color: boolean;\r\n verbose: boolean;\r\n acceptedPatterns?: string;\r\n concurrency?: string;\r\n batchSize?: string;\r\n parsingConcurrency?: string;\r\n discoveryConcurrency?: string;\r\n silent?: boolean;\r\n benchmark?: boolean;\r\n}\r\n\r\ninterface AnalysisPipelineResult {\r\n discoveryResult: DiscoveryResult;\r\n totalUrls: number;\r\n riskGroups: RiskGroup[];\r\n summary: RiskSummary;\r\n errors: Error[];\r\n executionTime: number;\r\n phaseTimings: PhaseTiming[];\r\n}\r\n\r\nexport const analyzeCommand = new Command('analyze')\r\n .description('Analyze sitemap for QA issues')\r\n .argument('<url>', 'Base URL to analyze')\r\n .option('--timeout <seconds>', 'HTTP timeout in seconds', '30')\r\n .option('--no-progress', 'Disable progress bar')\r\n .option('--output <format>', 'Output format: html or json', 'html')\r\n .option('--output-dir <path>', 'Output directory for reports')\r\n .option('--output-file <path>', 'Custom output filename')\r\n .option('--accepted-patterns <patterns>', 'Comma-separated regex patterns to exclude from risk detection')\r\n .option('--concurrency <number>', 'Number of concurrent workers for risk detection')\r\n .option('--batch-size <number>', 'URLs per batch for risk detection', '10000')\r\n .option('--parsing-concurrency <number>', 'Number of concurrent sitemap parsers', '50')\r\n .option('--discovery-concurrency <number>', 'Number of concurrent sitemap index fetches', '50')\r\n .option('--silent', 'Disable all progress output')\r\n .option('--benchmark', 'Save performance profile')\r\n .option('--no-color', 'Disable ANSI color codes in CLI output')\r\n .option('--verbose', 'Enable verbose logging', false)\r\n .action(async (url: string, options: AnalyzeOptions) => {\r\n let config: Config | undefined;\r\n \r\n try {\r\n // Validate options\r\n validateAnalyzeOptions(options);\r\n \r\n // Load configuration with hierarchy\r\n const loadedConfig = await loadConfig({\r\n ...options,\r\n baseUrl: url,\r\n outputFormat: options.output,\r\n riskDetectionConcurrency: options.concurrency ? parseInt(options.concurrency) : undefined,\r\n riskDetectionBatchSize: options.batchSize ? parseInt(options.batchSize) : undefined,\r\n parsingConcurrency: options.parsingConcurrency ? parseInt(options.parsingConcurrency) : undefined,\r\n discoveryConcurrency: options.discoveryConcurrency ? parseInt(options.discoveryConcurrency) : undefined,\r\n silent: options.silent,\r\n benchmark: options.benchmark,\r\n progressBar: options.progress,\r\n });\r\n config = loadedConfig;\r\n \r\n console.log(`\\n๐Ÿ” Analyzing ${url}...\\n`);\r\n \r\n // Run analysis pipeline\r\n const result = await runAnalysisPipeline(url, config);\r\n \r\n // Create output directory\r\n await fs.mkdir(config.outputDir, { recursive: true });\r\n \r\n // Handle output based on format\r\n if (options.output === 'json') {\r\n // JSON output - print to console and optionally save file\r\n const jsonReport = generateJsonReport(\r\n result.summary,\r\n result.discoveryResult,\r\n { totalCount: result.totalUrls, uniqueUrls: [], errors: [] },\r\n result.riskGroups,\r\n config,\r\n result.executionTime,\r\n { pretty: true, indent: 2 }\r\n );\r\n \r\n // Print JSON to console\r\n console.log('\\n' + jsonReport);\r\n \r\n // Also save to file if outputFile is specified\r\n if (options.outputFile) {\r\n const jsonFilePath = `${config.outputDir}/${options.outputFile}`;\r\n await fs.writeFile(jsonFilePath, jsonReport, 'utf-8');\r\n console.log(`\\n๐Ÿ“„ JSON report saved to: ${chalk.cyan(jsonFilePath)}`);\r\n }\r\n } else {\r\n // HTML output (default) - show CLI summary and save HTML report\r\n showCliSummary(result);\r\n \r\n const htmlFileName = options.outputFile || `sitemap-qa-report-${Date.now()}.html`;\r\n const htmlFilePath = `${config.outputDir}/${htmlFileName}`;\r\n await writeHtmlReport(\r\n result.summary,\r\n result.discoveryResult,\r\n result.totalUrls,\r\n config,\r\n htmlFilePath,\r\n result.errors,\r\n { maxUrlsPerGroup: 10 }\r\n );\r\n console.log(`\\n๐Ÿ“„ Full report saved to: ${chalk.cyan(htmlFilePath)}`);\r\n }\r\n \r\n // Exit with appropriate code\r\n const exitCode = determineExitCode(result);\r\n process.exit(exitCode);\r\n \r\n } catch (error) {\r\n handleAnalysisError(error, config!);\r\n process.exit(2);\r\n }\r\n });\r\n\r\n/**\r\n * Validate analyze command options\r\n */\r\nfunction validateAnalyzeOptions(options: AnalyzeOptions): void {\r\n // Validate output format\r\n const validFormats = ['json', 'html'];\r\n if (!validFormats.includes(options.output)) {\r\n throw new Error(\r\n `Invalid output format: ${options.output}. Must be one of: ${validFormats.join(', ')}`\r\n );\r\n }\r\n \r\n // Validate timeout\r\n const timeout = parseInt(options.timeout);\r\n if (isNaN(timeout) || timeout <= 0) {\r\n throw new Error(`Invalid timeout: ${options.timeout}. Must be a positive number.`);\r\n }\r\n}\r\n\r\n/**\r\n * Show simple CLI summary\r\n */\r\nfunction showCliSummary(result: AnalysisPipelineResult): void {\r\n const riskyUrlCount = result.summary.categoryInsights.reduce((sum, g) => sum + g.count, 0);\r\n \r\n // Visual separator\r\n console.log(chalk.dim('โ”€'.repeat(50)));\r\n \r\n if (riskyUrlCount === 0) {\r\n console.log(chalk.green('No issues found - sitemap looks clean!'));\r\n } else {\r\n // Build inline severity summary\r\n const { high, medium, low } = result.summary.severityBreakdown;\r\n const severityParts = [];\r\n if (high > 0) severityParts.push(chalk.red(`High: ${high}`));\r\n if (medium > 0) severityParts.push(chalk.yellow(`Medium: ${medium}`));\r\n if (low > 0) severityParts.push(chalk.blue(`Low: ${low}`));\r\n \r\n const severitySummary = severityParts.length > 0 ? ` (${severityParts.join(', ')})` : '';\r\n console.log(chalk.yellow(`โš ๏ธ ${riskyUrlCount} risky URLs found${severitySummary}`));\r\n }\r\n console.log('');\r\n}\r\n\r\n/**\r\n * Run complete analysis pipeline (5 phases) with timing and progress tracking\r\n */\r\nasync function runAnalysisPipeline(\r\n url: string,\r\n config: Config\r\n): Promise<AnalysisPipelineResult> {\r\n const overallStartTime = Date.now();\r\n const phaseTimings: PhaseTiming[] = [];\r\n const errors: Error[] = [];\r\n \r\n const showProgress = !config.silent && config.progressBar !== false && process.stdout.isTTY;\r\n \r\n // Phase 1: Discovery\r\n let phaseStart = Date.now();\r\n const discoverySpinner = showProgress ? ora({ text: 'Discovering sitemaps...', color: 'cyan' }).start() : null;\r\n \r\n const discoveryResult = await discoverSitemaps(url, config);\r\n \r\n if (discoverySpinner) {\r\n discoverySpinner.stop();\r\n }\r\n \r\n phaseTimings.push({\r\n name: 'Discovery',\r\n startTime: phaseStart,\r\n endTime: Date.now(),\r\n duration: Date.now() - phaseStart\r\n });\r\n \r\n // Check for access issues\r\n if (discoveryResult.accessIssues.length > 0) {\r\n if (!config.silent) {\r\n console.warn(chalk.yellow(`โš ๏ธ Warning: ${discoveryResult.accessIssues.length} sitemap(s) are access-blocked`));\r\n }\r\n for (const issue of discoveryResult.accessIssues) {\r\n errors.push(new Error(`Access blocked: ${issue.url} (${issue.statusCode})`));\r\n }\r\n }\r\n \r\n if (discoveryResult.sitemaps.length === 0) {\r\n throw new Error(`No sitemaps found at ${url}. Tried: /sitemap.xml, /sitemap_index.xml, /robots.txt`);\r\n }\r\n \r\n // Phase 2: Parsing & Extraction\r\n phaseStart = Date.now();\r\n let extractionResult;\r\n if (showProgress && discoveryResult.sitemaps.length > 10) {\r\n const parseBar = new cliProgress.SingleBar({\r\n format: '{bar} {percentage}% | {value}/{total} | ETA: {eta}s | {speed} sitemaps/sec',\r\n barCompleteChar: 'โ–ˆ',\r\n barIncompleteChar: 'โ–‘',\r\n hideCursor: true\r\n });\r\n \r\n parseBar.start(discoveryResult.sitemaps.length, 0, { speed: '0' });\r\n \r\n extractionResult = await extractAllUrls(\r\n discoveryResult.sitemaps,\r\n config,\r\n (completed, total) => {\r\n const elapsed = (Date.now() - phaseStart) / 1000;\r\n const speed = elapsed > 0 ? (completed / elapsed).toFixed(1) : '0';\r\n parseBar.update(completed, { speed });\r\n }\r\n );\r\n \r\n parseBar.stop();\r\n } else {\r\n extractionResult = await extractAllUrls(discoveryResult.sitemaps, config);\r\n }\r\n \r\n phaseTimings.push({\r\n name: 'Parsing',\r\n startTime: phaseStart,\r\n endTime: Date.now(),\r\n duration: Date.now() - phaseStart\r\n });\r\n \r\n // Don't print yet, will combine after deduplication\r\n \r\n // Collect errors\r\n if (extractionResult.errors.length > 0) {\r\n for (const err of extractionResult.errors) {\r\n if (typeof err === 'string') {\r\n errors.push(new Error(err));\r\n } else {\r\n errors.push(err);\r\n }\r\n }\r\n }\r\n \r\n if (extractionResult.allUrls.length === 0) {\r\n throw new Error('No URLs extracted from sitemaps');\r\n }\r\n \r\n // Phase 3: Consolidation & Deduplication\r\n phaseStart = Date.now();\r\n const consolidatedResult = consolidateUrls(extractionResult.allUrls);\r\n \r\n phaseTimings.push({\r\n name: 'Deduplication',\r\n startTime: phaseStart,\r\n endTime: Date.now(),\r\n duration: Date.now() - phaseStart\r\n });\r\n \r\n const duplicatesRemoved = extractionResult.allUrls.length - consolidatedResult.uniqueUrls.length;\r\n const duplicatePercentage = (duplicatesRemoved / extractionResult.allUrls.length) * 100;\r\n \r\n if (!config.silent) {\r\n // Show combined summary\r\n if (duplicatesRemoved > 100 || duplicatePercentage > 1) {\r\n // Significant duplicates - show them\r\n console.log(chalk.green(`Found ${discoveryResult.sitemaps.length} sitemap(s) โ†’ ${extractionResult.allUrls.length.toLocaleString()} URLs (${consolidatedResult.uniqueUrls.length.toLocaleString()} unique)`));\r\n } else {\r\n // Few/no duplicates - keep it simple\r\n console.log(chalk.green(`Found ${discoveryResult.sitemaps.length} sitemap(s) โ†’ ${extractionResult.allUrls.length.toLocaleString()} URLs`));\r\n }\r\n }\r\n \r\n // Phase 4: Risk Detection\r\n phaseStart = Date.now();\r\n const riskResult = await detectRisks(consolidatedResult.uniqueUrls, url, config);\r\n const riskGroups = groupRiskFindings(riskResult.findings);\r\n \r\n phaseTimings.push({\r\n name: 'Risk Detection',\r\n startTime: phaseStart,\r\n endTime: Date.now(),\r\n duration: Date.now() - phaseStart\r\n });\r\n \r\n // Phase 5: Generate Summary\r\n phaseStart = Date.now();\r\n const executionTime = Date.now() - overallStartTime;\r\n const summary = summarizeRisks({\r\n riskGroups: riskGroups.groups,\r\n totalUrls: consolidatedResult.uniqueUrls.length,\r\n sitemapUrl: url,\r\n processingTime: executionTime,\r\n });\r\n \r\n phaseTimings.push({\r\n name: 'Summarization',\r\n startTime: phaseStart,\r\n endTime: Date.now(),\r\n duration: Date.now() - phaseStart\r\n });\r\n \r\n // Display phase summary only in verbose mode\r\n if (!config.silent && config.verbose) {\r\n displayPhaseSummary(phaseTimings, executionTime);\r\n } else if (!config.silent) {\r\n // Calculate throughput\r\n const parsingPhase = phaseTimings.find(p => p.name === 'Parsing');\r\n const sitemapsPerSec = parsingPhase ? (discoveryResult.sitemaps.length / (parsingPhase.duration / 1000)).toFixed(1) : '0';\r\n \r\n console.log(chalk.green(`Analysis complete (${(executionTime / 1000).toFixed(1)}s ยท ${sitemapsPerSec} sitemaps/sec)\\n`));\r\n }\r\n \r\n // Save benchmark if requested\r\n if (config.benchmark) {\r\n await saveBenchmark(phaseTimings, url, executionTime, discoveryResult.sitemaps.length, consolidatedResult.uniqueUrls.length, config);\r\n }\r\n \r\n return {\r\n discoveryResult,\r\n totalUrls: consolidatedResult.uniqueUrls.length,\r\n riskGroups: riskGroups.groups,\r\n summary,\r\n errors,\r\n executionTime,\r\n phaseTimings,\r\n };\r\n}\r\n\r\n/**\r\n * Determine exit code based on analysis results\r\n */\r\nfunction determineExitCode(result: AnalysisPipelineResult): number {\r\n // Exit code 0: Success with no high-severity issues\r\n // Exit code 1: High-severity issues found\r\n // Exit code 2: Analysis failed with errors (handled in catch block)\r\n \r\n const highSeverityCount = result.summary.severityBreakdown.high;\r\n \r\n if (highSeverityCount > 0) {\r\n return 1; // High-severity issues found\r\n }\r\n \r\n return 0; // Success\r\n}\r\n\r\n/**\r\n * Handle analysis errors with user-friendly messages\r\n */\r\nfunction handleAnalysisError(error: unknown, config?: Config): void {\r\n console.error('\\nโŒ Analysis failed\\n');\r\n \r\n if (error instanceof Error) {\r\n console.error(`Error: ${error.message}`);\r\n \r\n if (config?.verbose && error.stack) {\r\n console.error('\\nStack trace:');\r\n console.error(error.stack);\r\n }\r\n \r\n // Provide helpful suggestions based on error message\r\n if (error.message.includes('No sitemaps found')) {\r\n console.error('\\nSuggestions:');\r\n console.error(' โ€ข Verify the base URL is correct');\r\n console.error(' โ€ข Check if the site has a sitemap');\r\n console.error(' โ€ข Ensure the sitemap is publicly accessible');\r\n } else if (error.message.includes('Network') || error.message.includes('timeout')) {\r\n console.error('\\nSuggestions:');\r\n console.error(' โ€ข Check your internet connection');\r\n console.error(' โ€ข Verify the URL is accessible');\r\n console.error(' โ€ข Try increasing the timeout with --timeout option');\r\n }\r\n } else {\r\n console.error('Unknown error occurred');\r\n console.error(String(error));\r\n }\r\n}\r\n\r\n/**\r\n * Display phase timing summary\r\n */\r\nfunction displayPhaseSummary(timings: PhaseTiming[], totalTime: number): void {\r\n console.log(chalk.green(`\\nAnalysis Complete (Total: ${(totalTime / 1000).toFixed(1)}s)\\n`));\r\n console.log(chalk.cyan('Phase Breakdown:'));\r\n \r\n for (const timing of timings) {\r\n const seconds = (timing.duration / 1000).toFixed(1);\r\n const percentage = ((timing.duration / totalTime) * 100).toFixed(1);\r\n const bar = 'โ€ข';\r\n \r\n console.log(` ${bar} ${timing.name.padEnd(15)}: ${seconds.padStart(5)}s (${percentage.padStart(5)}%)`);\r\n }\r\n \r\n console.log('');\r\n}\r\n\r\n/**\r\n * Save performance benchmark to file\r\n */\r\nasync function saveBenchmark(\r\n timings: PhaseTiming[],\r\n url: string,\r\n totalTime: number,\r\n sitemapCount: number,\r\n urlCount: number,\r\n config: Config\r\n): Promise<void> {\r\n const benchmark = {\r\n timestamp: new Date().toISOString(),\r\n url,\r\n total_duration_ms: totalTime,\r\n phases: timings.map(t => ({\r\n name: t.name.toLowerCase(),\r\n start_ms: t.startTime,\r\n end_ms: t.endTime,\r\n duration_ms: t.duration\r\n })),\r\n metrics: {\r\n sitemaps_processed: sitemapCount,\r\n urls_analyzed: urlCount,\r\n throughput: {\r\n urls_per_second: Math.round((urlCount / totalTime) * 1000),\r\n sitemaps_per_second: ((sitemapCount / totalTime) * 1000).toFixed(2)\r\n }\r\n },\r\n system_info: {\r\n cpu_count: os.cpus().length,\r\n node_version: process.version,\r\n platform: process.platform,\r\n memory_total_mb: Math.round(os.totalmem() / 1024 / 1024)\r\n },\r\n config: {\r\n discovery_concurrency: config.discoveryConcurrency,\r\n parsing_concurrency: config.parsingConcurrency,\r\n risk_detection_concurrency: config.riskDetectionConcurrency,\r\n risk_detection_batch_size: config.riskDetectionBatchSize\r\n }\r\n };\r\n \r\n const filename = `performance-profile-${Date.now()}.json`;\r\n await fs.writeFile(filename, JSON.stringify(benchmark, null, 2));\r\n console.log(chalk.blue(`๐Ÿ“Š Benchmark saved to: ${filename}`));\r\n}\r\n","import { readFile } from 'fs/promises';\r\nimport { existsSync } from 'fs';\r\nimport { join } from 'path';\r\nimport { homedir } from 'os';\r\nimport { DEFAULT_CONFIG, type Config } from '@/types/config';\r\n\r\nexport async function loadConfig(cliOptions: Record<string, any>): Promise<Config> {\r\n // Start with defaults\r\n let config: Partial<Config> = { ...DEFAULT_CONFIG };\r\n \r\n // Layer 4: Global config (~/.sitemap-qa/config.json)\r\n const globalConfigPath = join(homedir(), '.sitemap-qa', 'config.json');\r\n if (existsSync(globalConfigPath)) {\r\n try {\r\n const globalConfig = JSON.parse(await readFile(globalConfigPath, 'utf-8'));\r\n config = { ...config, ...globalConfig };\r\n } catch (error) {\r\n console.warn(`Warning: Failed to load global config: ${error}`);\r\n }\r\n }\r\n \r\n // Layer 3: Project config (.sitemap-qa.config.json)\r\n const projectConfigPath = join(process.cwd(), '.sitemap-qa.config.json');\r\n if (existsSync(projectConfigPath)) {\r\n try {\r\n const projectConfig = JSON.parse(await readFile(projectConfigPath, 'utf-8'));\r\n config = { ...config, ...projectConfig };\r\n } catch (error) {\r\n console.warn(`Warning: Failed to load project config: ${error}`);\r\n }\r\n }\r\n \r\n // Layer 2: Environment variables\r\n const envConfig = loadFromEnv();\r\n config = { ...config, ...envConfig };\r\n \r\n // Layer 1: CLI options (highest priority)\r\n config = mergeCliOptions(config, cliOptions);\r\n \r\n // Add baseUrl from cliOptions\r\n if (cliOptions.baseUrl) {\r\n config.baseUrl = cliOptions.baseUrl;\r\n }\r\n \r\n // Validate final config\r\n validateConfig(config as Config);\r\n \r\n return config as Config;\r\n}\r\n\r\nfunction loadFromEnv(): Partial<Config> {\r\n const env: Partial<Config> = {};\r\n \r\n if (process.env.SITEMAP_VERIFY_TIMEOUT) {\r\n env.timeout = parseInt(process.env.SITEMAP_VERIFY_TIMEOUT, 10);\r\n }\r\n \r\n return env;\r\n}\r\n\r\nfunction mergeCliOptions(config: Partial<Config>, cliOptions: Record<string, any>): Partial<Config> {\r\n const merged = { ...config };\r\n \r\n // Only override if explicitly set (not default string values)\r\n if (cliOptions.timeout && cliOptions.timeout !== '30') {\r\n merged.timeout = parseInt(cliOptions.timeout, 10);\r\n }\r\n \r\n if (cliOptions.output) {\r\n merged.outputFormat = cliOptions.output as 'json' | 'html';\r\n }\r\n \r\n if (cliOptions.outputDir) {\r\n merged.outputDir = cliOptions.outputDir;\r\n }\r\n \r\n if (cliOptions.verbose === true) {\r\n merged.verbose = true;\r\n }\r\n \r\n if (cliOptions.acceptedPatterns) {\r\n // Parse comma-separated patterns\r\n merged.acceptedPatterns = cliOptions.acceptedPatterns.split(',').map((p: string) => p.trim()).filter(Boolean);\r\n }\r\n \r\n return merged;\r\n}\r\n\r\nfunction validateConfig(config: Config): void {\r\n if (config.timeout < 1 || config.timeout > 300) {\r\n throw new Error('Timeout must be between 1 and 300 seconds');\r\n }\r\n \r\n if (!['json', 'html'].includes(config.outputFormat)) {\r\n throw new Error('Output format must be json or html');\r\n }\r\n}\r\n","export interface Config {\r\n // Network settings\r\n timeout: number; // HTTP timeout in seconds\r\n concurrency: number; // Concurrent sitemap fetches\r\n parsingConcurrency?: number; // Concurrent sitemap parsing (default: 50)\r\n discoveryConcurrency?: number; // Concurrent sitemap index discovery (default: 50)\r\n \r\n // Output settings\r\n outputFormat: 'json' | 'html';\r\n outputDir: string; // Output directory for reports\r\n verbose: boolean; // Verbose logging\r\n \r\n // Base URL for analysis\r\n baseUrl: string;\r\n \r\n // Risk detection settings\r\n acceptedPatterns?: string[]; // URL patterns to exclude from risk detection (regex strings)\r\n riskDetectionBatchSize?: number; // URLs per batch for risk detection (default: 10000)\r\n riskDetectionConcurrency?: number; // Number of concurrent batches for risk detection (default: auto-detect cores)\r\n \r\n // Progress & Metrics\r\n progressBar?: boolean; // Show progress bars (default: auto-detect TTY)\r\n silent?: boolean; // Disable all progress output\r\n benchmark?: boolean; // Save performance profile\r\n}\r\n\r\nexport const DEFAULT_CONFIG: Config = {\r\n timeout: 30,\r\n concurrency: 10,\r\n parsingConcurrency: 50, // Optimized for network-bound parallel parsing\r\n discoveryConcurrency: 50, // Optimized for recursive sitemap index discovery\r\n outputFormat: 'html',\r\n outputDir: './sitemap-qa/report',\r\n verbose: false,\r\n baseUrl: 'https://example.com', // Default for tests\r\n acceptedPatterns: [],\r\n riskDetectionBatchSize: 10000,\r\n riskDetectionConcurrency: undefined, // Auto-detect in risk-detector.ts\r\n progressBar: undefined, // Auto-detect TTY\r\n silent: false,\r\n benchmark: false,\r\n};\r\n","export class NetworkError extends Error {\r\n readonly code = 'NETWORK_ERROR';\r\n \r\n constructor(\r\n public readonly url: string,\r\n public readonly originalError: Error\r\n ) {\r\n super(`Network request failed for ${url}: ${originalError.message}`);\r\n this.name = 'NetworkError';\r\n }\r\n}\r\n\r\nexport class HttpError extends Error {\r\n readonly code = 'HTTP_ERROR';\r\n \r\n constructor(\r\n public readonly url: string,\r\n public readonly statusCode: number,\r\n public readonly statusText?: string\r\n ) {\r\n let message = `HTTP ${statusCode} error for ${url}`;\r\n \r\n // Add helpful context for common blocking scenarios\r\n if (statusCode === 403) {\r\n message += '\\n Note: 403 Forbidden often indicates bot protection (Cloudflare, etc.) or access restrictions';\r\n }\r\n \r\n super(message);\r\n this.name = 'HttpError';\r\n }\r\n}\r\n","import { NetworkError, HttpError } from '@/errors/network-errors';\r\nimport { chromium } from 'playwright';\r\nimport axios from 'axios';\r\nimport { Agent as HttpAgent } from 'http';\r\nimport { Agent as HttpsAgent } from 'https';\r\n\r\n// Create persistent HTTP agents for connection pooling\r\nconst httpAgent = new HttpAgent({ \r\n keepAlive: true, \r\n maxSockets: 200, // Allow many concurrent connections\r\n maxFreeSockets: 50,\r\n timeout: 15000\r\n});\r\n\r\nconst httpsAgent = new HttpsAgent({ \r\n keepAlive: true, \r\n maxSockets: 200,\r\n maxFreeSockets: 50,\r\n timeout: 15000\r\n});\r\n\r\n// Create axios instance with connection pooling\r\nconst axiosInstance = axios.create({\r\n httpAgent,\r\n httpsAgent,\r\n maxRedirects: 5,\r\n validateStatus: () => true // Don't throw on any status code\r\n});\r\n\r\n\r\nexport interface FetchOptions {\r\n timeout?: number; // Timeout in seconds\r\n maxRetries?: number; // Maximum retry attempts (default: 3)\r\n retryDelay?: number; // Initial retry delay in ms (default: 1000)\r\n useBrowser?: boolean; // Force use of headless browser (default: auto-detect on 403)\r\n disableBrowserFallback?: boolean; // Disable automatic browser fallback (for bulk operations)\r\n}\r\n\r\nexport interface FetchResult {\r\n content: string; // Response body as text\r\n statusCode: number; // HTTP status code\r\n url: string; // Final URL after redirects\r\n}\r\n\r\n/**\r\n * Fetch URL using headless browser (Playwright) to bypass bot protection\r\n */\r\nasync function fetchUrlWithBrowser(\r\n url: string,\r\n timeout: number\r\n): Promise<FetchResult> {\r\n let browser;\r\n try {\r\n browser = await chromium.launch({ \r\n headless: true,\r\n args: [\r\n '--disable-blink-features=AutomationControlled', // Hide automation flags\r\n '--disable-dev-shm-usage',\r\n '--no-sandbox'\r\n ]\r\n });\r\n \r\n const context = await browser.newContext({\r\n userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',\r\n viewport: { width: 1920, height: 1080 },\r\n locale: 'en-US',\r\n timezoneId: 'America/New_York',\r\n extraHTTPHeaders: {\r\n 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',\r\n 'Accept-Language': 'en-US,en;q=0.9',\r\n 'Accept-Encoding': 'gzip, deflate, br',\r\n 'DNT': '1',\r\n 'Connection': 'keep-alive',\r\n 'Upgrade-Insecure-Requests': '1'\r\n }\r\n });\r\n \r\n const page = await context.newPage();\r\n \r\n // Add script to mask automation\r\n await page.addInitScript(() => {\r\n // Override the navigator.webdriver property\r\n Object.defineProperty(navigator, 'webdriver', {\r\n get: () => false,\r\n });\r\n \r\n // Mock Chrome runtime\r\n (window as any).chrome = {\r\n runtime: {},\r\n };\r\n \r\n // Mock permissions\r\n const originalQuery = window.navigator.permissions.query;\r\n window.navigator.permissions.query = (parameters: any) =>\r\n parameters.name === 'notifications'\r\n ? Promise.resolve({ state: Notification.permission } as PermissionStatus)\r\n : originalQuery(parameters);\r\n });\r\n \r\n // Set timeout for page navigation\r\n page.setDefaultTimeout(timeout * 1000);\r\n \r\n // Navigate and wait for content to load\r\n const response = await page.goto(url, { \r\n waitUntil: 'domcontentloaded', // Changed from networkidle - faster for simple XML\r\n timeout: timeout * 1000 \r\n });\r\n \r\n if (!response) {\r\n throw new Error('No response received from page');\r\n }\r\n \r\n const statusCode = response.status();\r\n \r\n // For XML sitemaps, get the raw content\r\n const content = await page.content();\r\n const finalUrl = page.url();\r\n \r\n await browser.close();\r\n \r\n if (statusCode >= 200 && statusCode < 300) {\r\n return {\r\n content,\r\n statusCode,\r\n url: finalUrl\r\n };\r\n }\r\n \r\n throw new HttpError(finalUrl, statusCode);\r\n \r\n } catch (error: any) {\r\n if (browser) {\r\n await browser.close();\r\n }\r\n \r\n if (error.code === 'HTTP_ERROR') {\r\n throw error;\r\n }\r\n \r\n throw new NetworkError(url, error);\r\n }\r\n}\r\n\r\nexport async function fetchUrl(\r\n url: string,\r\n options: FetchOptions = {}\r\n): Promise<FetchResult> {\r\n const {\r\n timeout = 30,\r\n maxRetries = 3,\r\n retryDelay = 1000,\r\n useBrowser = false,\r\n disableBrowserFallback = false\r\n } = options;\r\n\r\n // Validate URL before starting retry loop\r\n new URL(url); // Throws TypeError if invalid\r\n\r\n const retryableStatuses = [408, 429, 500, 502, 503, 504];\r\n \r\n let lastError: Error | null = null;\r\n let attemptedBrowser = false;\r\n \r\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\r\n try {\r\n // If forced to use browser or if we previously got 403, use browser\r\n if (useBrowser || attemptedBrowser) {\r\n return await fetchUrlWithBrowser(url, timeout);\r\n }\r\n \r\n // Use axios with connection pooling for better performance\r\n const response = await axiosInstance.get(url, {\r\n timeout: timeout * 1000,\r\n headers: {\r\n 'User-Agent': 'sitemap-qa/1.0.0 (compatible; +https://github.com/Akotliar/sitemap-qa)',\r\n 'Accept': 'text/xml,application/xml,text/plain,*/*',\r\n 'Accept-Encoding': 'gzip, deflate',\r\n 'Connection': 'keep-alive'\r\n }\r\n });\r\n \r\n const statusCode = response.status;\r\n const body = response.data;\r\n \r\n // Success - return result\r\n if (statusCode >= 200 && statusCode < 300) {\r\n return {\r\n content: typeof body === 'string' ? body : JSON.stringify(body),\r\n statusCode: statusCode,\r\n url: response.request?.res?.responseUrl || url // Final URL after redirects\r\n };\r\n }\r\n \r\n // If we get 403, try with browser on next attempt (unless disabled)\r\n if (statusCode === 403 && !attemptedBrowser && !disableBrowserFallback) {\r\n attemptedBrowser = true;\r\n // Silently retry with headless browser\r\n continue; // Retry immediately with browser\r\n }\r\n \r\n // Non-retryable error - throw immediately\r\n if (!retryableStatuses.includes(statusCode)) {\r\n throw new HttpError(response.request?.res?.responseUrl || url, statusCode);\r\n }\r\n \r\n // Retryable error - continue to next attempt\r\n lastError = new HttpError(response.request?.res?.responseUrl || url, statusCode);\r\n \r\n } catch (error: any) {\r\n // Already formatted HttpError - rethrow if non-retryable\r\n if (error.code === 'HTTP_ERROR') {\r\n const httpError = error as HttpError;\r\n if (!retryableStatuses.includes(httpError.statusCode)) {\r\n throw error;\r\n }\r\n lastError = error;\r\n } else {\r\n // Network error (connection failed, timeout, DNS error, etc.)\r\n lastError = new NetworkError(url, error);\r\n }\r\n \r\n // Don't retry on last attempt\r\n if (attempt === maxRetries) break;\r\n }\r\n \r\n // Wait before retry (exponential backoff)\r\n if (attempt < maxRetries) {\r\n const delay = retryDelay * Math.pow(2, attempt);\r\n await new Promise(resolve => setTimeout(resolve, delay));\r\n }\r\n }\r\n \r\n // All retries exhausted - throw last error\r\n throw lastError!;\r\n}\r\n","import { Config } from '@/types/config';\r\nimport { fetchUrl } from '@/utils/http-client';\r\nimport { HttpError } from '@/errors/network-errors';\r\n\r\nexport interface SitemapAccessIssue {\r\n url: string;\r\n statusCode: number;\r\n error: string;\r\n}\r\n\r\nexport interface DiscoveryResult {\r\n sitemaps: string[];\r\n source: 'standard-path' | 'robots-txt' | 'none';\r\n accessIssues: SitemapAccessIssue[];\r\n}\r\n\r\n/**\r\n * Attempts to find sitemaps at standard paths (/sitemap.xml, /sitemap_index.xml, /sitemap-index.xml).\r\n * Tries all paths concurrently for fast discovery.\r\n * \r\n * @param baseUrl - The base URL of the website (origin only)\r\n * @param config - Configuration object containing timeout and verbose settings\r\n * @returns Object containing found sitemaps and any access issues (401/403 errors)\r\n */\r\nasync function tryStandardPaths(\r\n baseUrl: string,\r\n config: Config\r\n): Promise<{ sitemaps: string[]; issues: SitemapAccessIssue[] }> {\r\n const baseDomain = new URL(baseUrl).origin;\r\n const accessIssues: SitemapAccessIssue[] = [];\r\n \r\n const standardPaths = [\r\n '/sitemap.xml',\r\n '/sitemap_index.xml',\r\n '/sitemap-index.xml'\r\n ];\r\n \r\n // Try all standard paths concurrently\r\n const results = await Promise.allSettled(\r\n standardPaths.map(async (path) => {\r\n const sitemapUrl = `${baseDomain}${path}`;\r\n \r\n try {\r\n const result = await fetchUrl(sitemapUrl, { \r\n timeout: config.timeout,\r\n maxRetries: 0 // Don't retry on standard paths - fail fast\r\n });\r\n \r\n if (result.statusCode === 200) {\r\n if (config.verbose) {\r\n console.log(`โœ“ Found sitemap at: ${sitemapUrl}`);\r\n }\r\n return { found: true, url: sitemapUrl };\r\n }\r\n return { found: false };\r\n } catch (error) {\r\n if (error instanceof HttpError) {\r\n // Track 401/403 as access issues\r\n if (error.statusCode === 401 || error.statusCode === 403) {\r\n accessIssues.push({\r\n url: sitemapUrl,\r\n statusCode: error.statusCode,\r\n error: error.statusCode === 401 ? 'Unauthorized' : 'Access Denied'\r\n });\r\n \r\n if (config.verbose) {\r\n console.log(`โš  Access denied: ${sitemapUrl} (${error.statusCode})`);\r\n }\r\n } else if (config.verbose) {\r\n console.log(`โœ— Not found: ${sitemapUrl} (${error.statusCode})`);\r\n }\r\n } else if (config.verbose) {\r\n console.log(`โœ— Not found: ${sitemapUrl}`);\r\n }\r\n return { found: false };\r\n }\r\n })\r\n );\r\n \r\n // Find the first successful result\r\n for (const result of results) {\r\n if (result.status === 'fulfilled' && result.value.found) {\r\n return { sitemaps: [result.value.url!], issues: accessIssues };\r\n }\r\n }\r\n \r\n if (config.verbose) {\r\n console.log('No sitemap found at standard paths');\r\n }\r\n \r\n return { sitemaps: [], issues: accessIssues };\r\n}\r\n\r\n/**\r\n * Parses robots.txt file to extract sitemap URLs from 'Sitemap:' directives.\r\n * Validates that extracted URLs are valid before returning them.\r\n * \r\n * @param baseUrl - The base URL of the website (origin only)\r\n * @param config - Configuration object containing timeout and verbose settings\r\n * @returns Array of sitemap URLs found in robots.txt, or empty array if none found\r\n */\r\nasync function parseRobotsTxt(\r\n baseUrl: string,\r\n config: Config\r\n): Promise<string[]> {\r\n const robotsUrl = `${new URL(baseUrl).origin}/robots.txt`;\r\n \r\n try {\r\n const result = await fetchUrl(robotsUrl, {\r\n timeout: config.timeout,\r\n maxRetries: 1\r\n });\r\n \r\n const lines = result.content.split('\\n');\r\n const sitemaps: string[] = [];\r\n \r\n for (const line of lines) {\r\n const match = line.match(/^Sitemap:\\s*(.+)$/i);\r\n if (match) {\r\n const sitemapUrl = match[1].trim();\r\n \r\n try {\r\n new URL(sitemapUrl);\r\n sitemaps.push(sitemapUrl);\r\n } catch {\r\n if (config.verbose) {\r\n console.warn(`Invalid sitemap URL in robots.txt: ${sitemapUrl}`);\r\n }\r\n }\r\n }\r\n }\r\n \r\n if (config.verbose && sitemaps.length > 0) {\r\n console.log(`Found ${sitemaps.length} sitemap(s) in robots.txt`);\r\n }\r\n \r\n return sitemaps;\r\n \r\n } catch (error) {\r\n if (config.verbose) {\r\n console.log(`No robots.txt found at ${robotsUrl}`);\r\n }\r\n return [];\r\n }\r\n}\r\n\r\n/**\r\n * Determines if XML content is a sitemap index (contains references to other sitemaps).\r\n * Handles both standard <sitemapindex> format and malformed <urlset> format.\r\n * \r\n * For malformed indices (using <urlset> instead of <sitemapindex>), uses a heuristic:\r\n * checks if the majority of the first 5 URLs end in .xml or contain 'sitemap'.\r\n * \r\n * @param xmlContent - Raw XML content of the sitemap\r\n * @returns true if content is a sitemap index, false if it's a regular sitemap\r\n */\r\nfunction isSitemapIndex(xmlContent: string): boolean {\r\n // Check for proper sitemapindex format\r\n if (xmlContent.includes('<sitemapindex')) {\r\n return true;\r\n }\r\n \r\n // Check for malformed format: urlset containing sitemap URLs\r\n // This is a heuristic - if we see multiple URLs ending in .xml or containing 'sitemap'\r\n // within the first few URL entries, it's likely a malformed sitemap index\r\n if (xmlContent.includes('<urlset')) {\r\n const urlBlockRegex = /<url[^>]*>.*?<loc>([^<]+)<\\/loc>.*?<\\/url>/gs;\r\n const matches = Array.from(xmlContent.matchAll(urlBlockRegex));\r\n \r\n // Check first 5 URLs (or all if less than 5)\r\n const samplesToCheck = Math.min(5, matches.length);\r\n let sitemapLikeCount = 0;\r\n \r\n for (let i = 0; i < samplesToCheck; i++) {\r\n const url = matches[i][1].trim().toLowerCase();\r\n if (url.includes('sitemap') || url.endsWith('.xml')) {\r\n sitemapLikeCount++;\r\n }\r\n }\r\n \r\n // If majority of sampled URLs look like sitemaps, treat as malformed index\r\n return sitemapLikeCount > samplesToCheck / 2;\r\n }\r\n \r\n return false;\r\n}\r\n\r\n/**\r\n * Extracts sitemap URLs from a sitemap index file.\r\n * Handles both standard and malformed formats:\r\n * 1. Standard: <sitemapindex><sitemap><loc>...</loc></sitemap></sitemapindex>\r\n * 2. Malformed: <urlset><url><loc>sitemap.xml</loc></url></urlset>\r\n * \r\n * For malformed indices, only extracts URLs that look like sitemaps (contain 'sitemap' or end in .xml).\r\n * Validates all URLs before returning them.\r\n * \r\n * @param xmlContent - Raw XML content of the sitemap index\r\n * @returns Array of valid sitemap URLs extracted from the index\r\n */\r\nfunction extractSitemapIndexUrls(xmlContent: string): string[] {\r\n const urls: string[] = [];\r\n \r\n // For proper sitemapindex format: extract from <sitemap> blocks only\r\n if (xmlContent.includes('<sitemapindex')) {\r\n const sitemapBlockRegex = /<sitemap[^>]*>(.*?)<\\/sitemap>/gs;\r\n let sitemapMatch;\r\n \r\n while ((sitemapMatch = sitemapBlockRegex.exec(xmlContent)) !== null) {\r\n const locMatch = /<loc>([^<]+)<\\/loc>/i.exec(sitemapMatch[1]);\r\n if (locMatch) {\r\n const url = locMatch[1].trim();\r\n try {\r\n new URL(url);\r\n urls.push(url);\r\n } catch {\r\n // Invalid URL - skip\r\n }\r\n }\r\n }\r\n } else {\r\n // For malformed sitemap (urlset format but contains sitemap URLs)\r\n // Extract all <loc> tags from <url> blocks and check if they look like sitemaps\r\n const urlBlockRegex = /<url[^>]*>(.*?)<\\/url>/gs;\r\n let urlMatch;\r\n \r\n while ((urlMatch = urlBlockRegex.exec(xmlContent)) !== null) {\r\n const locMatch = /<loc>([^<]+)<\\/loc>/i.exec(urlMatch[1]);\r\n if (locMatch) {\r\n const url = locMatch[1].trim();\r\n \r\n // Check if this URL looks like a sitemap (ends with .xml or contains 'sitemap')\r\n if (url.toLowerCase().includes('sitemap') || url.toLowerCase().endsWith('.xml')) {\r\n try {\r\n new URL(url);\r\n urls.push(url);\r\n } catch {\r\n // Invalid URL - skip\r\n }\r\n }\r\n }\r\n }\r\n }\r\n \r\n return urls;\r\n}\r\n\r\n/**\r\n * Recursively discovers all sitemaps by following sitemap index references.\r\n * Processes sitemaps in batches for performance, avoiding duplicate processing.\r\n * \r\n * Algorithm:\r\n * 1. Fetch each sitemap URL\r\n * 2. If it's a sitemap index, extract child URLs and add to processing queue\r\n * 3. If it's a regular sitemap, add to final results\r\n * 4. Repeat until all sitemaps are processed or limit reached (1000 max)\r\n * \r\n * @param initialSitemaps - Array of sitemap URLs to start discovery from\r\n * @param config - Configuration object containing timeout, retry, and concurrency settings\r\n * @returns Array of all discovered regular sitemap URLs (excludes indices)\r\n */\r\nasync function discoverAllSitemaps(\r\n initialSitemaps: string[],\r\n config: Config\r\n): Promise<string[]> {\r\n const finalSitemaps: string[] = [];\r\n const toProcess = [...initialSitemaps];\r\n const processed = new Set<string>();\r\n const inaccessible = new Set<string>();\r\n const BATCH_SIZE = config.discoveryConcurrency || 50;\r\n \r\n while (toProcess.length > 0) {\r\n // Take a batch of URLs to process concurrently\r\n const batch = toProcess.splice(0, Math.min(BATCH_SIZE, toProcess.length));\r\n \r\n // Process batch and collect results atomically\r\n const batchResults = await Promise.all(batch.map(async (sitemapUrl) => {\r\n if (processed.has(sitemapUrl)) {\r\n if (config.verbose) {\r\n console.warn(`Skipping duplicate sitemap: ${sitemapUrl}`);\r\n }\r\n return { type: 'skip' as const };\r\n }\r\n \r\n processed.add(sitemapUrl);\r\n \r\n try {\r\n const result = await fetchUrl(sitemapUrl, {\r\n timeout: config.timeout,\r\n maxRetries: 2\r\n });\r\n \r\n if (isSitemapIndex(result.content)) {\r\n if (config.verbose) {\r\n console.log(`Found sitemap index: ${sitemapUrl}`);\r\n }\r\n \r\n const childUrls = extractSitemapIndexUrls(result.content);\r\n \r\n if (config.verbose) {\r\n console.log(` โ””โ”€ Contains ${childUrls.length} child sitemap(s)`);\r\n }\r\n \r\n return { type: 'index' as const, childUrls };\r\n } else {\r\n if (config.verbose) {\r\n console.log(`โœ“ Discovered sitemap: ${sitemapUrl}`);\r\n }\r\n \r\n return { type: 'sitemap' as const, url: sitemapUrl };\r\n }\r\n \r\n } catch (error) {\r\n inaccessible.add(sitemapUrl);\r\n \r\n if (config.verbose) {\r\n const message = error instanceof Error ? error.message : String(error);\r\n console.warn(`Failed to fetch sitemap ${sitemapUrl}: ${message}`);\r\n }\r\n \r\n return { type: 'failed' as const };\r\n }\r\n }));\r\n \r\n // Process results atomically after all promises complete\r\n for (const result of batchResults) {\r\n if (result.type === 'index') {\r\n toProcess.push(...result.childUrls);\r\n } else if (result.type === 'sitemap') {\r\n finalSitemaps.push(result.url);\r\n }\r\n }\r\n \r\n // Safety check\r\n if (processed.size > 1000) {\r\n console.warn(`โš ๏ธ Processed over 1000 sitemap URLs. Stopping to prevent excessive requests.`);\r\n break;\r\n }\r\n }\r\n \r\n if (finalSitemaps.length === 0 && inaccessible.size > 0) {\r\n console.warn(`\\nโš ๏ธ All ${inaccessible.size} sitemap(s) were inaccessible`);\r\n console.warn(`Common causes: 403/404 errors, network issues, or bot protection`);\r\n }\r\n \r\n return finalSitemaps;\r\n}\r\n\r\n/**\r\n * Main sitemap discovery function. Uses multiple strategies to find sitemaps for a website.\r\n * \r\n * Strategy 1: Check robots.txt for 'Sitemap:' directives (preferred method)\r\n * Strategy 2: Try standard paths (/sitemap.xml, /sitemap_index.xml, /sitemap-index.xml)\r\n * \r\n * For each strategy, recursively follows sitemap indices to discover all sitemaps.\r\n * Returns immediately when sitemaps are found via any strategy.\r\n * \r\n * Note: Axios automatically follows redirects (e.g., www vs non-www, HTTP to HTTPS),\r\n * so domain variants are handled transparently.\r\n * \r\n * @param baseUrl - The base URL of the website to analyze (e.g., 'https://example.com')\r\n * @param config - Configuration object containing timeout, retry, verbosity, and concurrency settings\r\n * @returns DiscoveryResult containing found sitemaps, discovery source, and any access issues\r\n */\r\nexport async function discoverSitemaps(\r\n baseUrl: string,\r\n config: Config\r\n): Promise<DiscoveryResult> {\r\n const normalizedUrl = new URL(baseUrl).origin;\r\n \r\n // Strategy 1: Try robots.txt first\r\n if (config.verbose) {\r\n console.log('Checking robots.txt for sitemap directives...');\r\n }\r\n \r\n const robotsSitemaps = await parseRobotsTxt(normalizedUrl, config);\r\n if (robotsSitemaps.length > 0) {\r\n const sitemaps = await discoverAllSitemaps(robotsSitemaps, config);\r\n // Even if all sitemaps from robots.txt are inaccessible (resulting in an empty array),\r\n // treat robots.txt as the authoritative source and do not fall back to standard paths.\r\n return {\r\n sitemaps,\r\n source: 'robots-txt',\r\n accessIssues: []\r\n };\r\n }\r\n \r\n // Strategy 2: Try standard paths as fallback\r\n if (config.verbose) {\r\n console.log('Trying standard sitemap paths...');\r\n }\r\n \r\n const { sitemaps: standardSitemaps, issues } = await tryStandardPaths(normalizedUrl, config);\r\n if (standardSitemaps.length > 0) {\r\n const sitemaps = await discoverAllSitemaps(standardSitemaps, config);\r\n if (sitemaps.length > 0) {\r\n return {\r\n sitemaps,\r\n source: 'standard-path',\r\n accessIssues: []\r\n };\r\n }\r\n\r\n // Standard sitemap paths were found but all sitemaps were inaccessible\r\n return {\r\n sitemaps: [],\r\n source: 'standard-path',\r\n accessIssues: issues\r\n };\r\n }\r\n \r\n // No sitemaps found\r\n return {\r\n sitemaps: [],\r\n source: 'none',\r\n accessIssues: issues\r\n };\r\n}\r\n","import { XMLParser, XMLValidator } from 'fast-xml-parser';\r\n\r\n// Module-level constants for performance optimization\r\nconst VALID_CHANGEFREQ = new Set([\r\n 'always',\r\n 'hourly',\r\n 'daily',\r\n 'weekly',\r\n 'monthly',\r\n 'yearly',\r\n 'never',\r\n]);\r\n\r\nexport interface UrlEntry {\r\n loc: string; // Required: URL location\r\n lastmod?: string; // Optional: Last modification date\r\n changefreq?: string; // Optional: Change frequency\r\n priority?: number; // Optional: Priority (0.0-1.0)\r\n source: string; // Which sitemap this came from\r\n extractedAt?: string; // ISO timestamp of extraction\r\n}\r\n\r\nexport interface ParseResult {\r\n urls: UrlEntry[]; // Successfully parsed URLs\r\n errors: string[]; // Parsing errors/warnings\r\n totalCount: number; // Total URLs parsed\r\n sitemapUrl: string; // Source sitemap URL\r\n}\r\n\r\nconst parser = new XMLParser({\r\n ignoreAttributes: false,\r\n attributeNamePrefix: '@_',\r\n textNodeName: '_text',\r\n parseAttributeValue: true,\r\n trimValues: true,\r\n allowBooleanAttributes: true,\r\n parseTagValue: false, // Keep values as strings for validation\r\n});\r\n\r\nfunction extractUrls(parsedXml: any, sitemapUrl: string): UrlEntry[] {\r\n const urls: UrlEntry[] = [];\r\n\r\n // Handle urlset format\r\n if (parsedXml.urlset) {\r\n // Normalize to array (single <url> vs multiple)\r\n const urlNodes = Array.isArray(parsedXml.urlset.url)\r\n ? parsedXml.urlset.url\r\n : [parsedXml.urlset.url];\r\n\r\n // Use traditional for-loop for better performance\r\n for (let i = 0; i < urlNodes.length; i++) {\r\n const node = urlNodes[i];\r\n // Skip entries without loc field\r\n if (!node || !node.loc) {\r\n continue;\r\n }\r\n\r\n urls.push({\r\n loc: node.loc,\r\n lastmod: node.lastmod,\r\n changefreq: node.changefreq,\r\n priority: node.priority ? parseFloat(node.priority) : undefined,\r\n source: sitemapUrl,\r\n });\r\n }\r\n }\r\n\r\n return urls;\r\n}\r\n\r\nexport async function parseSitemap(\r\n xml: string,\r\n sitemapUrl: string\r\n): Promise<ParseResult> {\r\n const errors: string[] = [];\r\n\r\n try {\r\n // Validate XML first\r\n const validationResult = XMLValidator.validate(xml);\r\n if (validationResult !== true) {\r\n const validationError = typeof validationResult === 'object'\r\n ? validationResult.err.msg\r\n : 'Invalid XML';\r\n return {\r\n urls: [],\r\n errors: [\r\n `[${sitemapUrl}] XML parsing failed: ${validationError}`,\r\n ],\r\n totalCount: 0,\r\n sitemapUrl,\r\n };\r\n }\r\n\r\n // Parse XML\r\n const parsed = parser.parse(xml);\r\n\r\n // Extract URLs\r\n const urls = extractUrls(parsed, sitemapUrl);\r\n\r\n // Validate extracted URLs\r\n const validUrls: UrlEntry[] = [];\r\n for (const entry of urls) {\r\n try {\r\n // Validate URL format\r\n new URL(entry.loc);\r\n\r\n // Validate priority range\r\n if (entry.priority !== undefined) {\r\n if (entry.priority < 0 || entry.priority > 1) {\r\n errors.push(\r\n `Invalid priority ${entry.priority} for ${entry.loc} - clamping to 0-1`\r\n );\r\n entry.priority = Math.max(0, Math.min(1, entry.priority));\r\n }\r\n }\r\n\r\n // Validate changefreq (using Set for O(1) lookup)\r\n if (entry.changefreq) {\r\n if (!VALID_CHANGEFREQ.has(entry.changefreq.toLowerCase())) {\r\n errors.push(\r\n `Invalid changefreq \"${entry.changefreq}\" for ${entry.loc}`\r\n );\r\n entry.changefreq = undefined;\r\n }\r\n }\r\n\r\n validUrls.push(entry);\r\n } catch (urlError) {\r\n errors.push(`Invalid URL format: ${entry.loc}`);\r\n }\r\n }\r\n\r\n return {\r\n urls: validUrls,\r\n errors,\r\n totalCount: validUrls.length,\r\n sitemapUrl,\r\n };\r\n } catch (parseError) {\r\n // XML parsing failed completely\r\n const errorMsg = parseError instanceof Error ? parseError.message : String(parseError);\r\n return {\r\n urls: [],\r\n errors: [\r\n `[${sitemapUrl}] XML parsing failed: ${errorMsg}`,\r\n ],\r\n totalCount: 0,\r\n sitemapUrl,\r\n };\r\n }\r\n}\r\n","/**\r\n * Batch Processing Utilities\r\n * Provides array chunking and concurrent batch processing for performance optimization\r\n */\r\n\r\n/**\r\n * Split array into chunks of specified size\r\n * @param array - Array to split into chunks\r\n * @param chunkSize - Size of each chunk\r\n * @returns Array of chunks\r\n */\r\nexport function chunkArray<T>(array: T[], chunkSize: number): T[][] {\r\n const chunks: T[][] = [];\r\n for (let i = 0; i < array.length; i += chunkSize) {\r\n chunks.push(array.slice(i, i + chunkSize));\r\n }\r\n return chunks;\r\n}\r\n\r\n/**\r\n * Process items in batches with controlled concurrency\r\n * Uses a proper worker pool that maintains concurrency as items complete\r\n * @param items - Array of items to process\r\n * @param concurrency - Maximum number of concurrent operations\r\n * @param processor - Async function to process each item\r\n * @param onProgress - Optional progress callback (completed, total)\r\n * @returns Array of results from processing all items\r\n */\r\nexport async function processInBatches<T, R>(\r\n items: T[],\r\n concurrency: number,\r\n processor: (item: T) => Promise<R>,\r\n onProgress?: (completed: number, total: number) => void\r\n): Promise<R[]> {\r\n const results: R[] = new Array(items.length);\r\n let completed = 0;\r\n let currentIndex = 0;\r\n const errors: Array<{index: number; error: any}> = [];\r\n \r\n // Create worker pool\r\n const workers = Array(Math.min(concurrency, items.length))\r\n .fill(null)\r\n .map(async () => {\r\n while (currentIndex < items.length) {\r\n const index = currentIndex++;\r\n const item = items[index];\r\n \r\n try {\r\n results[index] = await processor(item);\r\n } catch (error) {\r\n // Store error but don't stop worker - let it continue processing\r\n errors.push({ index, error });\r\n // Set a placeholder result (will be handled by caller)\r\n results[index] = null as any;\r\n }\r\n \r\n completed++;\r\n if (onProgress) {\r\n onProgress(completed, items.length);\r\n }\r\n }\r\n });\r\n \r\n await Promise.all(workers);\r\n \r\n // If there were errors, throw the first one for backward compatibility\r\n // but only after all work is done\r\n if (errors.length > 0) {\r\n console.warn(`Processed ${items.length} items with ${errors.length} errors`);\r\n }\r\n \r\n return results;\r\n}\r\n","import { Config } from '@/types/config';\r\nimport { UrlEntry, parseSitemap } from '@/core/parser';\r\nimport { fetchUrl } from '@/utils/http-client';\r\nimport { processInBatches } from '@/utils/batch-processor';\r\n\r\nexport interface ExtractionResult {\r\n allUrls: UrlEntry[]; // All URLs from all sitemaps\r\n sitemapsProcessed: number; // Number of sitemaps successfully parsed\r\n sitemapsFailed: number; // Number of sitemaps that failed\r\n totalUrls: number; // Total URLs extracted\r\n errors: string[]; // All errors collected\r\n}\r\n\r\nexport async function extractAllUrls(\r\n sitemapUrls: string[],\r\n config: Config,\r\n onProgress?: (completed: number, total: number) => void\r\n): Promise<ExtractionResult> {\r\n const allUrls: UrlEntry[] = [];\r\n const allErrors: string[] = [];\r\n let sitemapsProcessed = 0;\r\n let sitemapsFailed = 0;\r\n\r\n if (config.verbose) {\r\n console.log(`\\nExtracting URLs from ${sitemapUrls.length} sitemap(s)...`);\r\n }\r\n\r\n // Process sitemaps in parallel with configurable concurrency\r\n const CONCURRENCY = config.parsingConcurrency || 50; // Optimized for network-bound I/O\r\n \r\n if (!config.silent && config.verbose) {\r\n console.log(`Using parsing concurrency: ${CONCURRENCY}`);\r\n }\r\n \r\n const results = await processInBatches(\r\n sitemapUrls, \r\n CONCURRENCY, \r\n async (sitemapUrl) => {\r\n try {\r\n if (config.verbose) {\r\n console.log(`Extracting URLs from: ${sitemapUrl}`);\r\n }\r\n\r\n // Fetch sitemap content\r\n const response = await fetchUrl(sitemapUrl, {\r\n timeout: 10, // Fast timeout for sitemaps\r\n maxRetries: 0, // No retries - fail fast\r\n disableBrowserFallback: true // Don't use browser for bulk parsing\r\n });\r\n\r\n // Parse sitemap XML\r\n const parseResult = await parseSitemap(response.content, sitemapUrl);\r\n\r\n // Add extraction timestamp to each URL (optimized: single timestamp for batch)\r\n const extractedAt = new Date().toISOString();\r\n parseResult.urls.forEach(url => {\r\n url.extractedAt = extractedAt;\r\n });\r\n\r\n if (config.verbose) {\r\n console.log(` โœ“ Extracted ${parseResult.urls.length} URLs from ${sitemapUrl}`);\r\n }\r\n\r\n return {\r\n success: true,\r\n urls: parseResult.urls,\r\n errors: parseResult.errors,\r\n };\r\n } catch (error) {\r\n const errorMsg = `Failed to process ${sitemapUrl}: ${\r\n error instanceof Error ? error.message : String(error)\r\n }`;\r\n\r\n if (config.verbose) {\r\n console.error(` โœ— ${errorMsg}`);\r\n }\r\n\r\n return {\r\n success: false,\r\n urls: [],\r\n errors: [errorMsg],\r\n };\r\n }\r\n },\r\n onProgress // Pass progress callback to batch processor\r\n );\r\n\r\n // Aggregate results\r\n for (const result of results) {\r\n if (result.success) {\r\n sitemapsProcessed++;\r\n allUrls.push(...result.urls);\r\n } else {\r\n sitemapsFailed++;\r\n }\r\n allErrors.push(...result.errors);\r\n }\r\n\r\n if (config.verbose) {\r\n console.log(`\\nExtraction complete:`);\r\n console.log(` - Sitemaps processed: ${sitemapsProcessed}`);\r\n console.log(` - Sitemaps failed: ${sitemapsFailed}`);\r\n console.log(` - Total URLs: ${allUrls.length}`);\r\n console.log(` - Errors: ${allErrors.length}`);\r\n }\r\n\r\n return {\r\n allUrls,\r\n sitemapsProcessed,\r\n sitemapsFailed,\r\n totalUrls: allUrls.length,\r\n errors: allErrors,\r\n };\r\n}\r\n","import { UrlEntry } from '@/core/parser';\r\n\r\nexport interface ConsolidatedResult {\r\n uniqueUrls: UrlEntry[]; // Deduplicated URLs\r\n totalInputUrls: number; // Original count before deduplication\r\n duplicatesRemoved: number; // Number of duplicates removed\r\n duplicateGroups?: DuplicateGroup[]; // Optional: groups of duplicates for debugging\r\n}\r\n\r\nexport interface DuplicateGroup {\r\n url: string; // The canonical URL\r\n count: number; // How many times it appeared\r\n sources: string[]; // Which sitemaps contained it\r\n}\r\n\r\nexport function normalizeUrl(url: string): string {\r\n try {\r\n const parsed = new URL(url);\r\n\r\n // Remove trailing slash\r\n let pathname = parsed.pathname;\r\n if (pathname.endsWith('/') && pathname !== '/') {\r\n pathname = pathname.slice(0, -1);\r\n }\r\n\r\n // Sort query parameters alphabetically\r\n const params = Array.from(parsed.searchParams.entries()).sort(([a], [b]) =>\r\n a.localeCompare(b)\r\n );\r\n const sortedParams = new URLSearchParams(params);\r\n\r\n // Reconstruct URL\r\n return `${parsed.protocol}//${parsed.host}${pathname}${\r\n sortedParams.toString() ? '?' + sortedParams.toString() : ''\r\n }${parsed.hash}`;\r\n } catch {\r\n // If URL parsing fails, use original\r\n return url;\r\n }\r\n}\r\n\r\nfunction mergeUrlEntries(entries: UrlEntry[]): UrlEntry {\r\n if (entries.length === 1) return entries[0];\r\n\r\n // Use the first entry as base\r\n const merged: UrlEntry = { ...entries[0] };\r\n\r\n // Merge sources\r\n const sources = entries.map((e) => e.source);\r\n merged.source = sources.join(', ');\r\n\r\n // Use most recent lastmod\r\n const lastmods = entries\r\n .map((e) => e.lastmod)\r\n .filter((lm): lm is string => !!lm)\r\n .map((lm) => new Date(lm).getTime())\r\n .sort((a, b) => b - a);\r\n\r\n if (lastmods.length > 0) {\r\n merged.lastmod = new Date(lastmods[0]).toISOString();\r\n }\r\n\r\n // Use highest priority\r\n const priorities = entries\r\n .map((e) => e.priority)\r\n .filter((p): p is number => p !== undefined);\r\n\r\n if (priorities.length > 0) {\r\n merged.priority = Math.max(...priorities);\r\n }\r\n\r\n // Use most frequent changefreq (or first if tie)\r\n const changefreqs = entries\r\n .map((e) => e.changefreq)\r\n .filter((cf): cf is string => !!cf);\r\n\r\n if (changefreqs.length > 0) {\r\n const counts = new Map<string, number>();\r\n for (const cf of changefreqs) {\r\n counts.set(cf, (counts.get(cf) || 0) + 1);\r\n }\r\n const sorted = Array.from(counts.entries()).sort((a, b) => b[1] - a[1]);\r\n merged.changefreq = sorted[0][0];\r\n }\r\n\r\n // Use most recent extractedAt\r\n const extractedAts = entries\r\n .map((e) => e.extractedAt)\r\n .filter((ea): ea is string => !!ea)\r\n .map((ea) => new Date(ea).getTime())\r\n .sort((a, b) => b - a);\r\n\r\n if (extractedAts.length > 0) {\r\n merged.extractedAt = new Date(extractedAts[0]).toISOString();\r\n }\r\n\r\n return merged;\r\n}\r\n\r\nexport function consolidateUrls(\r\n urls: UrlEntry[],\r\n verbose: boolean = false\r\n): ConsolidatedResult {\r\n const totalInputUrls = urls.length;\r\n\r\n if (verbose) {\r\n console.log(`\\nConsolidating ${urls.length} URL(s)...`);\r\n }\r\n\r\n // Group by normalized URL\r\n const urlMap = new Map<string, UrlEntry[]>();\r\n\r\n for (const entry of urls) {\r\n const normalized = normalizeUrl(entry.loc);\r\n if (!urlMap.has(normalized)) {\r\n urlMap.set(normalized, []);\r\n }\r\n urlMap.get(normalized)!.push(entry);\r\n }\r\n\r\n // Merge duplicates\r\n const uniqueUrls: UrlEntry[] = [];\r\n const duplicateGroups: DuplicateGroup[] = [];\r\n\r\n for (const [normalized, entries] of urlMap.entries()) {\r\n const merged = mergeUrlEntries(entries);\r\n uniqueUrls.push(merged);\r\n\r\n if (entries.length > 1) {\r\n duplicateGroups.push({\r\n url: normalized,\r\n count: entries.length,\r\n sources: entries.map((e) => e.source),\r\n });\r\n }\r\n }\r\n\r\n if (verbose) {\r\n console.log(`Consolidation complete:`);\r\n console.log(` - Input URLs: ${totalInputUrls}`);\r\n console.log(` - Unique URLs: ${uniqueUrls.length}`);\r\n console.log(` - Duplicates removed: ${totalInputUrls - uniqueUrls.length}`);\r\n\r\n if (duplicateGroups.length > 0) {\r\n console.log(`\\nTop duplicates:`);\r\n const top5 = duplicateGroups\r\n .sort((a, b) => b.count - a.count)\r\n .slice(0, 5);\r\n\r\n for (const group of top5) {\r\n console.log(` - ${group.url} (${group.count} times)`);\r\n }\r\n }\r\n }\r\n\r\n return {\r\n uniqueUrls,\r\n totalInputUrls,\r\n duplicatesRemoved: totalInputUrls - uniqueUrls.length,\r\n duplicateGroups,\r\n };\r\n}\r\n","import { RiskPattern } from '@/core/risk-detector';\r\n\r\nexport const RISK_PATTERNS: RiskPattern[] = [\r\n // Sensitive Parameter Patterns (HIGH)\r\n {\r\n name: 'Authentication Parameter',\r\n category: 'sensitive_params',\r\n severity: 'high',\r\n regex: /[?&](token|auth|key|password|secret|apikey|session|credentials)=/i,\r\n description: 'Query parameter may contain sensitive authentication data'\r\n },\r\n {\r\n name: 'Debug Parameter',\r\n category: 'sensitive_params',\r\n severity: 'medium',\r\n regex: /[?&](debug|trace|verbose|test_mode)=/i,\r\n description: 'Query parameter may contain debug or diagnostic flag'\r\n },\r\n \r\n // Protocol Inconsistency Patterns (MEDIUM)\r\n {\r\n name: 'HTTP in HTTPS Site',\r\n category: 'protocol_inconsistency',\r\n severity: 'medium',\r\n regex: /^http:\\/\\//,\r\n description: 'HTTP URL in HTTPS sitemap (potential mixed content)'\r\n },\r\n \r\n // Test/Unfinished Content Patterns (MEDIUM)\r\n // Focuses on obvious test/placeholder patterns, avoiding false positives with legitimate content\r\n {\r\n name: 'Test Content Path',\r\n category: 'test_content',\r\n severity: 'medium',\r\n regex: /\\/(?:test-|demo-|sample-|temp-|temporary-|placeholder-)|\\/(test|demo|sample|temp|temporary|placeholder)(?:\\/|$)/i,\r\n description: 'URL path suggests test, demo, or unfinished content that may not be intended for indexing'\r\n }\r\n];\r\n","import { RiskPattern } from '@/core/risk-detector';\r\n\r\nexport interface DomainValidationOptions {\r\n allowedSubdomains?: string[]; // e.g., ['www', 'blog', 'shop']\r\n}\r\n\r\nfunction escapeRegex(str: string): string {\r\n return str.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\r\n}\r\n\r\nexport function extractRootDomain(hostname: string): string {\r\n const parts = hostname.split('.');\r\n if (parts.length >= 2) {\r\n return parts.slice(-2).join('.');\r\n }\r\n return hostname;\r\n}\r\n\r\nexport function createDomainMismatchPattern(\r\n baseUrl: string,\r\n options?: DomainValidationOptions\r\n): RiskPattern {\r\n const baseDomain = new URL(baseUrl).hostname;\r\n const rootDomain = extractRootDomain(baseDomain);\r\n \r\n // If allowed subdomains specified, create more lenient pattern\r\n if (options?.allowedSubdomains && options.allowedSubdomains.length > 0) {\r\n const escapedRoot = escapeRegex(rootDomain);\r\n const escapedSubdomains = options.allowedSubdomains.map(escapeRegex).join('|');\r\n \r\n // Match URLs that DON'T start with: (allowed-subdomain OR no-subdomain) + root domain\r\n // Pattern: NOT (www.example.com OR blog.example.com OR example.com)\r\n const pattern = `^https?://(?!(?:(?:${escapedSubdomains})\\\\.)?${escapedRoot}(?:/|$))`;\r\n \r\n return {\r\n name: 'Domain Mismatch',\r\n category: 'domain_mismatch',\r\n severity: 'high',\r\n regex: new RegExp(pattern),\r\n description: `URL does not match expected domain or allowed subdomains`\r\n };\r\n }\r\n \r\n // Default: Allow both www and non-www variants of the same root domain\r\n const escapedRoot = escapeRegex(rootDomain);\r\n \r\n // Match URLs that DON'T belong to the root domain (with or without www)\r\n // Pattern: NOT (example.com OR www.example.com)\r\n const pattern = `^https?://(?!(?:www\\\\.)?${escapedRoot}(?:/|$))`;\r\n \r\n return {\r\n name: 'Domain Mismatch',\r\n category: 'domain_mismatch',\r\n severity: 'high',\r\n regex: new RegExp(pattern),\r\n description: `URL does not match expected domain: ${rootDomain} (including www variant)`\r\n };\r\n}\r\n\r\nexport const ENVIRONMENT_PATTERNS: RiskPattern[] = [\r\n {\r\n name: 'Staging Subdomain',\r\n category: 'environment_leakage',\r\n severity: 'high',\r\n regex: /^https?:\\/\\/(staging|stg)\\./i,\r\n description: 'URL uses staging subdomain'\r\n },\r\n {\r\n name: 'Development Subdomain',\r\n category: 'environment_leakage',\r\n severity: 'high',\r\n regex: /^https?:\\/\\/(dev|development)\\./i,\r\n description: 'URL uses development subdomain'\r\n },\r\n {\r\n name: 'QA/Test Subdomain',\r\n category: 'environment_leakage',\r\n severity: 'high',\r\n regex: /^https?:\\/\\/(qa|test|uat|preprod)\\./i,\r\n description: 'URL uses test environment subdomain'\r\n },\r\n {\r\n name: 'Localhost URL',\r\n category: 'environment_leakage',\r\n severity: 'high',\r\n regex: /^https?:\\/\\/(localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0)/,\r\n description: 'URL points to localhost (development environment)'\r\n },\r\n {\r\n name: 'Environment in Path',\r\n category: 'environment_leakage',\r\n severity: 'high',\r\n regex: /^https?:\\/\\/[^/]+\\/(staging|dev|qa|uat|preprod)\\//i,\r\n description: 'URL path contains environment identifier at root level'\r\n }\r\n];\r\n","import { RiskPattern } from '@/core/risk-detector';\r\n\r\nexport const ADMIN_PATH_PATTERNS: RiskPattern[] = [\r\n {\r\n name: 'Admin Path',\r\n category: 'admin_paths',\r\n severity: 'high',\r\n regex: /\\/(admin|administrator)(?:\\/|$|\\?)/i,\r\n description: 'URL contains /admin or /administrator as a path segment'\r\n },\r\n {\r\n name: 'Dashboard Path',\r\n category: 'admin_paths',\r\n severity: 'high',\r\n regex: /\\/dashboard(?:\\/|$|\\?)/i,\r\n description: 'URL contains /dashboard as a path segment'\r\n },\r\n {\r\n name: 'Config Path',\r\n category: 'admin_paths',\r\n severity: 'high',\r\n regex: /\\/(config|configuration)(?:\\/|$|\\?)/i,\r\n description: 'URL contains /config or /configuration as a path segment'\r\n },\r\n {\r\n name: 'Console Path',\r\n category: 'admin_paths',\r\n severity: 'high',\r\n regex: /\\/console(?:\\/|$|\\?)/i,\r\n description: 'URL contains /console as a path segment'\r\n },\r\n {\r\n name: 'Control Panel Path',\r\n category: 'admin_paths',\r\n severity: 'high',\r\n regex: /\\/(cpanel|control-panel)(?:\\/|$|\\?)/i,\r\n description: 'URL contains control panel as a path segment'\r\n }\r\n];\r\n\r\n// Internal content patterns - lower severity as these may be legitimate public-facing content\r\n// that happens to use \"internal\" in the naming (e.g., internal ticket requests, internal forms)\r\nexport const INTERNAL_CONTENT_PATTERNS: RiskPattern[] = [\r\n {\r\n name: 'Internal Content Path',\r\n category: 'internal_content',\r\n severity: 'medium',\r\n regex: /\\/internal\\b/i,\r\n description: 'URL contains /internal path segment - may be internal-only content not intended for public indexing'\r\n }\r\n];\r\n\r\nexport const SENSITIVE_PARAM_PATTERNS: RiskPattern[] = [\r\n {\r\n name: 'Authentication Token Parameter',\r\n category: 'sensitive_params',\r\n severity: 'high',\r\n regex: /[?&](token|auth_token|access_token|api_token)=/i,\r\n description: 'Query parameter may contain authentication token'\r\n },\r\n {\r\n name: 'API Key Parameter',\r\n category: 'sensitive_params',\r\n severity: 'high',\r\n regex: /[?&](apikey|api_key|key)=/i,\r\n description: 'Query parameter may contain API key'\r\n },\r\n {\r\n name: 'Password Parameter',\r\n category: 'sensitive_params',\r\n severity: 'high',\r\n regex: /[?&](password|passwd|pwd)=/i,\r\n description: 'Query parameter may contain password'\r\n },\r\n {\r\n name: 'Secret Parameter',\r\n category: 'sensitive_params',\r\n severity: 'high',\r\n regex: /[?&](secret|client_secret)=/i,\r\n description: 'Query parameter may contain secret value'\r\n },\r\n {\r\n name: 'Session Parameter',\r\n category: 'sensitive_params',\r\n severity: 'high',\r\n regex: /[?&](session|sessionid|sid)=/i,\r\n description: 'Query parameter may contain session identifier'\r\n },\r\n {\r\n name: 'Credentials Parameter',\r\n category: 'sensitive_params',\r\n severity: 'high',\r\n regex: /[?&]credentials=/i,\r\n description: 'Query parameter may contain credentials'\r\n },\r\n {\r\n name: 'Debug Parameter',\r\n category: 'sensitive_params',\r\n severity: 'medium',\r\n regex: /[?&](debug|trace|verbose)=/i,\r\n description: 'Query parameter contains debug or diagnostic flag'\r\n },\r\n {\r\n name: 'Test Mode Parameter',\r\n category: 'sensitive_params',\r\n severity: 'medium',\r\n regex: /[?&](test_mode|test|testing)=/i,\r\n description: 'Query parameter indicates test mode'\r\n }\r\n];\r\n","export function sanitizeUrl(url: string): string {\r\n try {\r\n const parsed = new URL(url);\r\n const sensitiveParams = [\r\n 'token', 'auth', 'auth_token', 'access_token', 'api_token',\r\n 'apikey', 'api_key', 'key',\r\n 'password', 'passwd', 'pwd',\r\n 'secret', 'client_secret',\r\n 'session', 'sessionid', 'sid',\r\n 'credentials'\r\n ];\r\n \r\n for (const param of sensitiveParams) {\r\n if (parsed.searchParams.has(param)) {\r\n parsed.searchParams.set(param, '[REDACTED]');\r\n }\r\n }\r\n \r\n return parsed.toString();\r\n } catch {\r\n // Invalid URL - return as-is\r\n return url;\r\n }\r\n}\r\n\r\nexport function sanitizeUrls(urls: string[]): string[] {\r\n return urls.map(url => sanitizeUrl(url));\r\n}\r\n","import { RiskFinding, RiskCategory, Severity } from '@/core/risk-detector';\r\n\r\nexport interface RiskGroup {\r\n category: RiskCategory;\r\n severity: Severity;\r\n count: number;\r\n rationale: string;\r\n sampleUrls: string[];\r\n recommendedAction: string;\r\n allUrls?: string[]; // Optional: full list for detailed analysis\r\n}\r\n\r\nexport interface RiskGroupingResult {\r\n groups: RiskGroup[];\r\n totalRiskUrls: number;\r\n highSeverityCount: number;\r\n mediumSeverityCount: number;\r\n lowSeverityCount: number;\r\n}\r\n\r\nfunction generateRecommendation(\r\n category: RiskCategory,\r\n _severity: Severity,\r\n count: number\r\n): { rationale: string; recommendedAction: string } {\r\n switch (category) {\r\n case 'environment_leakage':\r\n return {\r\n rationale: `Production sitemap contains ${count} URL(s) from non-production environments (staging, dev, QA, test). This indicates configuration errors or environment leakage.`,\r\n recommendedAction: 'Verify sitemap generation excludes non-production environments. Review deployment configuration and environment filtering rules.'\r\n };\r\n \r\n case 'admin_paths':\r\n return {\r\n rationale: `${count} administrative path(s) detected in public sitemap (admin, dashboard, config). These paths may expose privileged access points.`,\r\n recommendedAction: 'Confirm if admin paths should be publicly indexed. Consider excluding via robots.txt or removing from sitemap. Verify access controls.'\r\n };\r\n \r\n case 'internal_content':\r\n return {\r\n rationale: `${count} URL(s) contain \"internal\" in the path. These may be internal-facing content not intended for public indexing.`,\r\n recommendedAction: 'Review URLs to determine if they should be publicly accessible. Consider excluding internal content from sitemap or adding noindex meta tags.'\r\n };\r\n \r\n case 'test_content':\r\n return {\r\n rationale: `${count} URL(s) contain test/demo/sample identifiers. These may be placeholder or unfinished content not intended for indexing.`,\r\n recommendedAction: 'Review and remove test content from production sitemaps. Verify content is production-ready before including in sitemap.'\r\n };\r\n \r\n case 'sensitive_params':\r\n return {\r\n rationale: `${count} URL(s) contain sensitive query parameters (token, auth, key, password, session). This may expose authentication credentials or debugging flags.`,\r\n recommendedAction: 'Review why sensitive parameters are in sitemap URLs. Remove authentication tokens from URLs. Consider POST requests for sensitive data.'\r\n };\r\n \r\n case 'protocol_inconsistency':\r\n return {\r\n rationale: `${count} URL(s) use HTTP protocol in HTTPS sitemap. This creates mixed content warnings and potential security issues.`,\r\n recommendedAction: 'Update URLs to use HTTPS consistently. Verify SSL certificate coverage. Check for hardcoded HTTP URLs in content.'\r\n };\r\n \r\n case 'domain_mismatch':\r\n return {\r\n rationale: `${count} URL(s) do not match expected base domain. This may indicate external links, CDN URLs, or configuration errors.`,\r\n recommendedAction: 'Verify if external domains are intentional. Review sitemap generation logic. Confirm CDN or subdomain configuration is correct.'\r\n };\r\n \r\n default:\r\n return {\r\n rationale: `${count} URL(s) flagged in category: ${category}`,\r\n recommendedAction: 'Review flagged URLs and determine appropriate action.'\r\n };\r\n }\r\n}\r\n\r\nexport function groupRiskFindings(\r\n findings: RiskFinding[],\r\n maxSampleUrls: number = 5\r\n): RiskGroupingResult {\r\n // Group by category\r\n const categoryMap = new Map<RiskCategory, RiskFinding[]>();\r\n \r\n for (const finding of findings) {\r\n if (!categoryMap.has(finding.category)) {\r\n categoryMap.set(finding.category, []);\r\n }\r\n categoryMap.get(finding.category)!.push(finding);\r\n }\r\n \r\n // Create groups\r\n const groups: RiskGroup[] = [];\r\n \r\n for (const [category, categoryFindings] of categoryMap.entries()) {\r\n // Get unique URLs for this category\r\n const uniqueUrls = Array.from(new Set(categoryFindings.map(f => f.url)));\r\n \r\n // Determine severity (highest severity in category)\r\n const severity = categoryFindings.reduce((highest, finding) => {\r\n const severityOrder: Severity[] = ['low', 'medium', 'high'];\r\n return severityOrder.indexOf(finding.severity) > severityOrder.indexOf(highest)\r\n ? finding.severity\r\n : highest;\r\n }, 'low' as Severity);\r\n \r\n // Select sample URLs\r\n const sampleUrls = uniqueUrls.slice(0, maxSampleUrls);\r\n \r\n // Generate rationale and recommendation\r\n const { rationale, recommendedAction } = generateRecommendation(category, severity, uniqueUrls.length);\r\n \r\n groups.push({\r\n category,\r\n severity,\r\n count: uniqueUrls.length,\r\n rationale,\r\n sampleUrls,\r\n recommendedAction,\r\n allUrls: uniqueUrls\r\n });\r\n }\r\n \r\n // Sort groups by severity (HIGH โ†’ MEDIUM โ†’ LOW)\r\n groups.sort((a, b) => {\r\n const severityOrder: Severity[] = ['high', 'medium', 'low'];\r\n return severityOrder.indexOf(a.severity) - severityOrder.indexOf(b.severity);\r\n });\r\n \r\n // Calculate summary counts\r\n const totalRiskUrls = new Set(findings.map(f => f.url)).size;\r\n const highSeverityCount = groups.filter(g => g.severity === 'high').reduce((sum, g) => sum + g.count, 0);\r\n const mediumSeverityCount = groups.filter(g => g.severity === 'medium').reduce((sum, g) => sum + g.count, 0);\r\n const lowSeverityCount = groups.filter(g => g.severity === 'low').reduce((sum, g) => sum + g.count, 0);\r\n \r\n return {\r\n groups,\r\n totalRiskUrls,\r\n highSeverityCount,\r\n mediumSeverityCount,\r\n lowSeverityCount\r\n };\r\n}\r\n","import { UrlEntry } from '@/core/parser';\r\nimport { Config } from '@/types/config';\r\nimport { RISK_PATTERNS } from '@/core/patterns/risk-patterns';\r\nimport { ENVIRONMENT_PATTERNS, createDomainMismatchPattern } from '@/core/patterns/domain-patterns';\r\nimport { ADMIN_PATH_PATTERNS, SENSITIVE_PARAM_PATTERNS, INTERNAL_CONTENT_PATTERNS } from '@/core/patterns/admin-patterns';\r\nimport { sanitizeUrl } from '@/utils/sanitizer';\r\nimport { groupRiskFindings, RiskGroup } from '@/core/risk-grouper';\r\nimport { chunkArray, processInBatches } from '@/utils/batch-processor';\r\nimport os from 'os';\r\n\r\nexport type RiskCategory = \r\n | 'environment_leakage'\r\n | 'admin_paths'\r\n | 'sensitive_params'\r\n | 'protocol_inconsistency'\r\n | 'domain_mismatch'\r\n | 'test_content'\r\n | 'internal_content';\r\n\r\nexport type Severity = 'high' | 'medium' | 'low';\r\n\r\nexport interface RiskPattern {\r\n name: string;\r\n category: RiskCategory;\r\n severity: Severity;\r\n regex: RegExp;\r\n description: string;\r\n}\r\n\r\nexport interface RiskFinding {\r\n url: string;\r\n category: RiskCategory;\r\n severity: Severity;\r\n pattern: string;\r\n rationale: string;\r\n matchedValue?: string; // Optional: the specific text that matched\r\n}\r\n\r\nexport interface RiskDetectionResult {\r\n findings: RiskFinding[];\r\n groups: RiskGroup[];\r\n totalUrlsAnalyzed: number;\r\n riskUrlCount: number;\r\n cleanUrlCount: number;\r\n highSeverityCount: number;\r\n mediumSeverityCount: number;\r\n lowSeverityCount: number;\r\n processingTimeMs: number;\r\n}\r\n\r\ninterface BatchResult {\r\n findings: RiskFinding[];\r\n urlsProcessed: number;\r\n}\r\n\r\n/**\r\n * Compile accepted patterns from config\r\n */\r\nfunction compileAcceptedPatterns(config: Config): RegExp[] {\r\n const patterns: RegExp[] = [];\r\n \r\n if (config.acceptedPatterns && config.acceptedPatterns.length > 0) {\r\n for (const pattern of config.acceptedPatterns) {\r\n try {\r\n // Convert user-friendly pattern to regex:\r\n // 1. Escape special regex chars except * \r\n // 2. Convert * to .* for wildcard matching\r\n // 3. Ensure pattern matches complete words/segments (not substrings)\r\n let regexPattern = pattern\r\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&') // Escape special chars except *\r\n .replace(/\\*/g, '[^/]*'); // Convert * to [^/]* (anything except /)\r\n \r\n // Add word boundary at the end to ensure it matches complete path segments\r\n if (!regexPattern.endsWith('$') && !regexPattern.includes('(?:')) {\r\n regexPattern = regexPattern + '(?:/|$|\\\\?|#)';\r\n }\r\n \r\n patterns.push(new RegExp(regexPattern, 'i'));\r\n } catch (error) {\r\n if (config.verbose) {\r\n console.warn(`Invalid accepted pattern: ${pattern}`);\r\n }\r\n }\r\n }\r\n }\r\n \r\n return patterns;\r\n}\r\n\r\n/**\r\n * Process a single batch of URLs for risk detection\r\n * (Called in parallel for multiple batches)\r\n */\r\nasync function detectRisksInBatch(\r\n urls: UrlEntry[],\r\n allPatterns: RiskPattern[],\r\n acceptedPatterns: RegExp[],\r\n expectedProtocol: string,\r\n verbose: boolean\r\n): Promise<BatchResult> {\r\n const findings: RiskFinding[] = [];\r\n \r\n for (const urlEntry of urls) {\r\n const url = urlEntry.loc;\r\n \r\n // Check if URL matches accepted patterns (early exit)\r\n let isAccepted = false;\r\n for (const acceptedPattern of acceptedPatterns) {\r\n if (acceptedPattern.test(url)) {\r\n isAccepted = true;\r\n break;\r\n }\r\n }\r\n if (isAccepted) continue;\r\n \r\n // Test each pattern against the URL\r\n for (const pattern of allPatterns) {\r\n // Special handling for protocol inconsistency\r\n if (pattern.category === 'protocol_inconsistency') {\r\n try {\r\n const urlProtocol = new URL(url).protocol;\r\n if (expectedProtocol === 'https:' && urlProtocol === 'http:') {\r\n findings.push({\r\n url,\r\n category: pattern.category,\r\n severity: pattern.severity,\r\n pattern: pattern.name,\r\n rationale: pattern.description,\r\n matchedValue: 'http://'\r\n });\r\n }\r\n } catch (error) {\r\n // Invalid URL - skip\r\n continue;\r\n }\r\n } else {\r\n // Standard regex matching\r\n try {\r\n const match = url.match(pattern.regex);\r\n if (match) {\r\n findings.push({\r\n url: pattern.category === 'sensitive_params' ? sanitizeUrl(url) : url,\r\n category: pattern.category,\r\n severity: pattern.severity,\r\n pattern: pattern.name,\r\n rationale: pattern.description,\r\n matchedValue: match[0]\r\n });\r\n }\r\n } catch (error) {\r\n if (verbose) {\r\n console.error(`Pattern matching failed for ${pattern.name}: ${error instanceof Error ? error.message : String(error)}`);\r\n }\r\n continue;\r\n }\r\n }\r\n }\r\n }\r\n \r\n return { findings, urlsProcessed: urls.length };\r\n}\r\n\r\nexport async function detectRisks(\r\n urls: UrlEntry[],\r\n baseUrl: string,\r\n config: Config\r\n): Promise<RiskDetectionResult> {\r\n const startTime = Date.now();\r\n \r\n // Pre-compile ALL patterns ONCE (not per batch)\r\n const domainPattern = createDomainMismatchPattern(baseUrl);\r\n const allPatterns = [\r\n ...RISK_PATTERNS,\r\n ...ENVIRONMENT_PATTERNS,\r\n ...ADMIN_PATH_PATTERNS,\r\n ...SENSITIVE_PARAM_PATTERNS,\r\n ...INTERNAL_CONTENT_PATTERNS,\r\n domainPattern\r\n ];\r\n \r\n // Compile accepted patterns once\r\n const acceptedPatterns = compileAcceptedPatterns(config);\r\n \r\n // Extract protocol once\r\n let expectedProtocol: string;\r\n try {\r\n expectedProtocol = new URL(baseUrl).protocol;\r\n } catch (error) {\r\n if (config.verbose) {\r\n console.warn(`Invalid base URL: ${baseUrl}, defaulting to https:`);\r\n }\r\n expectedProtocol = 'https:';\r\n }\r\n \r\n // Configure batch processing\r\n const BATCH_SIZE = config.riskDetectionBatchSize || 10000;\r\n const CONCURRENCY = config.riskDetectionConcurrency || Math.max(2, os.cpus().length - 1);\r\n const batches = chunkArray(urls, BATCH_SIZE);\r\n \r\n if (config.verbose) {\r\n console.log(`\\nRisk Detection Configuration:`);\r\n console.log(` - Total URLs: ${urls.length.toLocaleString()}`);\r\n console.log(` - Batch size: ${BATCH_SIZE.toLocaleString()}`);\r\n console.log(` - Concurrency: ${CONCURRENCY}`);\r\n console.log(` - Total batches: ${batches.length}`);\r\n try {\r\n console.log(` - Base domain: ${new URL(baseUrl).hostname}`);\r\n } catch (error) {\r\n console.log(` - Base URL: ${baseUrl}`);\r\n }\r\n if (acceptedPatterns.length > 0) {\r\n console.log(` - Accepted patterns: ${acceptedPatterns.length}`);\r\n }\r\n }\r\n \r\n // Progress tracking\r\n let completedBatches = 0;\r\n const totalBatches = batches.length;\r\n const batchStartTime = Date.now();\r\n \r\n // Process batches in parallel with concurrency limit\r\n const batchResults = await processInBatches(\r\n batches,\r\n CONCURRENCY,\r\n (batch) => detectRisksInBatch(batch, allPatterns, acceptedPatterns, expectedProtocol, config.verbose),\r\n (completed) => {\r\n completedBatches = completed;\r\n const pct = ((completed / totalBatches) * 100).toFixed(1);\r\n const elapsed = (Date.now() - batchStartTime) / 1000;\r\n const urlsProcessed = completed * BATCH_SIZE;\r\n const speed = Math.round(urlsProcessed / elapsed);\r\n const remaining = totalBatches - completed;\r\n const eta = Math.round((remaining * BATCH_SIZE) / speed);\r\n \r\n process.stdout.write(\r\n `\\r\\x1b[K Analyzing batch ${completed}/${totalBatches} (${pct}%) | ETA: ~${eta}s | ${speed.toLocaleString()} URLs/sec`\r\n );\r\n }\r\n );\r\n \r\n // Clear progress line\r\n process.stdout.write('\\r\\x1b[K');\r\n \r\n // Merge results from all batches\r\n const allFindings = batchResults.flatMap(r => r.findings);\r\n \r\n // Group findings\r\n const groupingResult = groupRiskFindings(allFindings);\r\n \r\n const processingTimeMs = Date.now() - startTime;\r\n \r\n if (config.verbose) {\r\n console.log(`\\nRisk Detection Summary:`);\r\n console.log(` - Total URLs analyzed: ${urls.length.toLocaleString()}`);\r\n console.log(` - Risk URLs found: ${groupingResult.totalRiskUrls.toLocaleString()}`);\r\n console.log(` - HIGH severity: ${groupingResult.highSeverityCount}`);\r\n console.log(` - MEDIUM severity: ${groupingResult.mediumSeverityCount}`);\r\n console.log(` - LOW severity: ${groupingResult.lowSeverityCount}`);\r\n console.log(` - Processing time: ${(processingTimeMs / 1000).toFixed(1)}s`);\r\n \r\n if (groupingResult.groups.length > 0) {\r\n console.log(`\\nRisk Categories Found:`);\r\n for (const group of groupingResult.groups) {\r\n console.log(` - ${group.category}: ${group.count} URLs (${group.severity.toUpperCase()})`);\r\n }\r\n }\r\n }\r\n \r\n return {\r\n findings: allFindings,\r\n groups: groupingResult.groups,\r\n totalUrlsAnalyzed: urls.length,\r\n riskUrlCount: groupingResult.totalRiskUrls,\r\n cleanUrlCount: urls.length - groupingResult.totalRiskUrls,\r\n highSeverityCount: groupingResult.highSeverityCount,\r\n mediumSeverityCount: groupingResult.mediumSeverityCount,\r\n lowSeverityCount: groupingResult.lowSeverityCount,\r\n processingTimeMs\r\n };\r\n}\r\n","import type { RiskGroup } from '@/core/risk-grouper';\r\n\r\nexport interface RiskSummaryRequest {\r\n riskGroups: RiskGroup[];\r\n totalUrls: number;\r\n sitemapUrl: string;\r\n processingTime?: number;\r\n}\r\n\r\nexport interface RiskSummary {\r\n overview: string;\r\n keyFindings: string[];\r\n categoryInsights: CategoryInsight[];\r\n severityBreakdown: {\r\n high: number;\r\n medium: number;\r\n low: number;\r\n };\r\n recommendations: string[];\r\n generatedBy: string;\r\n metadata: {\r\n tokensUsed: number;\r\n processingTime: number;\r\n model: string;\r\n };\r\n}\r\n\r\nexport interface CategoryInsight {\r\n category: string;\r\n count: number;\r\n severity: 'high' | 'medium' | 'low';\r\n summary: string;\r\n examples: string[];\r\n allUrls: string[]; // Full list of all URLs in this category\r\n}\r\n\r\nexport function summarizeRisks(request: RiskSummaryRequest): RiskSummary {\r\n const severityBreakdown = {\r\n high: 0,\r\n medium: 0,\r\n low: 0\r\n };\r\n \r\n const categoryInsights: CategoryInsight[] = request.riskGroups.map(group => {\r\n severityBreakdown[group.severity] += group.count;\r\n \r\n const urls = group.allUrls || group.sampleUrls;\r\n \r\n return {\r\n category: group.category,\r\n count: group.count,\r\n severity: group.severity,\r\n summary: group.rationale,\r\n examples: urls.slice(0, 3),\r\n allUrls: urls // Include all URLs for download functionality\r\n };\r\n });\r\n \r\n const totalRisks = request.riskGroups.reduce((sum, g) => sum + g.count, 0);\r\n const overview = totalRisks > 0\r\n ? `Found ${totalRisks} potentially risky URLs across ${request.riskGroups.length} categories in ${request.totalUrls} total URLs.`\r\n : `Analyzed ${request.totalUrls} URLs. No suspicious patterns detected.`;\r\n \r\n const keyFindings: string[] = [];\r\n if (severityBreakdown.high > 0) {\r\n keyFindings.push(`${severityBreakdown.high} high-severity issues require immediate attention`);\r\n }\r\n if (severityBreakdown.medium > 0) {\r\n keyFindings.push(`${severityBreakdown.medium} medium-severity issues should be reviewed`);\r\n }\r\n if (severityBreakdown.low > 0) {\r\n keyFindings.push(`${severityBreakdown.low} low-severity items flagged for awareness`);\r\n }\r\n \r\n return {\r\n overview,\r\n keyFindings,\r\n categoryInsights,\r\n severityBreakdown,\r\n recommendations: [],\r\n generatedBy: 'rule-based analysis',\r\n metadata: {\r\n tokensUsed: 0,\r\n processingTime: request.processingTime || 0,\r\n model: 'pattern-matching'\r\n }\r\n };\r\n}\r\n","import { promises as fs } from 'fs';\r\nimport type { RiskSummary } from '@/summarizer';\r\nimport type { DiscoveryResult } from '@/core/discovery';\r\nimport type { RiskGroup } from '@/core/risk-grouper';\r\nimport type { Config } from '@/types/config';\r\n\r\n// Version is injected at build time by tsup\r\ndeclare const __PACKAGE_VERSION__: string;\r\nconst TOOL_VERSION = typeof __PACKAGE_VERSION__ !== 'undefined' ? __PACKAGE_VERSION__ : 'dev';\r\n\r\nexport interface PhaseTiming {\r\n name: string;\r\n startTime: number;\r\n endTime: number;\r\n duration: number;\r\n}\r\n\r\nexport interface PerformanceMetrics {\r\n totalExecutionTimeMs: number;\r\n phaseTimings: Record<string, number>;\r\n throughput?: {\r\n urlsPerSecond: number;\r\n sitemapsPerSecond: number;\r\n };\r\n resourceUsage?: {\r\n peakMemoryMb?: number;\r\n cpuCoresUsed?: number;\r\n };\r\n}\r\n\r\nexport interface JsonReporterOptions {\r\n pretty?: boolean; // Pretty-print with indentation (default: true)\r\n indent?: number; // Indentation spaces (default: 2)\r\n includeMetadata?: boolean; // Include generation metadata (default: true)\r\n performanceMetrics?: PerformanceMetrics; // Performance timing data\r\n}\r\n\r\nexport interface ParseResult {\r\n totalCount: number;\r\n uniqueUrls: string[];\r\n errors: Error[];\r\n}\r\n\r\ninterface AnalysisMetadata {\r\n baseUrl: string;\r\n analysisTimestamp: string;\r\n toolVersion: string;\r\n executionTimeMs: number;\r\n analysisType: string;\r\n}\r\n\r\ninterface SuspiciousGroup {\r\n category: string;\r\n severity: string;\r\n count: number;\r\n pattern: string;\r\n rationale: string;\r\n sampleUrls: string[];\r\n recommendedAction: string;\r\n}\r\n\r\ninterface SummaryStats {\r\n highSeverityCount: number;\r\n mediumSeverityCount: number;\r\n lowSeverityCount: number;\r\n totalRiskyUrls: number;\r\n overallStatus: 'clean' | 'issues_found' | 'errors';\r\n}\r\n\r\ninterface RiskSummaryData {\r\n overview: string;\r\n keyFindings: string[];\r\n recommendations: string[];\r\n}\r\n\r\ninterface ErrorDetail {\r\n code: string;\r\n message: string;\r\n context?: Record<string, unknown>;\r\n}\r\n\r\ninterface AnalysisResult {\r\n analysisMetadata: AnalysisMetadata;\r\n sitemapsDiscovered: string[];\r\n totalUrlCount: number;\r\n urlsAnalyzed: number;\r\n suspiciousGroups: SuspiciousGroup[];\r\n riskSummary: RiskSummaryData;\r\n summary: SummaryStats;\r\n errors: ErrorDetail[];\r\n}\r\n\r\n/**\r\n * Generate JSON report from analysis results\r\n */\r\nexport function generateJsonReport(\r\n summary: RiskSummary,\r\n discoveryResult: DiscoveryResult,\r\n parseResult: ParseResult,\r\n riskGroups: RiskGroup[],\r\n config: Config,\r\n startTime: number,\r\n options: JsonReporterOptions = {}\r\n): string {\r\n const {\r\n pretty = true,\r\n indent = 2,\r\n performanceMetrics,\r\n } = options;\r\n \r\n const result = buildAnalysisResult(\r\n summary,\r\n discoveryResult,\r\n parseResult,\r\n riskGroups,\r\n config,\r\n startTime\r\n );\r\n \r\n const jsonOutput = transformToJsonOutput(result, performanceMetrics);\r\n \r\n if (pretty) {\r\n return JSON.stringify(jsonOutput, null, indent);\r\n } else {\r\n return JSON.stringify(jsonOutput);\r\n }\r\n}\r\n\r\n/**\r\n * Write JSON report to file\r\n */\r\nexport async function writeJsonReport(\r\n summary: RiskSummary,\r\n discoveryResult: DiscoveryResult,\r\n parseResult: ParseResult,\r\n riskGroups: RiskGroup[],\r\n config: Config,\r\n startTime: number,\r\n outputPath: string\r\n): Promise<void> {\r\n const jsonContent = generateJsonReport(\r\n summary,\r\n discoveryResult,\r\n parseResult,\r\n riskGroups,\r\n config,\r\n startTime\r\n );\r\n \r\n await fs.writeFile(outputPath, jsonContent, 'utf-8');\r\n}\r\n\r\n/**\r\n * Build internal analysis result structure\r\n */\r\nfunction buildAnalysisResult(\r\n summary: RiskSummary,\r\n discoveryResult: DiscoveryResult,\r\n parseResult: ParseResult,\r\n riskGroups: RiskGroup[],\r\n config: Config,\r\n startTime: number\r\n): AnalysisResult {\r\n const metadata = buildAnalysisMetadata(\r\n config.baseUrl || 'unknown',\r\n startTime,\r\n summary\r\n );\r\n \r\n const suspiciousGroups = riskGroups.map(group => ({\r\n category: group.category,\r\n severity: group.severity,\r\n count: group.count,\r\n pattern: group.category, // Use category as pattern identifier\r\n rationale: group.rationale,\r\n sampleUrls: group.sampleUrls.slice(0, 5), // Limit to 5 samples\r\n recommendedAction: group.recommendedAction\r\n }));\r\n \r\n const summaryStats: SummaryStats = {\r\n highSeverityCount: summary.severityBreakdown.high,\r\n mediumSeverityCount: summary.severityBreakdown.medium,\r\n lowSeverityCount: summary.severityBreakdown.low,\r\n totalRiskyUrls: riskGroups.reduce((sum, g) => sum + g.count, 0),\r\n overallStatus: determineOverallStatus(\r\n summary.severityBreakdown,\r\n parseResult.errors\r\n )\r\n };\r\n \r\n const riskSummary: RiskSummaryData = {\r\n overview: summary.overview,\r\n keyFindings: summary.keyFindings,\r\n recommendations: summary.recommendations\r\n };\r\n \r\n const errors = parseResult.errors.map(transformError);\r\n \r\n return {\r\n analysisMetadata: metadata,\r\n sitemapsDiscovered: discoveryResult.sitemaps,\r\n totalUrlCount: parseResult.totalCount,\r\n urlsAnalyzed: parseResult.totalCount,\r\n suspiciousGroups,\r\n riskSummary,\r\n summary: summaryStats,\r\n errors\r\n };\r\n}\r\n\r\n/**\r\n * Build analysis metadata\r\n */\r\nfunction buildAnalysisMetadata(\r\n baseUrl: string,\r\n startTime: number,\r\n summary: RiskSummary\r\n): AnalysisMetadata {\r\n return {\r\n baseUrl,\r\n analysisTimestamp: new Date().toISOString(),\r\n toolVersion: TOOL_VERSION,\r\n executionTimeMs: Date.now() - startTime,\r\n analysisType: summary.generatedBy\r\n };\r\n}\r\n\r\n/**\r\n * Determine overall status based on severity and errors\r\n */\r\nfunction determineOverallStatus(\r\n severityBreakdown: { high: number; medium: number; low: number },\r\n errors: Error[]\r\n): 'clean' | 'issues_found' | 'errors' {\r\n if (errors.length > 0) {\r\n return 'errors';\r\n }\r\n \r\n const totalIssues = severityBreakdown.high + severityBreakdown.medium + severityBreakdown.low;\r\n \r\n return totalIssues > 0 ? 'issues_found' : 'clean';\r\n}\r\n\r\n/**\r\n * Transform internal camelCase structure to external snake_case JSON\r\n */\r\nfunction transformToJsonOutput(result: AnalysisResult, performanceMetrics?: PerformanceMetrics): object {\r\n const output: any = {\r\n analysis_metadata: transformMetadata(result.analysisMetadata),\r\n sitemaps_discovered: result.sitemapsDiscovered,\r\n total_url_count: result.totalUrlCount,\r\n urls_analyzed: result.urlsAnalyzed,\r\n suspicious_groups: result.suspiciousGroups.map(transformGroup),\r\n risk_summary: transformRiskSummary(result.riskSummary),\r\n summary: transformSummary(result.summary),\r\n errors: result.errors\r\n };\r\n \r\n // Add performance metrics if provided\r\n if (performanceMetrics) {\r\n output.performance_metrics = {\r\n total_execution_time_ms: performanceMetrics.totalExecutionTimeMs,\r\n phase_timings: performanceMetrics.phaseTimings,\r\n throughput: performanceMetrics.throughput,\r\n resource_usage: performanceMetrics.resourceUsage\r\n };\r\n }\r\n \r\n return output;\r\n}\r\n\r\n/**\r\n * Transform metadata to snake_case\r\n */\r\nfunction transformMetadata(meta: AnalysisMetadata): object {\r\n return {\r\n base_url: meta.baseUrl,\r\n analysis_timestamp: meta.analysisTimestamp,\r\n tool_version: meta.toolVersion,\r\n execution_time_ms: meta.executionTimeMs,\r\n analysis_type: meta.analysisType\r\n };\r\n}\r\n\r\n/**\r\n * Transform suspicious group to snake_case\r\n */\r\nfunction transformGroup(group: SuspiciousGroup): object {\r\n return {\r\n category: group.category,\r\n severity: group.severity,\r\n count: group.count,\r\n pattern: group.pattern,\r\n rationale: group.rationale,\r\n sample_urls: group.sampleUrls,\r\n recommended_action: group.recommendedAction\r\n };\r\n}\r\n\r\n/**\r\n * Transform risk summary to snake_case\r\n */\r\nfunction transformRiskSummary(summary: RiskSummaryData): object {\r\n return {\r\n overview: summary.overview,\r\n key_findings: summary.keyFindings,\r\n recommendations: summary.recommendations\r\n };\r\n}\r\n\r\n/**\r\n * Transform summary stats to snake_case\r\n */\r\nfunction transformSummary(summary: SummaryStats): object {\r\n return {\r\n high_severity_count: summary.highSeverityCount,\r\n medium_severity_count: summary.mediumSeverityCount,\r\n low_severity_count: summary.lowSeverityCount,\r\n total_risky_urls: summary.totalRiskyUrls,\r\n overall_status: summary.overallStatus\r\n };\r\n}\r\n\r\n/**\r\n * Transform error to structured format\r\n */\r\nfunction transformError(error: Error): ErrorDetail {\r\n // Handle custom error types\r\n if ('code' in error) {\r\n const customError = error as any;\r\n const errorDetail: ErrorDetail = {\r\n code: customError.code || 'UNKNOWN_ERROR',\r\n message: error.message\r\n };\r\n \r\n // Add context for specific error types\r\n if ('attemptedPaths' in customError) {\r\n errorDetail.context = {\r\n attempted_paths: customError.attemptedPaths\r\n };\r\n } else if ('sitemapUrl' in customError && 'lineNumber' in customError) {\r\n errorDetail.context = {\r\n sitemap_url: customError.sitemapUrl,\r\n line_number: customError.lineNumber\r\n };\r\n } else if ('url' in customError) {\r\n errorDetail.context = {\r\n url: customError.url\r\n };\r\n }\r\n \r\n return errorDetail;\r\n }\r\n \r\n // Generic error\r\n return {\r\n code: 'UNKNOWN_ERROR',\r\n message: error.message\r\n };\r\n}\r\n","import { promises as fs } from 'fs';\r\nimport type { RiskSummary, CategoryInsight } from '@/summarizer';\r\nimport type { DiscoveryResult } from '@/core/discovery';\r\nimport type { Config } from '@/types/config';\r\n\r\n// Version is injected at build time by tsup\r\ndeclare const __PACKAGE_VERSION__: string;\r\nconst TOOL_VERSION = __PACKAGE_VERSION__;\r\n\r\nexport interface HtmlReporterOptions {\r\n maxUrlsPerGroup?: number; // Max sample URLs to display (default: 10)\r\n}\r\n\r\n/**\r\n * Generate HTML report from analysis results\r\n */\r\nexport function generateHtmlReport(\r\n summary: RiskSummary,\r\n discoveryResult: DiscoveryResult,\r\n totalUrls: number,\r\n config: Config,\r\n errors: Error[],\r\n options: HtmlReporterOptions = {}\r\n): string {\r\n const maxUrls = options.maxUrlsPerGroup ?? 10;\r\n const timestamp = new Date().toISOString();\r\n const riskyUrlCount = summary.categoryInsights.reduce((sum, g) => sum + g.count, 0);\r\n\r\n // Group by severity\r\n const highSeverity = summary.categoryInsights.filter((g) => g.severity === 'high');\r\n const mediumSeverity = summary.categoryInsights.filter((g) => g.severity === 'medium');\r\n const lowSeverity = summary.categoryInsights.filter((g) => g.severity === 'low');\r\n\r\n const html = `<!DOCTYPE html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\">\r\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n <title>Sitemap QA Report - ${config.baseUrl}</title>\r\n <style>\r\n * { margin: 0; padding: 0; box-sizing: border-box; }\r\n body {\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\r\n line-height: 1.6;\r\n color: #1f2937;\r\n background: #ffffff;\r\n padding: 24px;\r\n }\r\n .container {\r\n max-width: 1400px;\r\n margin: 0 auto;\r\n background: white;\r\n box-shadow: 0 1px 3px rgba(0,0,0,0.05);\r\n border-radius: 12px;\r\n overflow: hidden;\r\n border: 1px solid #e5e7eb;\r\n }\r\n .header {\r\n background: #0f172a;\r\n color: white;\r\n padding: 48px 40px;\r\n border-bottom: 3px solid #3b82f6;\r\n }\r\n .header h1 { \r\n font-size: 1.875rem; \r\n font-weight: 700;\r\n margin-bottom: 12px;\r\n letter-spacing: -0.025em;\r\n }\r\n .header .meta { \r\n opacity: 0.75; \r\n font-size: 0.875rem;\r\n font-weight: 400;\r\n }\r\n .summary {\r\n display: grid;\r\n grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\r\n gap: 1px;\r\n background: #e5e7eb;\r\n border-bottom: 1px solid #e5e7eb;\r\n }\r\n .summary-card {\r\n background: white;\r\n padding: 28px 32px;\r\n text-align: center;\r\n }\r\n .summary-card .label { \r\n font-size: 0.75rem; \r\n color: #6b7280;\r\n text-transform: uppercase; \r\n letter-spacing: 0.05em;\r\n font-weight: 600;\r\n margin-bottom: 8px;\r\n }\r\n .summary-card .value { \r\n font-size: 2.25rem; \r\n font-weight: 700;\r\n color: #0f172a;\r\n font-variant-numeric: tabular-nums;\r\n }\r\n .content { padding: 40px; }\r\n .status-clean {\r\n text-align: center;\r\n padding: 80px 32px;\r\n background: #f0fdf4;\r\n border-radius: 8px;\r\n border: 1px solid #86efac;\r\n }\r\n .status-clean h2 { \r\n font-size: 1.875rem;\r\n margin-bottom: 12px;\r\n color: #166534;\r\n font-weight: 700;\r\n }\r\n .status-clean p { \r\n font-size: 1rem;\r\n color: #65a30d;\r\n }\r\n .severity-section { margin-bottom: 32px; }\r\n .severity-section h2 {\r\n font-size: 1.125rem;\r\n font-weight: 600;\r\n padding: 16px 20px;\r\n margin-bottom: 16px;\r\n border-radius: 8px;\r\n display: flex;\r\n align-items: center;\r\n gap: 12px;\r\n cursor: pointer;\r\n user-select: none;\r\n transition: all 0.2s;\r\n }\r\n .severity-section h2:hover {\r\n opacity: 0.85;\r\n transform: translateY(-1px);\r\n }\r\n .severity-section h2::after {\r\n content: 'โ–ผ';\r\n margin-left: auto;\r\n font-size: 0.8em;\r\n transition: transform 0.3s ease;\r\n opacity: 0.7;\r\n }\r\n .severity-section h2.collapsed::after {\r\n transform: rotate(-90deg);\r\n }\r\n .severity-section h2.collapsed {\r\n margin-bottom: 0;\r\n }\r\n .severity-content {\r\n max-height: none;\r\n overflow: visible;\r\n transition: max-height 0.4s ease-out, opacity 0.3s ease-out;\r\n opacity: 1;\r\n }\r\n .severity-content.collapsed {\r\n max-height: 0;\r\n overflow: hidden;\r\n opacity: 0;\r\n }\r\n .severity-high { background: #fef2f2; color: #dc2626; border: 1px solid #fecaca; }\r\n .severity-medium { background: #fffbeb; color: #d97706; border: 1px solid #fde68a; }\r\n .severity-low { background: #eff6ff; color: #2563eb; border: 1px solid #dbeafe; }\r\n .risk-group {\r\n background: white;\r\n border: 1px solid #e5e7eb;\r\n border-radius: 8px;\r\n padding: 24px;\r\n margin-bottom: 16px;\r\n }\r\n .risk-group h3 {\r\n font-size: 1rem;\r\n margin-bottom: 12px;\r\n color: #0f172a;\r\n font-weight: 600;\r\n }\r\n .risk-group .count {\r\n display: inline-block;\r\n background: #3b82f6;\r\n color: white;\r\n padding: 2px 10px;\r\n border-radius: 9999px;\r\n font-size: 0.75rem;\r\n font-weight: 600;\r\n margin-left: 8px;\r\n }\r\n .risk-group .impact {\r\n color: #64748b;\r\n margin-bottom: 16px;\r\n font-size: 0.875rem;\r\n line-height: 1.6;\r\n }\r\n .risk-group .urls {\r\n background: #f8fafc;\r\n border: 1px solid #e2e8f0;\r\n border-radius: 6px;\r\n padding: 16px;\r\n }\r\n .risk-group .urls h4 {\r\n font-size: 0.75rem;\r\n color: #64748b;\r\n margin-bottom: 12px;\r\n text-transform: uppercase;\r\n letter-spacing: 0.05em;\r\n font-weight: 600;\r\n }\r\n .risk-group .urls ul { list-style: none; }\r\n .risk-group .urls li {\r\n padding: 10px 12px;\r\n border-bottom: 1px solid #e2e8f0;\r\n font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', monospace;\r\n font-size: 0.8125rem;\r\n color: #334155;\r\n background: white;\r\n margin-bottom: 4px;\r\n border-radius: 4px;\r\n word-break: break-all;\r\n line-height: 1.6;\r\n }\r\n .risk-group .urls li:last-child { border-bottom: none; margin-bottom: 0; }\r\n .risk-group .more {\r\n color: #3b82f6;\r\n font-style: italic;\r\n margin-top: 8px;\r\n font-size: 0.8125rem;\r\n }\r\n .download-btn {\r\n display: inline-block;\r\n background: #3b82f6;\r\n color: white;\r\n padding: 8px 16px;\r\n border-radius: 6px;\r\n text-decoration: none;\r\n font-size: 0.8125rem;\r\n font-weight: 500;\r\n margin-top: 12px;\r\n cursor: pointer;\r\n border: none;\r\n transition: all 0.15s;\r\n }\r\n .download-btn:hover {\r\n background: #2563eb;\r\n transform: translateY(-1px);\r\n box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);\r\n }\r\n .footer {\r\n background: #f8fafc;\r\n padding: 24px 40px;\r\n border-top: 1px solid #e5e7eb;\r\n text-align: center;\r\n color: #64748b;\r\n font-size: 0.8125rem;\r\n }\r\n .sitemaps {\r\n background: white;\r\n border: 1px solid #e5e7eb;\r\n border-radius: 8px;\r\n margin-bottom: 24px;\r\n overflow: hidden;\r\n }\r\n .sitemaps h3 {\r\n font-size: 1.125rem;\r\n font-weight: 600;\r\n padding: 16px 20px;\r\n margin: 0;\r\n color: #0f172a;\r\n background: #f8fafc;\r\n cursor: pointer;\r\n user-select: none;\r\n transition: all 0.15s;\r\n display: flex;\r\n align-items: center;\r\n gap: 10px;\r\n }\r\n .sitemaps h3:hover {\r\n background: #f1f5f9;\r\n }\r\n .sitemaps h3::after {\r\n content: 'โ–ผ';\r\n margin-left: auto;\r\n font-size: 0.8em;\r\n transition: transform 0.3s ease;\r\n opacity: 0.7;\r\n }\r\n .sitemaps h3.collapsed::after {\r\n transform: rotate(-90deg);\r\n }\r\n .sitemaps-content {\r\n max-height: none;\r\n overflow: visible;\r\n transition: max-height 0.4s ease-out, opacity 0.3s ease-out;\r\n opacity: 1;\r\n padding: 20px;\r\n }\r\n .sitemaps-content.collapsed {\r\n max-height: 0;\r\n overflow: hidden;\r\n opacity: 0;\r\n padding: 0 20px;\r\n }\r\n .sitemaps ul { list-style: none; }\r\n .sitemaps li {\r\n padding: 10px 12px;\r\n font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', monospace;\r\n font-size: 0.8125rem;\r\n color: #475569;\r\n word-break: break-all;\r\n line-height: 1.6;\r\n background: #f8fafc;\r\n margin-bottom: 4px;\r\n border-radius: 4px;\r\n }\r\n .sitemaps li:last-child { margin-bottom: 0; }\r\n .errors-section {\r\n background: #fffbeb;\r\n border-left: 4px solid #f59e0b;\r\n padding: 20px;\r\n margin-bottom: 24px;\r\n border-radius: 8px;\r\n border: 1px solid #fde68a;\r\n }\r\n .errors-section h3 {\r\n color: #92400e;\r\n margin-bottom: 16px;\r\n font-size: 1.125rem;\r\n font-weight: 600;\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n }\r\n .errors-section ul {\r\n list-style: none;\r\n padding: 0;\r\n }\r\n .errors-section li {\r\n padding: 12px;\r\n background: white;\r\n margin-bottom: 8px;\r\n border-radius: 6px;\r\n font-family: 'SF Mono', 'Monaco', 'Cascadia Code', 'Consolas', monospace;\r\n font-size: 0.8125rem;\r\n color: #78350f;\r\n word-break: break-all;\r\n line-height: 1.6;\r\n border: 1px solid #fde68a;\r\n }\r\n .errors-section li:last-child {\r\n margin-bottom: 0;\r\n }\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"container\">\r\n <div class=\"header\">\r\n <h1>Sitemap Analysis</h1>\r\n <div class=\"meta\">\r\n <div>${config.baseUrl}</div>\r\n <div>${new Date(timestamp).toLocaleString()}</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"summary\">\r\n <div class=\"summary-card\">\r\n <div class=\"label\">Sitemaps</div>\r\n <div class=\"value\">${discoveryResult.sitemaps.length}</div>\r\n </div>\r\n <div class=\"summary-card\">\r\n <div class=\"label\">URLs Analyzed</div>\r\n <div class=\"value\">${totalUrls.toLocaleString()}</div>\r\n </div>\r\n <div class=\"summary-card\">\r\n <div class=\"label\">Issues Found</div>\r\n <div class=\"value\" style=\"color: ${riskyUrlCount > 0 ? '#dc2626' : '#059669'}\">${riskyUrlCount}</div>\r\n </div>\r\n <div class=\"summary-card\">\r\n <div class=\"label\">Scan Time</div>\r\n <div class=\"value\">${(summary.metadata.processingTime / 1000).toFixed(1)}s</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"content\">\r\n ${errors.length > 0 ? `\r\n <div class=\"errors-section\">\r\n <h3>Parsing Errors & Warnings (${errors.length})</h3>\r\n <ul>\r\n ${errors.map(err => `<li>${err.message}</li>`).join('\\n ')}\r\n </ul>\r\n </div>\r\n ` : ''}\r\n\r\n ${discoveryResult.sitemaps.length > 0 ? `\r\n <div class=\"sitemaps\">\r\n <h3 class=\"collapsed\" onclick=\"toggleSection(this)\">Sitemaps Discovered (${discoveryResult.sitemaps.length})</h3>\r\n <div class=\"sitemaps-content collapsed\">\r\n <ul>\r\n ${discoveryResult.sitemaps.map(s => `<li>โ€ข ${s}</li>`).join('\\n ')}\r\n </ul>\r\n </div>\r\n </div>\r\n ` : ''}\r\n\r\n ${riskyUrlCount === 0 ? `\r\n <div class=\"status-clean\">\r\n <h2>No Issues Found</h2>\r\n <p>All URLs in the sitemap passed validation checks.</p>\r\n </div>\r\n ` : ''}\r\n\r\n ${highSeverity.length > 0 ? `\r\n <div class=\"severity-section\">\r\n <h2 class=\"severity-high\" onclick=\"toggleSection(this)\">High Severity (${highSeverity.reduce((sum, g) => sum + g.count, 0)} URLs)</h2>\r\n <div class=\"severity-content\">\r\n ${highSeverity.map(group => renderRiskGroup(group, maxUrls)).join('\\n ')}\r\n </div>\r\n </div>\r\n ` : ''}\r\n\r\n ${mediumSeverity.length > 0 ? `\r\n <div class=\"severity-section\">\r\n <h2 class=\"severity-medium\" onclick=\"toggleSection(this)\">Medium Severity (${mediumSeverity.reduce((sum, g) => sum + g.count, 0)} URLs)</h2>\r\n <div class=\"severity-content\">\r\n ${mediumSeverity.map(group => renderRiskGroup(group, maxUrls)).join('\\n ')}\r\n </div>\r\n </div>\r\n ` : ''}\r\n\r\n ${lowSeverity.length > 0 ? `\r\n <div class=\"severity-section\">\r\n <h2 class=\"severity-low\" onclick=\"toggleSection(this)\">Low Severity (${lowSeverity.reduce((sum, g) => sum + g.count, 0)} URLs)</h2>\r\n <div class=\"severity-content\">\r\n ${lowSeverity.map(group => renderRiskGroup(group, maxUrls)).join('\\n ')}\r\n </div>\r\n </div>\r\n ` : ''}\r\n </div>\r\n\r\n <div class=\"footer\">\r\n Generated by <strong>sitemap-qa</strong> v${TOOL_VERSION}\r\n </div>\r\n </div>\r\n \r\n <script>\r\n function toggleSection(header) {\r\n header.classList.toggle('collapsed');\r\n const content = header.nextElementSibling;\r\n content.classList.toggle('collapsed');\r\n }\r\n \r\n function downloadUrls(categorySlug, encodedUrls) {\r\n // Decode HTML entities and parse JSON\r\n const textarea = document.createElement('textarea');\r\n textarea.innerHTML = encodedUrls;\r\n const urls = JSON.parse(textarea.value);\r\n \r\n // Create text content (one URL per line)\r\n const textContent = urls.join('\\\\n');\r\n \r\n // Create blob and download\r\n const blob = new Blob([textContent], { type: 'text/plain' });\r\n const url = URL.createObjectURL(blob);\r\n const a = document.createElement('a');\r\n a.href = url;\r\n a.download = categorySlug + '_urls.txt';\r\n document.body.appendChild(a);\r\n a.click();\r\n document.body.removeChild(a);\r\n URL.revokeObjectURL(url);\r\n }\r\n </script>\r\n</body>\r\n</html>`;\r\n\r\n return html;\r\n}\r\n\r\nfunction renderRiskGroup(group: CategoryInsight, maxUrls: number): string {\r\n const categoryTitle = group.category\r\n .split('_')\r\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\r\n .join(' ');\r\n\r\n const urlsToShow = group.examples.slice(0, maxUrls);\r\n const remaining = group.count - urlsToShow.length;\r\n \r\n // Create sanitized filename for download\r\n const categorySlug = group.category.toLowerCase();\r\n \r\n // Encode all URLs as data for download functionality\r\n const allUrlsJson = JSON.stringify(group.allUrls);\r\n const encodedUrls = escapeHtml(allUrlsJson);\r\n\r\n return `<div class=\"risk-group\">\r\n <h3>${categoryTitle} <span class=\"count\">${group.count} URLs</span></h3>\r\n <div class=\"impact\">${group.summary}</div>\r\n <div class=\"urls\">\r\n <h4>Sample URLs</h4>\r\n <ul>\r\n ${urlsToShow.map(url => `<li>${escapeHtml(url)}</li>`).join('\\n ')}\r\n </ul>\r\n ${remaining > 0 ? `<div class=\"more\">... and ${remaining} more</div>` : ''}\r\n <button class=\"download-btn\" onclick=\"downloadUrls('${categorySlug}', '${encodedUrls}')\">๐Ÿ“ฅ Download All ${group.count} URLs</button>\r\n </div>\r\n </div>`;\r\n}\r\n\r\nfunction escapeHtml(text: string): string {\r\n return text\r\n .replace(/&/g, '&amp;')\r\n .replace(/</g, '&lt;')\r\n .replace(/>/g, '&gt;')\r\n .replace(/\"/g, '&quot;')\r\n .replace(/'/g, '&#039;');\r\n}\r\n\r\n/**\r\n * Write HTML report to file\r\n */\r\nexport async function writeHtmlReport(\r\n summary: RiskSummary,\r\n discoveryResult: DiscoveryResult,\r\n totalUrls: number,\r\n config: Config,\r\n outputPath: string,\r\n errors: Error[],\r\n options: HtmlReporterOptions = {}\r\n): Promise<void> {\r\n const htmlContent = generateHtmlReport(summary, discoveryResult, totalUrls, config, errors, options);\r\n await fs.writeFile(outputPath, htmlContent, 'utf-8');\r\n}\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,IAAAA,iBAAO;AACP,IAAAC,oBAAwB;;;ACFxB,uBAAwB;AACxB,IAAAC,aAA+B;AAC/B,iBAAgB;AAChB,mBAAkB;AAClB,0BAAwB;AACxB,IAAAC,aAAe;;;ACLf,sBAAyB;AACzB,gBAA2B;AAC3B,kBAAqB;AACrB,gBAAwB;;;ACuBjB,IAAM,iBAAyB;AAAA,EACpC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,oBAAoB;AAAA;AAAA,EACpB,sBAAsB;AAAA;AAAA,EACtB,cAAc;AAAA,EACd,WAAW;AAAA,EACX,SAAS;AAAA,EACT,SAAS;AAAA;AAAA,EACT,kBAAkB,CAAC;AAAA,EACnB,wBAAwB;AAAA,EACxB,0BAA0B;AAAA;AAAA,EAC1B,aAAa;AAAA;AAAA,EACb,QAAQ;AAAA,EACR,WAAW;AACb;;;ADnCA,eAAsB,WAAW,YAAkD;AAEjF,MAAI,SAA0B,EAAE,GAAG,eAAe;AAGlD,QAAM,uBAAmB,sBAAK,mBAAQ,GAAG,eAAe,aAAa;AACrE,UAAI,sBAAW,gBAAgB,GAAG;AAChC,QAAI;AACF,YAAM,eAAe,KAAK,MAAM,UAAM,0BAAS,kBAAkB,OAAO,CAAC;AACzE,eAAS,EAAE,GAAG,QAAQ,GAAG,aAAa;AAAA,IACxC,SAAS,OAAO;AACd,cAAQ,KAAK,0CAA0C,KAAK,EAAE;AAAA,IAChE;AAAA,EACF;AAGA,QAAM,wBAAoB,kBAAK,QAAQ,IAAI,GAAG,yBAAyB;AACvE,UAAI,sBAAW,iBAAiB,GAAG;AACjC,QAAI;AACF,YAAM,gBAAgB,KAAK,MAAM,UAAM,0BAAS,mBAAmB,OAAO,CAAC;AAC3E,eAAS,EAAE,GAAG,QAAQ,GAAG,cAAc;AAAA,IACzC,SAAS,OAAO;AACd,cAAQ,KAAK,2CAA2C,KAAK,EAAE;AAAA,IACjE;AAAA,EACF;AAGA,QAAM,YAAY,YAAY;AAC9B,WAAS,EAAE,GAAG,QAAQ,GAAG,UAAU;AAGnC,WAAS,gBAAgB,QAAQ,UAAU;AAG3C,MAAI,WAAW,SAAS;AACtB,WAAO,UAAU,WAAW;AAAA,EAC9B;AAGA,iBAAe,MAAgB;AAE/B,SAAO;AACT;AAEA,SAAS,cAA+B;AACtC,QAAM,MAAuB,CAAC;AAE9B,MAAI,QAAQ,IAAI,wBAAwB;AACtC,QAAI,UAAU,SAAS,QAAQ,IAAI,wBAAwB,EAAE;AAAA,EAC/D;AAEA,SAAO;AACT;AAEA,SAAS,gBAAgB,QAAyB,YAAkD;AAClG,QAAM,SAAS,EAAE,GAAG,OAAO;AAG3B,MAAI,WAAW,WAAW,WAAW,YAAY,MAAM;AACrD,WAAO,UAAU,SAAS,WAAW,SAAS,EAAE;AAAA,EAClD;AAEA,MAAI,WAAW,QAAQ;AACrB,WAAO,eAAe,WAAW;AAAA,EACnC;AAEA,MAAI,WAAW,WAAW;AACxB,WAAO,YAAY,WAAW;AAAA,EAChC;AAEA,MAAI,WAAW,YAAY,MAAM;AAC/B,WAAO,UAAU;AAAA,EACnB;AAEA,MAAI,WAAW,kBAAkB;AAE/B,WAAO,mBAAmB,WAAW,iBAAiB,MAAM,GAAG,EAAE,IAAI,CAAC,MAAc,EAAE,KAAK,CAAC,EAAE,OAAO,OAAO;AAAA,EAC9G;AAEA,SAAO;AACT;AAEA,SAAS,eAAe,QAAsB;AAC5C,MAAI,OAAO,UAAU,KAAK,OAAO,UAAU,KAAK;AAC9C,UAAM,IAAI,MAAM,2CAA2C;AAAA,EAC7D;AAEA,MAAI,CAAC,CAAC,QAAQ,MAAM,EAAE,SAAS,OAAO,YAAY,GAAG;AACnD,UAAM,IAAI,MAAM,oCAAoC;AAAA,EACtD;AACF;;;AEhGO,IAAM,eAAN,cAA2B,MAAM;AAAA,EAGtC,YACkB,KACA,eAChB;AACA,UAAM,8BAA8B,GAAG,KAAK,cAAc,OAAO,EAAE;AAHnD;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AAAA,EARS,OAAO;AASlB;AAEO,IAAM,YAAN,cAAwB,MAAM;AAAA,EAGnC,YACkB,KACA,YACA,YAChB;AACA,QAAI,UAAU,QAAQ,UAAU,cAAc,GAAG;AAGjD,QAAI,eAAe,KAAK;AACtB,iBAAW;AAAA,IACb;AAEA,UAAM,OAAO;AAXG;AACA;AACA;AAUhB,SAAK,OAAO;AAAA,EACd;AAAA,EAhBS,OAAO;AAiBlB;;;AC7BA,wBAAyB;AACzB,mBAAkB;AAClB,kBAAmC;AACnC,mBAAoC;AAGpC,IAAM,YAAY,IAAI,YAAAC,MAAU;AAAA,EAC9B,WAAW;AAAA,EACX,YAAY;AAAA;AAAA,EACZ,gBAAgB;AAAA,EAChB,SAAS;AACX,CAAC;AAED,IAAM,aAAa,IAAI,aAAAC,MAAW;AAAA,EAChC,WAAW;AAAA,EACX,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,SAAS;AACX,CAAC;AAGD,IAAM,gBAAgB,aAAAC,QAAM,OAAO;AAAA,EACjC;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd,gBAAgB,MAAM;AAAA;AACxB,CAAC;AAoBD,eAAe,oBACb,KACA,SACsB;AACtB,MAAI;AACJ,MAAI;AACF,cAAU,MAAM,2BAAS,OAAO;AAAA,MAC9B,UAAU;AAAA,MACV,MAAM;AAAA,QACJ;AAAA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,IACF,CAAC;AAED,UAAM,UAAU,MAAM,QAAQ,WAAW;AAAA,MACvC,WAAW;AAAA,MACX,UAAU,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,MACtC,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,kBAAkB;AAAA,QAChB,UAAU;AAAA,QACV,mBAAmB;AAAA,QACnB,mBAAmB;AAAA,QACnB,OAAO;AAAA,QACP,cAAc;AAAA,QACd,6BAA6B;AAAA,MAC/B;AAAA,IACF,CAAC;AAED,UAAM,OAAO,MAAM,QAAQ,QAAQ;AAGnC,UAAM,KAAK,cAAc,MAAM;AAE7B,aAAO,eAAe,WAAW,aAAa;AAAA,QAC5C,KAAK,MAAM;AAAA,MACb,CAAC;AAGD,MAAC,OAAe,SAAS;AAAA,QACvB,SAAS,CAAC;AAAA,MACZ;AAGA,YAAM,gBAAgB,OAAO,UAAU,YAAY;AACnD,aAAO,UAAU,YAAY,QAAQ,CAAC,eACpC,WAAW,SAAS,kBAChB,QAAQ,QAAQ,EAAE,OAAO,aAAa,WAAW,CAAqB,IACtE,cAAc,UAAU;AAAA,IAChC,CAAC;AAGD,SAAK,kBAAkB,UAAU,GAAI;AAGrC,UAAM,WAAW,MAAM,KAAK,KAAK,KAAK;AAAA,MACpC,WAAW;AAAA;AAAA,MACX,SAAS,UAAU;AAAA,IACrB,CAAC;AAED,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AAEA,UAAM,aAAa,SAAS,OAAO;AAGnC,UAAM,UAAU,MAAM,KAAK,QAAQ;AACnC,UAAM,WAAW,KAAK,IAAI;AAE1B,UAAM,QAAQ,MAAM;AAEpB,QAAI,cAAc,OAAO,aAAa,KAAK;AACzC,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,KAAK;AAAA,MACP;AAAA,IACF;AAEA,UAAM,IAAI,UAAU,UAAU,UAAU;AAAA,EAE1C,SAAS,OAAY;AACnB,QAAI,SAAS;AACX,YAAM,QAAQ,MAAM;AAAA,IACtB;AAEA,QAAI,MAAM,SAAS,cAAc;AAC/B,YAAM;AAAA,IACR;AAEA,UAAM,IAAI,aAAa,KAAK,KAAK;AAAA,EACnC;AACF;AAEA,eAAsB,SACpB,KACA,UAAwB,CAAC,GACH;AACtB,QAAM;AAAA,IACJ,UAAU;AAAA,IACV,aAAa;AAAA,IACb,aAAa;AAAA,IACb,aAAa;AAAA,IACb,yBAAyB;AAAA,EAC3B,IAAI;AAGJ,MAAI,IAAI,GAAG;AAEX,QAAM,oBAAoB,CAAC,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;AAEvD,MAAI,YAA0B;AAC9B,MAAI,mBAAmB;AAEvB,WAAS,UAAU,GAAG,WAAW,YAAY,WAAW;AACtD,QAAI;AAEF,UAAI,cAAc,kBAAkB;AAClC,eAAO,MAAM,oBAAoB,KAAK,OAAO;AAAA,MAC/C;AAGA,YAAM,WAAW,MAAM,cAAc,IAAI,KAAK;AAAA,QAC5C,SAAS,UAAU;AAAA,QACnB,SAAS;AAAA,UACP,cAAc;AAAA,UACd,UAAU;AAAA,UACV,mBAAmB;AAAA,UACnB,cAAc;AAAA,QAChB;AAAA,MACF,CAAC;AAED,YAAM,aAAa,SAAS;AAC5B,YAAM,OAAO,SAAS;AAGtB,UAAI,cAAc,OAAO,aAAa,KAAK;AACzC,eAAO;AAAA,UACL,SAAS,OAAO,SAAS,WAAW,OAAO,KAAK,UAAU,IAAI;AAAA,UAC9D;AAAA,UACA,KAAK,SAAS,SAAS,KAAK,eAAe;AAAA;AAAA,QAC7C;AAAA,MACF;AAGA,UAAI,eAAe,OAAO,CAAC,oBAAoB,CAAC,wBAAwB;AACtE,2BAAmB;AAEnB;AAAA,MACF;AAGA,UAAI,CAAC,kBAAkB,SAAS,UAAU,GAAG;AAC3C,cAAM,IAAI,UAAU,SAAS,SAAS,KAAK,eAAe,KAAK,UAAU;AAAA,MAC3E;AAGA,kBAAY,IAAI,UAAU,SAAS,SAAS,KAAK,eAAe,KAAK,UAAU;AAAA,IAEjF,SAAS,OAAY;AAEnB,UAAI,MAAM,SAAS,cAAc;AAC/B,cAAM,YAAY;AAClB,YAAI,CAAC,kBAAkB,SAAS,UAAU,UAAU,GAAG;AACrD,gBAAM;AAAA,QACR;AACA,oBAAY;AAAA,MACd,OAAO;AAEL,oBAAY,IAAI,aAAa,KAAK,KAAK;AAAA,MACzC;AAGA,UAAI,YAAY,WAAY;AAAA,IAC9B;AAGA,QAAI,UAAU,YAAY;AACxB,YAAM,QAAQ,aAAa,KAAK,IAAI,GAAG,OAAO;AAC9C,YAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,KAAK,CAAC;AAAA,IACzD;AAAA,EACF;AAGA,QAAM;AACR;;;AClNA,eAAe,iBACb,SACA,QAC+D;AAC/D,QAAM,aAAa,IAAI,IAAI,OAAO,EAAE;AACpC,QAAM,eAAqC,CAAC;AAE5C,QAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAGA,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,cAAc,IAAI,OAAO,SAAS;AAChC,YAAM,aAAa,GAAG,UAAU,GAAG,IAAI;AAEvC,UAAI;AACF,cAAM,SAAS,MAAM,SAAS,YAAY;AAAA,UACxC,SAAS,OAAO;AAAA,UAChB,YAAY;AAAA;AAAA,QACd,CAAC;AAED,YAAI,OAAO,eAAe,KAAK;AAC7B,cAAI,OAAO,SAAS;AAClB,oBAAQ,IAAI,4BAAuB,UAAU,EAAE;AAAA,UACjD;AACA,iBAAO,EAAE,OAAO,MAAM,KAAK,WAAW;AAAA,QACxC;AACA,eAAO,EAAE,OAAO,MAAM;AAAA,MACxB,SAAS,OAAO;AACd,YAAI,iBAAiB,WAAW;AAE9B,cAAI,MAAM,eAAe,OAAO,MAAM,eAAe,KAAK;AACxD,yBAAa,KAAK;AAAA,cAChB,KAAK;AAAA,cACL,YAAY,MAAM;AAAA,cAClB,OAAO,MAAM,eAAe,MAAM,iBAAiB;AAAA,YACrD,CAAC;AAED,gBAAI,OAAO,SAAS;AAClB,sBAAQ,IAAI,yBAAoB,UAAU,KAAK,MAAM,UAAU,GAAG;AAAA,YACpE;AAAA,UACF,WAAW,OAAO,SAAS;AACzB,oBAAQ,IAAI,qBAAgB,UAAU,KAAK,MAAM,UAAU,GAAG;AAAA,UAChE;AAAA,QACF,WAAW,OAAO,SAAS;AACzB,kBAAQ,IAAI,qBAAgB,UAAU,EAAE;AAAA,QAC1C;AACA,eAAO,EAAE,OAAO,MAAM;AAAA,MACxB;AAAA,IACF,CAAC;AAAA,EACH;AAGA,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,WAAW,eAAe,OAAO,MAAM,OAAO;AACvD,aAAO,EAAE,UAAU,CAAC,OAAO,MAAM,GAAI,GAAG,QAAQ,aAAa;AAAA,IAC/D;AAAA,EACF;AAEA,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI,oCAAoC;AAAA,EAClD;AAEA,SAAO,EAAE,UAAU,CAAC,GAAG,QAAQ,aAAa;AAC9C;AAUA,eAAe,eACb,SACA,QACmB;AACnB,QAAM,YAAY,GAAG,IAAI,IAAI,OAAO,EAAE,MAAM;AAE5C,MAAI;AACF,UAAM,SAAS,MAAM,SAAS,WAAW;AAAA,MACvC,SAAS,OAAO;AAAA,MAChB,YAAY;AAAA,IACd,CAAC;AAED,UAAM,QAAQ,OAAO,QAAQ,MAAM,IAAI;AACvC,UAAM,WAAqB,CAAC;AAE5B,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,KAAK,MAAM,oBAAoB;AAC7C,UAAI,OAAO;AACT,cAAM,aAAa,MAAM,CAAC,EAAE,KAAK;AAEjC,YAAI;AACF,cAAI,IAAI,UAAU;AAClB,mBAAS,KAAK,UAAU;AAAA,QAC1B,QAAQ;AACN,cAAI,OAAO,SAAS;AAClB,oBAAQ,KAAK,sCAAsC,UAAU,EAAE;AAAA,UACjE;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,SAAS,SAAS,GAAG;AACzC,cAAQ,IAAI,SAAS,SAAS,MAAM,2BAA2B;AAAA,IACjE;AAEA,WAAO;AAAA,EAET,SAAS,OAAO;AACd,QAAI,OAAO,SAAS;AAClB,cAAQ,IAAI,0BAA0B,SAAS,EAAE;AAAA,IACnD;AACA,WAAO,CAAC;AAAA,EACV;AACF;AAYA,SAAS,eAAe,YAA6B;AAEnD,MAAI,WAAW,SAAS,eAAe,GAAG;AACxC,WAAO;AAAA,EACT;AAKA,MAAI,WAAW,SAAS,SAAS,GAAG;AAClC,UAAM,gBAAgB;AACtB,UAAM,UAAU,MAAM,KAAK,WAAW,SAAS,aAAa,CAAC;AAG7D,UAAM,iBAAiB,KAAK,IAAI,GAAG,QAAQ,MAAM;AACjD,QAAI,mBAAmB;AAEvB,aAAS,IAAI,GAAG,IAAI,gBAAgB,KAAK;AACvC,YAAM,MAAM,QAAQ,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,YAAY;AAC7C,UAAI,IAAI,SAAS,SAAS,KAAK,IAAI,SAAS,MAAM,GAAG;AACnD;AAAA,MACF;AAAA,IACF;AAGA,WAAO,mBAAmB,iBAAiB;AAAA,EAC7C;AAEA,SAAO;AACT;AAcA,SAAS,wBAAwB,YAA8B;AAC7D,QAAM,OAAiB,CAAC;AAGxB,MAAI,WAAW,SAAS,eAAe,GAAG;AACxC,UAAM,oBAAoB;AAC1B,QAAI;AAEJ,YAAQ,eAAe,kBAAkB,KAAK,UAAU,OAAO,MAAM;AACnE,YAAM,WAAW,uBAAuB,KAAK,aAAa,CAAC,CAAC;AAC5D,UAAI,UAAU;AACZ,cAAM,MAAM,SAAS,CAAC,EAAE,KAAK;AAC7B,YAAI;AACF,cAAI,IAAI,GAAG;AACX,eAAK,KAAK,GAAG;AAAA,QACf,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AAGL,UAAM,gBAAgB;AACtB,QAAI;AAEJ,YAAQ,WAAW,cAAc,KAAK,UAAU,OAAO,MAAM;AAC3D,YAAM,WAAW,uBAAuB,KAAK,SAAS,CAAC,CAAC;AACxD,UAAI,UAAU;AACZ,cAAM,MAAM,SAAS,CAAC,EAAE,KAAK;AAG7B,YAAI,IAAI,YAAY,EAAE,SAAS,SAAS,KAAK,IAAI,YAAY,EAAE,SAAS,MAAM,GAAG;AAC/E,cAAI;AACF,gBAAI,IAAI,GAAG;AACX,iBAAK,KAAK,GAAG;AAAA,UACf,QAAQ;AAAA,UAER;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAgBA,eAAe,oBACb,iBACA,QACmB;AACnB,QAAM,gBAA0B,CAAC;AACjC,QAAM,YAAY,CAAC,GAAG,eAAe;AACrC,QAAM,YAAY,oBAAI,IAAY;AAClC,QAAM,eAAe,oBAAI,IAAY;AACrC,QAAM,aAAa,OAAO,wBAAwB;AAElD,SAAO,UAAU,SAAS,GAAG;AAE3B,UAAM,QAAQ,UAAU,OAAO,GAAG,KAAK,IAAI,YAAY,UAAU,MAAM,CAAC;AAGxE,UAAM,eAAe,MAAM,QAAQ,IAAI,MAAM,IAAI,OAAO,eAAe;AACrE,UAAI,UAAU,IAAI,UAAU,GAAG;AAC7B,YAAI,OAAO,SAAS;AAClB,kBAAQ,KAAK,+BAA+B,UAAU,EAAE;AAAA,QAC1D;AACA,eAAO,EAAE,MAAM,OAAgB;AAAA,MACjC;AAEA,gBAAU,IAAI,UAAU;AAExB,UAAI;AACF,cAAM,SAAS,MAAM,SAAS,YAAY;AAAA,UACxC,SAAS,OAAO;AAAA,UAChB,YAAY;AAAA,QACd,CAAC;AAED,YAAI,eAAe,OAAO,OAAO,GAAG;AAClC,cAAI,OAAO,SAAS;AAClB,oBAAQ,IAAI,wBAAwB,UAAU,EAAE;AAAA,UAClD;AAEA,gBAAM,YAAY,wBAAwB,OAAO,OAAO;AAExD,cAAI,OAAO,SAAS;AAClB,oBAAQ,IAAI,2BAAiB,UAAU,MAAM,mBAAmB;AAAA,UAClE;AAEA,iBAAO,EAAE,MAAM,SAAkB,UAAU;AAAA,QAC7C,OAAO;AACL,cAAI,OAAO,SAAS;AAClB,oBAAQ,IAAI,8BAAyB,UAAU,EAAE;AAAA,UACnD;AAEA,iBAAO,EAAE,MAAM,WAAoB,KAAK,WAAW;AAAA,QACrD;AAAA,MAEF,SAAS,OAAO;AACd,qBAAa,IAAI,UAAU;AAE3B,YAAI,OAAO,SAAS;AAClB,gBAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,kBAAQ,KAAK,2BAA2B,UAAU,KAAK,OAAO,EAAE;AAAA,QAClE;AAEA,eAAO,EAAE,MAAM,SAAkB;AAAA,MACnC;AAAA,IACF,CAAC,CAAC;AAGF,eAAW,UAAU,cAAc;AACjC,UAAI,OAAO,SAAS,SAAS;AAC3B,kBAAU,KAAK,GAAG,OAAO,SAAS;AAAA,MACpC,WAAW,OAAO,SAAS,WAAW;AACpC,sBAAc,KAAK,OAAO,GAAG;AAAA,MAC/B;AAAA,IACF;AAGA,QAAI,UAAU,OAAO,KAAM;AACzB,cAAQ,KAAK,yFAA+E;AAC5F;AAAA,IACF;AAAA,EACF;AAEA,MAAI,cAAc,WAAW,KAAK,aAAa,OAAO,GAAG;AACvD,YAAQ,KAAK;AAAA,oBAAa,aAAa,IAAI,+BAA+B;AAC1E,YAAQ,KAAK,kEAAkE;AAAA,EACjF;AAEA,SAAO;AACT;AAkBA,eAAsB,iBACpB,SACA,QAC0B;AAC1B,QAAM,gBAAgB,IAAI,IAAI,OAAO,EAAE;AAGvC,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI,+CAA+C;AAAA,EAC7D;AAEA,QAAM,iBAAiB,MAAM,eAAe,eAAe,MAAM;AACjE,MAAI,eAAe,SAAS,GAAG;AAC7B,UAAM,WAAW,MAAM,oBAAoB,gBAAgB,MAAM;AAGjE,WAAO;AAAA,MACL;AAAA,MACA,QAAQ;AAAA,MACR,cAAc,CAAC;AAAA,IACjB;AAAA,EACF;AAGA,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI,kCAAkC;AAAA,EAChD;AAEA,QAAM,EAAE,UAAU,kBAAkB,OAAO,IAAI,MAAM,iBAAiB,eAAe,MAAM;AAC3F,MAAI,iBAAiB,SAAS,GAAG;AAC/B,UAAM,WAAW,MAAM,oBAAoB,kBAAkB,MAAM;AACnE,QAAI,SAAS,SAAS,GAAG;AACvB,aAAO;AAAA,QACL;AAAA,QACA,QAAQ;AAAA,QACR,cAAc,CAAC;AAAA,MACjB;AAAA,IACF;AAGA,WAAO;AAAA,MACL,UAAU,CAAC;AAAA,MACX,QAAQ;AAAA,MACR,cAAc;AAAA,IAChB;AAAA,EACF;AAGA,SAAO;AAAA,IACL,UAAU,CAAC;AAAA,IACX,QAAQ;AAAA,IACR,cAAc;AAAA,EAChB;AACF;;;AChaA,6BAAwC;AAGxC,IAAM,mBAAmB,oBAAI,IAAI;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAkBD,IAAM,SAAS,IAAI,iCAAU;AAAA,EAC3B,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,cAAc;AAAA,EACd,qBAAqB;AAAA,EACrB,YAAY;AAAA,EACZ,wBAAwB;AAAA,EACxB,eAAe;AAAA;AACjB,CAAC;AAED,SAAS,YAAY,WAAgB,YAAgC;AACnE,QAAM,OAAmB,CAAC;AAG1B,MAAI,UAAU,QAAQ;AAEpB,UAAM,WAAW,MAAM,QAAQ,UAAU,OAAO,GAAG,IAC/C,UAAU,OAAO,MACjB,CAAC,UAAU,OAAO,GAAG;AAGzB,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,OAAO,SAAS,CAAC;AAEvB,UAAI,CAAC,QAAQ,CAAC,KAAK,KAAK;AACtB;AAAA,MACF;AAEA,WAAK,KAAK;AAAA,QACR,KAAK,KAAK;AAAA,QACV,SAAS,KAAK;AAAA,QACd,YAAY,KAAK;AAAA,QACjB,UAAU,KAAK,WAAW,WAAW,KAAK,QAAQ,IAAI;AAAA,QACtD,QAAQ;AAAA,MACV,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AACT;AAEA,eAAsB,aACpB,KACA,YACsB;AACtB,QAAM,SAAmB,CAAC;AAE1B,MAAI;AAEF,UAAM,mBAAmB,oCAAa,SAAS,GAAG;AAClD,QAAI,qBAAqB,MAAM;AAC7B,YAAM,kBAAkB,OAAO,qBAAqB,WAChD,iBAAiB,IAAI,MACrB;AACJ,aAAO;AAAA,QACL,MAAM,CAAC;AAAA,QACP,QAAQ;AAAA,UACN,IAAI,UAAU,yBAAyB,eAAe;AAAA,QACxD;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,MACF;AAAA,IACF;AAGA,UAAM,SAAS,OAAO,MAAM,GAAG;AAG/B,UAAM,OAAO,YAAY,QAAQ,UAAU;AAG3C,UAAM,YAAwB,CAAC;AAC/B,eAAW,SAAS,MAAM;AACxB,UAAI;AAEF,YAAI,IAAI,MAAM,GAAG;AAGjB,YAAI,MAAM,aAAa,QAAW;AAChC,cAAI,MAAM,WAAW,KAAK,MAAM,WAAW,GAAG;AAC5C,mBAAO;AAAA,cACL,oBAAoB,MAAM,QAAQ,QAAQ,MAAM,GAAG;AAAA,YACrD;AACA,kBAAM,WAAW,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,MAAM,QAAQ,CAAC;AAAA,UAC1D;AAAA,QACF;AAGA,YAAI,MAAM,YAAY;AACpB,cAAI,CAAC,iBAAiB,IAAI,MAAM,WAAW,YAAY,CAAC,GAAG;AACzD,mBAAO;AAAA,cACL,uBAAuB,MAAM,UAAU,SAAS,MAAM,GAAG;AAAA,YAC3D;AACA,kBAAM,aAAa;AAAA,UACrB;AAAA,QACF;AAEA,kBAAU,KAAK,KAAK;AAAA,MACtB,SAAS,UAAU;AACjB,eAAO,KAAK,uBAAuB,MAAM,GAAG,EAAE;AAAA,MAChD;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA,YAAY,UAAU;AAAA,MACtB;AAAA,IACF;AAAA,EACF,SAAS,YAAY;AAEnB,UAAM,WAAW,sBAAsB,QAAQ,WAAW,UAAU,OAAO,UAAU;AACrF,WAAO;AAAA,MACL,MAAM,CAAC;AAAA,MACP,QAAQ;AAAA,QACN,IAAI,UAAU,yBAAyB,QAAQ;AAAA,MACjD;AAAA,MACA,YAAY;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AACF;;;AC3IO,SAAS,WAAc,OAAY,WAA0B;AAClE,QAAM,SAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;AAChD,WAAO,KAAK,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;AAAA,EAC3C;AACA,SAAO;AACT;AAWA,eAAsB,iBACpB,OACA,aACA,WACA,YACc;AACd,QAAM,UAAe,IAAI,MAAM,MAAM,MAAM;AAC3C,MAAI,YAAY;AAChB,MAAI,eAAe;AACnB,QAAM,SAA6C,CAAC;AAGpD,QAAM,UAAU,MAAM,KAAK,IAAI,aAAa,MAAM,MAAM,CAAC,EACtD,KAAK,IAAI,EACT,IAAI,YAAY;AACf,WAAO,eAAe,MAAM,QAAQ;AAClC,YAAM,QAAQ;AACd,YAAM,OAAO,MAAM,KAAK;AAExB,UAAI;AACF,gBAAQ,KAAK,IAAI,MAAM,UAAU,IAAI;AAAA,MACvC,SAAS,OAAO;AAEd,eAAO,KAAK,EAAE,OAAO,MAAM,CAAC;AAE5B,gBAAQ,KAAK,IAAI;AAAA,MACnB;AAEA;AACA,UAAI,YAAY;AACd,mBAAW,WAAW,MAAM,MAAM;AAAA,MACpC;AAAA,IACF;AAAA,EACF,CAAC;AAEH,QAAM,QAAQ,IAAI,OAAO;AAIzB,MAAI,OAAO,SAAS,GAAG;AACrB,YAAQ,KAAK,aAAa,MAAM,MAAM,eAAe,OAAO,MAAM,SAAS;AAAA,EAC7E;AAEA,SAAO;AACT;;;AC3DA,eAAsB,eACpB,aACA,QACA,YAC2B;AAC3B,QAAM,UAAsB,CAAC;AAC7B,QAAM,YAAsB,CAAC;AAC7B,MAAI,oBAAoB;AACxB,MAAI,iBAAiB;AAErB,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI;AAAA,uBAA0B,YAAY,MAAM,gBAAgB;AAAA,EAC1E;AAGA,QAAM,cAAc,OAAO,sBAAsB;AAEjD,MAAI,CAAC,OAAO,UAAU,OAAO,SAAS;AACpC,YAAQ,IAAI,8BAA8B,WAAW,EAAE;AAAA,EACzD;AAEA,QAAM,UAAU,MAAM;AAAA,IACpB;AAAA,IACA;AAAA,IACA,OAAO,eAAe;AACpB,UAAI;AACF,YAAI,OAAO,SAAS;AAClB,kBAAQ,IAAI,yBAAyB,UAAU,EAAE;AAAA,QACnD;AAGA,cAAM,WAAW,MAAM,SAAS,YAAY;AAAA,UAC1C,SAAS;AAAA;AAAA,UACT,YAAY;AAAA;AAAA,UACZ,wBAAwB;AAAA;AAAA,QAC1B,CAAC;AAGD,cAAM,cAAc,MAAM,aAAa,SAAS,SAAS,UAAU;AAGnE,cAAM,eAAc,oBAAI,KAAK,GAAE,YAAY;AAC3C,oBAAY,KAAK,QAAQ,SAAO;AAC9B,cAAI,cAAc;AAAA,QACpB,CAAC;AAED,YAAI,OAAO,SAAS;AAClB,kBAAQ,IAAI,sBAAiB,YAAY,KAAK,MAAM,cAAc,UAAU,EAAE;AAAA,QAChF;AAEA,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,YAAY;AAAA,UAClB,QAAQ,YAAY;AAAA,QACtB;AAAA,MACF,SAAS,OAAO;AACd,cAAM,WAAW,qBAAqB,UAAU,KAC9C,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CACvD;AAEA,YAAI,OAAO,SAAS;AAClB,kBAAQ,MAAM,YAAO,QAAQ,EAAE;AAAA,QACjC;AAEA,eAAO;AAAA,UACL,SAAS;AAAA,UACT,MAAM,CAAC;AAAA,UACP,QAAQ,CAAC,QAAQ;AAAA,QACnB;AAAA,MACF;AAAA,IACF;AAAA,IACA;AAAA;AAAA,EACF;AAGA,aAAW,UAAU,SAAS;AAC5B,QAAI,OAAO,SAAS;AAClB;AACA,cAAQ,KAAK,GAAG,OAAO,IAAI;AAAA,IAC7B,OAAO;AACL;AAAA,IACF;AACA,cAAU,KAAK,GAAG,OAAO,MAAM;AAAA,EACjC;AAEA,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI;AAAA,qBAAwB;AACpC,YAAQ,IAAI,2BAA2B,iBAAiB,EAAE;AAC1D,YAAQ,IAAI,wBAAwB,cAAc,EAAE;AACpD,YAAQ,IAAI,mBAAmB,QAAQ,MAAM,EAAE;AAC/C,YAAQ,IAAI,eAAe,UAAU,MAAM,EAAE;AAAA,EAC/C;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,QAAQ;AAAA,IACnB,QAAQ;AAAA,EACV;AACF;;;AClGO,SAAS,aAAa,KAAqB;AAChD,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAG1B,QAAI,WAAW,OAAO;AACtB,QAAI,SAAS,SAAS,GAAG,KAAK,aAAa,KAAK;AAC9C,iBAAW,SAAS,MAAM,GAAG,EAAE;AAAA,IACjC;AAGA,UAAM,SAAS,MAAM,KAAK,OAAO,aAAa,QAAQ,CAAC,EAAE;AAAA,MAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MACpE,EAAE,cAAc,CAAC;AAAA,IACnB;AACA,UAAM,eAAe,IAAI,gBAAgB,MAAM;AAG/C,WAAO,GAAG,OAAO,QAAQ,KAAK,OAAO,IAAI,GAAG,QAAQ,GAClD,aAAa,SAAS,IAAI,MAAM,aAAa,SAAS,IAAI,EAC5D,GAAG,OAAO,IAAI;AAAA,EAChB,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,SAA+B;AACtD,MAAI,QAAQ,WAAW,EAAG,QAAO,QAAQ,CAAC;AAG1C,QAAM,SAAmB,EAAE,GAAG,QAAQ,CAAC,EAAE;AAGzC,QAAM,UAAU,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM;AAC3C,SAAO,SAAS,QAAQ,KAAK,IAAI;AAGjC,QAAM,WAAW,QACd,IAAI,CAAC,MAAM,EAAE,OAAO,EACpB,OAAO,CAAC,OAAqB,CAAC,CAAC,EAAE,EACjC,IAAI,CAAC,OAAO,IAAI,KAAK,EAAE,EAAE,QAAQ,CAAC,EAClC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAEvB,MAAI,SAAS,SAAS,GAAG;AACvB,WAAO,UAAU,IAAI,KAAK,SAAS,CAAC,CAAC,EAAE,YAAY;AAAA,EACrD;AAGA,QAAM,aAAa,QAChB,IAAI,CAAC,MAAM,EAAE,QAAQ,EACrB,OAAO,CAAC,MAAmB,MAAM,MAAS;AAE7C,MAAI,WAAW,SAAS,GAAG;AACzB,WAAO,WAAW,KAAK,IAAI,GAAG,UAAU;AAAA,EAC1C;AAGA,QAAM,cAAc,QACjB,IAAI,CAAC,MAAM,EAAE,UAAU,EACvB,OAAO,CAAC,OAAqB,CAAC,CAAC,EAAE;AAEpC,MAAI,YAAY,SAAS,GAAG;AAC1B,UAAM,SAAS,oBAAI,IAAoB;AACvC,eAAW,MAAM,aAAa;AAC5B,aAAO,IAAI,KAAK,OAAO,IAAI,EAAE,KAAK,KAAK,CAAC;AAAA,IAC1C;AACA,UAAM,SAAS,MAAM,KAAK,OAAO,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AACtE,WAAO,aAAa,OAAO,CAAC,EAAE,CAAC;AAAA,EACjC;AAGA,QAAM,eAAe,QAClB,IAAI,CAAC,MAAM,EAAE,WAAW,EACxB,OAAO,CAAC,OAAqB,CAAC,CAAC,EAAE,EACjC,IAAI,CAAC,OAAO,IAAI,KAAK,EAAE,EAAE,QAAQ,CAAC,EAClC,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAEvB,MAAI,aAAa,SAAS,GAAG;AAC3B,WAAO,cAAc,IAAI,KAAK,aAAa,CAAC,CAAC,EAAE,YAAY;AAAA,EAC7D;AAEA,SAAO;AACT;AAEO,SAAS,gBACd,MACA,UAAmB,OACC;AACpB,QAAM,iBAAiB,KAAK;AAE5B,MAAI,SAAS;AACX,YAAQ,IAAI;AAAA,gBAAmB,KAAK,MAAM,YAAY;AAAA,EACxD;AAGA,QAAM,SAAS,oBAAI,IAAwB;AAE3C,aAAW,SAAS,MAAM;AACxB,UAAM,aAAa,aAAa,MAAM,GAAG;AACzC,QAAI,CAAC,OAAO,IAAI,UAAU,GAAG;AAC3B,aAAO,IAAI,YAAY,CAAC,CAAC;AAAA,IAC3B;AACA,WAAO,IAAI,UAAU,EAAG,KAAK,KAAK;AAAA,EACpC;AAGA,QAAM,aAAyB,CAAC;AAChC,QAAM,kBAAoC,CAAC;AAE3C,aAAW,CAAC,YAAY,OAAO,KAAK,OAAO,QAAQ,GAAG;AACpD,UAAM,SAAS,gBAAgB,OAAO;AACtC,eAAW,KAAK,MAAM;AAEtB,QAAI,QAAQ,SAAS,GAAG;AACtB,sBAAgB,KAAK;AAAA,QACnB,KAAK;AAAA,QACL,OAAO,QAAQ;AAAA,QACf,SAAS,QAAQ,IAAI,CAAC,MAAM,EAAE,MAAM;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,EACF;AAEA,MAAI,SAAS;AACX,YAAQ,IAAI,yBAAyB;AACrC,YAAQ,IAAI,mBAAmB,cAAc,EAAE;AAC/C,YAAQ,IAAI,oBAAoB,WAAW,MAAM,EAAE;AACnD,YAAQ,IAAI,2BAA2B,iBAAiB,WAAW,MAAM,EAAE;AAE3E,QAAI,gBAAgB,SAAS,GAAG;AAC9B,cAAQ,IAAI;AAAA,gBAAmB;AAC/B,YAAM,OAAO,gBACV,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAChC,MAAM,GAAG,CAAC;AAEb,iBAAW,SAAS,MAAM;AACxB,gBAAQ,IAAI,OAAO,MAAM,GAAG,KAAK,MAAM,KAAK,SAAS;AAAA,MACvD;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,mBAAmB,iBAAiB,WAAW;AAAA,IAC/C;AAAA,EACF;AACF;;;AC/JO,IAAM,gBAA+B;AAAA;AAAA,EAE1C;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA;AAAA,EAGA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA;AAAA;AAAA,EAIA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AACF;;;AC/BA,SAAS,YAAY,KAAqB;AACxC,SAAO,IAAI,QAAQ,uBAAuB,MAAM;AAClD;AAEO,SAAS,kBAAkB,UAA0B;AAC1D,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,MAAI,MAAM,UAAU,GAAG;AACrB,WAAO,MAAM,MAAM,EAAE,EAAE,KAAK,GAAG;AAAA,EACjC;AACA,SAAO;AACT;AAEO,SAAS,4BACd,SACA,SACa;AACb,QAAM,aAAa,IAAI,IAAI,OAAO,EAAE;AACpC,QAAM,aAAa,kBAAkB,UAAU;AAG/C,MAAI,SAAS,qBAAqB,QAAQ,kBAAkB,SAAS,GAAG;AACtE,UAAMC,eAAc,YAAY,UAAU;AAC1C,UAAM,oBAAoB,QAAQ,kBAAkB,IAAI,WAAW,EAAE,KAAK,GAAG;AAI7E,UAAMC,WAAU,sBAAsB,iBAAiB,SAASD,YAAW;AAE3E,WAAO;AAAA,MACL,MAAM;AAAA,MACN,UAAU;AAAA,MACV,UAAU;AAAA,MACV,OAAO,IAAI,OAAOC,QAAO;AAAA,MACzB,aAAa;AAAA,IACf;AAAA,EACF;AAGA,QAAM,cAAc,YAAY,UAAU;AAI1C,QAAM,UAAU,2BAA2B,WAAW;AAEtD,SAAO;AAAA,IACL,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO,IAAI,OAAO,OAAO;AAAA,IACzB,aAAa,uCAAuC,UAAU;AAAA,EAChE;AACF;AAEO,IAAM,uBAAsC;AAAA,EACjD;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AACF;;;AC7FO,IAAM,sBAAqC;AAAA,EAChD;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AACF;AAIO,IAAM,4BAA2C;AAAA,EACtD;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AACF;AAEO,IAAM,2BAA0C;AAAA,EACrD;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,UAAU;AAAA,IACV,UAAU;AAAA,IACV,OAAO;AAAA,IACP,aAAa;AAAA,EACf;AACF;;;AC7GO,SAAS,YAAY,KAAqB;AAC/C,MAAI;AACF,UAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,UAAM,kBAAkB;AAAA,MACtB;AAAA,MAAS;AAAA,MAAQ;AAAA,MAAc;AAAA,MAAgB;AAAA,MAC/C;AAAA,MAAU;AAAA,MAAW;AAAA,MACrB;AAAA,MAAY;AAAA,MAAU;AAAA,MACtB;AAAA,MAAU;AAAA,MACV;AAAA,MAAW;AAAA,MAAa;AAAA,MACxB;AAAA,IACF;AAEA,eAAW,SAAS,iBAAiB;AACnC,UAAI,OAAO,aAAa,IAAI,KAAK,GAAG;AAClC,eAAO,aAAa,IAAI,OAAO,YAAY;AAAA,MAC7C;AAAA,IACF;AAEA,WAAO,OAAO,SAAS;AAAA,EACzB,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;;;ACHA,SAAS,uBACP,UACA,WACA,OACkD;AAClD,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO;AAAA,QACL,WAAW,+BAA+B,KAAK;AAAA,QAC/C,mBAAmB;AAAA,MACrB;AAAA,IAEF,KAAK;AACH,aAAO;AAAA,QACL,WAAW,GAAG,KAAK;AAAA,QACnB,mBAAmB;AAAA,MACrB;AAAA,IAEF,KAAK;AACH,aAAO;AAAA,QACL,WAAW,GAAG,KAAK;AAAA,QACnB,mBAAmB;AAAA,MACrB;AAAA,IAEF,KAAK;AACH,aAAO;AAAA,QACL,WAAW,GAAG,KAAK;AAAA,QACnB,mBAAmB;AAAA,MACrB;AAAA,IAEF,KAAK;AACH,aAAO;AAAA,QACL,WAAW,GAAG,KAAK;AAAA,QACnB,mBAAmB;AAAA,MACrB;AAAA,IAEF,KAAK;AACH,aAAO;AAAA,QACL,WAAW,GAAG,KAAK;AAAA,QACnB,mBAAmB;AAAA,MACrB;AAAA,IAEF,KAAK;AACH,aAAO;AAAA,QACL,WAAW,GAAG,KAAK;AAAA,QACnB,mBAAmB;AAAA,MACrB;AAAA,IAEF;AACE,aAAO;AAAA,QACL,WAAW,GAAG,KAAK,gCAAgC,QAAQ;AAAA,QAC3D,mBAAmB;AAAA,MACrB;AAAA,EACJ;AACF;AAEO,SAAS,kBACd,UACA,gBAAwB,GACJ;AAEpB,QAAM,cAAc,oBAAI,IAAiC;AAEzD,aAAW,WAAW,UAAU;AAC9B,QAAI,CAAC,YAAY,IAAI,QAAQ,QAAQ,GAAG;AACtC,kBAAY,IAAI,QAAQ,UAAU,CAAC,CAAC;AAAA,IACtC;AACA,gBAAY,IAAI,QAAQ,QAAQ,EAAG,KAAK,OAAO;AAAA,EACjD;AAGA,QAAM,SAAsB,CAAC;AAE7B,aAAW,CAAC,UAAU,gBAAgB,KAAK,YAAY,QAAQ,GAAG;AAEhE,UAAM,aAAa,MAAM,KAAK,IAAI,IAAI,iBAAiB,IAAI,OAAK,EAAE,GAAG,CAAC,CAAC;AAGvE,UAAM,WAAW,iBAAiB,OAAO,CAAC,SAAS,YAAY;AAC7D,YAAM,gBAA4B,CAAC,OAAO,UAAU,MAAM;AAC1D,aAAO,cAAc,QAAQ,QAAQ,QAAQ,IAAI,cAAc,QAAQ,OAAO,IAC1E,QAAQ,WACR;AAAA,IACN,GAAG,KAAiB;AAGpB,UAAM,aAAa,WAAW,MAAM,GAAG,aAAa;AAGpD,UAAM,EAAE,WAAW,kBAAkB,IAAI,uBAAuB,UAAU,UAAU,WAAW,MAAM;AAErG,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA,OAAO,WAAW;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAGA,SAAO,KAAK,CAAC,GAAG,MAAM;AACpB,UAAM,gBAA4B,CAAC,QAAQ,UAAU,KAAK;AAC1D,WAAO,cAAc,QAAQ,EAAE,QAAQ,IAAI,cAAc,QAAQ,EAAE,QAAQ;AAAA,EAC7E,CAAC;AAGD,QAAM,gBAAgB,IAAI,IAAI,SAAS,IAAI,OAAK,EAAE,GAAG,CAAC,EAAE;AACxD,QAAM,oBAAoB,OAAO,OAAO,OAAK,EAAE,aAAa,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AACvG,QAAM,sBAAsB,OAAO,OAAO,OAAK,EAAE,aAAa,QAAQ,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAC3G,QAAM,mBAAmB,OAAO,OAAO,OAAK,EAAE,aAAa,KAAK,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAErG,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;ACrIA,IAAAC,aAAe;AAkDf,SAAS,wBAAwB,QAA0B;AACzD,QAAM,WAAqB,CAAC;AAE5B,MAAI,OAAO,oBAAoB,OAAO,iBAAiB,SAAS,GAAG;AACjE,eAAW,WAAW,OAAO,kBAAkB;AAC7C,UAAI;AAKF,YAAI,eAAe,QAChB,QAAQ,sBAAsB,MAAM,EACpC,QAAQ,OAAO,OAAO;AAGzB,YAAI,CAAC,aAAa,SAAS,GAAG,KAAK,CAAC,aAAa,SAAS,KAAK,GAAG;AAChE,yBAAe,eAAe;AAAA,QAChC;AAEA,iBAAS,KAAK,IAAI,OAAO,cAAc,GAAG,CAAC;AAAA,MAC7C,SAAS,OAAO;AACd,YAAI,OAAO,SAAS;AAClB,kBAAQ,KAAK,6BAA6B,OAAO,EAAE;AAAA,QACrD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAMA,eAAe,mBACb,MACA,aACA,kBACA,kBACA,SACsB;AACtB,QAAM,WAA0B,CAAC;AAEjC,aAAW,YAAY,MAAM;AAC3B,UAAM,MAAM,SAAS;AAGrB,QAAI,aAAa;AACjB,eAAW,mBAAmB,kBAAkB;AAC9C,UAAI,gBAAgB,KAAK,GAAG,GAAG;AAC7B,qBAAa;AACb;AAAA,MACF;AAAA,IACF;AACA,QAAI,WAAY;AAGhB,eAAW,WAAW,aAAa;AAEjC,UAAI,QAAQ,aAAa,0BAA0B;AACjD,YAAI;AACF,gBAAM,cAAc,IAAI,IAAI,GAAG,EAAE;AACjC,cAAI,qBAAqB,YAAY,gBAAgB,SAAS;AAC5D,qBAAS,KAAK;AAAA,cACZ;AAAA,cACA,UAAU,QAAQ;AAAA,cAClB,UAAU,QAAQ;AAAA,cAClB,SAAS,QAAQ;AAAA,cACjB,WAAW,QAAQ;AAAA,cACnB,cAAc;AAAA,YAChB,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AAEd;AAAA,QACF;AAAA,MACF,OAAO;AAEL,YAAI;AACF,gBAAM,QAAQ,IAAI,MAAM,QAAQ,KAAK;AACrC,cAAI,OAAO;AACT,qBAAS,KAAK;AAAA,cACZ,KAAK,QAAQ,aAAa,qBAAqB,YAAY,GAAG,IAAI;AAAA,cAClE,UAAU,QAAQ;AAAA,cAClB,UAAU,QAAQ;AAAA,cAClB,SAAS,QAAQ;AAAA,cACjB,WAAW,QAAQ;AAAA,cACnB,cAAc,MAAM,CAAC;AAAA,YACvB,CAAC;AAAA,UACH;AAAA,QACF,SAAS,OAAO;AACd,cAAI,SAAS;AACX,oBAAQ,MAAM,+BAA+B,QAAQ,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,UACxH;AACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,EAAE,UAAU,eAAe,KAAK,OAAO;AAChD;AAEA,eAAsB,YACpB,MACA,SACA,QAC8B;AAC9B,QAAM,YAAY,KAAK,IAAI;AAG3B,QAAM,gBAAgB,4BAA4B,OAAO;AACzD,QAAM,cAAc;AAAA,IAClB,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,EACF;AAGA,QAAM,mBAAmB,wBAAwB,MAAM;AAGvD,MAAI;AACJ,MAAI;AACF,uBAAmB,IAAI,IAAI,OAAO,EAAE;AAAA,EACtC,SAAS,OAAO;AACd,QAAI,OAAO,SAAS;AAClB,cAAQ,KAAK,qBAAqB,OAAO,wBAAwB;AAAA,IACnE;AACA,uBAAmB;AAAA,EACrB;AAGA,QAAM,aAAa,OAAO,0BAA0B;AACpD,QAAM,cAAc,OAAO,4BAA4B,KAAK,IAAI,GAAG,WAAAC,QAAG,KAAK,EAAE,SAAS,CAAC;AACvF,QAAM,UAAU,WAAW,MAAM,UAAU;AAE3C,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI;AAAA,8BAAiC;AAC7C,YAAQ,IAAI,mBAAmB,KAAK,OAAO,eAAe,CAAC,EAAE;AAC7D,YAAQ,IAAI,mBAAmB,WAAW,eAAe,CAAC,EAAE;AAC5D,YAAQ,IAAI,oBAAoB,WAAW,EAAE;AAC7C,YAAQ,IAAI,sBAAsB,QAAQ,MAAM,EAAE;AAClD,QAAI;AACF,cAAQ,IAAI,oBAAoB,IAAI,IAAI,OAAO,EAAE,QAAQ,EAAE;AAAA,IAC7D,SAAS,OAAO;AACd,cAAQ,IAAI,iBAAiB,OAAO,EAAE;AAAA,IACxC;AACA,QAAI,iBAAiB,SAAS,GAAG;AAC/B,cAAQ,IAAI,0BAA0B,iBAAiB,MAAM,EAAE;AAAA,IACjE;AAAA,EACF;AAGA,MAAI,mBAAmB;AACvB,QAAM,eAAe,QAAQ;AAC7B,QAAM,iBAAiB,KAAK,IAAI;AAGhC,QAAM,eAAe,MAAM;AAAA,IACzB;AAAA,IACA;AAAA,IACA,CAAC,UAAU,mBAAmB,OAAO,aAAa,kBAAkB,kBAAkB,OAAO,OAAO;AAAA,IACpG,CAAC,cAAc;AACb,yBAAmB;AACnB,YAAM,OAAQ,YAAY,eAAgB,KAAK,QAAQ,CAAC;AACxD,YAAM,WAAW,KAAK,IAAI,IAAI,kBAAkB;AAChD,YAAM,gBAAgB,YAAY;AAClC,YAAM,QAAQ,KAAK,MAAM,gBAAgB,OAAO;AAChD,YAAM,YAAY,eAAe;AACjC,YAAM,MAAM,KAAK,MAAO,YAAY,aAAc,KAAK;AAEvD,cAAQ,OAAO;AAAA,QACb,6BAA6B,SAAS,IAAI,YAAY,KAAK,GAAG,cAAc,GAAG,OAAO,MAAM,eAAe,CAAC;AAAA,MAC9G;AAAA,IACF;AAAA,EACF;AAGA,UAAQ,OAAO,MAAM,UAAU;AAG/B,QAAM,cAAc,aAAa,QAAQ,OAAK,EAAE,QAAQ;AAGxD,QAAM,iBAAiB,kBAAkB,WAAW;AAEpD,QAAM,mBAAmB,KAAK,IAAI,IAAI;AAEtC,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI;AAAA,wBAA2B;AACvC,YAAQ,IAAI,4BAA4B,KAAK,OAAO,eAAe,CAAC,EAAE;AACtE,YAAQ,IAAI,wBAAwB,eAAe,cAAc,eAAe,CAAC,EAAE;AACnF,YAAQ,IAAI,sBAAsB,eAAe,iBAAiB,EAAE;AACpE,YAAQ,IAAI,wBAAwB,eAAe,mBAAmB,EAAE;AACxE,YAAQ,IAAI,qBAAqB,eAAe,gBAAgB,EAAE;AAClE,YAAQ,IAAI,yBAAyB,mBAAmB,KAAM,QAAQ,CAAC,CAAC,GAAG;AAE3E,QAAI,eAAe,OAAO,SAAS,GAAG;AACpC,cAAQ,IAAI;AAAA,uBAA0B;AACtC,iBAAW,SAAS,eAAe,QAAQ;AACzC,gBAAQ,IAAI,OAAO,MAAM,QAAQ,KAAK,MAAM,KAAK,UAAU,MAAM,SAAS,YAAY,CAAC,GAAG;AAAA,MAC5F;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU;AAAA,IACV,QAAQ,eAAe;AAAA,IACvB,mBAAmB,KAAK;AAAA,IACxB,cAAc,eAAe;AAAA,IAC7B,eAAe,KAAK,SAAS,eAAe;AAAA,IAC5C,mBAAmB,eAAe;AAAA,IAClC,qBAAqB,eAAe;AAAA,IACpC,kBAAkB,eAAe;AAAA,IACjC;AAAA,EACF;AACF;;;ACnPO,SAAS,eAAe,SAA0C;AACvE,QAAM,oBAAoB;AAAA,IACxB,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,KAAK;AAAA,EACP;AAEA,QAAM,mBAAsC,QAAQ,WAAW,IAAI,WAAS;AAC1E,sBAAkB,MAAM,QAAQ,KAAK,MAAM;AAE3C,UAAM,OAAO,MAAM,WAAW,MAAM;AAEpC,WAAO;AAAA,MACL,UAAU,MAAM;AAAA,MAChB,OAAO,MAAM;AAAA,MACb,UAAU,MAAM;AAAA,MAChB,SAAS,MAAM;AAAA,MACf,UAAU,KAAK,MAAM,GAAG,CAAC;AAAA,MACzB,SAAS;AAAA;AAAA,IACX;AAAA,EACF,CAAC;AAED,QAAM,aAAa,QAAQ,WAAW,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AACzE,QAAM,WAAW,aAAa,IAC1B,SAAS,UAAU,kCAAkC,QAAQ,WAAW,MAAM,kBAAkB,QAAQ,SAAS,iBACjH,YAAY,QAAQ,SAAS;AAEjC,QAAM,cAAwB,CAAC;AAC/B,MAAI,kBAAkB,OAAO,GAAG;AAC9B,gBAAY,KAAK,GAAG,kBAAkB,IAAI,mDAAmD;AAAA,EAC/F;AACA,MAAI,kBAAkB,SAAS,GAAG;AAChC,gBAAY,KAAK,GAAG,kBAAkB,MAAM,4CAA4C;AAAA,EAC1F;AACA,MAAI,kBAAkB,MAAM,GAAG;AAC7B,gBAAY,KAAK,GAAG,kBAAkB,GAAG,2CAA2C;AAAA,EACtF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB,CAAC;AAAA,IAClB,aAAa;AAAA,IACb,UAAU;AAAA,MACR,YAAY;AAAA,MACZ,gBAAgB,QAAQ,kBAAkB;AAAA,MAC1C,OAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC/EA,IAAM,eAAe,OAA6C,kBAAsB;AAuFjF,SAAS,mBACd,SACA,iBACA,aACA,YACA,QACA,WACA,UAA+B,CAAC,GACxB;AACR,QAAM;AAAA,IACJ,SAAS;AAAA,IACT,SAAS;AAAA,IACT;AAAA,EACF,IAAI;AAEJ,QAAM,SAAS;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,aAAa,sBAAsB,QAAQ,kBAAkB;AAEnE,MAAI,QAAQ;AACV,WAAO,KAAK,UAAU,YAAY,MAAM,MAAM;AAAA,EAChD,OAAO;AACL,WAAO,KAAK,UAAU,UAAU;AAAA,EAClC;AACF;AA6BA,SAAS,oBACP,SACA,iBACA,aACA,YACA,QACA,WACgB;AAChB,QAAM,WAAW;AAAA,IACf,OAAO,WAAW;AAAA,IAClB;AAAA,IACA;AAAA,EACF;AAEA,QAAM,mBAAmB,WAAW,IAAI,YAAU;AAAA,IAChD,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,IAChB,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA;AAAA,IACf,WAAW,MAAM;AAAA,IACjB,YAAY,MAAM,WAAW,MAAM,GAAG,CAAC;AAAA;AAAA,IACvC,mBAAmB,MAAM;AAAA,EAC3B,EAAE;AAEF,QAAM,eAA6B;AAAA,IACjC,mBAAmB,QAAQ,kBAAkB;AAAA,IAC7C,qBAAqB,QAAQ,kBAAkB;AAAA,IAC/C,kBAAkB,QAAQ,kBAAkB;AAAA,IAC5C,gBAAgB,WAAW,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAAA,IAC9D,eAAe;AAAA,MACb,QAAQ;AAAA,MACR,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,cAA+B;AAAA,IACnC,UAAU,QAAQ;AAAA,IAClB,aAAa,QAAQ;AAAA,IACrB,iBAAiB,QAAQ;AAAA,EAC3B;AAEA,QAAM,SAAS,YAAY,OAAO,IAAI,cAAc;AAEpD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,oBAAoB,gBAAgB;AAAA,IACpC,eAAe,YAAY;AAAA,IAC3B,cAAc,YAAY;AAAA,IAC1B;AAAA,IACA;AAAA,IACA,SAAS;AAAA,IACT;AAAA,EACF;AACF;AAKA,SAAS,sBACP,SACA,WACA,SACkB;AAClB,SAAO;AAAA,IACL;AAAA,IACA,oBAAmB,oBAAI,KAAK,GAAE,YAAY;AAAA,IAC1C,aAAa;AAAA,IACb,iBAAiB,KAAK,IAAI,IAAI;AAAA,IAC9B,cAAc,QAAQ;AAAA,EACxB;AACF;AAKA,SAAS,uBACP,mBACA,QACqC;AACrC,MAAI,OAAO,SAAS,GAAG;AACrB,WAAO;AAAA,EACT;AAEA,QAAM,cAAc,kBAAkB,OAAO,kBAAkB,SAAS,kBAAkB;AAE1F,SAAO,cAAc,IAAI,iBAAiB;AAC5C;AAKA,SAAS,sBAAsB,QAAwB,oBAAiD;AACtG,QAAM,SAAc;AAAA,IAClB,mBAAmB,kBAAkB,OAAO,gBAAgB;AAAA,IAC5D,qBAAqB,OAAO;AAAA,IAC5B,iBAAiB,OAAO;AAAA,IACxB,eAAe,OAAO;AAAA,IACtB,mBAAmB,OAAO,iBAAiB,IAAI,cAAc;AAAA,IAC7D,cAAc,qBAAqB,OAAO,WAAW;AAAA,IACrD,SAAS,iBAAiB,OAAO,OAAO;AAAA,IACxC,QAAQ,OAAO;AAAA,EACjB;AAGA,MAAI,oBAAoB;AACtB,WAAO,sBAAsB;AAAA,MAC3B,yBAAyB,mBAAmB;AAAA,MAC5C,eAAe,mBAAmB;AAAA,MAClC,YAAY,mBAAmB;AAAA,MAC/B,gBAAgB,mBAAmB;AAAA,IACrC;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,kBAAkB,MAAgC;AACzD,SAAO;AAAA,IACL,UAAU,KAAK;AAAA,IACf,oBAAoB,KAAK;AAAA,IACzB,cAAc,KAAK;AAAA,IACnB,mBAAmB,KAAK;AAAA,IACxB,eAAe,KAAK;AAAA,EACtB;AACF;AAKA,SAAS,eAAe,OAAgC;AACtD,SAAO;AAAA,IACL,UAAU,MAAM;AAAA,IAChB,UAAU,MAAM;AAAA,IAChB,OAAO,MAAM;AAAA,IACb,SAAS,MAAM;AAAA,IACf,WAAW,MAAM;AAAA,IACjB,aAAa,MAAM;AAAA,IACnB,oBAAoB,MAAM;AAAA,EAC5B;AACF;AAKA,SAAS,qBAAqB,SAAkC;AAC9D,SAAO;AAAA,IACL,UAAU,QAAQ;AAAA,IAClB,cAAc,QAAQ;AAAA,IACtB,iBAAiB,QAAQ;AAAA,EAC3B;AACF;AAKA,SAAS,iBAAiB,SAA+B;AACvD,SAAO;AAAA,IACL,qBAAqB,QAAQ;AAAA,IAC7B,uBAAuB,QAAQ;AAAA,IAC/B,oBAAoB,QAAQ;AAAA,IAC5B,kBAAkB,QAAQ;AAAA,IAC1B,gBAAgB,QAAQ;AAAA,EAC1B;AACF;AAKA,SAAS,eAAe,OAA2B;AAEjD,MAAI,UAAU,OAAO;AACnB,UAAM,cAAc;AACpB,UAAM,cAA2B;AAAA,MAC/B,MAAM,YAAY,QAAQ;AAAA,MAC1B,SAAS,MAAM;AAAA,IACjB;AAGA,QAAI,oBAAoB,aAAa;AACnC,kBAAY,UAAU;AAAA,QACpB,iBAAiB,YAAY;AAAA,MAC/B;AAAA,IACF,WAAW,gBAAgB,eAAe,gBAAgB,aAAa;AACrE,kBAAY,UAAU;AAAA,QACpB,aAAa,YAAY;AAAA,QACzB,aAAa,YAAY;AAAA,MAC3B;AAAA,IACF,WAAW,SAAS,aAAa;AAC/B,kBAAY,UAAU;AAAA,QACpB,KAAK,YAAY;AAAA,MACnB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAGA,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS,MAAM;AAAA,EACjB;AACF;;;ACvWA,IAAAC,aAA+B;AAO/B,IAAMC,gBAAe;AASd,SAAS,mBACd,SACA,iBACA,WACA,QACA,QACA,UAA+B,CAAC,GACxB;AACR,QAAM,UAAU,QAAQ,mBAAmB;AAC3C,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY;AACzC,QAAM,gBAAgB,QAAQ,iBAAiB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAGlF,QAAM,eAAe,QAAQ,iBAAiB,OAAO,CAAC,MAAM,EAAE,aAAa,MAAM;AACjF,QAAM,iBAAiB,QAAQ,iBAAiB,OAAO,CAAC,MAAM,EAAE,aAAa,QAAQ;AACrF,QAAM,cAAc,QAAQ,iBAAiB,OAAO,CAAC,MAAM,EAAE,aAAa,KAAK;AAE/E,QAAM,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA,+BAKgB,OAAO,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,eA8T9B,OAAO,OAAO;AAAA,eACd,IAAI,KAAK,SAAS,EAAE,eAAe,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,6BAOtB,gBAAgB,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA,6BAI/B,UAAU,eAAe,CAAC;AAAA;AAAA;AAAA;AAAA,2CAIZ,gBAAgB,IAAI,YAAY,SAAS,KAAK,aAAa;AAAA;AAAA;AAAA;AAAA,8BAIxE,QAAQ,SAAS,iBAAiB,KAAM,QAAQ,CAAC,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA,QAKxE,OAAO,SAAS,IAAI;AAAA;AAAA,yCAEa,OAAO,MAAM;AAAA;AAAA,YAE1C,OAAO,IAAI,SAAO,OAAO,IAAI,OAAO,OAAO,EAAE,KAAK,cAAc,CAAC;AAAA;AAAA;AAAA,UAGnE,EAAE;AAAA;AAAA,QAEJ,gBAAgB,SAAS,SAAS,IAAI;AAAA;AAAA,mFAEqC,gBAAgB,SAAS,MAAM;AAAA;AAAA;AAAA,cAGpG,gBAAgB,SAAS,IAAI,OAAK,cAAS,CAAC,OAAO,EAAE,KAAK,gBAAgB,CAAC;AAAA;AAAA;AAAA;AAAA,UAI/E,EAAE;AAAA;AAAA,QAEJ,kBAAkB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,UAKpB,EAAE;AAAA;AAAA,QAEJ,aAAa,SAAS,IAAI;AAAA;AAAA,iFAE+C,aAAa,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,CAAC;AAAA;AAAA,YAEtH,aAAa,IAAI,WAAS,gBAAgB,OAAO,OAAO,CAAC,EAAE,KAAK,cAAc,CAAC;AAAA;AAAA;AAAA,UAGjF,EAAE;AAAA;AAAA,QAEJ,eAAe,SAAS,IAAI;AAAA;AAAA,qFAEiD,eAAe,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,CAAC;AAAA;AAAA,YAE5H,eAAe,IAAI,WAAS,gBAAgB,OAAO,OAAO,CAAC,EAAE,KAAK,cAAc,CAAC;AAAA;AAAA;AAAA,UAGnF,EAAE;AAAA;AAAA,QAEJ,YAAY,SAAS,IAAI;AAAA;AAAA,+EAE8C,YAAY,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC,CAAC;AAAA;AAAA,YAEnH,YAAY,IAAI,WAAS,gBAAgB,OAAO,OAAO,CAAC,EAAE,KAAK,cAAc,CAAC;AAAA;AAAA;AAAA,UAGhF,EAAE;AAAA;AAAA;AAAA;AAAA,kDAIsCA,aAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmC5D,SAAO;AACT;AAEA,SAAS,gBAAgB,OAAwB,SAAyB;AACxE,QAAM,gBAAgB,MAAM,SACzB,MAAM,GAAG,EACT,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAC1D,KAAK,GAAG;AAEX,QAAM,aAAa,MAAM,SAAS,MAAM,GAAG,OAAO;AAClD,QAAM,YAAY,MAAM,QAAQ,WAAW;AAG3C,QAAM,eAAe,MAAM,SAAS,YAAY;AAGhD,QAAM,cAAc,KAAK,UAAU,MAAM,OAAO;AAChD,QAAM,cAAc,WAAW,WAAW;AAE1C,SAAO;AAAA,cACK,aAAa,wBAAwB,MAAM,KAAK;AAAA,8BAChC,MAAM,OAAO;AAAA;AAAA;AAAA;AAAA,cAI7B,WAAW,IAAI,SAAO,OAAO,WAAW,GAAG,CAAC,OAAO,EAAE,KAAK,gBAAgB,CAAC;AAAA;AAAA,YAE7E,YAAY,IAAI,6BAA6B,SAAS,gBAAgB,EAAE;AAAA,gEACpB,YAAY,OAAO,WAAW,8BAAuB,MAAM,KAAK;AAAA;AAAA;AAGhI;AAEA,SAAS,WAAW,MAAsB;AACxC,SAAO,KACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,QAAQ;AAC3B;AAKA,eAAsB,gBACpB,SACA,iBACA,WACA,QACA,YACA,QACA,UAA+B,CAAC,GACjB;AACf,QAAM,cAAc,mBAAmB,SAAS,iBAAiB,WAAW,QAAQ,QAAQ,OAAO;AACnG,QAAM,WAAAC,SAAG,UAAU,YAAY,aAAa,OAAO;AACrD;;;AlB1dO,IAAM,iBAAiB,IAAI,yBAAQ,SAAS,EAChD,YAAY,+BAA+B,EAC3C,SAAS,SAAS,qBAAqB,EACvC,OAAO,uBAAuB,2BAA2B,IAAI,EAC7D,OAAO,iBAAiB,sBAAsB,EAC9C,OAAO,qBAAqB,+BAA+B,MAAM,EACjE,OAAO,uBAAuB,8BAA8B,EAC5D,OAAO,wBAAwB,wBAAwB,EACvD,OAAO,kCAAkC,+DAA+D,EACxG,OAAO,0BAA0B,iDAAiD,EAClF,OAAO,yBAAyB,qCAAqC,OAAO,EAC5E,OAAO,kCAAkC,wCAAwC,IAAI,EACrF,OAAO,oCAAoC,8CAA8C,IAAI,EAC7F,OAAO,YAAY,6BAA6B,EAChD,OAAO,eAAe,0BAA0B,EAChD,OAAO,cAAc,wCAAwC,EAC7D,OAAO,aAAa,0BAA0B,KAAK,EACnD,OAAO,OAAO,KAAa,YAA4B;AACtD,MAAI;AAEJ,MAAI;AAEF,2BAAuB,OAAO;AAG9B,UAAM,eAAe,MAAM,WAAW;AAAA,MACpC,GAAG;AAAA,MACH,SAAS;AAAA,MACT,cAAc,QAAQ;AAAA,MACtB,0BAA0B,QAAQ,cAAc,SAAS,QAAQ,WAAW,IAAI;AAAA,MAChF,wBAAwB,QAAQ,YAAY,SAAS,QAAQ,SAAS,IAAI;AAAA,MAC1E,oBAAoB,QAAQ,qBAAqB,SAAS,QAAQ,kBAAkB,IAAI;AAAA,MACxF,sBAAsB,QAAQ,uBAAuB,SAAS,QAAQ,oBAAoB,IAAI;AAAA,MAC9F,QAAQ,QAAQ;AAAA,MAChB,WAAW,QAAQ;AAAA,MACnB,aAAa,QAAQ;AAAA,IACvB,CAAC;AACD,aAAS;AAET,YAAQ,IAAI;AAAA,sBAAkB,GAAG;AAAA,CAAO;AAGxC,UAAM,SAAS,MAAM,oBAAoB,KAAK,MAAM;AAGpD,UAAM,WAAAC,SAAG,MAAM,OAAO,WAAW,EAAE,WAAW,KAAK,CAAC;AAGpD,QAAI,QAAQ,WAAW,QAAQ;AAE7B,YAAM,aAAa;AAAA,QACjB,OAAO;AAAA,QACP,OAAO;AAAA,QACP,EAAE,YAAY,OAAO,WAAW,YAAY,CAAC,GAAG,QAAQ,CAAC,EAAE;AAAA,QAC3D,OAAO;AAAA,QACP;AAAA,QACA,OAAO;AAAA,QACP,EAAE,QAAQ,MAAM,QAAQ,EAAE;AAAA,MAC5B;AAGA,cAAQ,IAAI,OAAO,UAAU;AAG7B,UAAI,QAAQ,YAAY;AACtB,cAAM,eAAe,GAAG,OAAO,SAAS,IAAI,QAAQ,UAAU;AAC9D,cAAM,WAAAA,SAAG,UAAU,cAAc,YAAY,OAAO;AACpD,gBAAQ,IAAI;AAAA,kCAA8B,aAAAC,QAAM,KAAK,YAAY,CAAC,EAAE;AAAA,MACtE;AAAA,IACF,OAAO;AAEL,qBAAe,MAAM;AAErB,YAAM,eAAe,QAAQ,cAAc,qBAAqB,KAAK,IAAI,CAAC;AAC1E,YAAM,eAAe,GAAG,OAAO,SAAS,IAAI,YAAY;AACxD,YAAM;AAAA,QACJ,OAAO;AAAA,QACP,OAAO;AAAA,QACP,OAAO;AAAA,QACP;AAAA,QACA;AAAA,QACA,OAAO;AAAA,QACP,EAAE,iBAAiB,GAAG;AAAA,MACxB;AACA,cAAQ,IAAI;AAAA,kCAA8B,aAAAA,QAAM,KAAK,YAAY,CAAC,EAAE;AAAA,IACtE;AAGA,UAAM,WAAW,kBAAkB,MAAM;AACzC,YAAQ,KAAK,QAAQ;AAAA,EAEvB,SAAS,OAAO;AACd,wBAAoB,OAAO,MAAO;AAClC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAKH,SAAS,uBAAuB,SAA+B;AAE7D,QAAM,eAAe,CAAC,QAAQ,MAAM;AACpC,MAAI,CAAC,aAAa,SAAS,QAAQ,MAAM,GAAG;AAC1C,UAAM,IAAI;AAAA,MACR,0BAA0B,QAAQ,MAAM,qBAAqB,aAAa,KAAK,IAAI,CAAC;AAAA,IACtF;AAAA,EACF;AAGA,QAAM,UAAU,SAAS,QAAQ,OAAO;AACxC,MAAI,MAAM,OAAO,KAAK,WAAW,GAAG;AAClC,UAAM,IAAI,MAAM,oBAAoB,QAAQ,OAAO,8BAA8B;AAAA,EACnF;AACF;AAKA,SAAS,eAAe,QAAsC;AAC5D,QAAM,gBAAgB,OAAO,QAAQ,iBAAiB,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAGzF,UAAQ,IAAI,aAAAA,QAAM,IAAI,SAAI,OAAO,EAAE,CAAC,CAAC;AAErC,MAAI,kBAAkB,GAAG;AACvB,YAAQ,IAAI,aAAAA,QAAM,MAAM,wCAAwC,CAAC;AAAA,EACnE,OAAO;AAEL,UAAM,EAAE,MAAM,QAAQ,IAAI,IAAI,OAAO,QAAQ;AAC7C,UAAM,gBAAgB,CAAC;AACvB,QAAI,OAAO,EAAG,eAAc,KAAK,aAAAA,QAAM,IAAI,SAAS,IAAI,EAAE,CAAC;AAC3D,QAAI,SAAS,EAAG,eAAc,KAAK,aAAAA,QAAM,OAAO,WAAW,MAAM,EAAE,CAAC;AACpE,QAAI,MAAM,EAAG,eAAc,KAAK,aAAAA,QAAM,KAAK,QAAQ,GAAG,EAAE,CAAC;AAEzD,UAAM,kBAAkB,cAAc,SAAS,IAAI,KAAK,cAAc,KAAK,IAAI,CAAC,MAAM;AACtF,YAAQ,IAAI,aAAAA,QAAM,OAAO,iBAAO,aAAa,oBAAoB,eAAe,EAAE,CAAC;AAAA,EACrF;AACA,UAAQ,IAAI,EAAE;AAChB;AAKA,eAAe,oBACb,KACA,QACiC;AACjC,QAAM,mBAAmB,KAAK,IAAI;AAClC,QAAM,eAA8B,CAAC;AACrC,QAAM,SAAkB,CAAC;AAEzB,QAAM,eAAe,CAAC,OAAO,UAAU,OAAO,gBAAgB,SAAS,QAAQ,OAAO;AAGtF,MAAI,aAAa,KAAK,IAAI;AAC1B,QAAM,mBAAmB,mBAAe,WAAAC,SAAI,EAAE,MAAM,2BAA2B,OAAO,OAAO,CAAC,EAAE,MAAM,IAAI;AAE1G,QAAM,kBAAkB,MAAM,iBAAiB,KAAK,MAAM;AAE1D,MAAI,kBAAkB;AACpB,qBAAiB,KAAK;AAAA,EACxB;AAEA,eAAa,KAAK;AAAA,IAChB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS,KAAK,IAAI;AAAA,IAClB,UAAU,KAAK,IAAI,IAAI;AAAA,EACzB,CAAC;AAGD,MAAI,gBAAgB,aAAa,SAAS,GAAG;AAC3C,QAAI,CAAC,OAAO,QAAQ;AAClB,cAAQ,KAAK,aAAAD,QAAM,OAAO,0BAAgB,gBAAgB,aAAa,MAAM,gCAAgC,CAAC;AAAA,IAChH;AACA,eAAW,SAAS,gBAAgB,cAAc;AAChD,aAAO,KAAK,IAAI,MAAM,mBAAmB,MAAM,GAAG,KAAK,MAAM,UAAU,GAAG,CAAC;AAAA,IAC7E;AAAA,EACF;AAEA,MAAI,gBAAgB,SAAS,WAAW,GAAG;AACzC,UAAM,IAAI,MAAM,wBAAwB,GAAG,wDAAwD;AAAA,EACrG;AAGA,eAAa,KAAK,IAAI;AACtB,MAAI;AACJ,MAAI,gBAAgB,gBAAgB,SAAS,SAAS,IAAI;AACxD,UAAM,WAAW,IAAI,oBAAAE,QAAY,UAAU;AAAA,MACzC,QAAQ;AAAA,MACR,iBAAiB;AAAA,MACjB,mBAAmB;AAAA,MACnB,YAAY;AAAA,IACd,CAAC;AAED,aAAS,MAAM,gBAAgB,SAAS,QAAQ,GAAG,EAAE,OAAO,IAAI,CAAC;AAEjE,uBAAmB,MAAM;AAAA,MACvB,gBAAgB;AAAA,MAChB;AAAA,MACA,CAAC,WAAW,UAAU;AACpB,cAAM,WAAW,KAAK,IAAI,IAAI,cAAc;AAC5C,cAAM,QAAQ,UAAU,KAAK,YAAY,SAAS,QAAQ,CAAC,IAAI;AAC/D,iBAAS,OAAO,WAAW,EAAE,MAAM,CAAC;AAAA,MACtC;AAAA,IACF;AAEA,aAAS,KAAK;AAAA,EAChB,OAAO;AACL,uBAAmB,MAAM,eAAe,gBAAgB,UAAU,MAAM;AAAA,EAC1E;AAEA,eAAa,KAAK;AAAA,IAChB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS,KAAK,IAAI;AAAA,IAClB,UAAU,KAAK,IAAI,IAAI;AAAA,EACzB,CAAC;AAKD,MAAI,iBAAiB,OAAO,SAAS,GAAG;AACtC,eAAW,OAAO,iBAAiB,QAAQ;AACzC,UAAI,OAAO,QAAQ,UAAU;AAC3B,eAAO,KAAK,IAAI,MAAM,GAAG,CAAC;AAAA,MAC5B,OAAO;AACL,eAAO,KAAK,GAAG;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,MAAI,iBAAiB,QAAQ,WAAW,GAAG;AACzC,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AAGA,eAAa,KAAK,IAAI;AACtB,QAAM,qBAAqB,gBAAgB,iBAAiB,OAAO;AAEnE,eAAa,KAAK;AAAA,IAChB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS,KAAK,IAAI;AAAA,IAClB,UAAU,KAAK,IAAI,IAAI;AAAA,EACzB,CAAC;AAED,QAAM,oBAAoB,iBAAiB,QAAQ,SAAS,mBAAmB,WAAW;AAC1F,QAAM,sBAAuB,oBAAoB,iBAAiB,QAAQ,SAAU;AAEpF,MAAI,CAAC,OAAO,QAAQ;AAElB,QAAI,oBAAoB,OAAO,sBAAsB,GAAG;AAEtD,cAAQ,IAAI,aAAAF,QAAM,MAAM,SAAS,gBAAgB,SAAS,MAAM,sBAAiB,iBAAiB,QAAQ,OAAO,eAAe,CAAC,UAAU,mBAAmB,WAAW,OAAO,eAAe,CAAC,UAAU,CAAC;AAAA,IAC7M,OAAO;AAEL,cAAQ,IAAI,aAAAA,QAAM,MAAM,SAAS,gBAAgB,SAAS,MAAM,sBAAiB,iBAAiB,QAAQ,OAAO,eAAe,CAAC,OAAO,CAAC;AAAA,IAC3I;AAAA,EACF;AAGA,eAAa,KAAK,IAAI;AACtB,QAAM,aAAa,MAAM,YAAY,mBAAmB,YAAY,KAAK,MAAM;AAC/E,QAAM,aAAa,kBAAkB,WAAW,QAAQ;AAExD,eAAa,KAAK;AAAA,IAChB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS,KAAK,IAAI;AAAA,IAClB,UAAU,KAAK,IAAI,IAAI;AAAA,EACzB,CAAC;AAGD,eAAa,KAAK,IAAI;AACtB,QAAM,gBAAgB,KAAK,IAAI,IAAI;AACnC,QAAM,UAAU,eAAe;AAAA,IAC7B,YAAY,WAAW;AAAA,IACvB,WAAW,mBAAmB,WAAW;AAAA,IACzC,YAAY;AAAA,IACZ,gBAAgB;AAAA,EAClB,CAAC;AAED,eAAa,KAAK;AAAA,IAChB,MAAM;AAAA,IACN,WAAW;AAAA,IACX,SAAS,KAAK,IAAI;AAAA,IAClB,UAAU,KAAK,IAAI,IAAI;AAAA,EACzB,CAAC;AAGD,MAAI,CAAC,OAAO,UAAU,OAAO,SAAS;AACpC,wBAAoB,cAAc,aAAa;AAAA,EACjD,WAAW,CAAC,OAAO,QAAQ;AAEzB,UAAM,eAAe,aAAa,KAAK,OAAK,EAAE,SAAS,SAAS;AAChE,UAAM,iBAAiB,gBAAgB,gBAAgB,SAAS,UAAU,aAAa,WAAW,MAAO,QAAQ,CAAC,IAAI;AAEtH,YAAQ,IAAI,aAAAA,QAAM,MAAM,uBAAuB,gBAAgB,KAAM,QAAQ,CAAC,CAAC,UAAO,cAAc;AAAA,CAAkB,CAAC;AAAA,EACzH;AAGA,MAAI,OAAO,WAAW;AACpB,UAAM,cAAc,cAAc,KAAK,eAAe,gBAAgB,SAAS,QAAQ,mBAAmB,WAAW,QAAQ,MAAM;AAAA,EACrI;AAEA,SAAO;AAAA,IACL;AAAA,IACA,WAAW,mBAAmB,WAAW;AAAA,IACzC,YAAY,WAAW;AAAA,IACvB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,kBAAkB,QAAwC;AAKjE,QAAM,oBAAoB,OAAO,QAAQ,kBAAkB;AAE3D,MAAI,oBAAoB,GAAG;AACzB,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAKA,SAAS,oBAAoB,OAAgB,QAAuB;AAClE,UAAQ,MAAM,4BAAuB;AAErC,MAAI,iBAAiB,OAAO;AAC1B,YAAQ,MAAM,UAAU,MAAM,OAAO,EAAE;AAEvC,QAAI,QAAQ,WAAW,MAAM,OAAO;AAClC,cAAQ,MAAM,gBAAgB;AAC9B,cAAQ,MAAM,MAAM,KAAK;AAAA,IAC3B;AAGA,QAAI,MAAM,QAAQ,SAAS,mBAAmB,GAAG;AAC/C,cAAQ,MAAM,gBAAgB;AAC9B,cAAQ,MAAM,yCAAoC;AAClD,cAAQ,MAAM,0CAAqC;AACnD,cAAQ,MAAM,oDAA+C;AAAA,IAC/D,WAAW,MAAM,QAAQ,SAAS,SAAS,KAAK,MAAM,QAAQ,SAAS,SAAS,GAAG;AACjF,cAAQ,MAAM,gBAAgB;AAC9B,cAAQ,MAAM,yCAAoC;AAClD,cAAQ,MAAM,uCAAkC;AAChD,cAAQ,MAAM,2DAAsD;AAAA,IACtE;AAAA,EACF,OAAO;AACL,YAAQ,MAAM,wBAAwB;AACtC,YAAQ,MAAM,OAAO,KAAK,CAAC;AAAA,EAC7B;AACF;AAKA,SAAS,oBAAoB,SAAwB,WAAyB;AAC5E,UAAQ,IAAI,aAAAA,QAAM,MAAM;AAAA,6BAAgC,YAAY,KAAM,QAAQ,CAAC,CAAC;AAAA,CAAM,CAAC;AAC3F,UAAQ,IAAI,aAAAA,QAAM,KAAK,kBAAkB,CAAC;AAE1C,aAAW,UAAU,SAAS;AAC5B,UAAM,WAAW,OAAO,WAAW,KAAM,QAAQ,CAAC;AAClD,UAAM,cAAe,OAAO,WAAW,YAAa,KAAK,QAAQ,CAAC;AAClE,UAAM,MAAM;AAEZ,YAAQ,IAAI,KAAK,GAAG,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC,KAAK,QAAQ,SAAS,CAAC,CAAC,MAAM,WAAW,SAAS,CAAC,CAAC,IAAI;AAAA,EACxG;AAEA,UAAQ,IAAI,EAAE;AAChB;AAKA,eAAe,cACb,SACA,KACA,WACA,cACA,UACA,QACe;AACf,QAAM,YAAY;AAAA,IAChB,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC;AAAA,IACA,mBAAmB;AAAA,IACnB,QAAQ,QAAQ,IAAI,QAAM;AAAA,MACxB,MAAM,EAAE,KAAK,YAAY;AAAA,MACzB,UAAU,EAAE;AAAA,MACZ,QAAQ,EAAE;AAAA,MACV,aAAa,EAAE;AAAA,IACjB,EAAE;AAAA,IACF,SAAS;AAAA,MACP,oBAAoB;AAAA,MACpB,eAAe;AAAA,MACf,YAAY;AAAA,QACV,iBAAiB,KAAK,MAAO,WAAW,YAAa,GAAI;AAAA,QACzD,sBAAuB,eAAe,YAAa,KAAM,QAAQ,CAAC;AAAA,MACpE;AAAA,IACF;AAAA,IACA,aAAa;AAAA,MACX,WAAW,WAAAG,QAAG,KAAK,EAAE;AAAA,MACrB,cAAc,QAAQ;AAAA,MACtB,UAAU,QAAQ;AAAA,MAClB,iBAAiB,KAAK,MAAM,WAAAA,QAAG,SAAS,IAAI,OAAO,IAAI;AAAA,IACzD;AAAA,IACA,QAAQ;AAAA,MACN,uBAAuB,OAAO;AAAA,MAC9B,qBAAqB,OAAO;AAAA,MAC5B,4BAA4B,OAAO;AAAA,MACnC,2BAA2B,OAAO;AAAA,IACpC;AAAA,EACF;AAEA,QAAM,WAAW,uBAAuB,KAAK,IAAI,CAAC;AAClD,QAAM,WAAAJ,SAAG,UAAU,UAAU,KAAK,UAAU,WAAW,MAAM,CAAC,CAAC;AAC/D,UAAQ,IAAI,aAAAC,QAAM,KAAK,iCAA0B,QAAQ,EAAE,CAAC;AAC9D;;;ADheA,IAAM,UAAU,IAAI,0BAAQ;AAE5B,QACG,KAAK,YAAY,EACjB,QAAQ,OAAO,EACf,YAAY,+BAA+B;AAE9C,QAAQ,WAAW,cAAc;AAGjC,QAAQ,GAAG,sBAAsB,CAAC,QAAQ,YAAY;AACpD,UAAQ,MAAM,2BAA2B,SAAS,WAAW,MAAM;AACnE,UAAQ,KAAK,CAAC;AAChB,CAAC;AAGD,QAAQ,GAAG,UAAU,MAAM;AACzB,UAAQ,IAAI,+BAA+B;AAC3C,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,QAAQ,GAAG,WAAW,MAAM;AAC1B,UAAQ,IAAI,+BAA+B;AAC3C,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,QAAQ,MAAM;","names":["import_config","import_commander","import_fs","import_os","HttpAgent","HttpsAgent","axios","escapedRoot","pattern","import_os","os","import_fs","TOOL_VERSION","fs","fs","chalk","ora","cliProgress","os"]}
package/dist/index.d.cts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node