commit-report 1.0.1 → 1.0.2
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.
- package/README.md +2 -2
- package/dist/index.js +1794 -463
- package/dist/index.js.map +1 -1
- package/package.json +5 -3
- package/templates/report-scripts/00-advanced-derived.html +462 -0
- package/templates/report-scripts/00-filter-state.html +374 -0
- package/templates/report-scripts/00-report-controls.html +272 -0
- package/templates/report-scripts/01-core.html +255 -0
- package/templates/report-scripts/02-commit-details.html +275 -0
- package/templates/report-scripts/03-basic-charts.html +378 -0
- package/templates/report-scripts/04-trend-charts.html +309 -0
- package/templates/report-scripts/05-tables-team-stability.html +372 -0
- package/templates/report-scripts/06-pressure-churn.html +339 -0
- package/templates/report-scripts/07-collab-debt-ai.html +534 -0
- package/templates/report-scripts/08-engineering.html +200 -0
- package/templates/report-scripts/09-extensions.html +313 -0
- package/templates/report-scripts/10-runtime.html +54 -0
- package/templates/report-sections/01-overview.html +342 -0
- package/templates/report-sections/02-advanced.html +406 -0
- package/templates/report.html +40 -1998
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/index.ts","../src/scanner/index.ts","../src/analyzer/index.ts","../src/analyzer/git-log-parser.ts","../src/analyzer/stats-calculator.ts","../src/analyzer/advanced/team-health.ts","../src/analyzer/advanced/code-stability.ts","../src/analyzer/advanced/work-pressure.ts","../src/analyzer/advanced/contributor-churn.ts","../src/analyzer/advanced/collaboration.ts","../src/analyzer/advanced/index.ts","../src/reporter/index.ts","../src/reporter/html-builder.ts","../src/cli/time-utils.ts"],"sourcesContent":["import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { checkbox } from '@inquirer/prompts';\nimport { scanRepositories } from '../scanner/index.js';\nimport { analyzeRepos } from '../analyzer/index.js';\nimport { generateReport } from '../reporter/index.js';\nimport { parsePeriod, resolveTimeRange } from './time-utils.js';\nimport type { CliOptions, RepoInfo } from '../types/index.js';\n\nconst program = new Command();\n\nprogram\n .name('commit-report')\n .description('Git 提交统计工具,生成可视化 HTML 报告')\n .version('1.0.0')\n .argument('[directory]', '要扫描的目录路径', process.cwd())\n .option('-p, --period <period>', '时间预设 (7d/1m/3m/6m/1y/all)', 'all')\n .option('-f, --from <date>', '起始日期 (YYYY-MM-DD)')\n .option('-t, --to <date>', '结束日期 (YYYY-MM-DD)')\n .option('-a, --author <name>', '过滤作者')\n .option('-o, --output <file>', '输出文件名', 'commit-report.html')\n .option('--no-open', '不自动打开浏览器')\n .option('-d, --depth <number>', '最大扫描深度', '20')\n .action(async (directory: string, opts: CliOptions) => {\n try {\n await run(directory, opts);\n } catch (error) {\n if (error instanceof Error && error.message === 'USER_CANCEL') {\n console.log(chalk.yellow('\\n已取消操作'));\n process.exit(0);\n }\n console.error(chalk.red(`\\n错误: ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n });\n\nasync function run(directory: string, opts: CliOptions): Promise<void> {\n // 1. 检查 Git 是否安装\n await checkGitInstalled();\n\n // 2. 解析时间范围\n const timeRange = resolveTimeRange(opts);\n\n // 3. 扫描仓库\n const repos = await scanRepositories({\n targetDir: directory,\n maxDepth: Number(opts.depth),\n });\n\n if (repos.length === 0) {\n console.log(chalk.red('未找到 Git 仓库'));\n process.exit(1);\n }\n\n // 4. 选择仓库\n let selectedRepos: RepoInfo[];\n\n if (repos.length === 1) {\n selectedRepos = repos;\n console.log(chalk.cyan(`找到 1 个 Git 仓库: ${repos[0].name}`));\n } else {\n console.log(chalk.cyan(`\\n找到 ${repos.length} 个 Git 仓库:\\n`));\n\n const selected = await checkbox<string>({\n message: '选择要分析的仓库(空格选择,回车确认)',\n choices: repos.map((repo) => ({\n name: `${repo.name} (${repo.commitCount} commits)`,\n value: repo.path,\n checked: true,\n })),\n });\n\n if (selected.length === 0) {\n console.log(chalk.yellow('未选择任何仓库'));\n process.exit(0);\n }\n\n selectedRepos = repos.filter((r) => selected.includes(r.path));\n }\n\n const timeRangeText = timeRange\n ? `${formatDate(timeRange.from)} ~ ${formatDate(timeRange.to)}`\n : '所有提交';\n console.log(\n chalk.gray(\n `\\n已选择 ${selectedRepos.length} 个仓库,时间范围:${timeRangeText}\\n`\n )\n );\n\n // 5. 分析提交记录\n const stats = await analyzeRepos({\n repos: selectedRepos,\n timeRange,\n author: opts.author,\n });\n\n if (stats.totalCommits === 0) {\n console.log(chalk.yellow('该时间段无提交记录'));\n }\n\n // 6. 生成报告\n await generateReport(stats, {\n outputPath: opts.output,\n autoOpen: opts.open,\n timeRange,\n repoNames: selectedRepos.map((r) => r.name),\n });\n}\n\nasync function checkGitInstalled(): Promise<void> {\n const { execSync } = await import('child_process');\n try {\n execSync('git --version', { stdio: 'ignore' });\n } catch {\n console.error(chalk.red('请先安装 Git'));\n process.exit(1);\n }\n}\n\nfunction formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n\n// 处理 Ctrl+C\nprocess.on('SIGINT', () => {\n console.log(chalk.yellow('\\n已取消操作'));\n process.exit(0);\n});\n\nprogram.parse();\n","import { readdir, stat, access } from 'node:fs/promises';\nimport { join, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { confirm } from '@inquirer/prompts';\nimport type { ScanOptions, RepoInfo } from '../types/index.js';\n\n/** 扫描时忽略的目录名 */\nconst IGNORE_DIRS = new Set([\n 'node_modules',\n '.git',\n 'vendor',\n 'dist',\n 'build',\n '.cache',\n '.next',\n '.nuxt',\n '__pycache__',\n 'target',\n]);\n\n/**\n * 递归扫描目录,查找所有 Git 仓库\n */\nexport async function scanRepositories(options: ScanOptions): Promise<RepoInfo[]> {\n const spinner = ora('扫描仓库中...').start();\n const repos: RepoInfo[] = [];\n let deepScanConfirmed = false;\n\n async function scan(dir: string, depth: number): Promise<void> {\n // 深度超过限制时,提示用户确认\n if (depth > options.maxDepth && !deepScanConfirmed) {\n spinner.stop();\n const shouldContinue = await confirm({\n message: `扫描深度已超过 ${options.maxDepth} 层,是否继续?`,\n default: false,\n });\n\n if (!shouldContinue) {\n return;\n }\n deepScanConfirmed = true;\n spinner.start('继续扫描中...');\n }\n\n // 检查当前目录是否有 .git\n try {\n const gitDir = join(dir, '.git');\n await access(gitDir);\n\n // 找到 .git,获取仓库信息\n const repoInfo = getRepoInfo(dir);\n if (repoInfo) {\n repos.push(repoInfo);\n spinner.text = `扫描仓库中... 已找到 ${repos.length} 个`;\n }\n // 找到 .git 后不再深层继续(避免子模块重复)\n return;\n } catch {\n // 没有 .git,继续扫描子目录\n }\n\n // 递归扫描子目录\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (IGNORE_DIRS.has(entry.name)) continue;\n if (entry.name.startsWith('.') && entry.name !== '.git') continue;\n\n await scan(join(dir, entry.name), depth + 1);\n }\n } catch {\n // 权限不足时跳过\n }\n }\n\n await scan(options.targetDir, 0);\n\n if (repos.length > 0) {\n spinner.succeed(`找到 ${repos.length} 个 Git 仓库`);\n } else {\n spinner.fail('未找到 Git 仓库');\n }\n\n return repos;\n}\n\n/**\n * 获取单个仓库的基本信息\n */\nfunction getRepoInfo(repoPath: string): RepoInfo | null {\n try {\n const countStr = execSync('git rev-list --count HEAD', {\n cwd: repoPath,\n stdio: ['pipe', 'pipe', 'ignore'],\n encoding: 'utf-8',\n }).trim();\n\n const commitCount = parseInt(countStr, 10) || 0;\n const name = basename(repoPath);\n\n return {\n path: repoPath,\n name,\n commitCount,\n };\n } catch {\n // 仓库可能损坏或为空\n return null;\n }\n}\n","import ora from 'ora';\nimport chalk from 'chalk';\nimport { parseGitLog } from './git-log-parser.js';\nimport { calculateStats, mergeStats } from './stats-calculator.js';\nimport { calculateAdvancedStats } from './advanced/index.js';\nimport type { AnalyzeOptions, CommitStats } from '../types/index.js';\n\n/**\n * 分析所有选定仓库的提交记录\n */\nexport async function analyzeRepos(options: AnalyzeOptions): Promise<CommitStats> {\n const { repos, timeRange, author } = options;\n const spinner = ora('分析提交记录...').start();\n const allStats: CommitStats[] = [];\n\n for (let i = 0; i < repos.length; i++) {\n const repo = repos[i];\n spinner.text = `分析提交记录 (${i + 1}/${repos.length}) - ${repo.name}`;\n\n try {\n const commits = await parseGitLog(repo.path, timeRange, author);\n\n if (commits.length > 100000) {\n spinner.info(\n chalk.yellow(`${repo.name} 包含 ${commits.length.toLocaleString()} 条提交,处理可能需要一些时间...`)\n );\n spinner.start();\n }\n\n const stats = calculateStats(commits);\n\n // 新增:计算高级统计\n const advancedStats = calculateAdvancedStats(commits);\n\n // 合并核心统计和高级统计\n const fullStats: CommitStats = {\n ...stats,\n ...advancedStats,\n };\n\n allStats.push(fullStats);\n } catch (error) {\n spinner.warn(\n chalk.yellow(\n `跳过仓库 ${repo.name}: ${error instanceof Error ? error.message : '未知错误'}`\n )\n );\n spinner.start();\n }\n }\n\n const merged = mergeStats(allStats);\n\n spinner.succeed(\n `分析完成: ${merged.totalCommits.toLocaleString()} 条提交, ` +\n `${merged.authors.length} 位作者, ` +\n `+${merged.linesAdded.toLocaleString()} / -${merged.linesDeleted.toLocaleString()} 行`\n );\n\n return merged;\n}\n","import { execSync } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport ig from 'ignore';\nimport type { CommitRecord, FileChange, TimeRange } from '../types/index.js';\n\n/** git log 的格式化分隔符 */\nconst COMMIT_SEPARATOR = '---COMMITX_SEP---';\nconst FIELD_SEPARATOR = '|';\nconst FORMAT = `${COMMIT_SEPARATOR}%H${FIELD_SEPARATOR}%an${FIELD_SEPARATOR}%ae${FIELD_SEPARATOR}%aI${FIELD_SEPARATOR}%s`;\n\n/**\n * 从指定仓库解析 git log,返回提交记录数组\n * timeRange 为 null 时表示获取所有提交\n */\nexport async function parseGitLog(\n repoPath: string,\n timeRange: TimeRange | null,\n author?: string\n): Promise<CommitRecord[]> {\n const ignoreFilter = await loadGitignore(repoPath);\n\n const args = [\n 'git',\n 'log',\n `--format=\"${FORMAT}\"`,\n '--numstat',\n ];\n\n if (timeRange) {\n args.push(`--since=\"${timeRange.from.toISOString()}\"`);\n args.push(`--until=\"${timeRange.to.toISOString()}\"`);\n }\n\n if (author) {\n args.push(`--author=\"${author}\"`);\n }\n\n let output: string;\n try {\n output = execSync(args.join(' '), {\n cwd: repoPath,\n encoding: 'utf-8',\n maxBuffer: 100 * 1024 * 1024, // 100MB buffer for large repos\n stdio: ['pipe', 'pipe', 'ignore'],\n });\n } catch {\n return [];\n }\n\n if (!output.trim()) {\n return [];\n }\n\n return parseOutput(output, ignoreFilter);\n}\n\n/**\n * 解析 git log 输出文本为 CommitRecord 数组\n */\nfunction parseOutput(\n output: string,\n ignoreFilter: ReturnType<typeof ig>\n): CommitRecord[] {\n const commits: CommitRecord[] = [];\n const blocks = output.split(COMMIT_SEPARATOR).filter((b) => b.trim());\n\n for (const block of blocks) {\n const lines = block.trim().split('\\n');\n if (lines.length === 0) continue;\n\n // 第一行是提交元信息\n const headerLine = lines[0].replace(/^\"|\"$/g, '');\n const parts = headerLine.split(FIELD_SEPARATOR);\n if (parts.length < 5) continue;\n\n const [hash, authorName, email, dateStr, ...messageParts] = parts;\n const message = messageParts.join(FIELD_SEPARATOR); // message 可能包含 |\n\n // 后续行是 numstat(新增\\t删除\\t文件路径)\n const files: FileChange[] = [];\n for (let i = 1; i < lines.length; i++) {\n const line = lines[i].trim();\n if (!line) continue;\n\n const tabParts = line.split('\\t');\n if (tabParts.length !== 3) continue;\n\n const [addedStr, deletedStr, filePath] = tabParts;\n\n // 二进制文件显示为 -\n const added = addedStr === '-' ? 0 : parseInt(addedStr, 10) || 0;\n const deleted = deletedStr === '-' ? 0 : parseInt(deletedStr, 10) || 0;\n\n // 应用 gitignore 过滤\n if (ignoreFilter.ignores(filePath)) continue;\n\n files.push({ added, deleted, path: filePath });\n }\n\n commits.push({\n hash,\n author: authorName,\n email,\n date: new Date(dateStr),\n message,\n files,\n });\n }\n\n return commits;\n}\n\n/**\n * 读取仓库的 .gitignore 规则\n */\nasync function loadGitignore(repoPath: string): ReturnType<typeof ig> {\n const ignoreInstance = ig();\n\n try {\n const content = await readFile(join(repoPath, '.gitignore'), 'utf-8');\n ignoreInstance.add(content);\n } catch {\n // 没有 .gitignore 文件,跳过\n }\n\n // 默认忽略一些常见的生成文件\n ignoreInstance.add([\n 'package-lock.json',\n 'pnpm-lock.yaml',\n 'yarn.lock',\n ]);\n\n return ignoreInstance;\n}\n","import type {\n CommitRecord,\n CommitStats,\n AuthorStats,\n FileTypeStats,\n DirectoryStats,\n BusiestDay,\n QualityMetrics,\n TimePatterns,\n TrendData,\n CollaborationMetrics,\n CommitMessageStats,\n AuthorFileTypeContribution,\n HotFile,\n WeeklyPoint,\n CumulativePoint,\n SoloFile,\n CollabFile,\n} from '../types/index.js';\nimport { extname } from 'node:path';\n\n/**\n * 根据解析后的提交记录,计算所有统计维度\n */\nexport function calculateStats(commits: CommitRecord[]): CommitStats {\n if (commits.length === 0) {\n return emptyStats();\n }\n\n // 排序:按日期升序\n const sorted = [...commits].sort(\n (a, b) => a.date.getTime() - b.date.getTime()\n );\n\n // 基础统计\n let totalLinesAdded = 0;\n let totalLinesDeleted = 0;\n const allFilePaths = new Set<string>();\n\n // 作者维度\n const authorMap = new Map<string, AuthorStats>();\n\n // 文件类型维度\n const fileTypeMap = new Map<string, FileTypeStats>();\n\n // 目录维度\n const directoryMap = new Map<string, DirectoryStats>();\n const directoryCommitSet = new Map<string, Set<string>>(); // dir -> Set<commitHash>\n\n // 时间分布\n const hourlyDistribution = new Array<number>(24).fill(0);\n const dailyHeatmap: Record<string, number> = {};\n const hourlyByAuthor: Map<number, Map<string, number>> = new Map(); // hour -> (author -> count)\n\n // 每日统计(用于计算最繁忙的一天)\n const dailyCounts = new Map<string, number>();\n\n for (const commit of sorted) {\n // 时间分布\n const hour = commit.date.getHours();\n hourlyDistribution[hour]++;\n\n // 按小时统计作者\n if (!hourlyByAuthor.has(hour)) {\n hourlyByAuthor.set(hour, new Map());\n }\n const hourAuthors = hourlyByAuthor.get(hour)!;\n hourAuthors.set(commit.author, (hourAuthors.get(commit.author) || 0) + 1);\n\n const dateKey = formatDateKey(commit.date);\n dailyHeatmap[dateKey] = (dailyHeatmap[dateKey] || 0) + 1;\n dailyCounts.set(dateKey, (dailyCounts.get(dateKey) || 0) + 1);\n\n // 作者统计\n const authorKey = commit.email.toLowerCase();\n let authorStat = authorMap.get(authorKey);\n if (!authorStat) {\n authorStat = {\n name: commit.author,\n email: commit.email,\n commits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n lastActiveDate: commit.date,\n };\n authorMap.set(authorKey, authorStat);\n }\n authorStat.commits++;\n authorStat.lastActiveDate = commit.date;\n\n // 文件级别统计\n for (const file of commit.files) {\n totalLinesAdded += file.added;\n totalLinesDeleted += file.deleted;\n allFilePaths.add(file.path);\n\n // 作者行数统计\n authorStat.linesAdded += file.added;\n authorStat.linesDeleted += file.deleted;\n\n // 文件类型统计\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n let ftStat = fileTypeMap.get(ext);\n if (!ftStat) {\n ftStat = { extension: ext, added: 0, deleted: 0, fileCount: 0 };\n fileTypeMap.set(ext, ftStat);\n }\n ftStat.added += file.added;\n ftStat.deleted += file.deleted;\n\n // 目录统计 —— 取第一层目录\n const topDir = getTopDirectory(file.path);\n let dirStat = directoryMap.get(topDir);\n if (!dirStat) {\n dirStat = { path: topDir, commits: 0, linesChanged: 0 };\n directoryMap.set(topDir, dirStat);\n directoryCommitSet.set(topDir, new Set());\n }\n dirStat.linesChanged += file.added + file.deleted;\n directoryCommitSet.get(topDir)!.add(commit.hash);\n }\n }\n\n // 计算目录的 commit 数量\n for (const [dir, commitSet] of directoryCommitSet) {\n const dirStat = directoryMap.get(dir);\n if (dirStat) {\n dirStat.commits = commitSet.size;\n }\n }\n\n // 计算文件类型的 fileCount\n const fileCountByExt = new Map<string, Set<string>>();\n for (const filePath of allFilePaths) {\n const ext = extname(filePath).toLowerCase() || '(无扩展名)';\n if (!fileCountByExt.has(ext)) {\n fileCountByExt.set(ext, new Set());\n }\n fileCountByExt.get(ext)!.add(filePath);\n }\n for (const [ext, files] of fileCountByExt) {\n const ftStat = fileTypeMap.get(ext);\n if (ftStat) {\n ftStat.fileCount = files.size;\n }\n }\n\n // 最繁忙的一天\n let busiestDay: BusiestDay = { date: '', count: 0 };\n for (const [date, count] of dailyCounts) {\n if (count > busiestDay.count) {\n busiestDay = { date, count };\n }\n }\n\n // 排序结果\n const authors = Array.from(authorMap.values()).sort(\n (a, b) => b.commits - a.commits\n );\n const fileTypes = Array.from(fileTypeMap.values()).sort(\n (a, b) => b.added + b.deleted - (a.added + a.deleted)\n );\n const directories = Array.from(directoryMap.values())\n .sort((a, b) => b.linesChanged - a.linesChanged)\n .slice(0, 10); // TOP 10\n\n // 转换 hourlyByAuthor 为数组格式\n const hourlyByAuthorArray = Array.from({ length: 24 }, (_, hour) => {\n const authorMap = hourlyByAuthor.get(hour);\n const authors: Record<string, number> = {};\n if (authorMap) {\n authorMap.forEach((count, author) => {\n authors[author] = count;\n });\n }\n return {\n count: hourlyDistribution[hour],\n authors,\n };\n });\n\n return {\n totalCommits: sorted.length,\n linesAdded: totalLinesAdded,\n linesDeleted: totalLinesDeleted,\n filesChanged: allFilePaths.size,\n firstCommitDate: sorted[0].date,\n lastCommitDate: sorted[sorted.length - 1].date,\n busiestDay,\n authors,\n fileTypes,\n directories,\n hourlyDistribution,\n dailyHeatmap,\n hourlyByAuthor: hourlyByAuthorArray,\n quality: calculateQualityMetrics(sorted),\n timePatterns: calculateTimePatterns(sorted),\n trends: calculateTrends(sorted),\n collaboration: calculateCollaboration(sorted),\n messageStats: calculateMessageStats(sorted),\n authorFileTypeContributions: calculateAuthorFileTypeContributions(sorted),\n };\n}\n\n/**\n * 合并多个仓库的统计结果\n */\nexport function mergeStats(statsList: CommitStats[]): CommitStats {\n if (statsList.length === 0) return emptyStats();\n if (statsList.length === 1) return statsList[0];\n\n const merged = emptyStats();\n\n for (const stats of statsList) {\n merged.totalCommits += stats.totalCommits;\n merged.linesAdded += stats.linesAdded;\n merged.linesDeleted += stats.linesDeleted;\n merged.filesChanged += stats.filesChanged;\n\n // 时间维度\n if (\n !merged.firstCommitDate ||\n stats.firstCommitDate < merged.firstCommitDate\n ) {\n merged.firstCommitDate = stats.firstCommitDate;\n }\n if (\n !merged.lastCommitDate ||\n stats.lastCommitDate > merged.lastCommitDate\n ) {\n merged.lastCommitDate = stats.lastCommitDate;\n }\n\n // 小时分布\n for (let i = 0; i < 24; i++) {\n merged.hourlyDistribution[i] += stats.hourlyDistribution[i];\n }\n\n // 每日热力图\n for (const [date, count] of Object.entries(stats.dailyHeatmap)) {\n merged.dailyHeatmap[date] = (merged.dailyHeatmap[date] || 0) + count;\n }\n\n // 作者合并\n for (const author of stats.authors) {\n const existing = merged.authors.find(\n (a) => a.email.toLowerCase() === author.email.toLowerCase()\n );\n if (existing) {\n existing.commits += author.commits;\n existing.linesAdded += author.linesAdded;\n existing.linesDeleted += author.linesDeleted;\n if (author.lastActiveDate > existing.lastActiveDate) {\n existing.lastActiveDate = author.lastActiveDate;\n }\n } else {\n merged.authors.push({ ...author });\n }\n }\n\n // 文件类型合并\n for (const ft of stats.fileTypes) {\n const existing = merged.fileTypes.find(\n (f) => f.extension === ft.extension\n );\n if (existing) {\n existing.added += ft.added;\n existing.deleted += ft.deleted;\n existing.fileCount += ft.fileCount;\n } else {\n merged.fileTypes.push({ ...ft });\n }\n }\n\n // 目录合并\n for (const dir of stats.directories) {\n const existing = merged.directories.find((d) => d.path === dir.path);\n if (existing) {\n existing.commits += dir.commits;\n existing.linesChanged += dir.linesChanged;\n } else {\n merged.directories.push({ ...dir });\n }\n }\n\n // 质量指标合并\n merged.quality.avgFilesPerCommit += stats.quality.avgFilesPerCommit;\n merged.quality.avgLinesPerCommit += stats.quality.avgLinesPerCommit;\n merged.quality.churnRate += stats.quality.churnRate;\n for (const hf of stats.quality.hotFiles) {\n const existing = merged.quality.hotFiles.find((h) => h.path === hf.path);\n if (existing) {\n existing.modifyCount += hf.modifyCount;\n for (const author of hf.authors) {\n if (!existing.authors.includes(author)) {\n existing.authors.push(author);\n }\n }\n } else {\n merged.quality.hotFiles.push({ ...hf, authors: [...hf.authors] });\n }\n }\n\n // 时间模式合并\n for (let i = 0; i < 7; i++) {\n merged.timePatterns.weekdayDistribution[i] +=\n stats.timePatterns.weekdayDistribution[i];\n }\n\n // 趋势数据合并\n for (const wp of stats.trends.weeklyTrend) {\n const existing = merged.trends.weeklyTrend.find((w) => w.week === wp.week);\n if (existing) {\n existing.commits += wp.commits;\n existing.linesAdded += wp.linesAdded;\n existing.linesDeleted += wp.linesDeleted;\n } else {\n merged.trends.weeklyTrend.push({ ...wp });\n }\n }\n for (const cp of stats.trends.cumulativeLines) {\n const existing = merged.trends.cumulativeLines.find(\n (c) => c.date === cp.date\n );\n if (existing) {\n existing.netLines += cp.netLines;\n } else {\n merged.trends.cumulativeLines.push({ ...cp });\n }\n }\n\n // 协作指标合并\n for (const sf of stats.collaboration.soloFiles) {\n const existing = merged.collaboration.soloFiles.find(\n (s) => s.path === sf.path\n );\n if (existing) {\n existing.commits += sf.commits;\n } else {\n merged.collaboration.soloFiles.push({ ...sf });\n }\n }\n for (const ch of stats.collaboration.collaborationHotspots) {\n const existing = merged.collaboration.collaborationHotspots.find(\n (c) => c.path === ch.path\n );\n if (existing) {\n existing.totalCommits += ch.totalCommits;\n existing.authorCount = Math.max(existing.authorCount, ch.authorCount);\n } else {\n merged.collaboration.collaborationHotspots.push({ ...ch });\n }\n }\n\n // Commit Message 统计合并\n for (const [type, count] of Object.entries(stats.messageStats.typeDistribution)) {\n merged.messageStats.typeDistribution[type] =\n (merged.messageStats.typeDistribution[type] || 0) + count;\n }\n merged.messageStats.avgMessageLength += stats.messageStats.avgMessageLength;\n }\n\n // 重新计算最繁忙的一天\n let busiestDay: BusiestDay = { date: '', count: 0 };\n for (const [date, count] of Object.entries(merged.dailyHeatmap)) {\n if (count > busiestDay.count) {\n busiestDay = { date, count };\n }\n }\n merged.busiestDay = busiestDay;\n\n // 排序\n merged.authors.sort((a, b) => b.commits - a.commits);\n merged.fileTypes.sort(\n (a, b) => b.added + b.deleted - (a.added + a.deleted)\n );\n merged.directories.sort((a, b) => b.linesChanged - a.linesChanged);\n merged.directories = merged.directories.slice(0, 10);\n\n // 重新计算扩展字段的平均值和排序\n const repoCount = statsList.length;\n merged.quality.avgFilesPerCommit /= repoCount;\n merged.quality.avgLinesPerCommit /= repoCount;\n merged.quality.churnRate /= repoCount;\n merged.quality.hotFiles.sort((a, b) => b.modifyCount - a.modifyCount);\n merged.quality.hotFiles = merged.quality.hotFiles.slice(0, 10);\n\n // 时间模式重新计算\n const totalWeekdayCommits = merged.timePatterns.weekdayDistribution.reduce(\n (a, b) => a + b,\n 0\n );\n if (totalWeekdayCommits > 0) {\n merged.timePatterns.weekendCommits =\n (merged.timePatterns.weekdayDistribution[5] +\n merged.timePatterns.weekdayDistribution[6]) /\n totalWeekdayCommits;\n }\n\n // 趋势数据排序\n merged.trends.weeklyTrend.sort((a, b) => a.week.localeCompare(b.week));\n merged.trends.cumulativeLines.sort((a, b) => a.date.localeCompare(b.date));\n\n // 重新计算累计代码量\n let cumulative = 0;\n for (const point of merged.trends.cumulativeLines) {\n cumulative += point.netLines;\n point.netLines = cumulative;\n }\n\n // 协作指标排序\n merged.collaboration.soloFiles.sort((a, b) => b.commits - a.commits);\n merged.collaboration.soloFiles = merged.collaboration.soloFiles.slice(0, 10);\n merged.collaboration.collaborationHotspots.sort(\n (a, b) => b.totalCommits - a.totalCommits\n );\n merged.collaboration.collaborationHotspots =\n merged.collaboration.collaborationHotspots.slice(0, 10);\n\n // Commit Message 平均长度\n merged.messageStats.avgMessageLength /= repoCount;\n\n // 作者文件类型贡献合并\n const contributionMap = new Map<string, AuthorFileTypeContribution>();\n for (const stats of statsList) {\n for (const contrib of stats.authorFileTypeContributions) {\n const key = `${contrib.email.toLowerCase()}|||${contrib.extension}`;\n const existing = contributionMap.get(key);\n if (existing) {\n existing.linesAdded += contrib.linesAdded;\n existing.linesDeleted += contrib.linesDeleted;\n existing.commits += contrib.commits;\n existing.fileCount += contrib.fileCount;\n } else {\n contributionMap.set(key, { ...contrib });\n }\n }\n }\n merged.authorFileTypeContributions = Array.from(contributionMap.values())\n .sort((a, b) => {\n const totalA = a.linesAdded + a.linesDeleted;\n const totalB = b.linesAdded + b.linesDeleted;\n return totalB - totalA;\n })\n .slice(0, 20);\n\n // ============================================================\n // 高级统计字段不进行多仓库合并\n // ============================================================\n // 原因:高级统计(teamHealth, stability, workPressure, contributorChurn, advancedCollaboration)\n // 需要原始 CommitRecord[] 数据才能准确计算。在 mergeStats 中仅有聚合后的统计数据,\n // 强行合并会导致结果不准确(例如 busFactor、revertRate 等指标无法简单累加)。\n //\n // 当前行为:\n // - 单仓库场景:直接返回(第184行),保留所有高级统计\n // - 多仓库场景:merged 中高级字段保持 undefined\n //\n // 如需多仓库的高级统计分析,建议:\n // 1. 使用时间范围过滤单个仓库\n // 2. 或在 analyzer/index.ts 中基于合并后的原始 commits 重新计算\n\n return merged;\n}\n\n/** 创建空的统计对象 */\nfunction emptyStats(): CommitStats {\n return {\n totalCommits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n filesChanged: 0,\n firstCommitDate: new Date(),\n lastCommitDate: new Date(),\n busiestDay: { date: '', count: 0 },\n authors: [],\n fileTypes: [],\n directories: [],\n hourlyDistribution: new Array<number>(24).fill(0),\n dailyHeatmap: {},\n quality: emptyQualityMetrics(),\n timePatterns: emptyTimePatterns(),\n trends: emptyTrendData(),\n collaboration: emptyCollaborationMetrics(),\n messageStats: emptyMessageStats(),\n authorFileTypeContributions: [],\n };\n}\n\n/** 获取文件路径的第一层目录 */\nfunction getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\n/** 格式化日期为 YYYY-MM-DD */\nfunction formatDateKey(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n}\n\n// ============================================================\n// 扩展统计计算函数\n// ============================================================\n\n/** 计算代码质量指标 */\nfunction calculateQualityMetrics(commits: CommitRecord[]): QualityMetrics {\n if (commits.length === 0) {\n return emptyQualityMetrics();\n }\n\n // 平均每次提交的文件数\n const totalFiles = commits.reduce((sum, c) => sum + c.files.length, 0);\n const avgFilesPerCommit = totalFiles / commits.length;\n\n // 平均每次提交的行数\n const totalLines = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.added + f.deleted, 0),\n 0\n );\n const avgLinesPerCommit = totalLines / commits.length;\n\n // 代码流失率\n const totalAdded = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.added, 0),\n 0\n );\n const totalDeleted = commits.reduce(\n (sum, c) => sum + c.files.reduce((s, f) => s + f.deleted, 0),\n 0\n );\n const churnRate = totalAdded > 0 ? totalDeleted / totalAdded : 0;\n\n // 热点文件\n const fileModifyMap = new Map<string, { count: number; authors: Set<string> }>();\n for (const commit of commits) {\n for (const file of commit.files) {\n const entry = fileModifyMap.get(file.path) || { count: 0, authors: new Set() };\n entry.count++;\n entry.authors.add(commit.author);\n fileModifyMap.set(file.path, entry);\n }\n }\n\n const hotFiles: HotFile[] = Array.from(fileModifyMap.entries())\n .map(([path, data]) => ({\n path,\n modifyCount: data.count,\n authors: Array.from(data.authors),\n }))\n .sort((a, b) => b.modifyCount - a.modifyCount)\n .slice(0, 10);\n\n return { avgFilesPerCommit, avgLinesPerCommit, churnRate, hotFiles };\n}\n\n/** 计算时间模式指标 */\nfunction calculateTimePatterns(commits: CommitRecord[]): TimePatterns {\n if (commits.length === 0) {\n return emptyTimePatterns();\n }\n\n const weekdayDistribution = new Array<number>(7).fill(0);\n const weekdayByAuthor: Map<number, Map<string, number>> = new Map(); // weekday -> (author -> count)\n\n for (const commit of commits) {\n const day = commit.date.getDay(); // 0=周日\n const idx = day === 0 ? 6 : day - 1; // 转为周一=0\n weekdayDistribution[idx]++;\n\n // 按周几统计作者\n if (!weekdayByAuthor.has(idx)) {\n weekdayByAuthor.set(idx, new Map());\n }\n const dayAuthors = weekdayByAuthor.get(idx)!;\n dayAuthors.set(commit.author, (dayAuthors.get(commit.author) || 0) + 1);\n }\n\n // 周末提交占比\n const weekendCommits =\n (weekdayDistribution[5] + weekdayDistribution[6]) / commits.length;\n\n // 提交间隔\n const sorted = [...commits].sort(\n (a, b) => a.date.getTime() - b.date.getTime()\n );\n let totalInterval = 0;\n for (let i = 1; i < sorted.length; i++) {\n totalInterval += sorted[i].date.getTime() - sorted[i - 1].date.getTime();\n }\n const avgCommitInterval =\n sorted.length > 1 ? totalInterval / (sorted.length - 1) / 3600000 : 0;\n\n // 连续提交天数\n const { longestStreak, currentStreak } = calculateStreaks(sorted);\n\n // 转换 weekdayByAuthor 为数组格式\n const weekdayByAuthorArray = Array.from({ length: 7 }, (_, day) => {\n const authorMap = weekdayByAuthor.get(day);\n const authors: Record<string, number> = {};\n if (authorMap) {\n authorMap.forEach((count, author) => {\n authors[author] = count;\n });\n }\n return {\n count: weekdayDistribution[day],\n authors,\n };\n });\n\n return {\n weekdayDistribution,\n weekendCommits,\n avgCommitInterval,\n longestStreak,\n currentStreak,\n weekdayByAuthor: weekdayByAuthorArray,\n };\n}\n\n/** 计算连续提交天数 */\nfunction calculateStreaks(sortedCommits: CommitRecord[]): {\n longestStreak: number;\n currentStreak: number;\n} {\n if (sortedCommits.length === 0) {\n return { longestStreak: 0, currentStreak: 0 };\n }\n\n // 提取唯一日期\n const uniqueDates = new Set<string>();\n for (const commit of sortedCommits) {\n uniqueDates.add(formatDateKey(commit.date));\n }\n\n const sortedDates = Array.from(uniqueDates).sort();\n if (sortedDates.length === 0) {\n return { longestStreak: 0, currentStreak: 0 };\n }\n\n let longestStreak = 1;\n let currentStreakCount = 1;\n let tempStreak = 1;\n\n for (let i = 1; i < sortedDates.length; i++) {\n const prevDate = new Date(sortedDates[i - 1]);\n const currDate = new Date(sortedDates[i]);\n const diffDays = Math.round(\n (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (diffDays === 1) {\n tempStreak++;\n } else {\n tempStreak = 1;\n }\n\n longestStreak = Math.max(longestStreak, tempStreak);\n }\n\n // 计算当前连续天数(从最后一天往前数)\n const today = formatDateKey(new Date());\n const lastCommitDate = sortedDates[sortedDates.length - 1];\n\n // 如果最后提交日期是今天或昨天,计算当前连续\n const lastDate = new Date(lastCommitDate);\n const todayDate = new Date(today);\n const daysSinceLastCommit = Math.round(\n (todayDate.getTime() - lastDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (daysSinceLastCommit <= 1) {\n currentStreakCount = 1;\n for (let i = sortedDates.length - 2; i >= 0; i--) {\n const currDate = new Date(sortedDates[i + 1]);\n const prevDate = new Date(sortedDates[i]);\n const diffDays = Math.round(\n (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (diffDays === 1) {\n currentStreakCount++;\n } else {\n break;\n }\n }\n } else {\n currentStreakCount = 0;\n }\n\n return { longestStreak, currentStreak: currentStreakCount };\n}\n\n/** 获取 ISO 周标识 */\nfunction getWeekKey(date: Date): string {\n const d = new Date(date);\n d.setHours(0, 0, 0, 0);\n d.setDate(d.getDate() + 4 - (d.getDay() || 7));\n const yearStart = new Date(d.getFullYear(), 0, 1);\n const weekNo = Math.ceil(\n ((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7\n );\n return `${d.getFullYear()}-W${String(weekNo).padStart(2, '0')}`;\n}\n\n/** 计算趋势数据 */\nfunction calculateTrends(commits: CommitRecord[]): TrendData {\n if (commits.length === 0) {\n return emptyTrendData();\n }\n\n // 周趋势\n const weekMap = new Map<string, WeeklyPoint>();\n for (const commit of commits) {\n const week = getWeekKey(commit.date);\n const entry = weekMap.get(week) || {\n week,\n commits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n };\n entry.commits++;\n for (const file of commit.files) {\n entry.linesAdded += file.added;\n entry.linesDeleted += file.deleted;\n }\n weekMap.set(week, entry);\n }\n const weeklyTrend = Array.from(weekMap.values()).sort((a, b) =>\n a.week.localeCompare(b.week)\n );\n\n // 累计代码量\n const dailyNet = new Map<string, number>();\n for (const commit of commits) {\n const dateKey = formatDateKey(commit.date);\n const net = commit.files.reduce((sum, f) => sum + f.added - f.deleted, 0);\n dailyNet.set(dateKey, (dailyNet.get(dateKey) || 0) + net);\n }\n\n let cumulative = 0;\n const cumulativeLines: CumulativePoint[] = Array.from(dailyNet.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([date, net]) => {\n cumulative += net;\n return { date, netLines: cumulative };\n });\n\n return { weeklyTrend, cumulativeLines };\n}\n\n/** 计算协作指标 */\nfunction calculateCollaboration(commits: CommitRecord[]): CollaborationMetrics {\n if (commits.length === 0) {\n return emptyCollaborationMetrics();\n }\n\n const fileAuthors = new Map<string, Set<string>>();\n const fileCommits = new Map<string, number>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const authors = fileAuthors.get(file.path) || new Set();\n authors.add(commit.email.toLowerCase());\n fileAuthors.set(file.path, authors);\n fileCommits.set(file.path, (fileCommits.get(file.path) || 0) + 1);\n }\n }\n\n const soloFiles: SoloFile[] = [];\n const collaborationHotspots: CollabFile[] = [];\n\n for (const [path, authors] of fileAuthors) {\n const commitCount = fileCommits.get(path) || 0;\n if (authors.size === 1 && commitCount >= 3) {\n soloFiles.push({\n path,\n author: Array.from(authors)[0],\n commits: commitCount,\n });\n } else if (authors.size >= 2 && commitCount >= 5) {\n collaborationHotspots.push({\n path,\n authorCount: authors.size,\n totalCommits: commitCount,\n });\n }\n }\n\n return {\n soloFiles: soloFiles.sort((a, b) => b.commits - a.commits).slice(0, 10),\n collaborationHotspots: collaborationHotspots\n .sort((a, b) => b.totalCommits - a.totalCommits)\n .slice(0, 10),\n };\n}\n\n/** 计算 Commit Message 统计 */\nfunction calculateMessageStats(commits: CommitRecord[]): CommitMessageStats {\n if (commits.length === 0) {\n return emptyMessageStats();\n }\n\n const typeDistribution: Record<string, number> = {};\n let totalLength = 0;\n\n const typeRegex =\n /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\\(.+\\))?:/i;\n\n for (const commit of commits) {\n totalLength += commit.message.length;\n const match = commit.message.match(typeRegex);\n if (match) {\n const type = match[1].toLowerCase();\n typeDistribution[type] = (typeDistribution[type] || 0) + 1;\n } else {\n typeDistribution['other'] = (typeDistribution['other'] || 0) + 1;\n }\n }\n\n return {\n typeDistribution,\n avgMessageLength: totalLength / commits.length,\n };\n}\n\n/** 计算作者文件类型贡献 */\nfunction calculateAuthorFileTypeContributions(\n commits: CommitRecord[]\n): AuthorFileTypeContribution[] {\n if (commits.length === 0) {\n return [];\n }\n\n // 数据结构: Map<author-email-extension, contribution>\n const contributionMap = new Map<string, AuthorFileTypeContribution>();\n\n // 跟踪每个作者-扩展名组合修改的唯一文件\n const uniqueFilesMap = new Map<string, Set<string>>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n const key = `${commit.email.toLowerCase()}|||${ext}`;\n\n let contribution = contributionMap.get(key);\n if (!contribution) {\n contribution = {\n author: commit.author,\n email: commit.email,\n extension: ext,\n linesAdded: 0,\n linesDeleted: 0,\n commits: 0,\n fileCount: 0,\n };\n contributionMap.set(key, contribution);\n uniqueFilesMap.set(key, new Set());\n }\n\n contribution.linesAdded += file.added;\n contribution.linesDeleted += file.deleted;\n uniqueFilesMap.get(key)!.add(file.path);\n }\n }\n\n // 统计每个组合的提交数(去重)\n const commitCountMap = new Map<string, Set<string>>();\n for (const commit of commits) {\n for (const file of commit.files) {\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n const key = `${commit.email.toLowerCase()}|||${ext}`;\n\n if (!commitCountMap.has(key)) {\n commitCountMap.set(key, new Set());\n }\n commitCountMap.get(key)!.add(commit.hash);\n }\n }\n\n // 更新 commits 和 fileCount\n for (const [key, contribution] of contributionMap) {\n contribution.commits = commitCountMap.get(key)?.size || 0;\n contribution.fileCount = uniqueFilesMap.get(key)?.size || 0;\n }\n\n // 按总变更行数(增+删)降序排序,取 TOP 20\n return Array.from(contributionMap.values())\n .sort((a, b) => {\n const totalA = a.linesAdded + a.linesDeleted;\n const totalB = b.linesAdded + b.linesDeleted;\n return totalB - totalA;\n })\n .slice(0, 20);\n}\n\n// ============================================================\n// 空值工厂函数\n// ============================================================\n\nfunction emptyQualityMetrics(): QualityMetrics {\n return {\n avgFilesPerCommit: 0,\n avgLinesPerCommit: 0,\n churnRate: 0,\n hotFiles: [],\n };\n}\n\nfunction emptyTimePatterns(): TimePatterns {\n return {\n weekdayDistribution: new Array<number>(7).fill(0),\n weekendCommits: 0,\n avgCommitInterval: 0,\n longestStreak: 0,\n currentStreak: 0,\n };\n}\n\nfunction emptyTrendData(): TrendData {\n return {\n weeklyTrend: [],\n cumulativeLines: [],\n };\n}\n\nfunction emptyCollaborationMetrics(): CollaborationMetrics {\n return {\n soloFiles: [],\n collaborationHotspots: [],\n };\n}\n\nfunction emptyMessageStats(): CommitMessageStats {\n return {\n typeDistribution: {},\n avgMessageLength: 0,\n };\n}\n","import type { CommitRecord, TeamHealthMetrics, CriticalAuthor } from '../../types/index.js';\n\n/**\n * 计算团队健康度指标\n */\nexport function calculateTeamHealth(commits: CommitRecord[]): TeamHealthMetrics {\n // 边界情况:空提交\n if (commits.length === 0) {\n return emptyTeamHealth();\n }\n\n // Step 1: 构建数据结构\n const authorFiles = buildAuthorFilesMap(commits);\n const fileAuthors = buildFileAuthorsMap(commits);\n\n // 边界情况:无文件变更\n if (fileAuthors.size === 0) {\n return emptyTeamHealth();\n }\n\n // Step 2: 计算每个作者的知识独占度\n const authorScores = calculateAuthorScores(authorFiles, fileAuthors);\n\n // Step 3: 筛选关键人员(评分 >10)\n const criticalAuthors = authorScores.filter((a) => a.knowledgeScore > 10);\n\n // Step 4: 计算知识分布均匀度\n const totalUniqueFiles = criticalAuthors.reduce(\n (sum, a) => sum + a.uniqueFiles.length,\n 0\n );\n const knowledgeDistribution = 1 - totalUniqueFiles / fileAuthors.size;\n\n // Step 5: 评估风险等级\n const busFactor = criticalAuthors.length;\n const riskLevel = busFactor === 1 ? 'high' : busFactor <= 3 ? 'medium' : 'low';\n\n return {\n busFactor,\n criticalAuthors,\n knowledgeDistribution,\n riskLevel,\n };\n}\n\n/**\n * 构建 作者 -> 文件集合 映射\n */\nfunction buildAuthorFilesMap(commits: CommitRecord[]): Map<string, Set<string>> {\n const map = new Map<string, Set<string>>();\n\n for (const commit of commits) {\n const authorKey = `${commit.author} <${commit.email}>`;\n if (!map.has(authorKey)) {\n map.set(authorKey, new Set());\n }\n const files = map.get(authorKey)!;\n for (const file of commit.files) {\n files.add(file.path);\n }\n }\n\n return map;\n}\n\n/**\n * 构建 文件 -> (作者 -> 提交次数) 映射\n */\nfunction buildFileAuthorsMap(commits: CommitRecord[]): Map<string, Map<string, number>> {\n const map = new Map<string, Map<string, number>>();\n\n for (const commit of commits) {\n const authorKey = `${commit.author} <${commit.email}>`;\n for (const file of commit.files) {\n if (!map.has(file.path)) {\n map.set(file.path, new Map());\n }\n const authors = map.get(file.path)!;\n authors.set(authorKey, (authors.get(authorKey) || 0) + 1);\n }\n }\n\n return map;\n}\n\n/**\n * 计算每个作者的知识独占度评分\n */\nfunction calculateAuthorScores(\n authorFiles: Map<string, Set<string>>,\n fileAuthors: Map<string, Map<string, number>>\n): CriticalAuthor[] {\n const scores: CriticalAuthor[] = [];\n\n for (const [authorKey, files] of authorFiles) {\n const uniqueFiles: string[] = [];\n const dominantFiles: string[] = [];\n\n for (const filePath of files) {\n const authors = fileAuthors.get(filePath)!;\n const authorCount = authors.size;\n\n // 独有文件:只有该作者修改过\n if (authorCount === 1) {\n uniqueFiles.push(filePath);\n continue;\n }\n\n // 主导文件:该作者贡献 >50%\n const authorCommits = authors.get(authorKey) || 0;\n const totalCommits = Array.from(authors.values()).reduce((a, b) => a + b, 0);\n if (authorCommits / totalCommits > 0.5) {\n dominantFiles.push(filePath);\n }\n }\n\n // 知识独占度评分 = (独有文件*2 + 主导文件) / 总文件数 * 100\n const knowledgeScore =\n ((uniqueFiles.length * 2 + dominantFiles.length) / fileAuthors.size) * 100;\n\n const [name, email] = parseAuthorKey(authorKey);\n scores.push({\n name,\n email,\n uniqueFiles,\n dominantFiles,\n knowledgeScore,\n });\n }\n\n return scores;\n}\n\n/**\n * 解析作者键 \"name <email>\" -> [name, email]\n */\nfunction parseAuthorKey(authorKey: string): [string, string] {\n const match = authorKey.match(/^(.+) <(.+)>$/);\n if (match) {\n return [match[1], match[2]];\n }\n return [authorKey, ''];\n}\n\nfunction emptyTeamHealth(): TeamHealthMetrics {\n return {\n busFactor: 0,\n criticalAuthors: [],\n knowledgeDistribution: 1,\n riskLevel: 'low',\n };\n}\n","import type { CommitRecord, StabilityMetrics, FileChurn, DirectoryChurn } from '../../types/index.js';\n\n/**\n * 计算代码稳定性指标\n */\nexport function calculateStability(commits: CommitRecord[]): StabilityMetrics {\n if (commits.length === 0) {\n return emptyStability();\n }\n\n // Step 1: 统计文件级流失率\n const fileStats = new Map<string, { added: number; deleted: number; modifyCount: number }>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const stat = fileStats.get(file.path) || { added: 0, deleted: 0, modifyCount: 0 };\n stat.added += file.added;\n stat.deleted += file.deleted;\n stat.modifyCount++;\n fileStats.set(file.path, stat);\n }\n }\n\n // 转换为 FileChurn 数组,按流失率排序,取 TOP 20\n const fileChurnRate: FileChurn[] = Array.from(fileStats.entries())\n .map(([path, { added, deleted, modifyCount }]) => ({\n path,\n added,\n deleted,\n churnRate: added > 0 ? deleted / added : 0,\n modifyCount,\n isUnstable: deleted / Math.max(added, 1) > 0.5,\n }))\n .sort((a, b) => b.churnRate - a.churnRate)\n .slice(0, 20);\n\n // Step 2: 统计目录级流失率\n const dirStats = new Map<string, { added: number; deleted: number; files: Set<string> }>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const dir = getTopDirectory(file.path);\n const stat = dirStats.get(dir) || { added: 0, deleted: 0, files: new Set() };\n stat.added += file.added;\n stat.deleted += file.deleted;\n stat.files.add(file.path);\n dirStats.set(dir, stat);\n }\n }\n\n const directoryChurnRate: DirectoryChurn[] = Array.from(dirStats.entries())\n .map(([path, { added, deleted, files }]) => ({\n path,\n churnRate: added > 0 ? deleted / added : 0,\n totalChanges: added + deleted,\n fileCount: files.size,\n }))\n .sort((a, b) => b.churnRate - a.churnRate)\n .slice(0, 10);\n\n // Step 3: 计算 Revert 率\n const revertCommits = commits.filter((c) => /revert|rollback/i.test(c.message)).length;\n const revertRate = revertCommits / commits.length;\n\n // Step 4: 计算 Fix 提交率\n const fixCommits = commits.filter((c) => /^fix(\\(.+\\))?:/i.test(c.message)).length;\n const fixCommitRate = fixCommits / commits.length;\n\n // Step 5: 计算稳定性评分\n const avgChurnRate =\n fileChurnRate.length > 0\n ? fileChurnRate.reduce((sum, f) => sum + f.churnRate, 0) / fileChurnRate.length\n : 0;\n\n const stabilityScore = Math.max(\n 0,\n Math.round(100 - (avgChurnRate * 50 + revertRate * 100 + fixCommitRate * 50))\n );\n\n return {\n fileChurnRate,\n directoryChurnRate,\n revertRate: Math.round(revertRate * 100) / 100,\n fixCommitRate: Math.round(fixCommitRate * 100) / 100,\n stabilityScore,\n };\n}\n\n/**\n * 获取文件路径的第一层目录\n */\nfunction getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\nfunction emptyStability(): StabilityMetrics {\n return {\n fileChurnRate: [],\n directoryChurnRate: [],\n revertRate: 0,\n fixCommitRate: 0,\n stabilityScore: 100,\n };\n}\n","import type { CommitRecord, WorkPressureMetrics, HolidayCommit } from '../../types/index.js';\n\n/**\n * 计算工作压力指标\n */\nexport function calculateWorkPressure(commits: CommitRecord[]): WorkPressureMetrics {\n if (commits.length === 0) {\n return emptyWorkPressure();\n }\n\n let lateNightCommits = 0;\n let earlyMorningCommits = 0;\n let weekendCommits = 0;\n\n const holidayMap = new Map<string, { name: string; commits: number }>();\n const holidays = getHolidays();\n\n for (const commit of commits) {\n const hour = commit.date.getHours();\n const day = commit.date.getDay();\n const dateKey = formatDate(commit.date);\n\n // 深夜 (23:00-02:00)\n if (hour >= 23 || hour < 2) {\n lateNightCommits++;\n }\n\n // 凌晨 (02:00-06:00)\n if (hour >= 2 && hour < 6) {\n earlyMorningCommits++;\n }\n\n // 周末\n if (day === 0 || day === 6) {\n weekendCommits++;\n }\n\n // 假期\n const holiday = holidays.get(dateKey);\n if (holiday) {\n const entry = holidayMap.get(dateKey) || { name: holiday, commits: 0 };\n entry.commits++;\n holidayMap.set(dateKey, entry);\n }\n }\n\n const holidayCommits: HolidayCommit[] = Array.from(holidayMap.entries())\n .map(([date, { name, commits }]) => ({ date, holidayName: name, commits }))\n .sort((a, b) => b.commits - a.commits);\n\n // 非工作时间占比\n const holidayTotal = holidayCommits.reduce((sum, h) => sum + h.commits, 0);\n const offHoursCount = lateNightCommits + earlyMorningCommits + weekendCommits + holidayTotal;\n const offHoursRate = offHoursCount / commits.length;\n\n // 压力评分 (0-100)\n const lateNightWeight = (lateNightCommits / commits.length) * 40;\n const earlyMorningWeight = (earlyMorningCommits / commits.length) * 30;\n const weekendWeight = (weekendCommits / commits.length) * 20;\n const holidayWeight = (holidayCommits.length > 0 ? 1 : 0) * 10;\n\n const pressureScore = Math.round(lateNightWeight + earlyMorningWeight + weekendWeight + holidayWeight);\n\n return {\n lateNightCommits,\n earlyMorningCommits,\n weekendCommits,\n holidayCommits,\n pressureScore,\n offHoursRate: Math.round(offHoursRate * 1000) / 1000\n };\n}\n\nfunction emptyWorkPressure(): WorkPressureMetrics {\n return {\n lateNightCommits: 0,\n earlyMorningCommits: 0,\n weekendCommits: 0,\n holidayCommits: [],\n pressureScore: 0,\n offHoursRate: 0,\n };\n}\n\n/**\n * 获取中国 2024-2026 主要节假日\n */\nfunction getHolidays(): Map<string, string> {\n const holidays = new Map<string, string>();\n\n // 2024\n const dates2024: Array<[string, string]> = [\n ['2024-01-01', '元旦'],\n ['2024-02-10', '春节'], ['2024-02-11', '春节'], ['2024-02-12', '春节'],\n ['2024-04-04', '清明节'], ['2024-04-05', '清明节'], ['2024-04-06', '清明节'],\n ['2024-05-01', '劳动节'], ['2024-05-02', '劳动节'], ['2024-05-03', '劳动节'],\n ['2024-06-10', '端午节'],\n ['2024-09-15', '中秋节'], ['2024-09-16', '中秋节'], ['2024-09-17', '中秋节'],\n ['2024-10-01', '国庆节'], ['2024-10-02', '国庆节'], ['2024-10-03', '国庆节']\n ];\n\n // 2025\n const dates2025: Array<[string, string]> = [\n ['2025-01-01', '元旦'],\n ['2025-01-29', '春节'], ['2025-01-30', '春节'], ['2025-01-31', '春节'],\n ['2025-04-04', '清明节'], ['2025-04-05', '清明节'], ['2025-04-06', '清明节'],\n ['2025-05-01', '劳动节'], ['2025-05-02', '劳动节'], ['2025-05-03', '劳动节'],\n ['2025-05-31', '端午节'],\n ['2025-10-01', '国庆节'], ['2025-10-02', '国庆节'], ['2025-10-06', '中秋节']\n ];\n\n // 2026\n const dates2026: Array<[string, string]> = [\n ['2026-01-01', '元旦'],\n ['2026-02-17', '春节'], ['2026-02-18', '春节'], ['2026-02-19', '春节'],\n ['2026-04-05', '清明节'],\n ['2026-05-01', '劳动节'],\n ['2026-06-19', '端午节'],\n ['2026-09-25', '中秋节'],\n ['2026-10-01', '国庆节'], ['2026-10-02', '国庆节']\n ];\n\n [...dates2024, ...dates2025, ...dates2026].forEach(([date, name]) => {\n holidays.set(date, name);\n });\n\n return holidays;\n}\n\n/**\n * 格式化日期为 YYYY-MM-DD\n */\nfunction formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n","import type { CommitRecord, ContributorChurnMetrics } from '../../types/index.js';\n\n/**\n * 计算贡献者流失率指标\n */\nexport function calculateContributorChurn(commits: CommitRecord[]): ContributorChurnMetrics {\n if (commits.length === 0) {\n return emptyContributorChurn();\n }\n\n // 统计每个作者的最后提交时间\n const authorLastCommit = new Map<string, {\n name: string;\n email: string;\n lastDate: Date;\n firstDate: Date;\n totalCommits: number;\n }>();\n\n for (const commit of commits) {\n const key = commit.email.toLowerCase();\n const existing = authorLastCommit.get(key);\n\n if (!existing) {\n authorLastCommit.set(key, {\n name: commit.author,\n email: commit.email,\n lastDate: commit.date,\n firstDate: commit.date,\n totalCommits: 1\n });\n } else {\n if (commit.date > existing.lastDate) {\n existing.lastDate = commit.date;\n }\n if (commit.date < existing.firstDate) {\n existing.firstDate = commit.date;\n }\n existing.totalCommits++;\n }\n }\n\n // 四级分类\n const now = new Date();\n const active: AuthorDetail[] = [];\n const occasional: AuthorDetail[] = [];\n const dormant: AuthorDetail[] = [];\n const lost: AuthorDetail[] = [];\n const newJoiners: AuthorDetail[] = [];\n\n for (const [, author] of authorLastCommit) {\n const daysSinceLast = Math.floor((now.getTime() - author.lastDate.getTime()) / (1000 * 60 * 60 * 24));\n const daysSinceFirst = Math.floor((now.getTime() - author.firstDate.getTime()) / (1000 * 60 * 60 * 24));\n\n const detail: AuthorDetail = {\n name: author.name,\n email: author.email,\n lastCommitDate: author.lastDate,\n daysSinceLastCommit: daysSinceLast,\n totalCommits: author.totalCommits\n };\n\n // 新加入(首次提交 <30天)\n if (daysSinceFirst < 30) {\n newJoiners.push(detail);\n }\n\n // 按最后活跃时间分类\n if (daysSinceLast < 30) {\n active.push(detail);\n } else if (daysSinceLast < 90) {\n occasional.push(detail);\n } else if (daysSinceLast < 180) {\n dormant.push(detail);\n } else {\n lost.push(detail);\n }\n }\n\n // 计算比率并排序\n const totalAuthors = authorLastCommit.size;\n const churnRate = totalAuthors > 0 ? lost.length / totalAuthors : 0;\n const retentionRate = totalAuthors > 0 ? active.length / totalAuthors : 0;\n const growthRate = totalAuthors > 0 ? newJoiners.length / totalAuthors : 0;\n\n return {\n active: active.sort((a, b) => b.totalCommits - a.totalCommits),\n occasional: occasional.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n dormant: dormant.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n lost: lost.sort((a, b) => b.totalCommits - a.totalCommits),\n newJoiners: newJoiners.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n churnRate: Math.round(churnRate * 1000) / 1000,\n retentionRate: Math.round(retentionRate * 1000) / 1000,\n growthRate: Math.round(growthRate * 1000) / 1000\n };\n}\n\nfunction emptyContributorChurn(): ContributorChurnMetrics {\n return {\n active: [],\n occasional: [],\n dormant: [],\n lost: [],\n newJoiners: [],\n churnRate: 0,\n retentionRate: 0,\n growthRate: 0,\n };\n}\n","import type { CommitRecord, AdvancedCollaborationMetrics, FilePair, AuthorPair } from '../../types/index.js';\n\nconst MAX_FILES_PER_COMMIT = 50;\n\n/**\n * 计算高级协作指标\n */\nexport function calculateAdvancedCollaboration(commits: CommitRecord[]): AdvancedCollaborationMetrics {\n if (commits.length === 0) {\n return emptyCollaboration();\n }\n\n // 统计文件修改次数\n const fileModifyCount = new Map<string, number>();\n for (const commit of commits) {\n commit.files.forEach(f => {\n fileModifyCount.set(f.path, (fileModifyCount.get(f.path) || 0) + 1);\n });\n }\n\n // Step 1: 检测紧密耦合 - 统计同提交文件对\n const tightCoupling = detectTightCoupling(commits, fileModifyCount);\n\n // Step 2: 检测结对编程\n const pairProgramming = detectPairProgramming(commits);\n\n // Step 3: 计算耦合评分\n const avgCoupling = tightCoupling.length > 0\n ? tightCoupling.reduce((sum, p) => sum + p.coupling, 0) / tightCoupling.length\n : 0;\n const couplingScore = Math.min(100, Math.round(avgCoupling * 100));\n\n return {\n tightCoupling,\n frequentPairs: [], // 简化版:跳过 24h 窗口分析\n pairProgramming,\n couplingScore,\n };\n}\n\n/**\n * 检测紧密耦合:统计同一提交中的文件对\n */\nfunction detectTightCoupling(\n commits: CommitRecord[],\n fileModifyCount: Map<string, number>\n): FilePair[] {\n const coCommitPairs = new Map<string, { count: number; file1: string; file2: string }>();\n\n for (const commit of commits) {\n // 性能优化:限制文件数,避免大型提交导致组合爆炸\n const files = commit.files\n .map(f => f.path)\n .slice(0, MAX_FILES_PER_COMMIT)\n .sort();\n\n // 组合文件对\n for (let i = 0; i < files.length; i++) {\n for (let j = i + 1; j < files.length; j++) {\n const pairKey = `${files[i]}|||${files[j]}`;\n const existing = coCommitPairs.get(pairKey) || {\n count: 0,\n file1: files[i],\n file2: files[j],\n };\n existing.count++;\n coCommitPairs.set(pairKey, existing);\n }\n }\n }\n\n // 计算耦合度并筛选\n const tightCoupling: FilePair[] = Array.from(coCommitPairs.values())\n .map(({ file1, file2, count }) => {\n const count1 = fileModifyCount.get(file1) || 1;\n const count2 = fileModifyCount.get(file2) || 1;\n const coupling = count / Math.min(count1, count2);\n\n return {\n file1,\n file2,\n coOccurrence: count,\n coupling,\n };\n })\n .filter(p => p.coOccurrence >= 3) // 至少共同出现 3 次\n .sort((a, b) => b.coupling - a.coupling)\n .slice(0, 20); // TOP 20\n\n return tightCoupling;\n}\n\n/**\n * 检测结对编程:找出在多个文件上协作的作者对\n */\nfunction detectPairProgramming(commits: CommitRecord[]): AuthorPair[] {\n // 统计每个文件的作者贡献\n const fileAuthors = new Map<string, Map<string, number>>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n if (!fileAuthors.has(file.path)) {\n fileAuthors.set(file.path, new Map());\n }\n const authorMap = fileAuthors.get(file.path)!;\n const email = commit.email.toLowerCase();\n authorMap.set(email, (authorMap.get(email) || 0) + 1);\n }\n }\n\n // 识别结对编程:两个作者都是文件的主要贡献者\n const authorPairMap = new Map<string, {\n author1: string;\n author2: string;\n files: Set<string>;\n count: number;\n }>();\n\n for (const [filePath, authorMap] of fileAuthors) {\n if (authorMap.size >= 2) {\n // 取贡献最多的两个作者\n const sortedAuthors = Array.from(authorMap.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 2);\n\n if (sortedAuthors.length === 2) {\n // 规范化作者对的键(按字母序)\n const [a1, a2] = [sortedAuthors[0][0], sortedAuthors[1][0]].sort();\n const pairKey = `${a1}|||${a2}`;\n\n const existing = authorPairMap.get(pairKey) || {\n author1: a1,\n author2: a2,\n files: new Set<string>(),\n count: 0,\n };\n\n existing.files.add(filePath);\n existing.count++;\n authorPairMap.set(pairKey, existing);\n }\n }\n }\n\n // 筛选并排序\n const pairProgramming: AuthorPair[] = Array.from(authorPairMap.values())\n .filter(p => p.files.size >= 3) // 至少 3 个共享文件\n .map(p => ({\n author1: p.author1,\n author2: p.author2,\n sharedFiles: Array.from(p.files),\n collaborationCount: p.count,\n }))\n .sort((a, b) => b.collaborationCount - a.collaborationCount)\n .slice(0, 10); // TOP 10\n\n return pairProgramming;\n}\n\nfunction emptyCollaboration(): AdvancedCollaborationMetrics {\n return {\n tightCoupling: [],\n frequentPairs: [],\n pairProgramming: [],\n couplingScore: 0,\n };\n}\n","import type { CommitRecord } from '../../types/index.js';\nimport type {\n TeamHealthMetrics,\n StabilityMetrics,\n WorkPressureMetrics,\n ContributorChurnMetrics,\n AdvancedCollaborationMetrics,\n} from '../../types/index.js';\n\nimport { calculateTeamHealth } from './team-health.js';\nimport { calculateStability } from './code-stability.js';\nimport { calculateWorkPressure } from './work-pressure.js';\nimport { calculateContributorChurn } from './contributor-churn.js';\nimport { calculateAdvancedCollaboration } from './collaboration.js';\n\n/**\n * 高级统计结果集合\n */\nexport interface AdvancedStats {\n teamHealth: TeamHealthMetrics;\n stability: StabilityMetrics;\n workPressure: WorkPressureMetrics;\n contributorChurn: ContributorChurnMetrics;\n advancedCollaboration: AdvancedCollaborationMetrics;\n}\n\n/**\n * 一次性计算所有高级统计\n */\nexport function calculateAdvancedStats(commits: CommitRecord[]): AdvancedStats {\n return {\n teamHealth: calculateTeamHealth(commits),\n stability: calculateStability(commits),\n workPressure: calculateWorkPressure(commits),\n contributorChurn: calculateContributorChurn(commits),\n advancedCollaboration: calculateAdvancedCollaboration(commits),\n };\n}\n\n// 导出单独的计算函数\nexport {\n calculateTeamHealth,\n calculateStability,\n calculateWorkPressure,\n calculateContributorChurn,\n calculateAdvancedCollaboration,\n};\n","import { writeFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport open from 'open';\nimport { buildHtml } from './html-builder.js';\nimport type { CommitStats, ReportOptions } from '../types/index.js';\n\n/**\n * 生成 HTML 报告并可选地打开浏览器\n */\nexport async function generateReport(\n stats: CommitStats,\n options: ReportOptions\n): Promise<void> {\n const spinner = ora('生成报告...').start();\n\n try {\n const html = await buildHtml(stats, options);\n const outputPath = resolve(process.cwd(), options.outputPath);\n\n await writeFile(outputPath, html, 'utf-8');\n spinner.succeed(`报告已生成: ${chalk.cyan(outputPath)}`);\n\n if (options.autoOpen) {\n await open(outputPath);\n console.log(chalk.green('✓ 已在浏览器中打开'));\n }\n } catch (error) {\n spinner.fail('生成报告失败');\n throw error;\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { CommitStats, ReportOptions, ReportData } from '../types/index.js';\n\n/**\n * 组装完整的 HTML 报告\n */\nexport async function buildHtml(\n stats: CommitStats,\n options: ReportOptions\n): Promise<string> {\n const template = await loadTemplate();\n\n const reportData: ReportData = {\n stats: serializeStats(stats),\n generatedAt: new Date().toLocaleString('zh-CN'),\n timeRange: options.timeRange\n ? {\n from: options.timeRange.from.toISOString().split('T')[0],\n to: options.timeRange.to.toISOString().split('T')[0],\n }\n : null,\n repos: options.repoNames,\n };\n\n // 安全地序列化数据(防止 XSS)\n const jsonData = JSON.stringify(reportData)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026');\n\n return template.replace('__REPORT_DATA__', jsonData);\n}\n\n/**\n * 将 CommitStats 转为可 JSON 序列化的格式\n * Date 对象转为 ISO 字符串\n */\nfunction serializeStats(stats: CommitStats): Record<string, unknown> {\n const serializeAuthorDetail = (author: { lastCommitDate: Date; [key: string]: unknown }) => ({\n ...author,\n lastCommitDate: author.lastCommitDate.toISOString(),\n });\n\n return {\n ...stats,\n firstCommitDate: stats.firstCommitDate.toISOString(),\n lastCommitDate: stats.lastCommitDate.toISOString(),\n authors: stats.authors.map((a) => ({\n ...a,\n lastActiveDate: a.lastActiveDate.toISOString(),\n })),\n contributorChurn: stats.contributorChurn\n ? {\n ...stats.contributorChurn,\n active: stats.contributorChurn.active.map(serializeAuthorDetail),\n occasional: stats.contributorChurn.occasional.map(serializeAuthorDetail),\n dormant: stats.contributorChurn.dormant.map(serializeAuthorDetail),\n lost: stats.contributorChurn.lost.map(serializeAuthorDetail),\n newJoiners: stats.contributorChurn.newJoiners.map(serializeAuthorDetail),\n }\n : undefined,\n };\n}\n\n/**\n * 加载 HTML 模板\n */\nasync function loadTemplate(): Promise<string> {\n // 支持两种路径:开发模式和打包后模式\n const currentDir = dirname(fileURLToPath(import.meta.url));\n\n // 打包后: dist/index.js -> ../templates/report.html\n // 开发模式: src/reporter/html-builder.ts -> ../../templates/report.html\n const possiblePaths = [\n resolve(currentDir, '../templates/report.html'),\n resolve(currentDir, '../../templates/report.html'),\n resolve(currentDir, '../../../templates/report.html'),\n ];\n\n for (const templatePath of possiblePaths) {\n try {\n return await readFile(templatePath, 'utf-8');\n } catch {\n // 继续尝试下一个路径\n }\n }\n\n throw new Error('无法找到 HTML 模板文件');\n}\n","import type { CliOptions, TimeRange } from '../types/index.js';\n\n/** 时间预设格式: 7d / 1m / 3m / 6m / 1y / all */\nconst PERIOD_REGEX = /^(\\d+)(d|m|y)$/;\n\n/**\n * 解析时间预设字符串,返回起止时间范围\n * 'all' 表示不限时间(从 1970 年至今)\n */\nexport function parsePeriod(period: string): TimeRange | null {\n if (period === 'all') {\n return null;\n }\n\n const match = PERIOD_REGEX.exec(period);\n if (!match) {\n throw new Error(`无效的时间预设: \"${period}\",支持格式: 7d, 1m, 3m, 6m, 1y, all`);\n }\n\n const amount = parseInt(match[1], 10);\n const unit = match[2];\n const to = new Date();\n const from = new Date();\n\n switch (unit) {\n case 'd':\n from.setDate(from.getDate() - amount);\n break;\n case 'm':\n from.setMonth(from.getMonth() - amount);\n break;\n case 'y':\n from.setFullYear(from.getFullYear() - amount);\n break;\n }\n\n return { from, to };\n}\n\n/**\n * 解析日期字符串 YYYY-MM-DD\n */\nfunction parseDate(dateStr: string): Date {\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) {\n throw new Error(`无效的日期格式: \"${dateStr}\",请使用 YYYY-MM-DD 格式`);\n }\n return date;\n}\n\n/**\n * 根据 CLI 参数解析最终的时间范围\n * --from / --to 优先于 --period\n * 返回 null 表示不限时间\n */\nexport function resolveTimeRange(opts: CliOptions): TimeRange | null {\n if (opts.from || opts.to) {\n const to = opts.to ? parseDate(opts.to) : new Date();\n const from = opts.from ? parseDate(opts.from) : (() => {\n const d = new Date(to);\n d.setMonth(d.getMonth() - 3);\n return d;\n })();\n\n if (from > to) {\n throw new Error('起始日期不能晚于结束日期');\n }\n\n return { from, to };\n }\n\n return parsePeriod(opts.period);\n}\n"],"mappings":"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","names":["chalk","ora","execSync","join","authorMap","authors","stat","getTopDirectory","commits","ora","resolve","chalk","ora","readFile","ora","resolve","chalk","chalk","formatDate","execSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli/index.ts","../src/scanner/index.ts","../src/analyzer/index.ts","../src/analyzer/git-log-parser.ts","../src/analyzer/stats-calculator.ts","../src/analyzer/tech-debt/ai-detector.ts","../src/analyzer/ai-stats-calculator.ts","../src/analyzer/stats-utils.ts","../src/analyzer/extended-stats.ts","../src/analyzer/stats-metrics.ts","../src/analyzer/stats-empty.ts","../src/analyzer/advanced/team-health.ts","../src/analyzer/advanced/code-stability.ts","../src/analyzer/advanced/work-pressure.ts","../src/analyzer/advanced/contributor-churn.ts","../src/analyzer/advanced/collaboration.ts","../src/analyzer/advanced/index.ts","../src/analyzer/tech-debt/risk-scorer.ts","../src/analyzer/tech-debt/duplication.ts","../src/analyzer/tech-debt/prioritizer.ts","../src/analyzer/tech-debt/index.ts","../src/analyzer/engineering-metrics.ts","../src/reporter/index.ts","../src/reporter/html-builder.ts","../src/cli/time-utils.ts"],"sourcesContent":["import { Command } from 'commander';\nimport chalk from 'chalk';\nimport { checkbox } from '@inquirer/prompts';\nimport { scanRepositories } from '../scanner/index.js';\nimport { analyzeRepos } from '../analyzer/index.js';\nimport { generateReport } from '../reporter/index.js';\nimport { resolveTimeRange } from './time-utils.js';\nimport type { CliOptions, RepoInfo } from '../types/index.js';\n\nconst program = new Command();\n\nprogram\n .name('commit-report')\n .description('Git 提交统计工具,生成可视化 HTML 报告')\n .version('1.0.1')\n .argument('[directory]', '要扫描的目录路径', process.cwd())\n .option('-p, --period <period>', '时间预设 (7d/1m/3m/6m/1y/all)', 'all')\n .option('-f, --from <date>', '起始日期 (YYYY-MM-DD)')\n .option('-t, --to <date>', '结束日期 (YYYY-MM-DD)')\n .option('-a, --author <name>', '过滤作者')\n .option('-o, --output <file>', '输出文件名', 'commit-report.html')\n .option('--no-open', '不自动打开浏览器')\n .option('-d, --depth <number>', '最大扫描深度', '20')\n .action(async (directory: string, opts: CliOptions) => {\n try {\n await run(directory, opts);\n } catch (error) {\n if (error instanceof Error && error.message === 'USER_CANCEL') {\n console.log(chalk.yellow('\\n已取消操作'));\n process.exit(0);\n }\n console.error(chalk.red(`\\n错误: ${error instanceof Error ? error.message : String(error)}`));\n process.exit(1);\n }\n });\n\nasync function run(directory: string, opts: CliOptions): Promise<void> {\n // 1. 检查 Git 是否安装\n await checkGitInstalled();\n\n // 2. 解析时间范围\n const timeRange = resolveTimeRange(opts);\n\n // 3. 扫描仓库\n const repos = await scanRepositories({\n targetDir: directory,\n maxDepth: Number(opts.depth),\n });\n\n if (repos.length === 0) {\n console.log(chalk.red('未找到 Git 仓库'));\n process.exit(1);\n }\n\n // 4. 选择仓库\n let selectedRepos: RepoInfo[];\n\n if (repos.length === 1) {\n selectedRepos = repos;\n console.log(chalk.cyan(`找到 1 个 Git 仓库: ${repos[0].name}`));\n } else {\n console.log(chalk.cyan(`\\n找到 ${repos.length} 个 Git 仓库:\\n`));\n\n const selected = await checkbox<string>({\n message: '选择要分析的仓库(空格选择,回车确认)',\n choices: repos.map((repo) => ({\n name: `${repo.name} (${repo.commitCount} commits)`,\n value: repo.path,\n checked: true,\n })),\n });\n\n if (selected.length === 0) {\n console.log(chalk.yellow('未选择任何仓库'));\n process.exit(0);\n }\n\n selectedRepos = repos.filter((r) => selected.includes(r.path));\n }\n\n const timeRangeText = timeRange\n ? `${formatDate(timeRange.from)} ~ ${formatDate(timeRange.to)}`\n : '所有提交';\n console.log(\n chalk.gray(\n `\\n已选择 ${selectedRepos.length} 个仓库,时间范围:${timeRangeText}\\n`\n )\n );\n\n // 5. 分析提交记录\n const stats = await analyzeRepos({\n repos: selectedRepos,\n timeRange,\n author: opts.author,\n });\n\n if (stats.totalCommits === 0) {\n console.log(chalk.yellow('该时间段无提交记录'));\n }\n\n // 6. 生成报告\n await generateReport(stats, {\n outputPath: opts.output,\n autoOpen: opts.open,\n timeRange,\n repoNames: selectedRepos.map((r) => r.name),\n });\n}\n\nasync function checkGitInstalled(): Promise<void> {\n const { execSync } = await import('child_process');\n try {\n execSync('git --version', { stdio: 'ignore' });\n } catch {\n console.error(chalk.red('请先安装 Git'));\n process.exit(1);\n }\n}\n\nfunction formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n\n// 处理 Ctrl+C\nprocess.on('SIGINT', () => {\n console.log(chalk.yellow('\\n已取消操作'));\n process.exit(0);\n});\n\nprogram.parse();\n","import { readdir, stat, access } from 'node:fs/promises';\nimport { join, basename } from 'node:path';\nimport { execSync } from 'node:child_process';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport { confirm } from '@inquirer/prompts';\nimport type { ScanOptions, RepoInfo } from '../types/index.js';\n\n/** 扫描时忽略的目录名 */\nconst IGNORE_DIRS = new Set([\n 'node_modules',\n '.git',\n 'vendor',\n 'dist',\n 'build',\n '.cache',\n '.next',\n '.nuxt',\n '__pycache__',\n 'target',\n]);\n\n/**\n * 递归扫描目录,查找所有 Git 仓库\n */\nexport async function scanRepositories(options: ScanOptions): Promise<RepoInfo[]> {\n const spinner = ora('扫描仓库中...').start();\n const repos: RepoInfo[] = [];\n let deepScanConfirmed = false;\n\n async function scan(dir: string, depth: number): Promise<void> {\n // 深度超过限制时,提示用户确认\n if (depth > options.maxDepth && !deepScanConfirmed) {\n spinner.stop();\n const shouldContinue = await confirm({\n message: `扫描深度已超过 ${options.maxDepth} 层,是否继续?`,\n default: false,\n });\n\n if (!shouldContinue) {\n return;\n }\n deepScanConfirmed = true;\n spinner.start('继续扫描中...');\n }\n\n // 检查当前目录是否有 .git\n try {\n const gitDir = join(dir, '.git');\n await access(gitDir);\n\n // 找到 .git,获取仓库信息\n const repoInfo = getRepoInfo(dir);\n if (repoInfo) {\n repos.push(repoInfo);\n spinner.text = `扫描仓库中... 已找到 ${repos.length} 个`;\n }\n // 找到 .git 后不再深层继续(避免子模块重复)\n return;\n } catch {\n // 没有 .git,继续扫描子目录\n }\n\n // 递归扫描子目录\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n if (IGNORE_DIRS.has(entry.name)) continue;\n if (entry.name.startsWith('.') && entry.name !== '.git') continue;\n\n await scan(join(dir, entry.name), depth + 1);\n }\n } catch {\n // 权限不足时跳过\n }\n }\n\n await scan(options.targetDir, 0);\n\n if (repos.length > 0) {\n spinner.succeed(`找到 ${repos.length} 个 Git 仓库`);\n } else {\n spinner.fail('未找到 Git 仓库');\n }\n\n return repos;\n}\n\n/**\n * 获取单个仓库的基本信息\n */\nfunction getRepoInfo(repoPath: string): RepoInfo | null {\n try {\n const countStr = execSync('git rev-list --count HEAD', {\n cwd: repoPath,\n stdio: ['pipe', 'pipe', 'ignore'],\n encoding: 'utf-8',\n }).trim();\n\n const commitCount = parseInt(countStr, 10) || 0;\n const name = basename(repoPath);\n\n return {\n path: repoPath,\n name,\n commitCount,\n };\n } catch {\n // 仓库可能损坏或为空\n return null;\n }\n}\n","import ora from 'ora';\nimport chalk from 'chalk';\nimport { parseGitLog } from './git-log-parser.js';\nimport { calculateStats, mergeStats } from './stats-calculator.js';\nimport { calculateAdvancedStats } from './advanced/index.js';\nimport { calculateTechDebt } from './tech-debt/index.js';\nimport { calculateEngineeringMetrics } from './engineering-metrics.js';\nimport type { AnalyzeOptions, CommitStats } from '../types/index.js';\n\n/**\n * 分析所有选定仓库的提交记录\n */\nexport async function analyzeRepos(options: AnalyzeOptions): Promise<CommitStats> {\n const { repos, timeRange, author } = options;\n const spinner = ora('分析提交记录...').start();\n const allStats: CommitStats[] = [];\n\n for (let i = 0; i < repos.length; i++) {\n const repo = repos[i];\n spinner.text = `分析提交记录 (${i + 1}/${repos.length}) - ${repo.name}`;\n\n try {\n const commits = await parseGitLog(repo.path, timeRange, author);\n\n if (commits.length > 100000) {\n spinner.info(\n chalk.yellow(`${repo.name} 包含 ${commits.length.toLocaleString()} 条提交,处理可能需要一些时间...`)\n );\n spinner.start();\n }\n\n const stats = calculateStats(commits);\n\n // 计算高级统计\n const advancedStats = calculateAdvancedStats(commits);\n\n // 计算技术债(仅单仓库场景)\n let techDebt;\n let engineering;\n if (repos.length === 1) {\n spinner.text = `分析技术债 - ${repo.name}`;\n techDebt = await calculateTechDebt(commits, repo.path);\n spinner.text = `分析工程质量 - ${repo.name}`;\n engineering = calculateEngineeringMetrics(commits, repo.path);\n }\n\n // 合并核心统计和高级统计\n const fullStats: CommitStats = {\n ...stats,\n ...advancedStats,\n ...(techDebt && { techDebt }),\n ...(engineering && { engineering }),\n };\n fullStats.commitDetails.forEach((commit) => {\n commit.repoName = repo.name;\n });\n fullStats.aiMetrics?.highAICommits.forEach((commit) => {\n commit.repoName = repo.name;\n });\n fullStats.directoryAIStats?.forEach((directory) => {\n directory.repoName = repo.name;\n directory.displayPath = repos.length > 1 ? `${repo.name} / ${directory.path}` : directory.path;\n });\n\n allStats.push(fullStats);\n } catch (error) {\n spinner.warn(\n chalk.yellow(\n `跳过仓库 ${repo.name}: ${error instanceof Error ? error.message : '未知错误'}`\n )\n );\n spinner.start();\n }\n }\n\n const merged = mergeStats(allStats);\n\n spinner.succeed(\n `分析完成: ${merged.totalCommits.toLocaleString()} 条提交, ` +\n `${merged.authors.length} 位作者, ` +\n `+${merged.linesAdded.toLocaleString()} / -${merged.linesDeleted.toLocaleString()} 行`\n );\n\n return merged;\n}\n","import { execFileSync } from 'node:child_process';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport ig from 'ignore';\nimport type { Ignore } from 'ignore';\nimport type { CommitRecord, FileChange, TimeRange } from '../types/index.js';\n\n/** git log 的格式化分隔符 */\nconst COMMIT_SEPARATOR = '---COMMITX_SEP---';\nconst COMMIT_END = '---COMMITX_END---';\nconst FIELD_SEPARATOR = '|';\nconst FORMAT =\n `${COMMIT_SEPARATOR}%H${FIELD_SEPARATOR}%P${FIELD_SEPARATOR}%an` +\n `${FIELD_SEPARATOR}%ae${FIELD_SEPARATOR}%aI${FIELD_SEPARATOR}%s%n%B${COMMIT_END}`;\n\n/**\n * 从指定仓库解析 git log,返回提交记录数组\n * timeRange 为 null 时表示获取所有提交\n */\nexport async function parseGitLog(\n repoPath: string,\n timeRange: TimeRange | null,\n author?: string\n): Promise<CommitRecord[]> {\n const ignoreFilter = await loadGitignore(repoPath);\n\n const args = [\n 'log',\n `--format=${FORMAT}`,\n '--raw',\n '--numstat',\n '--find-renames',\n '--find-copies',\n ];\n\n if (timeRange) {\n args.push(`--since=${timeRange.from.toISOString()}`);\n args.push(`--until=${timeRange.to.toISOString()}`);\n }\n\n if (author) {\n args.push(`--author=${author}`);\n }\n\n let output: string;\n try {\n output = execFileSync('git', args, {\n cwd: repoPath,\n encoding: 'utf-8',\n maxBuffer: 100 * 1024 * 1024, // 100MB buffer for large repos\n stdio: ['pipe', 'pipe', 'ignore'],\n });\n } catch {\n return [];\n }\n\n if (!output.trim()) {\n return [];\n }\n\n return parseOutput(output, ignoreFilter);\n}\n\n/**\n * 解析 git log 输出文本为 CommitRecord 数组\n */\nfunction parseOutput(\n output: string,\n ignoreFilter: ReturnType<typeof ig>\n): CommitRecord[] {\n const commits: CommitRecord[] = [];\n const blocks = output.split(COMMIT_SEPARATOR).filter((b) => b.trim());\n\n for (const block of blocks) {\n const endIndex = block.indexOf(COMMIT_END);\n const metadataText = endIndex >= 0 ? block.slice(0, endIndex) : block;\n const changesText = endIndex >= 0\n ? block.slice(endIndex + COMMIT_END.length)\n : '';\n const metadataLines = metadataText.trim().split('\\n');\n if (metadataLines.length === 0) continue;\n\n // 第一行是提交元信息\n const headerLine = metadataLines[0].replace(/^\"|\"$/g, '');\n const parts = headerLine.split(FIELD_SEPARATOR);\n if (parts.length < 6) continue;\n\n const [hash, parentHashText, authorName, email, dateStr, ...messageParts] = parts;\n const message = messageParts.join(FIELD_SEPARATOR); // message 可能包含 |\n const body = metadataLines.slice(1).join('\\n').trim();\n const parentHashes = parentHashText.trim()\n ? parentHashText.trim().split(/\\s+/)\n : [];\n\n const changeLines = changesText.trim().split('\\n');\n const statusByPath = parseFileStatuses(changeLines);\n\n // 后续行包含 raw status 和 numstat(新增\\t删除\\t文件路径)\n const files: FileChange[] = [];\n for (const rawLine of changeLines) {\n const line = rawLine.trim();\n if (!line) continue;\n\n const tabParts = line.split('\\t');\n if (tabParts.length !== 3) continue;\n\n const [addedStr, deletedStr, filePath] = tabParts;\n\n // 二进制文件显示为 -\n const added = addedStr === '-' ? 0 : parseInt(addedStr, 10) || 0;\n const deleted = deletedStr === '-' ? 0 : parseInt(deletedStr, 10) || 0;\n\n // 应用 gitignore 过滤\n if (ignoreFilter.ignores(filePath)) continue;\n\n const status = statusByPath.get(filePath);\n files.push({\n added,\n deleted,\n path: filePath,\n ...(status && { status }),\n });\n }\n\n commits.push({\n hash,\n author: authorName,\n email,\n date: new Date(dateStr),\n message,\n body,\n parentHashes,\n files,\n });\n }\n\n return commits;\n}\n\nfunction parseFileStatuses(lines: string[]): Map<string, FileChange['status']> {\n const statusByPath = new Map<string, FileChange['status']>();\n\n for (const line of lines) {\n if (!line.startsWith(':')) continue;\n\n const parts = line.trim().split('\\t');\n if (parts.length < 2) continue;\n\n const metadata = parts[0].split(/\\s+/);\n const statusToken = metadata[4] || '';\n const status = normalizeStatus(statusToken);\n const path = status === 'renamed' || status === 'copied'\n ? parts[2] || parts[1]\n : parts[1];\n\n if (path) {\n statusByPath.set(path, status);\n }\n }\n\n return statusByPath;\n}\n\nfunction normalizeStatus(statusToken: string): FileChange['status'] {\n const type = statusToken[0];\n if (type === 'A') return 'added';\n if (type === 'M') return 'modified';\n if (type === 'D') return 'deleted';\n if (type === 'R') return 'renamed';\n if (type === 'C') return 'copied';\n return 'unknown';\n}\n\n/**\n * 读取仓库的 .gitignore 规则\n */\nasync function loadGitignore(repoPath: string): Promise<Ignore> {\n const ignoreInstance = ig();\n\n try {\n const content = await readFile(join(repoPath, '.gitignore'), 'utf-8');\n ignoreInstance.add(content);\n } catch {\n // 没有 .gitignore 文件,跳过\n }\n\n // 默认忽略一些常见的生成文件\n ignoreInstance.add([\n 'package-lock.json',\n 'pnpm-lock.yaml',\n 'yarn.lock',\n ]);\n\n return ignoreInstance;\n}\n","import type {\n CommitRecord,\n CommitStats,\n AuthorStats,\n FileTypeStats,\n DirectoryStats,\n BusiestDay,\n AuthorFileTypeContribution,\n AICommit,\n AuthorAIStats,\n DirectoryAIStats,\n AITrendPoint,\n} from '../types/index.js';\nimport { extname } from 'node:path';\nimport { calculateAIMetrics } from './ai-stats-calculator.js';\nimport {\n calculateAIQualityRisk,\n calculateChangeSizeDistribution,\n calculateDirectoryCoupling,\n} from './extended-stats.js';\nimport { emptyStats } from './stats-empty.js';\nimport {\n calculateAuthorFileTypeContributions,\n calculateCollaboration,\n calculateCommitDetails,\n calculateMessageStats,\n calculateQualityMetrics,\n calculateTimePatterns,\n calculateTrends,\n} from './stats-metrics.js';\nimport { formatDateKey, getTopDirectory } from './stats-utils.js';\n\n/**\n * 根据解析后的提交记录,计算所有统计维度\n */\nexport function calculateStats(commits: CommitRecord[]): CommitStats {\n if (commits.length === 0) {\n return emptyStats();\n }\n\n // 排序:按日期升序\n const sorted = [...commits].sort(\n (a, b) => a.date.getTime() - b.date.getTime()\n );\n\n // 基础统计\n let totalLinesAdded = 0;\n let totalLinesDeleted = 0;\n const allFilePaths = new Set<string>();\n\n // 作者维度\n const authorMap = new Map<string, AuthorStats>();\n\n // 文件类型维度\n const fileTypeMap = new Map<string, FileTypeStats>();\n\n // 目录维度\n const directoryMap = new Map<string, DirectoryStats>();\n const directoryCommitSet = new Map<string, Set<string>>(); // dir -> Set<commitHash>\n\n // 时间分布\n const hourlyDistribution = new Array<number>(24).fill(0);\n const dailyHeatmap: Record<string, number> = {};\n const hourlyByAuthor: Map<number, Map<string, number>> = new Map(); // hour -> (author -> count)\n\n // 每日统计(用于计算最繁忙的一天)\n const dailyCounts = new Map<string, number>();\n\n for (const commit of sorted) {\n // 时间分布\n const hour = commit.date.getHours();\n hourlyDistribution[hour]++;\n\n // 按小时统计作者\n if (!hourlyByAuthor.has(hour)) {\n hourlyByAuthor.set(hour, new Map());\n }\n const hourAuthors = hourlyByAuthor.get(hour)!;\n hourAuthors.set(commit.author, (hourAuthors.get(commit.author) || 0) + 1);\n\n const dateKey = formatDateKey(commit.date);\n dailyHeatmap[dateKey] = (dailyHeatmap[dateKey] || 0) + 1;\n dailyCounts.set(dateKey, (dailyCounts.get(dateKey) || 0) + 1);\n\n // 作者统计\n const authorKey = commit.email.toLowerCase();\n let authorStat = authorMap.get(authorKey);\n if (!authorStat) {\n authorStat = {\n name: commit.author,\n email: commit.email,\n commits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n lastActiveDate: commit.date,\n };\n authorMap.set(authorKey, authorStat);\n }\n authorStat.commits++;\n authorStat.lastActiveDate = commit.date;\n\n // 文件级别统计\n for (const file of commit.files) {\n totalLinesAdded += file.added;\n totalLinesDeleted += file.deleted;\n allFilePaths.add(file.path);\n\n // 作者行数统计\n authorStat.linesAdded += file.added;\n authorStat.linesDeleted += file.deleted;\n\n // 文件类型统计\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n let ftStat = fileTypeMap.get(ext);\n if (!ftStat) {\n ftStat = { extension: ext, added: 0, deleted: 0, fileCount: 0 };\n fileTypeMap.set(ext, ftStat);\n }\n ftStat.added += file.added;\n ftStat.deleted += file.deleted;\n\n // 目录统计 —— 取第一层目录\n const topDir = getTopDirectory(file.path);\n let dirStat = directoryMap.get(topDir);\n if (!dirStat) {\n dirStat = { path: topDir, commits: 0, linesChanged: 0 };\n directoryMap.set(topDir, dirStat);\n directoryCommitSet.set(topDir, new Set());\n }\n dirStat.linesChanged += file.added + file.deleted;\n directoryCommitSet.get(topDir)!.add(commit.hash);\n }\n }\n\n // 计算目录的 commit 数量\n for (const [dir, commitSet] of directoryCommitSet) {\n const dirStat = directoryMap.get(dir);\n if (dirStat) {\n dirStat.commits = commitSet.size;\n }\n }\n\n // 计算文件类型的 fileCount\n const fileCountByExt = new Map<string, Set<string>>();\n for (const filePath of allFilePaths) {\n const ext = extname(filePath).toLowerCase() || '(无扩展名)';\n if (!fileCountByExt.has(ext)) {\n fileCountByExt.set(ext, new Set());\n }\n fileCountByExt.get(ext)!.add(filePath);\n }\n for (const [ext, files] of fileCountByExt) {\n const ftStat = fileTypeMap.get(ext);\n if (ftStat) {\n ftStat.fileCount = files.size;\n }\n }\n\n // 最繁忙的一天\n let busiestDay: BusiestDay = { date: '', count: 0 };\n for (const [date, count] of dailyCounts) {\n if (count > busiestDay.count) {\n busiestDay = { date, count };\n }\n }\n\n // 排序结果\n const authors = Array.from(authorMap.values()).sort(\n (a, b) => b.commits - a.commits\n );\n const fileTypes = Array.from(fileTypeMap.values()).sort(\n (a, b) => b.added + b.deleted - (a.added + a.deleted)\n );\n const directories = Array.from(directoryMap.values())\n .sort((a, b) => b.linesChanged - a.linesChanged)\n .slice(0, 10); // TOP 10\n\n // 转换 hourlyByAuthor 为数组格式\n const hourlyByAuthorArray = Array.from({ length: 24 }, (_, hour) => {\n const authorMap = hourlyByAuthor.get(hour);\n const authors: Record<string, number> = {};\n if (authorMap) {\n authorMap.forEach((count, author) => {\n authors[author] = count;\n });\n }\n return {\n count: hourlyDistribution[hour],\n authors,\n };\n });\n\n const aiStats = calculateAIMetrics(sorted);\n\n return {\n totalCommits: sorted.length,\n linesAdded: totalLinesAdded,\n linesDeleted: totalLinesDeleted,\n filesChanged: allFilePaths.size,\n firstCommitDate: sorted[0].date,\n lastCommitDate: sorted[sorted.length - 1].date,\n busiestDay,\n authors,\n fileTypes,\n directories,\n hourlyDistribution,\n dailyHeatmap,\n hourlyByAuthor: hourlyByAuthorArray,\n quality: calculateQualityMetrics(sorted),\n timePatterns: calculateTimePatterns(sorted),\n trends: calculateTrends(sorted),\n collaboration: calculateCollaboration(sorted),\n messageStats: calculateMessageStats(sorted),\n authorFileTypeContributions: calculateAuthorFileTypeContributions(sorted),\n commitDetails: calculateCommitDetails(sorted),\n aiMetrics: aiStats.aiMetrics,\n authorAIStats: aiStats.authorAIStats,\n directoryAIStats: aiStats.directoryAIStats,\n aiTrends: aiStats.aiTrends,\n changeSizeDistribution: calculateChangeSizeDistribution(sorted),\n directoryCoupling: calculateDirectoryCoupling(sorted),\n aiQualityRisk: calculateAIQualityRisk(sorted),\n };\n}\n\n/**\n * 合并多个仓库的统计结果\n */\nexport function mergeStats(statsList: CommitStats[]): CommitStats {\n if (statsList.length === 0) return emptyStats();\n if (statsList.length === 1) return statsList[0];\n\n const merged = emptyStats();\n\n for (const stats of statsList) {\n merged.totalCommits += stats.totalCommits;\n merged.linesAdded += stats.linesAdded;\n merged.linesDeleted += stats.linesDeleted;\n merged.filesChanged += stats.filesChanged;\n\n // 时间维度\n if (\n !merged.firstCommitDate ||\n stats.firstCommitDate < merged.firstCommitDate\n ) {\n merged.firstCommitDate = stats.firstCommitDate;\n }\n if (\n !merged.lastCommitDate ||\n stats.lastCommitDate > merged.lastCommitDate\n ) {\n merged.lastCommitDate = stats.lastCommitDate;\n }\n\n // 小时分布\n for (let i = 0; i < 24; i++) {\n merged.hourlyDistribution[i] += stats.hourlyDistribution[i];\n }\n\n // 每日热力图\n for (const [date, count] of Object.entries(stats.dailyHeatmap)) {\n merged.dailyHeatmap[date] = (merged.dailyHeatmap[date] || 0) + count;\n }\n\n // 作者合并\n for (const author of stats.authors) {\n const existing = merged.authors.find(\n (a) => a.email.toLowerCase() === author.email.toLowerCase()\n );\n if (existing) {\n existing.commits += author.commits;\n existing.linesAdded += author.linesAdded;\n existing.linesDeleted += author.linesDeleted;\n if (author.lastActiveDate > existing.lastActiveDate) {\n existing.lastActiveDate = author.lastActiveDate;\n }\n } else {\n merged.authors.push({ ...author });\n }\n }\n\n // 文件类型合并\n for (const ft of stats.fileTypes) {\n const existing = merged.fileTypes.find(\n (f) => f.extension === ft.extension\n );\n if (existing) {\n existing.added += ft.added;\n existing.deleted += ft.deleted;\n existing.fileCount += ft.fileCount;\n } else {\n merged.fileTypes.push({ ...ft });\n }\n }\n\n // 目录合并\n for (const dir of stats.directories) {\n const existing = merged.directories.find((d) => d.path === dir.path);\n if (existing) {\n existing.commits += dir.commits;\n existing.linesChanged += dir.linesChanged;\n } else {\n merged.directories.push({ ...dir });\n }\n }\n\n // 质量指标合并\n merged.quality.avgFilesPerCommit += stats.quality.avgFilesPerCommit;\n merged.quality.avgLinesPerCommit += stats.quality.avgLinesPerCommit;\n merged.quality.churnRate += stats.quality.churnRate;\n for (const hf of stats.quality.hotFiles) {\n const existing = merged.quality.hotFiles.find((h) => h.path === hf.path);\n if (existing) {\n existing.modifyCount += hf.modifyCount;\n for (const author of hf.authors) {\n if (!existing.authors.includes(author)) {\n existing.authors.push(author);\n }\n }\n } else {\n merged.quality.hotFiles.push({ ...hf, authors: [...hf.authors] });\n }\n }\n\n // 时间模式合并\n for (let i = 0; i < 7; i++) {\n merged.timePatterns.weekdayDistribution[i] +=\n stats.timePatterns.weekdayDistribution[i];\n }\n\n // 趋势数据合并\n for (const wp of stats.trends.weeklyTrend) {\n const existing = merged.trends.weeklyTrend.find((w) => w.week === wp.week);\n if (existing) {\n existing.commits += wp.commits;\n existing.linesAdded += wp.linesAdded;\n existing.linesDeleted += wp.linesDeleted;\n } else {\n merged.trends.weeklyTrend.push({ ...wp });\n }\n }\n for (const cp of stats.trends.cumulativeLines) {\n const existing = merged.trends.cumulativeLines.find(\n (c) => c.date === cp.date\n );\n if (existing) {\n existing.netLines += cp.netLines;\n } else {\n merged.trends.cumulativeLines.push({ ...cp });\n }\n }\n\n // 协作指标合并\n for (const sf of stats.collaboration.soloFiles) {\n const existing = merged.collaboration.soloFiles.find(\n (s) => s.path === sf.path\n );\n if (existing) {\n existing.commits += sf.commits;\n } else {\n merged.collaboration.soloFiles.push({ ...sf });\n }\n }\n for (const ch of stats.collaboration.collaborationHotspots) {\n const existing = merged.collaboration.collaborationHotspots.find(\n (c) => c.path === ch.path\n );\n if (existing) {\n existing.totalCommits += ch.totalCommits;\n existing.authorCount = Math.max(existing.authorCount, ch.authorCount);\n } else {\n merged.collaboration.collaborationHotspots.push({ ...ch });\n }\n }\n\n // Commit Message 统计合并\n for (const [type, count] of Object.entries(stats.messageStats.typeDistribution)) {\n merged.messageStats.typeDistribution[type] =\n (merged.messageStats.typeDistribution[type] || 0) + count;\n }\n merged.messageStats.avgMessageLength += stats.messageStats.avgMessageLength;\n\n merged.commitDetails.push(...stats.commitDetails);\n }\n\n // 重新计算最繁忙的一天\n let busiestDay: BusiestDay = { date: '', count: 0 };\n for (const [date, count] of Object.entries(merged.dailyHeatmap)) {\n if (count > busiestDay.count) {\n busiestDay = { date, count };\n }\n }\n merged.busiestDay = busiestDay;\n\n // 排序\n merged.authors.sort((a, b) => b.commits - a.commits);\n merged.fileTypes.sort(\n (a, b) => b.added + b.deleted - (a.added + a.deleted)\n );\n merged.directories.sort((a, b) => b.linesChanged - a.linesChanged);\n merged.directories = merged.directories.slice(0, 10);\n\n // 重新计算扩展字段的平均值和排序\n const repoCount = statsList.length;\n merged.quality.avgFilesPerCommit /= repoCount;\n merged.quality.avgLinesPerCommit /= repoCount;\n merged.quality.churnRate /= repoCount;\n merged.quality.hotFiles.sort((a, b) => b.modifyCount - a.modifyCount);\n merged.quality.hotFiles = merged.quality.hotFiles.slice(0, 10);\n\n // 时间模式重新计算\n const totalWeekdayCommits = merged.timePatterns.weekdayDistribution.reduce(\n (a, b) => a + b,\n 0\n );\n if (totalWeekdayCommits > 0) {\n merged.timePatterns.weekendCommits =\n (merged.timePatterns.weekdayDistribution[5] +\n merged.timePatterns.weekdayDistribution[6]) /\n totalWeekdayCommits;\n }\n\n // 趋势数据排序\n merged.trends.weeklyTrend.sort((a, b) => a.week.localeCompare(b.week));\n merged.trends.cumulativeLines.sort((a, b) => a.date.localeCompare(b.date));\n\n // 重新计算累计代码量\n let cumulative = 0;\n for (const point of merged.trends.cumulativeLines) {\n cumulative += point.netLines;\n point.netLines = cumulative;\n }\n\n // 协作指标排序\n merged.collaboration.soloFiles.sort((a, b) => b.commits - a.commits);\n merged.collaboration.soloFiles = merged.collaboration.soloFiles.slice(0, 10);\n merged.collaboration.collaborationHotspots.sort(\n (a, b) => b.totalCommits - a.totalCommits\n );\n merged.collaboration.collaborationHotspots =\n merged.collaboration.collaborationHotspots.slice(0, 10);\n\n // Commit Message 平均长度\n merged.messageStats.avgMessageLength /= repoCount;\n\n // 提交明细排序\n merged.commitDetails.sort((a, b) => a.date.getTime() - b.date.getTime());\n\n mergeAIStats(merged, statsList);\n\n // 作者文件类型贡献合并\n const contributionMap = new Map<string, AuthorFileTypeContribution>();\n for (const stats of statsList) {\n for (const contrib of stats.authorFileTypeContributions) {\n const key = `${contrib.email.toLowerCase()}|||${contrib.extension}`;\n const existing = contributionMap.get(key);\n if (existing) {\n existing.linesAdded += contrib.linesAdded;\n existing.linesDeleted += contrib.linesDeleted;\n existing.commits += contrib.commits;\n existing.fileCount += contrib.fileCount;\n } else {\n contributionMap.set(key, { ...contrib });\n }\n }\n }\n merged.authorFileTypeContributions = Array.from(contributionMap.values())\n .sort((a, b) => {\n const totalA = a.linesAdded + a.linesDeleted;\n const totalB = b.linesAdded + b.linesDeleted;\n return totalB - totalA;\n })\n .slice(0, 20);\n\n // ============================================================\n // 高级统计字段不进行多仓库合并\n // ============================================================\n // 原因:高级统计(teamHealth, stability, workPressure, contributorChurn, advancedCollaboration)\n // 需要原始 CommitRecord[] 数据才能准确计算。在 mergeStats 中仅有聚合后的统计数据,\n // 强行合并会导致结果不准确(例如 busFactor、revertRate 等指标无法简单累加)。\n //\n // 当前行为:\n // - 单仓库场景:直接返回(第184行),保留所有高级统计\n // - 多仓库场景:merged 中高级字段保持 undefined\n //\n // 如需多仓库的高级统计分析,建议:\n // 1. 使用时间范围过滤单个仓库\n // 2. 或在 analyzer/index.ts 中基于合并后的原始 commits 重新计算\n\n return merged;\n}\n\nfunction mergeAIStats(merged: CommitStats, statsList: CommitStats[]): void {\n const aiStatsList = statsList.filter((stats) => stats.aiMetrics);\n if (aiStatsList.length === 0) return;\n\n const highAICommits: AICommit[] = [];\n const authorMap = new Map<string, AuthorAIStats>();\n const directoryMap = new Map<string, DirectoryAIStats>();\n const trendMap = new Map<string, AITrendPoint>();\n let totalAILines = 0;\n let totalLines = 0;\n let suspiciousCommits = 0;\n\n for (const stats of aiStatsList) {\n const metrics = stats.aiMetrics!;\n totalAILines += metrics.totalAILines;\n totalLines += metrics.totalLines;\n suspiciousCommits += metrics.suspiciousCommits;\n highAICommits.push(...metrics.highAICommits);\n\n for (const author of stats.authorAIStats || []) {\n const key = author.email.toLowerCase();\n const existing = authorMap.get(key);\n if (existing) {\n existing.aiLines += author.aiLines;\n existing.totalLines += author.totalLines;\n existing.aiPercentage = calculatePercentage(existing.aiLines, existing.totalLines);\n } else {\n authorMap.set(key, { ...author });\n }\n }\n\n for (const directory of stats.directoryAIStats || []) {\n const key = directory.repoName ? `${directory.repoName}|||${directory.path}` : directory.path;\n const displayPath = directory.repoName\n ? `${directory.repoName} / ${directory.path}`\n : directory.displayPath || directory.path;\n const existing = directoryMap.get(key);\n if (existing) {\n existing.commits += directory.commits;\n existing.aiLines += directory.aiLines;\n existing.totalLines += directory.totalLines;\n existing.aiPercentage = calculatePercentage(existing.aiLines, existing.totalLines);\n existing.displayPath = displayPath;\n existing.lastModified =\n new Date(directory.lastModified) > new Date(existing.lastModified)\n ? directory.lastModified\n : existing.lastModified;\n existing.isHighRisk = existing.commits > 50 && existing.aiPercentage > 60;\n } else {\n directoryMap.set(key, { ...directory, displayPath });\n }\n }\n\n for (const trend of stats.aiTrends || []) {\n const existing = trendMap.get(trend.week);\n if (existing) {\n existing.aiLines += trend.aiLines;\n existing.totalLines += trend.totalLines;\n existing.aiPercentage = calculatePercentage(existing.aiLines, existing.totalLines);\n } else {\n trendMap.set(trend.week, { ...trend });\n }\n }\n }\n\n merged.aiMetrics = {\n totalAILines,\n totalLines,\n aiPercentage: calculatePercentage(totalAILines, totalLines),\n suspiciousCommits,\n highAICommits: highAICommits.sort((a, b) => b.aiScore - a.aiScore).slice(0, 20),\n };\n merged.authorAIStats = Array.from(authorMap.values())\n .sort((a, b) => b.aiPercentage - a.aiPercentage);\n merged.directoryAIStats = Array.from(directoryMap.values())\n .sort((a, b) => b.aiPercentage - a.aiPercentage);\n merged.aiTrends = Array.from(trendMap.values())\n .sort((a, b) => a.week.localeCompare(b.week));\n}\n\nfunction calculatePercentage(part: number, total: number): number {\n return total > 0 ? (part / total) * 100 : 0;\n}\n","import type { CommitRecord, AIDetectionResult, AIScoreEvaluation, SuspiciousFile } from '../../types/index.js';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { existsSync } from 'node:fs';\n\nexport function calculateAIScore(commit: CommitRecord): number {\n return evaluateAIScore(commit).score;\n}\n\nexport function evaluateAIScore(commit: CommitRecord): AIScoreEvaluation {\n let score = 0;\n const reasons: string[] = [];\n const totalLines = commit.files.reduce((sum, f) => sum + f.added, 0);\n const addedFiles = commit.files.filter((file) => file.status === 'added').length;\n\n if (mentionsAITool(commit.message) || mentionsAITool(commit.body || '')) {\n score += 55;\n reasons.push('AI 工具关键词');\n }\n\n if (mentionsGeneratedOutput(commit.message) || mentionsGeneratedOutput(commit.body || '')) {\n score += 15;\n reasons.push('生成产物关键词');\n }\n\n if (totalLines > 100 && commit.files.length >= 3 && addedFiles / commit.files.length > 0.6) {\n score += 20;\n reasons.push('大批量新增文件');\n }\n\n if (totalLines > 80 && commit.files.reduce((sum, f) => sum + f.deleted, 0) <= totalLines * 0.05) {\n score += 15;\n reasons.push('低删除率大新增');\n }\n\n if (totalLines > 1000 && commit.files.length > 10 && isGenericMessage(commit.message)) {\n score += 40;\n reasons.push('大型通用提交');\n } else if (totalLines > 500) {\n score += 20;\n reasons.push('大型提交');\n }\n\n const anomalousFiles = commit.files.filter(f => hasAnomalousNaming(f.path));\n if (anomalousFiles.length > 0) {\n score += Math.min(30, (anomalousFiles.length / commit.files.length) * 30);\n reasons.push('异常命名');\n }\n\n return { score: Math.min(100, score), reasons };\n}\n\nexport async function detectAICode(\n commits: CommitRecord[],\n repoPath: string\n): Promise<AIDetectionResult> {\n const suspiciousFiles: SuspiciousFile[] = [];\n\n for (const commit of commits) {\n const totalLines = commit.files.reduce((sum, f) => sum + f.added, 0);\n\n if (totalLines > 1000 && commit.files.length > 10) {\n if (isGenericMessage(commit.message)) {\n suspiciousFiles.push({\n commit: commit.hash,\n reason: 'large-commit-generic-message',\n score: 80,\n description: `大型提交(${totalLines}行)配合通用提交信息`,\n });\n }\n }\n\n for (const file of commit.files) {\n if (hasAnomalousNaming(file.path)) {\n suspiciousFiles.push({\n file: file.path,\n reason: 'anomalous-naming',\n score: 60,\n description: '命名模式异常(如 function1, temp1)',\n });\n }\n }\n }\n\n const uniqueFiles = new Set(commits.flatMap(c => c.files.map(f => f.path)));\n const filesToCheck = Array.from(uniqueFiles).slice(0, 50);\n\n for (const filePath of filesToCheck) {\n const fullPath = join(repoPath, filePath);\n if (!existsSync(fullPath)) continue;\n\n try {\n const content = await readFile(fullPath, 'utf-8');\n const commentDensity = calculateCommentDensity(content);\n\n if (commentDensity > 0.3) {\n suspiciousFiles.push({\n file: filePath,\n reason: 'excessive-comments',\n score: 70,\n description: `注释密度过高(${(commentDensity * 100).toFixed(1)}%)`,\n });\n }\n } catch {\n // 忽略读取错误\n }\n }\n\n const uniqueSuspicious = Array.from(\n new Map(suspiciousFiles.map(f => [f.file || f.commit, f])).values()\n );\n\n return {\n suspiciousFiles: uniqueSuspicious.sort((a, b) => b.score - a.score).slice(0, 20),\n totalSuspicious: uniqueSuspicious.length,\n };\n}\n\nfunction isGenericMessage(message: string): boolean {\n const genericPatterns = [\n /^fix$/i,\n /^update$/i,\n /^refactor$/i,\n /^fix bug$/i,\n /^update code$/i,\n /^bug fix$/i,\n /^minor fix$/i,\n /^wip$/i,\n /^temp$/i,\n /^test$/i,\n ];\n\n const normalized = message.trim().toLowerCase();\n return genericPatterns.some(pattern => pattern.test(normalized));\n}\n\nfunction mentionsAITool(text: string): boolean {\n const aiPatterns = [\n /\\bai\\b/i,\n /\\bcopilot\\b/i,\n /\\bclaude\\b/i,\n /\\bcursor\\b/i,\n /\\bchatgpt\\b/i,\n /\\bgpt[-\\s]?\\d*\\b/i,\n ];\n\n return aiPatterns.some(pattern => pattern.test(text));\n}\n\nfunction mentionsGeneratedOutput(text: string): boolean {\n return /\\bgenerated\\b|\\bcodegen\\b|\\bauto[-\\s]?generated\\b/i.test(text);\n}\n\nfunction hasAnomalousNaming(path: string): boolean {\n const anomalousPatterns = [\n /function\\d+/i,\n /temp\\d+/i,\n /test\\d+/i,\n /file\\d+/i,\n /component\\d+/i,\n /generated/i,\n /auto[-_]?generated/i,\n /untitled/i,\n /copy\\d*/i,\n ];\n\n return anomalousPatterns.some(pattern => pattern.test(path));\n}\n\nfunction calculateCommentDensity(content: string): number {\n const lines = content.split('\\n');\n let commentLines = 0;\n let codeLines = 0;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n\n if (\n trimmed.startsWith('//') ||\n trimmed.startsWith('/*') ||\n trimmed.startsWith('*') ||\n trimmed.startsWith('#')\n ) {\n commentLines++;\n } else {\n codeLines++;\n }\n }\n\n const totalLines = commentLines + codeLines;\n return totalLines > 0 ? commentLines / totalLines : 0;\n}\n","import type {\n CommitRecord,\n AIMetrics,\n AICommit,\n AuthorAIStats,\n DirectoryAIStats,\n AITrendPoint,\n} from '../types/index.js';\nimport { evaluateAIScore } from './tech-debt/ai-detector.js';\nimport { extname } from 'node:path';\n\nexport function calculateAIMetrics(commits: CommitRecord[]): {\n aiMetrics: AIMetrics;\n authorAIStats: AuthorAIStats[];\n directoryAIStats: DirectoryAIStats[];\n aiTrends: AITrendPoint[];\n} {\n if (commits.length === 0) {\n return {\n aiMetrics: { totalAILines: 0, totalLines: 0, aiPercentage: 0, suspiciousCommits: 0, highAICommits: [] },\n authorAIStats: [],\n directoryAIStats: [],\n aiTrends: [],\n };\n }\n\n const aiCommits: AICommit[] = [];\n const authorAIMap = new Map<string, { aiLines: number; totalLines: number }>();\n const directoryAIMap = new Map<string, { aiLines: number; totalLines: number; commits: Set<string>; lastModified: Date }>();\n const weeklyAIMap = new Map<string, { aiLines: number; totalLines: number }>();\n\n let totalAILines = 0;\n let totalLines = 0;\n let suspiciousCommits = 0;\n\n for (const commit of commits) {\n const aiEvaluation = evaluateAIScore(commit);\n const aiScore = aiEvaluation.score;\n const commitLines = commit.files.reduce((sum, f) => sum + f.added, 0);\n const estimatedAILines = Math.round((commitLines * aiScore) / 100);\n\n totalLines += commitLines;\n totalAILines += estimatedAILines;\n\n if (aiScore > 50) {\n suspiciousCommits++;\n aiCommits.push({\n hash: commit.hash,\n author: commit.author,\n date: commit.date,\n aiScore,\n estimatedAILines,\n linesAdded: commitLines,\n filesCount: commit.files.length,\n message: commit.message,\n reasons: aiEvaluation.reasons,\n });\n }\n\n const authorKey = commit.email.toLowerCase();\n const authorData = authorAIMap.get(authorKey) || { aiLines: 0, totalLines: 0 };\n authorData.aiLines += estimatedAILines;\n authorData.totalLines += commitLines;\n authorAIMap.set(authorKey, authorData);\n\n for (const file of commit.files) {\n const dir = getTopDirectory(file.path);\n const fileAILines = Math.round((file.added * aiScore) / 100);\n\n const dirData = directoryAIMap.get(dir) || {\n aiLines: 0,\n totalLines: 0,\n commits: new Set(),\n lastModified: commit.date,\n };\n dirData.aiLines += fileAILines;\n dirData.totalLines += file.added;\n dirData.commits.add(commit.hash);\n if (commit.date > dirData.lastModified) {\n dirData.lastModified = commit.date;\n }\n directoryAIMap.set(dir, dirData);\n }\n\n const week = getWeekKey(commit.date);\n const weekData = weeklyAIMap.get(week) || { aiLines: 0, totalLines: 0 };\n weekData.aiLines += estimatedAILines;\n weekData.totalLines += commitLines;\n weeklyAIMap.set(week, weekData);\n }\n\n const aiMetrics: AIMetrics = {\n totalAILines,\n totalLines,\n aiPercentage: totalLines > 0 ? (totalAILines / totalLines) * 100 : 0,\n suspiciousCommits,\n highAICommits: aiCommits.sort((a, b) => b.aiScore - a.aiScore).slice(0, 20),\n };\n\n const authorAIStats: AuthorAIStats[] = [];\n const authorMap = new Map<string, { name: string; email: string }>();\n for (const commit of commits) {\n authorMap.set(commit.email.toLowerCase(), { name: commit.author, email: commit.email });\n }\n\n for (const [authorKey, data] of authorAIMap) {\n const author = authorMap.get(authorKey);\n if (author) {\n authorAIStats.push({\n author: author.name,\n email: author.email,\n aiLines: data.aiLines,\n totalLines: data.totalLines,\n aiPercentage: data.totalLines > 0 ? (data.aiLines / data.totalLines) * 100 : 0,\n });\n }\n }\n\n const directoryAIStats: DirectoryAIStats[] = [];\n for (const [path, data] of directoryAIMap) {\n const aiPercentage = data.totalLines > 0 ? (data.aiLines / data.totalLines) * 100 : 0;\n const isHighRisk = data.commits.size > 50 && aiPercentage > 60;\n\n directoryAIStats.push({\n path,\n displayPath: path,\n commits: data.commits.size,\n aiLines: data.aiLines,\n totalLines: data.totalLines,\n aiPercentage,\n lastModified: data.lastModified,\n isHighRisk,\n });\n }\n\n const aiTrends: AITrendPoint[] = Array.from(weeklyAIMap.entries())\n .map(([week, data]) => ({\n week,\n aiLines: data.aiLines,\n totalLines: data.totalLines,\n aiPercentage: data.totalLines > 0 ? (data.aiLines / data.totalLines) * 100 : 0,\n }))\n .sort((a, b) => a.week.localeCompare(b.week));\n\n return {\n aiMetrics,\n authorAIStats: authorAIStats.sort((a, b) => b.aiPercentage - a.aiPercentage),\n directoryAIStats: directoryAIStats.sort((a, b) => b.aiPercentage - a.aiPercentage),\n aiTrends,\n };\n}\n\nfunction getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\nfunction getWeekKey(date: Date): string {\n const year = date.getFullYear();\n const startOfYear = new Date(year, 0, 1);\n const days = Math.floor((date.getTime() - startOfYear.getTime()) / (24 * 60 * 60 * 1000));\n const week = Math.ceil((days + startOfYear.getDay() + 1) / 7);\n return `${year}-W${week.toString().padStart(2, '0')}`;\n}\n","/** 获取文件路径的第一层目录 */\nexport function getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\n/** 格式化日期为 YYYY-MM-DD */\nexport function formatDateKey(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n return `${y}-${m}-${d}`;\n}\n","import type {\n AIQualityRiskFile,\n AIQualityRiskMetrics,\n AIQualityScatterPoint,\n ChangeSizeBucket,\n ChangeSizeDistribution,\n CommitRecord,\n DirectoryCouplingMetrics,\n DirectoryMatrixCell,\n DirectoryPair,\n LargeCommit,\n} from '../types/index.js';\nimport { calculateAIScore } from './tech-debt/ai-detector.js';\nimport { getTopDirectory } from './stats-utils.js';\n\nconst MAX_FILES_PER_COMMIT_FOR_PAIRS = 80;\nconst MAX_DIRS_FOR_MATRIX = 12;\n\n/**\n * #2 变更尺寸分布\n */\nexport function calculateChangeSizeDistribution(\n commits: CommitRecord[]\n): ChangeSizeDistribution {\n if (commits.length === 0) {\n return {\n buckets: emptyBuckets(),\n avgChangeSize: 0,\n medianChangeSize: 0,\n p95ChangeSize: 0,\n largeCommits: [],\n };\n }\n\n const sizes: number[] = [];\n const commitInfo: LargeCommit[] = [];\n\n for (const commit of commits) {\n let total = 0;\n for (const f of commit.files) total += f.added + f.deleted;\n sizes.push(total);\n commitInfo.push({\n hash: commit.hash,\n author: commit.author,\n date: commit.date,\n message: commit.message,\n totalLines: total,\n filesCount: commit.files.length,\n });\n }\n\n // 分桶\n const counts = { XS: 0, S: 0, M: 0, L: 0, XL: 0 };\n for (const size of sizes) {\n if (size < 10) counts.XS++;\n else if (size < 50) counts.S++;\n else if (size < 200) counts.M++;\n else if (size < 1000) counts.L++;\n else counts.XL++;\n }\n\n const total = sizes.length;\n const buckets: ChangeSizeBucket[] = [\n { label: 'XS', range: '<10', count: counts.XS, percentage: pct(counts.XS, total) },\n { label: 'S', range: '10-49', count: counts.S, percentage: pct(counts.S, total) },\n { label: 'M', range: '50-199', count: counts.M, percentage: pct(counts.M, total) },\n { label: 'L', range: '200-999', count: counts.L, percentage: pct(counts.L, total) },\n { label: 'XL', range: '≥1000', count: counts.XL, percentage: pct(counts.XL, total) },\n ];\n\n const sorted = [...sizes].sort((a, b) => a - b);\n const avgChangeSize = sizes.reduce((a, b) => a + b, 0) / sizes.length;\n const medianChangeSize = sorted[Math.floor(sorted.length / 2)] ?? 0;\n const p95ChangeSize = sorted[Math.floor(sorted.length * 0.95)] ?? 0;\n\n const largeCommits = commitInfo\n .sort((a, b) => b.totalLines - a.totalLines)\n .slice(0, 20);\n\n return {\n buckets,\n avgChangeSize,\n medianChangeSize,\n p95ChangeSize,\n largeCommits,\n };\n}\n\n/**\n * #6 目录耦合(跨第一层目录共变)\n */\nexport function calculateDirectoryCoupling(\n commits: CommitRecord[]\n): DirectoryCouplingMetrics {\n if (commits.length === 0) {\n return { pairs: [], matrix: [], directories: [] };\n }\n\n // 每个目录被改动的提交数\n const dirCommitCount = new Map<string, number>();\n // 跨目录共现次数\n const pairMap = new Map<string, { dir1: string; dir2: string; count: number }>();\n\n for (const commit of commits) {\n const files = commit.files.slice(0, MAX_FILES_PER_COMMIT_FOR_PAIRS);\n const dirs = new Set<string>();\n for (const f of files) dirs.add(getTopDirectory(f.path));\n\n const dirArr = Array.from(dirs).sort();\n for (const d of dirArr) {\n dirCommitCount.set(d, (dirCommitCount.get(d) || 0) + 1);\n }\n\n for (let i = 0; i < dirArr.length; i++) {\n for (let j = i + 1; j < dirArr.length; j++) {\n const key = `${dirArr[i]}|||${dirArr[j]}`;\n const existing = pairMap.get(key) || { dir1: dirArr[i], dir2: dirArr[j], count: 0 };\n existing.count++;\n pairMap.set(key, existing);\n }\n }\n }\n\n // 计算耦合度\n const pairs: DirectoryPair[] = Array.from(pairMap.values())\n .map(({ dir1, dir2, count }) => {\n const c1 = dirCommitCount.get(dir1) || 1;\n const c2 = dirCommitCount.get(dir2) || 1;\n const coupling = count / Math.min(c1, c2);\n return { dir1, dir2, coOccurrence: count, coupling };\n })\n .filter((p) => p.coOccurrence >= 2)\n .sort((a, b) => b.coupling - a.coupling)\n .slice(0, 30);\n\n // 矩阵:取活跃 TOP N 目录\n const topDirs = Array.from(dirCommitCount.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, MAX_DIRS_FOR_MATRIX)\n .map(([d]) => d);\n\n const matrix: DirectoryMatrixCell[] = [];\n for (const d1 of topDirs) {\n for (const d2 of topDirs) {\n if (d1 === d2) {\n matrix.push({ dir1: d1, dir2: d2, value: dirCommitCount.get(d1) || 0 });\n } else {\n const key = d1 < d2 ? `${d1}|||${d2}` : `${d2}|||${d1}`;\n matrix.push({ dir1: d1, dir2: d2, value: pairMap.get(key)?.count || 0 });\n }\n }\n }\n\n return {\n pairs,\n matrix,\n directories: topDirs,\n };\n}\n\n/**\n * #18 AI × 质量交叉风险\n */\nexport function calculateAIQualityRisk(commits: CommitRecord[]): AIQualityRiskMetrics {\n if (commits.length === 0) {\n return {\n files: [],\n scatter: [],\n summary: { highAIHighChurn: 0, highAILowChurn: 0, lowAIHighChurn: 0, lowAILowChurn: 0 },\n };\n }\n\n // 文件级聚合:累计 AI 分数(加权)+ churn\n const fileMap = new Map<\n string,\n { added: number; deleted: number; modifyCount: number; weightedAI: number; weight: number }\n >();\n\n for (const commit of commits) {\n const aiScore = calculateAIScore(commit);\n for (const file of commit.files) {\n const lines = file.added + file.deleted;\n const entry = fileMap.get(file.path) || {\n added: 0,\n deleted: 0,\n modifyCount: 0,\n weightedAI: 0,\n weight: 0,\n };\n entry.added += file.added;\n entry.deleted += file.deleted;\n entry.modifyCount++;\n // 用 lines 作为权重,避免一次小修改主导分数\n const w = Math.max(lines, 1);\n entry.weightedAI += aiScore * w;\n entry.weight += w;\n fileMap.set(file.path, entry);\n }\n }\n\n const files: AIQualityRiskFile[] = [];\n const scatter: AIQualityScatterPoint[] = [];\n let highAIHighChurn = 0,\n highAILowChurn = 0,\n lowAIHighChurn = 0,\n lowAILowChurn = 0;\n\n for (const [path, data] of fileMap) {\n const avgAI = data.weight > 0 ? data.weightedAI / data.weight : 0;\n const churnRate = data.added > 0 ? data.deleted / data.added : 0;\n const totalLines = data.added + data.deleted;\n\n // 仅纳入有意义的文件(修改 ≥ 2 次或行数 ≥ 50)\n if (data.modifyCount < 2 && totalLines < 50) continue;\n\n // 综合风险:AI 占比 × churn × log(modifyCount)\n const riskScore =\n (avgAI / 100) * Math.min(churnRate, 2) * Math.log2(data.modifyCount + 1) * 50;\n\n files.push({\n path,\n aiScore: avgAI,\n churnRate,\n modifyCount: data.modifyCount,\n totalLines,\n riskScore,\n });\n\n scatter.push({ path, aiScore: avgAI, churnRate, modifyCount: data.modifyCount });\n\n const highAI = avgAI > 50;\n const highChurn = churnRate > 0.5;\n if (highAI && highChurn) highAIHighChurn++;\n else if (highAI) highAILowChurn++;\n else if (highChurn) lowAIHighChurn++;\n else lowAILowChurn++;\n }\n\n files.sort((a, b) => b.riskScore - a.riskScore);\n\n return {\n files: files.slice(0, 20),\n scatter: scatter.slice(0, 500), // 限制散点数\n summary: { highAIHighChurn, highAILowChurn, lowAIHighChurn, lowAILowChurn },\n };\n}\n\nfunction emptyBuckets(): ChangeSizeBucket[] {\n return [\n { label: 'XS', range: '<10', count: 0, percentage: 0 },\n { label: 'S', range: '10-49', count: 0, percentage: 0 },\n { label: 'M', range: '50-199', count: 0, percentage: 0 },\n { label: 'L', range: '200-999', count: 0, percentage: 0 },\n { label: 'XL', range: '≥1000', count: 0, percentage: 0 },\n ];\n}\n\nfunction pct(part: number, total: number): number {\n return total > 0 ? (part / total) * 100 : 0;\n}\n","import { extname } from 'node:path';\nimport type {\n AuthorFileTypeContribution,\n CollabFile,\n CollaborationMetrics,\n CommitDetail,\n CommitMessageStats,\n CommitRecord,\n CumulativePoint,\n HotFile,\n QualityMetrics,\n SoloFile,\n TimePatterns,\n TrendData,\n WeeklyPoint,\n} from '../types/index.js';\nimport { formatDateKey } from './stats-utils.js';\n\n/** 生成报告页筛选用的提交明细 */\nexport function calculateCommitDetails(commits: CommitRecord[]): CommitDetail[] {\n return commits.map((commit) => {\n let linesAdded = 0;\n let linesDeleted = 0;\n\n for (const file of commit.files) {\n linesAdded += file.added;\n linesDeleted += file.deleted;\n }\n\n return {\n hash: commit.hash,\n author: commit.author,\n email: commit.email,\n repoName: '',\n date: commit.date,\n message: commit.message,\n linesAdded,\n linesDeleted,\n files: commit.files.map((file) => ({ ...file })),\n };\n });\n}\n\n/** 计算代码质量指标 */\nexport function calculateQualityMetrics(commits: CommitRecord[]): QualityMetrics {\n if (commits.length === 0) {\n return emptyQualityMetrics();\n }\n\n const totalFiles = commits.reduce((sum, commit) => sum + commit.files.length, 0);\n const avgFilesPerCommit = totalFiles / commits.length;\n\n const totalLines = commits.reduce(\n (sum, commit) => sum + commit.files.reduce((s, file) => s + file.added + file.deleted, 0),\n 0\n );\n const avgLinesPerCommit = totalLines / commits.length;\n\n const totalAdded = commits.reduce(\n (sum, commit) => sum + commit.files.reduce((s, file) => s + file.added, 0),\n 0\n );\n const totalDeleted = commits.reduce(\n (sum, commit) => sum + commit.files.reduce((s, file) => s + file.deleted, 0),\n 0\n );\n const churnRate = totalAdded > 0 ? totalDeleted / totalAdded : 0;\n\n const fileModifyMap = new Map<string, { count: number; authors: Set<string> }>();\n for (const commit of commits) {\n for (const file of commit.files) {\n const entry = fileModifyMap.get(file.path) || { count: 0, authors: new Set() };\n entry.count++;\n entry.authors.add(commit.author);\n fileModifyMap.set(file.path, entry);\n }\n }\n\n const hotFiles: HotFile[] = Array.from(fileModifyMap.entries())\n .map(([path, data]) => ({\n path,\n modifyCount: data.count,\n authors: Array.from(data.authors),\n }))\n .sort((a, b) => b.modifyCount - a.modifyCount)\n .slice(0, 10);\n\n return { avgFilesPerCommit, avgLinesPerCommit, churnRate, hotFiles };\n}\n\n/** 计算时间模式指标 */\nexport function calculateTimePatterns(commits: CommitRecord[]): TimePatterns {\n if (commits.length === 0) {\n return emptyTimePatterns();\n }\n\n const weekdayDistribution = new Array<number>(7).fill(0);\n const weekdayByAuthor: Map<number, Map<string, number>> = new Map();\n\n for (const commit of commits) {\n const day = commit.date.getDay();\n const idx = day === 0 ? 6 : day - 1;\n weekdayDistribution[idx]++;\n\n if (!weekdayByAuthor.has(idx)) {\n weekdayByAuthor.set(idx, new Map());\n }\n const dayAuthors = weekdayByAuthor.get(idx)!;\n dayAuthors.set(commit.author, (dayAuthors.get(commit.author) || 0) + 1);\n }\n\n const weekendCommits =\n (weekdayDistribution[5] + weekdayDistribution[6]) / commits.length;\n\n const sorted = [...commits].sort(\n (a, b) => a.date.getTime() - b.date.getTime()\n );\n let totalInterval = 0;\n for (let i = 1; i < sorted.length; i++) {\n totalInterval += sorted[i].date.getTime() - sorted[i - 1].date.getTime();\n }\n const avgCommitInterval =\n sorted.length > 1 ? totalInterval / (sorted.length - 1) / 3600000 : 0;\n\n const { longestStreak, currentStreak } = calculateStreaks(sorted);\n\n const weekdayByAuthorArray = Array.from({ length: 7 }, (_, day) => {\n const authorMap = weekdayByAuthor.get(day);\n const authors: Record<string, number> = {};\n if (authorMap) {\n authorMap.forEach((count, author) => {\n authors[author] = count;\n });\n }\n return {\n count: weekdayDistribution[day],\n authors,\n };\n });\n\n return {\n weekdayDistribution,\n weekendCommits,\n avgCommitInterval,\n longestStreak,\n currentStreak,\n weekdayByAuthor: weekdayByAuthorArray,\n };\n}\n\n/** 计算趋势数据 */\nexport function calculateTrends(commits: CommitRecord[]): TrendData {\n if (commits.length === 0) {\n return emptyTrendData();\n }\n\n const weekMap = new Map<string, WeeklyPoint>();\n for (const commit of commits) {\n const week = getWeekKey(commit.date);\n const entry = weekMap.get(week) || {\n week,\n commits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n };\n entry.commits++;\n for (const file of commit.files) {\n entry.linesAdded += file.added;\n entry.linesDeleted += file.deleted;\n }\n weekMap.set(week, entry);\n }\n const weeklyTrend = Array.from(weekMap.values()).sort((a, b) =>\n a.week.localeCompare(b.week)\n );\n\n const dailyNet = new Map<string, number>();\n for (const commit of commits) {\n const dateKey = formatDateKey(commit.date);\n const net = commit.files.reduce((sum, file) => sum + file.added - file.deleted, 0);\n dailyNet.set(dateKey, (dailyNet.get(dateKey) || 0) + net);\n }\n\n let cumulative = 0;\n const cumulativeLines: CumulativePoint[] = Array.from(dailyNet.entries())\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([date, net]) => {\n cumulative += net;\n return { date, netLines: cumulative };\n });\n\n return { weeklyTrend, cumulativeLines };\n}\n\n/** 计算协作指标 */\nexport function calculateCollaboration(commits: CommitRecord[]): CollaborationMetrics {\n if (commits.length === 0) {\n return emptyCollaborationMetrics();\n }\n\n const fileAuthors = new Map<string, Set<string>>();\n const fileCommits = new Map<string, number>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const authors = fileAuthors.get(file.path) || new Set();\n authors.add(commit.email.toLowerCase());\n fileAuthors.set(file.path, authors);\n fileCommits.set(file.path, (fileCommits.get(file.path) || 0) + 1);\n }\n }\n\n const soloFiles: SoloFile[] = [];\n const collaborationHotspots: CollabFile[] = [];\n\n for (const [path, authors] of fileAuthors) {\n const commitCount = fileCommits.get(path) || 0;\n if (authors.size === 1 && commitCount >= 3) {\n soloFiles.push({\n path,\n author: Array.from(authors)[0],\n commits: commitCount,\n });\n } else if (authors.size >= 2 && commitCount >= 5) {\n collaborationHotspots.push({\n path,\n authorCount: authors.size,\n totalCommits: commitCount,\n });\n }\n }\n\n return {\n soloFiles: soloFiles.sort((a, b) => b.commits - a.commits).slice(0, 10),\n collaborationHotspots: collaborationHotspots\n .sort((a, b) => b.totalCommits - a.totalCommits)\n .slice(0, 10),\n };\n}\n\n/** 计算 Commit Message 统计 */\nexport function calculateMessageStats(commits: CommitRecord[]): CommitMessageStats {\n if (commits.length === 0) {\n return emptyMessageStats();\n }\n\n const typeDistribution: Record<string, number> = {};\n let totalLength = 0;\n\n const typeRegex =\n /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\\(.+\\))?:/i;\n\n for (const commit of commits) {\n totalLength += commit.message.length;\n const match = commit.message.match(typeRegex);\n if (match) {\n const type = match[1].toLowerCase();\n typeDistribution[type] = (typeDistribution[type] || 0) + 1;\n } else {\n typeDistribution['other'] = (typeDistribution['other'] || 0) + 1;\n }\n }\n\n return {\n typeDistribution,\n avgMessageLength: totalLength / commits.length,\n };\n}\n\n/** 计算作者文件类型贡献 */\nexport function calculateAuthorFileTypeContributions(\n commits: CommitRecord[]\n): AuthorFileTypeContribution[] {\n if (commits.length === 0) {\n return [];\n }\n\n const contributionMap = new Map<string, AuthorFileTypeContribution>();\n const uniqueFilesMap = new Map<string, Set<string>>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n const key = `${commit.email.toLowerCase()}|||${ext}`;\n\n let contribution = contributionMap.get(key);\n if (!contribution) {\n contribution = {\n author: commit.author,\n email: commit.email,\n extension: ext,\n linesAdded: 0,\n linesDeleted: 0,\n commits: 0,\n fileCount: 0,\n };\n contributionMap.set(key, contribution);\n uniqueFilesMap.set(key, new Set());\n }\n\n contribution.linesAdded += file.added;\n contribution.linesDeleted += file.deleted;\n uniqueFilesMap.get(key)!.add(file.path);\n }\n }\n\n const commitCountMap = new Map<string, Set<string>>();\n for (const commit of commits) {\n for (const file of commit.files) {\n const ext = extname(file.path).toLowerCase() || '(无扩展名)';\n const key = `${commit.email.toLowerCase()}|||${ext}`;\n\n if (!commitCountMap.has(key)) {\n commitCountMap.set(key, new Set());\n }\n commitCountMap.get(key)!.add(commit.hash);\n }\n }\n\n for (const [key, contribution] of contributionMap) {\n contribution.commits = commitCountMap.get(key)?.size || 0;\n contribution.fileCount = uniqueFilesMap.get(key)?.size || 0;\n }\n\n return Array.from(contributionMap.values())\n .sort((a, b) => {\n const totalA = a.linesAdded + a.linesDeleted;\n const totalB = b.linesAdded + b.linesDeleted;\n return totalB - totalA;\n })\n .slice(0, 20);\n}\n\nexport function emptyQualityMetrics(): QualityMetrics {\n return {\n avgFilesPerCommit: 0,\n avgLinesPerCommit: 0,\n churnRate: 0,\n hotFiles: [],\n };\n}\n\nexport function emptyTimePatterns(): TimePatterns {\n return {\n weekdayDistribution: new Array<number>(7).fill(0),\n weekendCommits: 0,\n avgCommitInterval: 0,\n longestStreak: 0,\n currentStreak: 0,\n };\n}\n\nexport function emptyTrendData(): TrendData {\n return {\n weeklyTrend: [],\n cumulativeLines: [],\n };\n}\n\nexport function emptyCollaborationMetrics(): CollaborationMetrics {\n return {\n soloFiles: [],\n collaborationHotspots: [],\n };\n}\n\nexport function emptyMessageStats(): CommitMessageStats {\n return {\n typeDistribution: {},\n avgMessageLength: 0,\n };\n}\n\n/** 计算连续提交天数 */\nfunction calculateStreaks(sortedCommits: CommitRecord[]): {\n longestStreak: number;\n currentStreak: number;\n} {\n if (sortedCommits.length === 0) {\n return { longestStreak: 0, currentStreak: 0 };\n }\n\n const uniqueDates = new Set<string>();\n for (const commit of sortedCommits) {\n uniqueDates.add(formatDateKey(commit.date));\n }\n\n const sortedDates = Array.from(uniqueDates).sort();\n if (sortedDates.length === 0) {\n return { longestStreak: 0, currentStreak: 0 };\n }\n\n let longestStreak = 1;\n let currentStreakCount = 1;\n let tempStreak = 1;\n\n for (let i = 1; i < sortedDates.length; i++) {\n const prevDate = new Date(sortedDates[i - 1]);\n const currDate = new Date(sortedDates[i]);\n const diffDays = Math.round(\n (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (diffDays === 1) {\n tempStreak++;\n } else {\n tempStreak = 1;\n }\n\n longestStreak = Math.max(longestStreak, tempStreak);\n }\n\n const today = formatDateKey(new Date());\n const lastCommitDate = sortedDates[sortedDates.length - 1];\n const lastDate = new Date(lastCommitDate);\n const todayDate = new Date(today);\n const daysSinceLastCommit = Math.round(\n (todayDate.getTime() - lastDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (daysSinceLastCommit <= 1) {\n currentStreakCount = 1;\n for (let i = sortedDates.length - 2; i >= 0; i--) {\n const currDate = new Date(sortedDates[i + 1]);\n const prevDate = new Date(sortedDates[i]);\n const diffDays = Math.round(\n (currDate.getTime() - prevDate.getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (diffDays === 1) {\n currentStreakCount++;\n } else {\n break;\n }\n }\n } else {\n currentStreakCount = 0;\n }\n\n return { longestStreak, currentStreak: currentStreakCount };\n}\n\n/** 获取 ISO 周标识 */\nfunction getWeekKey(date: Date): string {\n const d = new Date(date);\n d.setHours(0, 0, 0, 0);\n d.setDate(d.getDate() + 4 - (d.getDay() || 7));\n const yearStart = new Date(d.getFullYear(), 0, 1);\n const weekNo = Math.ceil(\n ((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7\n );\n return `${d.getFullYear()}-W${String(weekNo).padStart(2, '0')}`;\n}\n","import type { CommitStats } from '../types/index.js';\nimport {\n emptyCollaborationMetrics,\n emptyMessageStats,\n emptyQualityMetrics,\n emptyTimePatterns,\n emptyTrendData,\n} from './stats-metrics.js';\n\n/** 创建空的统计对象 */\nexport function emptyStats(): CommitStats {\n return {\n totalCommits: 0,\n linesAdded: 0,\n linesDeleted: 0,\n filesChanged: 0,\n firstCommitDate: new Date(),\n lastCommitDate: new Date(),\n busiestDay: { date: '', count: 0 },\n authors: [],\n fileTypes: [],\n directories: [],\n hourlyDistribution: new Array<number>(24).fill(0),\n dailyHeatmap: {},\n quality: emptyQualityMetrics(),\n timePatterns: emptyTimePatterns(),\n trends: emptyTrendData(),\n collaboration: emptyCollaborationMetrics(),\n messageStats: emptyMessageStats(),\n authorFileTypeContributions: [],\n commitDetails: [],\n };\n}\n","import type { CommitRecord, TeamHealthMetrics, CriticalAuthor } from '../../types/index.js';\n\n/**\n * 计算团队健康度指标\n */\nexport function calculateTeamHealth(commits: CommitRecord[]): TeamHealthMetrics {\n // 边界情况:空提交\n if (commits.length === 0) {\n return emptyTeamHealth();\n }\n\n // Step 1: 构建数据结构\n const authorFiles = buildAuthorFilesMap(commits);\n const fileAuthors = buildFileAuthorsMap(commits);\n\n // 边界情况:无文件变更\n if (fileAuthors.size === 0) {\n return emptyTeamHealth();\n }\n\n // Step 2: 计算每个作者的知识独占度\n const authorScores = calculateAuthorScores(authorFiles, fileAuthors);\n\n // Step 3: 筛选关键人员(评分 >10)\n const criticalAuthors = authorScores.filter((a) => a.knowledgeScore > 10);\n\n // Step 4: 计算知识分布均匀度\n const totalUniqueFiles = criticalAuthors.reduce(\n (sum, a) => sum + a.uniqueFiles.length,\n 0\n );\n const knowledgeDistribution = 1 - totalUniqueFiles / fileAuthors.size;\n\n // Step 5: 评估风险等级\n const busFactor = criticalAuthors.length;\n const riskLevel = busFactor === 1 ? 'high' : busFactor <= 3 ? 'medium' : 'low';\n\n return {\n busFactor,\n criticalAuthors,\n knowledgeDistribution,\n riskLevel,\n };\n}\n\n/**\n * 构建 作者 -> 文件集合 映射\n */\nfunction buildAuthorFilesMap(commits: CommitRecord[]): Map<string, Set<string>> {\n const map = new Map<string, Set<string>>();\n\n for (const commit of commits) {\n const authorKey = `${commit.author} <${commit.email}>`;\n if (!map.has(authorKey)) {\n map.set(authorKey, new Set());\n }\n const files = map.get(authorKey)!;\n for (const file of commit.files) {\n files.add(file.path);\n }\n }\n\n return map;\n}\n\n/**\n * 构建 文件 -> (作者 -> 提交次数) 映射\n */\nfunction buildFileAuthorsMap(commits: CommitRecord[]): Map<string, Map<string, number>> {\n const map = new Map<string, Map<string, number>>();\n\n for (const commit of commits) {\n const authorKey = `${commit.author} <${commit.email}>`;\n for (const file of commit.files) {\n if (!map.has(file.path)) {\n map.set(file.path, new Map());\n }\n const authors = map.get(file.path)!;\n authors.set(authorKey, (authors.get(authorKey) || 0) + 1);\n }\n }\n\n return map;\n}\n\n/**\n * 计算每个作者的知识独占度评分\n */\nfunction calculateAuthorScores(\n authorFiles: Map<string, Set<string>>,\n fileAuthors: Map<string, Map<string, number>>\n): CriticalAuthor[] {\n const scores: CriticalAuthor[] = [];\n\n for (const [authorKey, files] of authorFiles) {\n const uniqueFiles: string[] = [];\n const dominantFiles: string[] = [];\n\n for (const filePath of files) {\n const authors = fileAuthors.get(filePath)!;\n const authorCount = authors.size;\n\n // 独有文件:只有该作者修改过\n if (authorCount === 1) {\n uniqueFiles.push(filePath);\n continue;\n }\n\n // 主导文件:该作者贡献 >50%\n const authorCommits = authors.get(authorKey) || 0;\n const totalCommits = Array.from(authors.values()).reduce((a, b) => a + b, 0);\n if (authorCommits / totalCommits > 0.5) {\n dominantFiles.push(filePath);\n }\n }\n\n // 知识独占度评分 = (独有文件*2 + 主导文件) / 总文件数 * 100\n const knowledgeScore =\n ((uniqueFiles.length * 2 + dominantFiles.length) / fileAuthors.size) * 100;\n\n const [name, email] = parseAuthorKey(authorKey);\n scores.push({\n name,\n email,\n uniqueFiles,\n dominantFiles,\n knowledgeScore,\n });\n }\n\n return scores;\n}\n\n/**\n * 解析作者键 \"name <email>\" -> [name, email]\n */\nfunction parseAuthorKey(authorKey: string): [string, string] {\n const match = authorKey.match(/^(.+) <(.+)>$/);\n if (match) {\n return [match[1], match[2]];\n }\n return [authorKey, ''];\n}\n\nfunction emptyTeamHealth(): TeamHealthMetrics {\n return {\n busFactor: 0,\n criticalAuthors: [],\n knowledgeDistribution: 1,\n riskLevel: 'low',\n };\n}\n","import type { CommitRecord, StabilityMetrics, FileChurn, DirectoryChurn } from '../../types/index.js';\n\n/**\n * 计算代码稳定性指标\n */\nexport function calculateStability(commits: CommitRecord[]): StabilityMetrics {\n if (commits.length === 0) {\n return emptyStability();\n }\n\n // Step 1: 统计文件级流失率\n const fileStats = new Map<string, { added: number; deleted: number; modifyCount: number }>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const stat = fileStats.get(file.path) || { added: 0, deleted: 0, modifyCount: 0 };\n stat.added += file.added;\n stat.deleted += file.deleted;\n stat.modifyCount++;\n fileStats.set(file.path, stat);\n }\n }\n\n // 转换为 FileChurn 数组,按流失率排序,取 TOP 20\n const fileChurnRate: FileChurn[] = Array.from(fileStats.entries())\n .map(([path, { added, deleted, modifyCount }]) => ({\n path,\n added,\n deleted,\n churnRate: added > 0 ? deleted / added : 0,\n modifyCount,\n isUnstable: deleted / Math.max(added, 1) > 0.5,\n }))\n .sort((a, b) => b.churnRate - a.churnRate)\n .slice(0, 20);\n\n // Step 2: 统计目录级流失率\n const dirStats = new Map<string, { added: number; deleted: number; files: Set<string> }>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n const dir = getTopDirectory(file.path);\n const stat = dirStats.get(dir) || { added: 0, deleted: 0, files: new Set() };\n stat.added += file.added;\n stat.deleted += file.deleted;\n stat.files.add(file.path);\n dirStats.set(dir, stat);\n }\n }\n\n const directoryChurnRate: DirectoryChurn[] = Array.from(dirStats.entries())\n .map(([path, { added, deleted, files }]) => ({\n path,\n churnRate: added > 0 ? deleted / added : 0,\n totalChanges: added + deleted,\n fileCount: files.size,\n }))\n .sort((a, b) => b.churnRate - a.churnRate)\n .slice(0, 10);\n\n // Step 3: 计算 Revert 率\n const revertCommits = commits.filter((c) => /revert|rollback/i.test(c.message)).length;\n const revertRate = revertCommits / commits.length;\n\n // Step 4: 计算 Fix 提交率\n const fixCommits = commits.filter((c) => /^fix(\\(.+\\))?:/i.test(c.message)).length;\n const fixCommitRate = fixCommits / commits.length;\n\n // Step 5: 计算稳定性评分\n const avgChurnRate =\n fileChurnRate.length > 0\n ? fileChurnRate.reduce((sum, f) => sum + f.churnRate, 0) / fileChurnRate.length\n : 0;\n\n const stabilityScore = Math.max(\n 0,\n Math.round(100 - (avgChurnRate * 50 + revertRate * 100 + fixCommitRate * 50))\n );\n\n return {\n fileChurnRate,\n directoryChurnRate,\n revertRate: Math.round(revertRate * 100) / 100,\n fixCommitRate: Math.round(fixCommitRate * 100) / 100,\n stabilityScore,\n };\n}\n\n/**\n * 获取文件路径的第一层目录\n */\nfunction getTopDirectory(filePath: string): string {\n const parts = filePath.split('/');\n return parts.length > 1 ? parts[0] : '(根目录)';\n}\n\nfunction emptyStability(): StabilityMetrics {\n return {\n fileChurnRate: [],\n directoryChurnRate: [],\n revertRate: 0,\n fixCommitRate: 0,\n stabilityScore: 100,\n };\n}\n","import type { CommitRecord, WorkPressureMetrics, HolidayCommit } from '../../types/index.js';\n\n/**\n * 计算工作压力指标\n */\nexport function calculateWorkPressure(commits: CommitRecord[]): WorkPressureMetrics {\n if (commits.length === 0) {\n return emptyWorkPressure();\n }\n\n let lateNightCommits = 0;\n let earlyMorningCommits = 0;\n let weekendCommits = 0;\n\n const holidayMap = new Map<string, { name: string; commits: number }>();\n const holidays = getHolidays();\n\n for (const commit of commits) {\n const hour = commit.date.getHours();\n const day = commit.date.getDay();\n const dateKey = formatDate(commit.date);\n\n // 深夜 (23:00-02:00)\n if (hour >= 23 || hour < 2) {\n lateNightCommits++;\n }\n\n // 凌晨 (02:00-06:00)\n if (hour >= 2 && hour < 6) {\n earlyMorningCommits++;\n }\n\n // 周末\n if (day === 0 || day === 6) {\n weekendCommits++;\n }\n\n // 假期\n const holiday = holidays.get(dateKey);\n if (holiday) {\n const entry = holidayMap.get(dateKey) || { name: holiday, commits: 0 };\n entry.commits++;\n holidayMap.set(dateKey, entry);\n }\n }\n\n const holidayCommits: HolidayCommit[] = Array.from(holidayMap.entries())\n .map(([date, { name, commits }]) => ({ date, holidayName: name, commits }))\n .sort((a, b) => b.commits - a.commits);\n\n // 非工作时间占比\n const holidayTotal = holidayCommits.reduce((sum, h) => sum + h.commits, 0);\n const offHoursCount = lateNightCommits + earlyMorningCommits + weekendCommits + holidayTotal;\n const offHoursRate = offHoursCount / commits.length;\n\n // 压力评分 (0-100)\n const lateNightWeight = (lateNightCommits / commits.length) * 40;\n const earlyMorningWeight = (earlyMorningCommits / commits.length) * 30;\n const weekendWeight = (weekendCommits / commits.length) * 20;\n const holidayWeight = (holidayCommits.length > 0 ? 1 : 0) * 10;\n\n const pressureScore = Math.round(lateNightWeight + earlyMorningWeight + weekendWeight + holidayWeight);\n\n return {\n lateNightCommits,\n earlyMorningCommits,\n weekendCommits,\n holidayCommits,\n pressureScore,\n offHoursRate: Math.round(offHoursRate * 1000) / 1000\n };\n}\n\nfunction emptyWorkPressure(): WorkPressureMetrics {\n return {\n lateNightCommits: 0,\n earlyMorningCommits: 0,\n weekendCommits: 0,\n holidayCommits: [],\n pressureScore: 0,\n offHoursRate: 0,\n };\n}\n\n/**\n * 获取中国 2024-2026 主要节假日\n */\nfunction getHolidays(): Map<string, string> {\n const holidays = new Map<string, string>();\n\n // 2024\n const dates2024: Array<[string, string]> = [\n ['2024-01-01', '元旦'],\n ['2024-02-10', '春节'], ['2024-02-11', '春节'], ['2024-02-12', '春节'],\n ['2024-04-04', '清明节'], ['2024-04-05', '清明节'], ['2024-04-06', '清明节'],\n ['2024-05-01', '劳动节'], ['2024-05-02', '劳动节'], ['2024-05-03', '劳动节'],\n ['2024-06-10', '端午节'],\n ['2024-09-15', '中秋节'], ['2024-09-16', '中秋节'], ['2024-09-17', '中秋节'],\n ['2024-10-01', '国庆节'], ['2024-10-02', '国庆节'], ['2024-10-03', '国庆节']\n ];\n\n // 2025\n const dates2025: Array<[string, string]> = [\n ['2025-01-01', '元旦'],\n ['2025-01-29', '春节'], ['2025-01-30', '春节'], ['2025-01-31', '春节'],\n ['2025-04-04', '清明节'], ['2025-04-05', '清明节'], ['2025-04-06', '清明节'],\n ['2025-05-01', '劳动节'], ['2025-05-02', '劳动节'], ['2025-05-03', '劳动节'],\n ['2025-05-31', '端午节'],\n ['2025-10-01', '国庆节'], ['2025-10-02', '国庆节'], ['2025-10-06', '中秋节']\n ];\n\n // 2026\n const dates2026: Array<[string, string]> = [\n ['2026-01-01', '元旦'],\n ['2026-02-17', '春节'], ['2026-02-18', '春节'], ['2026-02-19', '春节'],\n ['2026-04-05', '清明节'],\n ['2026-05-01', '劳动节'],\n ['2026-06-19', '端午节'],\n ['2026-09-25', '中秋节'],\n ['2026-10-01', '国庆节'], ['2026-10-02', '国庆节']\n ];\n\n [...dates2024, ...dates2025, ...dates2026].forEach(([date, name]) => {\n holidays.set(date, name);\n });\n\n return holidays;\n}\n\n/**\n * 格式化日期为 YYYY-MM-DD\n */\nfunction formatDate(date: Date): string {\n return date.toISOString().split('T')[0];\n}\n","import type {\n AuthorDetail,\n CommitRecord,\n ContributorChurnMetrics,\n} from '../../types/index.js';\n\n/**\n * 计算贡献者流失率指标\n */\nexport function calculateContributorChurn(commits: CommitRecord[]): ContributorChurnMetrics {\n if (commits.length === 0) {\n return emptyContributorChurn();\n }\n\n // 统计每个作者的最后提交时间\n const authorLastCommit = new Map<string, {\n name: string;\n email: string;\n lastDate: Date;\n firstDate: Date;\n totalCommits: number;\n }>();\n\n for (const commit of commits) {\n const key = commit.email.toLowerCase();\n const existing = authorLastCommit.get(key);\n\n if (!existing) {\n authorLastCommit.set(key, {\n name: commit.author,\n email: commit.email,\n lastDate: commit.date,\n firstDate: commit.date,\n totalCommits: 1\n });\n } else {\n if (commit.date > existing.lastDate) {\n existing.lastDate = commit.date;\n }\n if (commit.date < existing.firstDate) {\n existing.firstDate = commit.date;\n }\n existing.totalCommits++;\n }\n }\n\n // 四级分类\n const now = new Date();\n const active: AuthorDetail[] = [];\n const occasional: AuthorDetail[] = [];\n const dormant: AuthorDetail[] = [];\n const lost: AuthorDetail[] = [];\n const newJoiners: AuthorDetail[] = [];\n\n for (const [, author] of authorLastCommit) {\n const daysSinceLast = Math.floor((now.getTime() - author.lastDate.getTime()) / (1000 * 60 * 60 * 24));\n const daysSinceFirst = Math.floor((now.getTime() - author.firstDate.getTime()) / (1000 * 60 * 60 * 24));\n\n const detail: AuthorDetail = {\n name: author.name,\n email: author.email,\n lastCommitDate: author.lastDate,\n daysSinceLastCommit: daysSinceLast,\n totalCommits: author.totalCommits\n };\n\n // 新加入(首次提交 <30天)\n if (daysSinceFirst < 30) {\n newJoiners.push(detail);\n }\n\n // 按最后活跃时间分类\n if (daysSinceLast < 30) {\n active.push(detail);\n } else if (daysSinceLast < 90) {\n occasional.push(detail);\n } else if (daysSinceLast < 180) {\n dormant.push(detail);\n } else {\n lost.push(detail);\n }\n }\n\n // 计算比率并排序\n const totalAuthors = authorLastCommit.size;\n const churnRate = totalAuthors > 0 ? lost.length / totalAuthors : 0;\n const retentionRate = totalAuthors > 0 ? active.length / totalAuthors : 0;\n const growthRate = totalAuthors > 0 ? newJoiners.length / totalAuthors : 0;\n\n return {\n active: active.sort((a, b) => b.totalCommits - a.totalCommits),\n occasional: occasional.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n dormant: dormant.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n lost: lost.sort((a, b) => b.totalCommits - a.totalCommits),\n newJoiners: newJoiners.sort((a, b) => a.daysSinceLastCommit - b.daysSinceLastCommit),\n churnRate: Math.round(churnRate * 1000) / 1000,\n retentionRate: Math.round(retentionRate * 1000) / 1000,\n growthRate: Math.round(growthRate * 1000) / 1000\n };\n}\n\nfunction emptyContributorChurn(): ContributorChurnMetrics {\n return {\n active: [],\n occasional: [],\n dormant: [],\n lost: [],\n newJoiners: [],\n churnRate: 0,\n retentionRate: 0,\n growthRate: 0,\n };\n}\n","import type { CommitRecord, AdvancedCollaborationMetrics, FilePair, AuthorPair } from '../../types/index.js';\n\nconst MAX_FILES_PER_COMMIT = 50;\n\n/**\n * 计算高级协作指标\n */\nexport function calculateAdvancedCollaboration(commits: CommitRecord[]): AdvancedCollaborationMetrics {\n if (commits.length === 0) {\n return emptyCollaboration();\n }\n\n // 统计文件修改次数\n const fileModifyCount = new Map<string, number>();\n for (const commit of commits) {\n commit.files.forEach(f => {\n fileModifyCount.set(f.path, (fileModifyCount.get(f.path) || 0) + 1);\n });\n }\n\n // Step 1: 检测紧密耦合 - 统计同提交文件对\n const tightCoupling = detectTightCoupling(commits, fileModifyCount);\n\n // Step 2: 检测结对编程\n const pairProgramming = detectPairProgramming(commits);\n\n // Step 3: 计算耦合评分\n const avgCoupling = tightCoupling.length > 0\n ? tightCoupling.reduce((sum, p) => sum + p.coupling, 0) / tightCoupling.length\n : 0;\n const couplingScore = Math.min(100, Math.round(avgCoupling * 100));\n\n return {\n tightCoupling,\n frequentPairs: [], // 简化版:跳过 24h 窗口分析\n pairProgramming,\n couplingScore,\n };\n}\n\n/**\n * 检测紧密耦合:统计同一提交中的文件对\n */\nfunction detectTightCoupling(\n commits: CommitRecord[],\n fileModifyCount: Map<string, number>\n): FilePair[] {\n const coCommitPairs = new Map<string, { count: number; file1: string; file2: string }>();\n\n for (const commit of commits) {\n // 性能优化:限制文件数,避免大型提交导致组合爆炸\n const files = commit.files\n .map(f => f.path)\n .slice(0, MAX_FILES_PER_COMMIT)\n .sort();\n\n // 组合文件对\n for (let i = 0; i < files.length; i++) {\n for (let j = i + 1; j < files.length; j++) {\n const pairKey = `${files[i]}|||${files[j]}`;\n const existing = coCommitPairs.get(pairKey) || {\n count: 0,\n file1: files[i],\n file2: files[j],\n };\n existing.count++;\n coCommitPairs.set(pairKey, existing);\n }\n }\n }\n\n // 计算耦合度并筛选\n const tightCoupling: FilePair[] = Array.from(coCommitPairs.values())\n .map(({ file1, file2, count }) => {\n const count1 = fileModifyCount.get(file1) || 1;\n const count2 = fileModifyCount.get(file2) || 1;\n const coupling = count / Math.min(count1, count2);\n\n return {\n file1,\n file2,\n coOccurrence: count,\n coupling,\n };\n })\n .filter(p => p.coOccurrence >= 3) // 至少共同出现 3 次\n .sort((a, b) => b.coupling - a.coupling)\n .slice(0, 20); // TOP 20\n\n return tightCoupling;\n}\n\n/**\n * 检测结对编程:找出在多个文件上协作的作者对\n */\nfunction detectPairProgramming(commits: CommitRecord[]): AuthorPair[] {\n // 统计每个文件的作者贡献\n const fileAuthors = new Map<string, Map<string, number>>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n if (!fileAuthors.has(file.path)) {\n fileAuthors.set(file.path, new Map());\n }\n const authorMap = fileAuthors.get(file.path)!;\n const email = commit.email.toLowerCase();\n authorMap.set(email, (authorMap.get(email) || 0) + 1);\n }\n }\n\n // 识别结对编程:两个作者都是文件的主要贡献者\n const authorPairMap = new Map<string, {\n author1: string;\n author2: string;\n files: Set<string>;\n count: number;\n }>();\n\n for (const [filePath, authorMap] of fileAuthors) {\n if (authorMap.size >= 2) {\n // 取贡献最多的两个作者\n const sortedAuthors = Array.from(authorMap.entries())\n .sort((a, b) => b[1] - a[1])\n .slice(0, 2);\n\n if (sortedAuthors.length === 2) {\n // 规范化作者对的键(按字母序)\n const [a1, a2] = [sortedAuthors[0][0], sortedAuthors[1][0]].sort();\n const pairKey = `${a1}|||${a2}`;\n\n const existing = authorPairMap.get(pairKey) || {\n author1: a1,\n author2: a2,\n files: new Set<string>(),\n count: 0,\n };\n\n existing.files.add(filePath);\n existing.count++;\n authorPairMap.set(pairKey, existing);\n }\n }\n }\n\n // 筛选并排序\n const pairProgramming: AuthorPair[] = Array.from(authorPairMap.values())\n .filter(p => p.files.size >= 3) // 至少 3 个共享文件\n .map(p => ({\n author1: p.author1,\n author2: p.author2,\n sharedFiles: Array.from(p.files),\n collaborationCount: p.count,\n }))\n .sort((a, b) => b.collaborationCount - a.collaborationCount)\n .slice(0, 10); // TOP 10\n\n return pairProgramming;\n}\n\nfunction emptyCollaboration(): AdvancedCollaborationMetrics {\n return {\n tightCoupling: [],\n frequentPairs: [],\n pairProgramming: [],\n couplingScore: 0,\n };\n}\n","import type { CommitRecord } from '../../types/index.js';\nimport type {\n TeamHealthMetrics,\n StabilityMetrics,\n WorkPressureMetrics,\n ContributorChurnMetrics,\n AdvancedCollaborationMetrics,\n} from '../../types/index.js';\n\nimport { calculateTeamHealth } from './team-health.js';\nimport { calculateStability } from './code-stability.js';\nimport { calculateWorkPressure } from './work-pressure.js';\nimport { calculateContributorChurn } from './contributor-churn.js';\nimport { calculateAdvancedCollaboration } from './collaboration.js';\n\n/**\n * 高级统计结果集合\n */\nexport interface AdvancedStats {\n teamHealth: TeamHealthMetrics;\n stability: StabilityMetrics;\n workPressure: WorkPressureMetrics;\n contributorChurn: ContributorChurnMetrics;\n advancedCollaboration: AdvancedCollaborationMetrics;\n}\n\n/**\n * 一次性计算所有高级统计\n */\nexport function calculateAdvancedStats(commits: CommitRecord[]): AdvancedStats {\n return {\n teamHealth: calculateTeamHealth(commits),\n stability: calculateStability(commits),\n workPressure: calculateWorkPressure(commits),\n contributorChurn: calculateContributorChurn(commits),\n advancedCollaboration: calculateAdvancedCollaboration(commits),\n };\n}\n\n// 导出单独的计算函数\nexport {\n calculateTeamHealth,\n calculateStability,\n calculateWorkPressure,\n calculateContributorChurn,\n calculateAdvancedCollaboration,\n};\n","import type { CommitRecord, RiskFile } from '../../types/index.js';\n\nexport function calculateRiskScores(commits: CommitRecord[]): RiskFile[] {\n if (commits.length === 0) return [];\n\n const fileMap = new Map<string, FileRiskData>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n if (!fileMap.has(file.path)) {\n fileMap.set(file.path, {\n path: file.path,\n commits: [],\n authors: new Set(),\n totalAdded: 0,\n totalDeleted: 0,\n lastModified: commit.date,\n });\n }\n\n const data = fileMap.get(file.path)!;\n data.commits.push(commit);\n data.authors.add(commit.author);\n data.totalAdded += file.added;\n data.totalDeleted += file.deleted;\n if (commit.date > data.lastModified) {\n data.lastModified = commit.date;\n }\n }\n }\n\n const riskFiles: RiskFile[] = [];\n\n for (const [path, data] of fileMap) {\n const complexity = calculateComplexity(data);\n const churnRate = calculateChurnRate(data, commits.length);\n const testCoverage = estimateTestCoverage(path, fileMap);\n const knowledgeRisk = calculateKnowledgeRisk(data);\n\n const riskScore =\n complexity * 0.3 +\n churnRate * 100 * 0.25 +\n (100 - testCoverage) * 0.2 +\n knowledgeRisk * 0.15 +\n (data.totalAdded > 500 ? 10 : 0);\n\n riskFiles.push({\n path,\n riskScore: Math.min(riskScore, 100),\n complexity,\n churnRate,\n testCoverage,\n knowledgeRisk,\n primaryAuthor: getMostActiveAuthor(data),\n lastModified: data.lastModified,\n });\n }\n\n return riskFiles.sort((a, b) => b.riskScore - a.riskScore);\n}\n\ninterface FileRiskData {\n path: string;\n commits: CommitRecord[];\n authors: Set<string>;\n totalAdded: number;\n totalDeleted: number;\n lastModified: Date;\n}\n\nfunction calculateComplexity(data: FileRiskData): number {\n const linesOfCode = data.totalAdded;\n\n if (linesOfCode > 1000) return 90;\n if (linesOfCode > 500) return 70;\n if (linesOfCode > 300) return 50;\n if (linesOfCode > 100) return 30;\n return 10;\n}\n\nfunction calculateChurnRate(data: FileRiskData, totalCommits: number): number {\n return data.commits.length / totalCommits;\n}\n\nfunction estimateTestCoverage(path: string, fileMap: Map<string, FileRiskData>): number {\n const testPatterns = ['.test.', '.spec.', '__tests__', '/test/', '/tests/'];\n const isTestFile = testPatterns.some(pattern => path.includes(pattern));\n\n if (isTestFile) return 100;\n\n const baseName = path.replace(/\\.(ts|js|tsx|jsx)$/, '');\n const possibleTestFiles = [\n `${baseName}.test.ts`,\n `${baseName}.test.js`,\n `${baseName}.spec.ts`,\n `${baseName}.spec.js`,\n ];\n\n for (const testFile of possibleTestFiles) {\n if (fileMap.has(testFile)) {\n return 80;\n }\n }\n\n const dir = path.split('/').slice(0, -1).join('/');\n for (const [filePath] of fileMap) {\n if (filePath.startsWith(dir) && testPatterns.some(p => filePath.includes(p))) {\n return 40;\n }\n }\n\n return 0;\n}\n\nfunction calculateKnowledgeRisk(data: FileRiskData): number {\n const authorCount = data.authors.size;\n\n if (authorCount === 1) return 100;\n if (authorCount === 2) return 60;\n if (authorCount === 3) return 30;\n return 10;\n}\n\nfunction getMostActiveAuthor(data: FileRiskData): string {\n const authorCommits = new Map<string, number>();\n\n for (const commit of data.commits) {\n authorCommits.set(commit.author, (authorCommits.get(commit.author) || 0) + 1);\n }\n\n let maxAuthor = '';\n let maxCommits = 0;\n\n for (const [author, count] of authorCommits) {\n if (count > maxCommits) {\n maxCommits = count;\n maxAuthor = author;\n }\n }\n\n return maxAuthor;\n}\n","import type { DuplicationResult, DuplicationCluster, DuplicationFileScore } from '../../types/index.js';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { existsSync, readdirSync, statSync } from 'node:fs';\nimport { createHash } from 'node:crypto';\n\nexport async function detectDuplication(repoPath: string): Promise<DuplicationResult> {\n const codeFiles = findCodeFiles(repoPath);\n const fileHashes = new Map<string, string[]>();\n const fileSizes = new Map<string, number>();\n\n for (const file of codeFiles.slice(0, 100)) {\n try {\n const content = await readFile(file, 'utf-8');\n const normalized = normalizeCode(content);\n const hash = simpleHash(normalized);\n\n if (!fileHashes.has(hash)) {\n fileHashes.set(hash, []);\n }\n fileHashes.get(hash)!.push(file.replace(repoPath, '').replace(/^[/\\\\]/, ''));\n fileSizes.set(file, content.split('\\n').length);\n } catch {\n // 忽略读取错误\n }\n }\n\n const clusters: DuplicationCluster[] = [];\n for (const [hash, files] of fileHashes) {\n if (files.length > 1) {\n const lines = fileSizes.get(join(repoPath, files[0])) || 0;\n clusters.push({\n files,\n similarity: 100,\n lines,\n });\n }\n }\n\n const fileScores = new Map<string, number>();\n for (const cluster of clusters) {\n for (const file of cluster.files) {\n fileScores.set(file, (fileScores.get(file) || 0) + cluster.lines);\n }\n }\n\n const sortedScores: DuplicationFileScore[] = Array.from(fileScores.entries())\n .map(([file, score]) => ({ file, score }))\n .sort((a, b) => b.score - a.score);\n\n return {\n clusters: clusters.sort((a, b) => b.lines - a.lines).slice(0, 10),\n fileScores: sortedScores.slice(0, 20),\n };\n}\n\nfunction findCodeFiles(dir: string, maxDepth = 3, currentDepth = 0): string[] {\n if (currentDepth > maxDepth) return [];\n\n const files: string[] = [];\n const ignorePatterns = ['node_modules', '.git', 'dist', 'build', 'coverage', '.next'];\n\n try {\n const entries = readdirSync(dir);\n\n for (const entry of entries) {\n if (ignorePatterns.includes(entry)) continue;\n\n const fullPath = join(dir, entry);\n try {\n const stat = statSync(fullPath);\n\n if (stat.isDirectory()) {\n files.push(...findCodeFiles(fullPath, maxDepth, currentDepth + 1));\n } else if (stat.isFile() && isCodeFile(entry)) {\n files.push(fullPath);\n }\n } catch {\n // 忽略权限错误\n }\n }\n } catch {\n // 忽略目录读取错误\n }\n\n return files;\n}\n\nfunction isCodeFile(filename: string): boolean {\n const codeExtensions = ['.ts', '.js', '.tsx', '.jsx', '.py', '.java', '.go', '.rs', '.cpp', '.c'];\n return codeExtensions.some(ext => filename.endsWith(ext));\n}\n\nfunction normalizeCode(content: string): string {\n return content\n .replace(/\\/\\/.*$/gm, '')\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')\n .replace(/\\s+/g, ' ')\n .trim();\n}\n\nfunction simpleHash(content: string): string {\n return createHash('md5').update(content).digest('hex');\n}\n","import type { RiskFile, ActionItem } from '../../types/index.js';\n\nexport function prioritizeActions(riskFiles: RiskFile[]): ActionItem[] {\n const actionItems: ActionItem[] = riskFiles.map(file => {\n const impact = calculateImpact(file);\n const effort = estimateEffort(file);\n const priority = (file.riskScore * impact) / Math.max(effort, 1);\n\n return {\n file: file.path,\n riskLevel: getRiskLevel(file.riskScore),\n impact,\n effort,\n priority,\n suggestedAction: generateSuggestion(file),\n owner: file.primaryAuthor,\n };\n });\n\n return actionItems.sort((a, b) => b.priority - a.priority).slice(0, 10);\n}\n\nfunction calculateImpact(file: RiskFile): number {\n let impact = 50;\n\n if (file.churnRate > 0.5) impact += 20;\n if (file.knowledgeRisk > 70) impact += 15;\n if (file.testCoverage < 30) impact += 15;\n\n return Math.min(impact, 100);\n}\n\nfunction estimateEffort(file: RiskFile): number {\n let effort = 1;\n\n if (file.complexity > 70) effort += 2;\n if (file.complexity > 50) effort += 1;\n if (file.knowledgeRisk > 70) effort += 1;\n\n return effort;\n}\n\nfunction getRiskLevel(score: number): 'critical' | 'high' | 'medium' | 'low' {\n if (score >= 80) return 'critical';\n if (score >= 60) return 'high';\n if (score >= 40) return 'medium';\n return 'low';\n}\n\nfunction generateSuggestion(file: RiskFile): string {\n const suggestions: string[] = [];\n\n if (file.complexity > 70) {\n suggestions.push('拆分为多个小文件');\n }\n\n if (file.testCoverage < 30) {\n suggestions.push('增加单元测试覆盖');\n }\n\n if (file.knowledgeRisk > 70) {\n suggestions.push('进行知识分享和代码审查');\n }\n\n if (file.churnRate > 0.5) {\n suggestions.push('重构以提高稳定性');\n }\n\n if (suggestions.length === 0) {\n suggestions.push('代码审查和文档完善');\n }\n\n return suggestions.join(';');\n}\n","import type {\n AIDetectionResult,\n CommitRecord,\n DuplicationResult,\n RadarDimension,\n RiskFile,\n TechDebtStats,\n TrendPoint,\n} from '../../types/index.js';\nimport { calculateRiskScores } from './risk-scorer.js';\nimport { detectAICode } from './ai-detector.js';\nimport { detectDuplication } from './duplication.js';\nimport { prioritizeActions } from './prioritizer.js';\n\nexport async function calculateTechDebt(\n commits: CommitRecord[],\n repoPath: string\n): Promise<TechDebtStats> {\n if (commits.length === 0) {\n return emptyTechDebt();\n }\n\n const riskFiles = calculateRiskScores(commits);\n const aiDetection = await detectAICode(commits, repoPath);\n const duplication = await detectDuplication(repoPath);\n const actionItems = prioritizeActions(riskFiles);\n\n const radar = calculateRadarDimensions(riskFiles, aiDetection, duplication);\n const trends = calculateTrends(commits);\n\n return {\n radar,\n highRiskFiles: riskFiles.slice(0, 10),\n aiDetection,\n duplication,\n trends,\n actionItems,\n };\n}\n\nfunction calculateRadarDimensions(\n riskFiles: RiskFile[],\n aiDetection: AIDetectionResult,\n duplication: DuplicationResult\n): RadarDimension[] {\n const avgComplexity = riskFiles.length > 0\n ? riskFiles.reduce((sum, f) => sum + f.complexity, 0) / riskFiles.length\n : 0;\n\n const avgDuplication = duplication.fileScores.length > 0\n ? duplication.fileScores.reduce((sum, file) => sum + file.score, 0) / duplication.fileScores.length\n : 0;\n\n const avgTestCoverage = riskFiles.length > 0\n ? riskFiles.reduce((sum, f) => sum + f.testCoverage, 0) / riskFiles.length\n : 100;\n\n const avgKnowledgeRisk = riskFiles.length > 0\n ? riskFiles.reduce((sum, f) => sum + f.knowledgeRisk, 0) / riskFiles.length\n : 0;\n\n const avgChurnRate = riskFiles.length > 0\n ? riskFiles.reduce((sum, f) => sum + f.churnRate, 0) / riskFiles.length\n : 0;\n\n return [\n {\n dimension: 'Complexity',\n score: Math.min(avgComplexity, 100),\n riskLevel: avgComplexity > 70 ? 'high' : avgComplexity > 40 ? 'medium' : 'low',\n description: '代码复杂度',\n affectedFiles: riskFiles.filter(f => f.complexity > 70).length,\n },\n {\n dimension: 'Duplication',\n score: Math.min(avgDuplication / 10, 100),\n riskLevel: avgDuplication > 700 ? 'high' : avgDuplication > 400 ? 'medium' : 'low',\n description: '代码重复度',\n affectedFiles: duplication.fileScores.filter((file) => file.score > 100).length,\n },\n {\n dimension: 'Test Coverage',\n score: 100 - avgTestCoverage,\n riskLevel: avgTestCoverage < 30 ? 'high' : avgTestCoverage < 60 ? 'medium' : 'low',\n description: '测试覆盖率',\n affectedFiles: riskFiles.filter(f => f.testCoverage < 30).length,\n },\n {\n dimension: 'Documentation',\n score: aiDetection.suspiciousFiles.filter((file) => file.reason === 'excessive-comments').length * 10,\n riskLevel: aiDetection.suspiciousFiles.length > 10 ? 'high' : aiDetection.suspiciousFiles.length > 5 ? 'medium' : 'low',\n description: '文档完整性',\n affectedFiles: aiDetection.suspiciousFiles.length,\n },\n {\n dimension: 'Stability',\n score: avgChurnRate * 100,\n riskLevel: avgChurnRate > 0.7 ? 'high' : avgChurnRate > 0.4 ? 'medium' : 'low',\n description: '代码稳定性',\n affectedFiles: riskFiles.filter(f => f.churnRate > 0.7).length,\n },\n {\n dimension: 'Knowledge Risk',\n score: avgKnowledgeRisk,\n riskLevel: avgKnowledgeRisk > 70 ? 'high' : avgKnowledgeRisk > 40 ? 'medium' : 'low',\n description: '知识集中度',\n affectedFiles: riskFiles.filter(f => f.knowledgeRisk > 70).length,\n },\n ];\n}\n\nfunction calculateTrends(commits: CommitRecord[]): TrendPoint[] {\n const sorted = [...commits].sort((a, b) => a.date.getTime() - b.date.getTime());\n const trends = [];\n const interval = Math.max(1, Math.floor(sorted.length / 20));\n\n for (let i = 0; i < sorted.length; i += interval) {\n const chunk = sorted.slice(Math.max(0, i - interval), i + 1);\n const debt = chunk.reduce((sum, c) => {\n const largeFiles = c.files.filter(f => f.added > 100).length;\n return sum + largeFiles;\n }, 0);\n\n trends.push({\n date: sorted[i].date.toISOString().split('T')[0],\n debt,\n });\n }\n\n return trends;\n}\n\nfunction emptyTechDebt(): TechDebtStats {\n return {\n radar: [],\n highRiskFiles: [],\n aiDetection: { suspiciousFiles: [], totalSuspicious: 0 },\n duplication: { clusters: [], fileScores: [] },\n trends: [],\n actionItems: [],\n };\n}\n","import { execFileSync } from 'node:child_process';\nimport type {\n BugFixAnalysis,\n BugFixHotFile,\n ChangeMixMetrics,\n CodeOwnershipMetrics,\n CommitQualityMetrics,\n CommitRecord,\n FileChange,\n FileOwnerContribution,\n FileOwnership,\n ReviewParticipant,\n ReviewQualityMetrics,\n EngineeringMetrics,\n} from '../types/index.js';\n\nconst CONVENTIONAL_RE =\n /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)(\\([^)]+\\))?!?:\\s+\\S/i;\nconst SCOPED_CONVENTIONAL_RE =\n /^(feat|fix|docs|style|refactor|test|chore|perf|ci|build|revert)\\([^)]+\\)!?:\\s+\\S/i;\nconst FIX_RE = /^fix(\\([^)]+\\))?!?:\\s+\\S/i;\nconst REVIEW_TRAILER_RE =\n /^(Co-authored-by|Signed-off-by):\\s*(.*?)\\s*<([^<>]+)>$/gim;\nconst DEFAULT_OWNERSHIP_FILE_LIMIT = 200;\nconst OWNERSHIP_EXTENSIONS = new Set([\n '.c',\n '.cpp',\n '.css',\n '.go',\n '.h',\n '.html',\n '.java',\n '.js',\n '.jsx',\n '.md',\n '.py',\n '.rs',\n '.scss',\n '.ts',\n '.tsx',\n '.vue',\n]);\n\ninterface CodeOwnershipOptions {\n maxFiles?: number;\n candidateFiles?: string[];\n}\n\nexport function calculateEngineeringMetrics(\n commits: CommitRecord[],\n repoPath?: string\n): EngineeringMetrics {\n return {\n ...(repoPath && {\n codeOwnership: calculateCodeOwnership(repoPath, {\n candidateFiles: selectOwnershipCandidates(commits),\n }),\n }),\n bugFixHotFiles: calculateBugFixHotFiles(commits),\n reviewQuality: calculateReviewQuality(commits),\n commitQuality: calculateCommitQuality(commits),\n changeMix: calculateChangeMix(commits),\n };\n}\n\nexport function calculateCodeOwnership(\n repoPath: string,\n options: CodeOwnershipOptions = {}\n): CodeOwnershipMetrics {\n const trackedFiles = listTrackedFiles(repoPath);\n const trackedSet = new Set(trackedFiles);\n const maxFiles = options.maxFiles ?? DEFAULT_OWNERSHIP_FILE_LIMIT;\n const candidates = options.candidateFiles\n ? options.candidateFiles.filter((file) => trackedSet.has(file))\n : trackedFiles;\n const filesToBlame = candidates\n .filter(isOwnershipTargetFile)\n .slice(0, maxFiles);\n const files: FileOwnership[] = [];\n\n for (const filePath of filesToBlame) {\n const ownership = calculateFileOwnership(repoPath, filePath);\n if (ownership) {\n files.push(ownership);\n }\n }\n\n files.sort((a, b) => {\n if (b.ownerLines !== a.ownerLines) return b.ownerLines - a.ownerLines;\n return b.ownershipRatio - a.ownershipRatio;\n });\n\n return {\n totalFiles: files.length,\n files,\n };\n}\n\nfunction calculateBugFixHotFiles(commits: CommitRecord[]): BugFixAnalysis {\n const hotFileMap = new Map<\n string,\n { fixCount: number; lastFixDate: Date; fixAuthors: Set<string> }\n >();\n let fixCommitCount = 0;\n\n for (const commit of commits) {\n if (!FIX_RE.test(commit.message)) continue;\n\n fixCommitCount++;\n for (const file of commit.files) {\n const entry = hotFileMap.get(file.path) || {\n fixCount: 0,\n lastFixDate: commit.date,\n fixAuthors: new Set<string>(),\n };\n entry.fixCount++;\n entry.fixAuthors.add(commit.author);\n if (commit.date > entry.lastFixDate) {\n entry.lastFixDate = commit.date;\n }\n hotFileMap.set(file.path, entry);\n }\n }\n\n const hotFiles: BugFixHotFile[] = Array.from(hotFileMap.entries())\n .map(([path, data]) => ({\n path,\n fixCount: data.fixCount,\n lastFixDate: data.lastFixDate,\n fixAuthors: Array.from(data.fixAuthors),\n }))\n .sort((a, b) => {\n if (b.fixCount !== a.fixCount) return b.fixCount - a.fixCount;\n return b.lastFixDate.getTime() - a.lastFixDate.getTime();\n })\n .slice(0, 20);\n\n return { fixCommitCount, hotFiles };\n}\n\nfunction calculateReviewQuality(commits: CommitRecord[]): ReviewQualityMetrics {\n const reviewerMap = new Map<\n string,\n ReviewParticipant & { firstSeenIndex: number }\n >();\n let mergeCommitCount = 0;\n let reviewedMergeCount = 0;\n let order = 0;\n\n for (const commit of commits) {\n if (!isMergeCommit(commit)) continue;\n\n mergeCommitCount++;\n const reviewers = parseReviewers(commit.body || commit.message);\n if (reviewers.length > 0) {\n reviewedMergeCount++;\n }\n\n for (const reviewer of reviewers) {\n const key = reviewer.email.toLowerCase();\n const existing = reviewerMap.get(key);\n if (existing) {\n existing.commits++;\n } else {\n reviewerMap.set(key, {\n ...reviewer,\n commits: 1,\n firstSeenIndex: order,\n });\n order++;\n }\n }\n }\n\n const reviewers = Array.from(reviewerMap.values())\n .sort((a, b) => {\n if (b.commits !== a.commits) return b.commits - a.commits;\n return a.firstSeenIndex - b.firstSeenIndex;\n })\n .map(({ firstSeenIndex, ...reviewer }) => reviewer);\n\n return {\n mergeCommitCount,\n reviewedMergeCount,\n reviewParticipationRate:\n mergeCommitCount > 0 ? roundRatio(reviewedMergeCount / mergeCommitCount) : 0,\n reviewers,\n };\n}\n\nfunction calculateCommitQuality(commits: CommitRecord[]): CommitQualityMetrics {\n if (commits.length === 0) {\n return {\n score: 0,\n conventionalRate: 0,\n scopeCoverageRate: 0,\n averageMessageLength: 0,\n typeDistribution: {},\n };\n }\n\n let conventionalCount = 0;\n let scopedCount = 0;\n let totalLength = 0;\n const typeDistribution: Record<string, number> = {};\n\n for (const commit of commits) {\n totalLength += commit.message.length;\n\n const conventionalMatch = commit.message.match(CONVENTIONAL_RE);\n if (conventionalMatch) {\n conventionalCount++;\n const type = conventionalMatch[1].toLowerCase();\n typeDistribution[type] = (typeDistribution[type] || 0) + 1;\n } else {\n typeDistribution.other = (typeDistribution.other || 0) + 1;\n }\n\n if (SCOPED_CONVENTIONAL_RE.test(commit.message)) {\n scopedCount++;\n }\n }\n\n const averageMessageLength = roundTo(totalLength / commits.length, 1);\n const conventionalRate = roundRatio(conventionalCount / commits.length);\n const scopeCoverageRate = roundRatio(scopedCount / commits.length);\n const messageLengthScore = Math.min(averageMessageLength / 24, 1);\n const score = Math.round(\n conventionalRate * 60 + scopeCoverageRate * 25 + messageLengthScore * 15\n );\n\n return {\n score,\n conventionalRate,\n scopeCoverageRate,\n averageMessageLength,\n typeDistribution,\n };\n}\n\nfunction calculateChangeMix(commits: CommitRecord[]): ChangeMixMetrics {\n let createdFiles = 0;\n let deletedFiles = 0;\n let modifiedFiles = 0;\n\n for (const commit of commits) {\n for (const file of commit.files) {\n if (file.status === 'added' || file.status === 'copied') {\n createdFiles++;\n } else if (file.status === 'deleted') {\n deletedFiles++;\n } else {\n modifiedFiles++;\n }\n }\n }\n\n const total = createdFiles + deletedFiles + modifiedFiles;\n\n return {\n createdFiles,\n deletedFiles,\n modifiedFiles,\n featureRatio: total > 0 ? roundRatio((createdFiles + deletedFiles) / total) : 0,\n refactorRatio: total > 0 ? roundRatio(modifiedFiles / total) : 0,\n };\n}\n\nfunction selectOwnershipCandidates(commits: CommitRecord[]): string[] {\n const fileCounts = new Map<string, number>();\n\n for (const commit of commits) {\n for (const file of commit.files) {\n fileCounts.set(file.path, (fileCounts.get(file.path) || 0) + 1);\n }\n }\n\n return Array.from(fileCounts.entries())\n .sort((a, b) => {\n if (b[1] !== a[1]) return b[1] - a[1];\n return a[0].localeCompare(b[0]);\n })\n .map(([file]) => file);\n}\n\nfunction listTrackedFiles(repoPath: string): string[] {\n let output = '';\n try {\n output = execFileSync('git', ['ls-files', '-z'], {\n cwd: repoPath,\n encoding: 'utf-8',\n maxBuffer: 100 * 1024 * 1024,\n stdio: ['pipe', 'pipe', 'ignore'],\n });\n } catch {\n return [];\n }\n\n return output\n .split('\\0')\n .filter((path) => path && !isIgnoredOwnershipFile(path));\n}\n\nfunction calculateFileOwnership(\n repoPath: string,\n filePath: string\n): FileOwnership | null {\n let output = '';\n try {\n output = execFileSync('git', ['blame', 'HEAD', '--line-porcelain', '--', filePath], {\n cwd: repoPath,\n encoding: 'utf-8',\n maxBuffer: 100 * 1024 * 1024,\n stdio: ['pipe', 'pipe', 'ignore'],\n });\n } catch {\n return null;\n }\n\n const contributors = parseBlameContributors(output);\n const totalLines = Array.from(contributors.values()).reduce(\n (sum, contributor) => sum + contributor.lines,\n 0\n );\n if (totalLines === 0) return null;\n\n const sortedContributors: FileOwnerContribution[] = Array.from(\n contributors.values()\n )\n .map((contributor) => ({\n ...contributor,\n ratio: contributor.lines / totalLines,\n }))\n .sort((a, b) => b.lines - a.lines);\n\n const owner = sortedContributors[0];\n\n return {\n path: filePath,\n ownerName: owner.name,\n ownerEmail: owner.email,\n ownerLines: owner.lines,\n totalLines,\n ownershipRatio: owner.ratio,\n contributors: sortedContributors,\n };\n}\n\nfunction parseBlameContributors(\n output: string\n): Map<string, { name: string; email: string; lines: number }> {\n const contributors = new Map<string, { name: string; email: string; lines: number }>();\n let currentName = '';\n\n for (const line of output.split('\\n')) {\n if (line.startsWith('author ')) {\n currentName = line.slice('author '.length);\n } else if (line.startsWith('author-mail ')) {\n const email = line\n .slice('author-mail '.length)\n .replace(/^<|>$/g, '')\n .toLowerCase();\n const key = email || currentName;\n const existing = contributors.get(key) || {\n name: currentName || email,\n email,\n lines: 0,\n };\n existing.lines++;\n contributors.set(key, existing);\n }\n }\n\n return contributors;\n}\n\nfunction parseReviewers(body: string): ReviewParticipant[] {\n const reviewers: ReviewParticipant[] = [];\n const seen = new Set<string>();\n REVIEW_TRAILER_RE.lastIndex = 0;\n\n let match = REVIEW_TRAILER_RE.exec(body);\n while (match) {\n const name = match[2].trim();\n const email = match[3].trim().toLowerCase();\n if (!seen.has(email)) {\n reviewers.push({ name, email, commits: 0 });\n seen.add(email);\n }\n match = REVIEW_TRAILER_RE.exec(body);\n }\n\n return reviewers;\n}\n\nfunction isMergeCommit(commit: CommitRecord): boolean {\n return (commit.parentHashes?.length || 0) > 1 || /^Merge\\b/i.test(commit.message);\n}\n\nfunction isIgnoredOwnershipFile(filePath: string): boolean {\n return /(^|\\/)(package-lock\\.json|pnpm-lock\\.yaml|yarn\\.lock)$/.test(filePath);\n}\n\nfunction isOwnershipTargetFile(filePath: string): boolean {\n if (isIgnoredOwnershipFile(filePath)) return false;\n const extension = filePath.includes('.')\n ? filePath.slice(filePath.lastIndexOf('.')).toLowerCase()\n : '';\n return OWNERSHIP_EXTENSIONS.has(extension);\n}\n\nfunction roundRatio(value: number): number {\n return roundTo(value, 2);\n}\n\nfunction roundTo(value: number, digits: number): number {\n const factor = 10 ** digits;\n return Math.round(value * factor) / factor;\n}\n","import { writeFile } from 'node:fs/promises';\nimport { resolve } from 'node:path';\nimport chalk from 'chalk';\nimport ora from 'ora';\nimport open from 'open';\nimport { buildHtml } from './html-builder.js';\nimport type { CommitStats, ReportOptions } from '../types/index.js';\n\n/**\n * 生成 HTML 报告并可选地打开浏览器\n */\nexport async function generateReport(\n stats: CommitStats,\n options: ReportOptions\n): Promise<void> {\n const spinner = ora('生成报告...').start();\n\n try {\n const html = await buildHtml(stats, options);\n const outputPath = resolve(process.cwd(), options.outputPath);\n\n await writeFile(outputPath, html, 'utf-8');\n spinner.succeed(`报告已生成: ${chalk.cyan(outputPath)}`);\n\n if (options.autoOpen) {\n await open(outputPath);\n console.log(chalk.green('✓ 已在浏览器中打开'));\n }\n } catch (error) {\n spinner.fail('生成报告失败');\n throw error;\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, dirname } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport type { CommitStats, ReportOptions, ReportData } from '../types/index.js';\n\ntype JsonReportData = Omit<ReportData, 'stats'> & {\n stats: Record<string, unknown>;\n};\n\nconst REPORT_SCRIPT_FILES = [\n 'report-scripts/01-core.html',\n 'report-scripts/00-filter-state.html',\n 'report-scripts/00-advanced-derived.html',\n 'report-scripts/00-report-controls.html',\n 'report-scripts/02-commit-details.html',\n 'report-scripts/03-basic-charts.html',\n 'report-scripts/04-trend-charts.html',\n 'report-scripts/05-tables-team-stability.html',\n 'report-scripts/06-pressure-churn.html',\n 'report-scripts/08-engineering.html',\n 'report-scripts/09-extensions.html',\n 'report-scripts/07-collab-debt-ai.html',\n 'report-scripts/10-runtime.html',\n];\n\nconst REPORT_SECTION_FILES = [\n 'report-sections/01-overview.html',\n 'report-sections/02-advanced.html',\n];\n\n/**\n * 组装完整的 HTML 报告\n */\nexport async function buildHtml(\n stats: CommitStats,\n options: ReportOptions\n): Promise<string> {\n const template = await loadTemplateFile('report.html');\n const reportSections = await loadTemplateParts(REPORT_SECTION_FILES);\n const reportScript = await loadReportScript();\n\n const reportData: JsonReportData = {\n stats: serializeStats(stats),\n generatedAt: new Date().toLocaleString('zh-CN'),\n timeRange: options.timeRange\n ? {\n from: options.timeRange.from.toISOString().split('T')[0],\n to: options.timeRange.to.toISOString().split('T')[0],\n }\n : null,\n repos: options.repoNames,\n };\n\n // 安全地序列化数据(防止 XSS)\n const jsonData = JSON.stringify(reportData)\n .replace(/</g, '\\\\u003c')\n .replace(/>/g, '\\\\u003e')\n .replace(/&/g, '\\\\u0026');\n\n return template\n .replace('__REPORT_DATA__', jsonData)\n .replace('__REPORT_SECTIONS__', reportSections)\n .replace('__REPORT_SCRIPT__', reportScript);\n}\n\n/**\n * 将 CommitStats 转为可 JSON 序列化的格式\n * Date 对象转为 ISO 字符串\n */\nfunction serializeStats(stats: CommitStats): Record<string, unknown> {\n const serializeAuthorDetail = <T extends { lastCommitDate: Date }>(author: T) => ({\n ...author,\n lastCommitDate: author.lastCommitDate.toISOString(),\n });\n\n return {\n ...stats,\n firstCommitDate: stats.firstCommitDate.toISOString(),\n lastCommitDate: stats.lastCommitDate.toISOString(),\n authors: stats.authors.map((a) => ({\n ...a,\n lastActiveDate: a.lastActiveDate.toISOString(),\n })),\n commitDetails: stats.commitDetails.map((commit) => ({\n ...commit,\n date: commit.date.toISOString(),\n })),\n contributorChurn: stats.contributorChurn\n ? {\n ...stats.contributorChurn,\n active: stats.contributorChurn.active.map(serializeAuthorDetail),\n occasional: stats.contributorChurn.occasional.map(serializeAuthorDetail),\n dormant: stats.contributorChurn.dormant.map(serializeAuthorDetail),\n lost: stats.contributorChurn.lost.map(serializeAuthorDetail),\n newJoiners: stats.contributorChurn.newJoiners.map(serializeAuthorDetail),\n }\n : undefined,\n changeSizeDistribution: stats.changeSizeDistribution\n ? {\n ...stats.changeSizeDistribution,\n largeCommits: stats.changeSizeDistribution.largeCommits.map((c) => ({\n ...c,\n date: c.date.toISOString(),\n })),\n }\n : undefined,\n };\n}\n\nasync function loadReportScript(): Promise<string> {\n return loadTemplateParts(REPORT_SCRIPT_FILES);\n}\n\nasync function loadTemplateParts(fileNames: string[]): Promise<string> {\n const parts = await Promise.all(\n fileNames.map((fileName) => loadTemplateFile(fileName))\n );\n\n return parts.join('\\n');\n}\n\n/**\n * 加载 HTML 模板\n */\nasync function loadTemplateFile(fileName: string): Promise<string> {\n // 支持两种路径:开发模式和打包后模式\n const currentDir = dirname(fileURLToPath(import.meta.url));\n\n // 打包后: dist/index.js -> ../templates\n // 开发模式: src/reporter/html-builder.ts -> ../../templates\n const possiblePaths = [\n resolve(currentDir, '../templates', fileName),\n resolve(currentDir, '../../templates', fileName),\n resolve(currentDir, '../../../templates', fileName),\n ];\n\n for (const templatePath of possiblePaths) {\n try {\n return await readFile(templatePath, 'utf-8');\n } catch {\n // 继续尝试下一个路径\n }\n }\n\n throw new Error(`无法找到 HTML 模板文件: ${fileName}`);\n}\n","import type { CliOptions, TimeRange } from '../types/index.js';\n\n/** 时间预设格式: 7d / 1m / 3m / 6m / 1y / all */\nconst PERIOD_REGEX = /^(\\d+)(d|m|y)$/;\n\n/**\n * 解析时间预设字符串,返回起止时间范围\n * 'all' 表示不限时间(从 1970 年至今)\n */\nexport function parsePeriod(period: string): TimeRange | null {\n if (period === 'all') {\n return null;\n }\n\n const match = PERIOD_REGEX.exec(period);\n if (!match) {\n throw new Error(`无效的时间预设: \"${period}\",支持格式: 7d, 1m, 3m, 6m, 1y, all`);\n }\n\n const amount = parseInt(match[1], 10);\n const unit = match[2];\n const to = new Date();\n const from = new Date();\n\n switch (unit) {\n case 'd':\n from.setDate(from.getDate() - amount);\n break;\n case 'm':\n from.setMonth(from.getMonth() - amount);\n break;\n case 'y':\n from.setFullYear(from.getFullYear() - amount);\n break;\n }\n\n return { from, to };\n}\n\n/**\n * 解析日期字符串 YYYY-MM-DD\n */\nfunction parseDate(dateStr: string): Date {\n const date = new Date(dateStr);\n if (isNaN(date.getTime())) {\n throw new Error(`无效的日期格式: \"${dateStr}\",请使用 YYYY-MM-DD 格式`);\n }\n return date;\n}\n\n/**\n * 根据 CLI 参数解析最终的时间范围\n * --from / --to 优先于 --period\n * 返回 null 表示不限时间\n */\nexport function resolveTimeRange(opts: CliOptions): TimeRange | null {\n if (opts.from || opts.to) {\n const to = opts.to ? parseDate(opts.to) : new Date();\n const from = opts.from ? parseDate(opts.from) : (() => {\n const d = new Date(to);\n d.setMonth(d.getMonth() - 3);\n return d;\n })();\n\n if (from > to) {\n throw new Error('起始日期不能晚于结束日期');\n }\n\n return { from, to };\n }\n\n return parsePeriod(opts.period);\n}\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,OAAOA,YAAW;AAClB,SAAS,gBAAgB;;;ACFzB,SAAS,SAAe,cAAc;AACtC,SAAS,MAAM,gBAAgB;AAC/B,SAAS,gBAAgB;AAEzB,OAAO,SAAS;AAChB,SAAS,eAAe;AAIxB,IAAM,cAAc,oBAAI,IAAI;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAKD,eAAsB,iBAAiB,SAA2C;AAChF,QAAM,UAAU,IAAI,mCAAU,EAAE,MAAM;AACtC,QAAM,QAAoB,CAAC;AAC3B,MAAI,oBAAoB;AAExB,iBAAe,KAAK,KAAa,OAA8B;AAE7D,QAAI,QAAQ,QAAQ,YAAY,CAAC,mBAAmB;AAClD,cAAQ,KAAK;AACb,YAAM,iBAAiB,MAAM,QAAQ;AAAA,QACnC,SAAS,8CAAW,QAAQ,QAAQ;AAAA,QACpC,SAAS;AAAA,MACX,CAAC;AAED,UAAI,CAAC,gBAAgB;AACnB;AAAA,MACF;AACA,0BAAoB;AACpB,cAAQ,MAAM,mCAAU;AAAA,IAC1B;AAGA,QAAI;AACF,YAAM,SAAS,KAAK,KAAK,MAAM;AAC/B,YAAM,OAAO,MAAM;AAGnB,YAAM,WAAW,YAAY,GAAG;AAChC,UAAI,UAAU;AACZ,cAAM,KAAK,QAAQ;AACnB,gBAAQ,OAAO,wDAAgB,MAAM,MAAM;AAAA,MAC7C;AAEA;AAAA,IACF,QAAQ;AAAA,IAER;AAGA,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAE1D,iBAAW,SAAS,SAAS;AAC3B,YAAI,CAAC,MAAM,YAAY,EAAG;AAC1B,YAAI,YAAY,IAAI,MAAM,IAAI,EAAG;AACjC,YAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,SAAS,OAAQ;AAEzD,cAAM,KAAK,KAAK,KAAK,MAAM,IAAI,GAAG,QAAQ,CAAC;AAAA,MAC7C;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,KAAK,QAAQ,WAAW,CAAC;AAE/B,MAAI,MAAM,SAAS,GAAG;AACpB,YAAQ,QAAQ,gBAAM,MAAM,MAAM,0BAAW;AAAA,EAC/C,OAAO;AACL,YAAQ,KAAK,qCAAY;AAAA,EAC3B;AAEA,SAAO;AACT;AAKA,SAAS,YAAY,UAAmC;AACtD,MAAI;AACF,UAAM,WAAW,SAAS,6BAA6B;AAAA,MACrD,KAAK;AAAA,MACL,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,MAChC,UAAU;AAAA,IACZ,CAAC,EAAE,KAAK;AAER,UAAM,cAAc,SAAS,UAAU,EAAE,KAAK;AAC9C,UAAM,OAAO,SAAS,QAAQ;AAE9B,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA;AAAA,IACF;AAAA,EACF,QAAQ;AAEN,WAAO;AAAA,EACT;AACF;;;ACjHA,OAAOC,UAAS;AAChB,OAAO,WAAW;;;ACDlB,SAAS,oBAAoB;AAC7B,SAAS,gBAAgB;AACzB,SAAS,QAAAC,aAAY;AACrB,OAAO,QAAQ;AAKf,IAAM,mBAAmB;AACzB,IAAM,aAAa;AACnB,IAAM,kBAAkB;AACxB,IAAM,SACJ,GAAG,gBAAgB,KAAK,eAAe,KAAK,eAAe,MACxD,eAAe,MAAM,eAAe,MAAM,eAAe,SAAS,UAAU;AAMjF,eAAsB,YACpB,UACA,WACA,QACyB;AACzB,QAAM,eAAe,MAAM,cAAc,QAAQ;AAEjD,QAAM,OAAO;AAAA,IACX;AAAA,IACA,YAAY,MAAM;AAAA,IAClB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,MAAI,WAAW;AACb,SAAK,KAAK,WAAW,UAAU,KAAK,YAAY,CAAC,EAAE;AACnD,SAAK,KAAK,WAAW,UAAU,GAAG,YAAY,CAAC,EAAE;AAAA,EACnD;AAEA,MAAI,QAAQ;AACV,SAAK,KAAK,YAAY,MAAM,EAAE;AAAA,EAChC;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,aAAa,OAAO,MAAM;AAAA,MACjC,KAAK;AAAA,MACL,UAAU;AAAA,MACV,WAAW,MAAM,OAAO;AAAA;AAAA,MACxB,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,IAClC,CAAC;AAAA,EACH,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,MAAI,CAAC,OAAO,KAAK,GAAG;AAClB,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,YAAY,QAAQ,YAAY;AACzC;AAKA,SAAS,YACP,QACA,cACgB;AAChB,QAAM,UAA0B,CAAC;AACjC,QAAM,SAAS,OAAO,MAAM,gBAAgB,EAAE,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC;AAEpE,aAAW,SAAS,QAAQ;AAC1B,UAAM,WAAW,MAAM,QAAQ,UAAU;AACzC,UAAM,eAAe,YAAY,IAAI,MAAM,MAAM,GAAG,QAAQ,IAAI;AAChE,UAAM,cAAc,YAAY,IAC5B,MAAM,MAAM,WAAW,WAAW,MAAM,IACxC;AACJ,UAAM,gBAAgB,aAAa,KAAK,EAAE,MAAM,IAAI;AACpD,QAAI,cAAc,WAAW,EAAG;AAGhC,UAAM,aAAa,cAAc,CAAC,EAAE,QAAQ,UAAU,EAAE;AACxD,UAAM,QAAQ,WAAW,MAAM,eAAe;AAC9C,QAAI,MAAM,SAAS,EAAG;AAEtB,UAAM,CAAC,MAAM,gBAAgB,YAAY,OAAO,SAAS,GAAG,YAAY,IAAI;AAC5E,UAAM,UAAU,aAAa,KAAK,eAAe;AACjD,UAAM,OAAO,cAAc,MAAM,CAAC,EAAE,KAAK,IAAI,EAAE,KAAK;AACpD,UAAM,eAAe,eAAe,KAAK,IACrC,eAAe,KAAK,EAAE,MAAM,KAAK,IACjC,CAAC;AAEL,UAAM,cAAc,YAAY,KAAK,EAAE,MAAM,IAAI;AACjD,UAAM,eAAe,kBAAkB,WAAW;AAGlD,UAAM,QAAsB,CAAC;AAC7B,eAAW,WAAW,aAAa;AACjC,YAAM,OAAO,QAAQ,KAAK;AAC1B,UAAI,CAAC,KAAM;AAEX,YAAM,WAAW,KAAK,MAAM,GAAI;AAChC,UAAI,SAAS,WAAW,EAAG;AAE3B,YAAM,CAAC,UAAU,YAAY,QAAQ,IAAI;AAGzC,YAAM,QAAQ,aAAa,MAAM,IAAI,SAAS,UAAU,EAAE,KAAK;AAC/D,YAAM,UAAU,eAAe,MAAM,IAAI,SAAS,YAAY,EAAE,KAAK;AAGrE,UAAI,aAAa,QAAQ,QAAQ,EAAG;AAEpC,YAAM,SAAS,aAAa,IAAI,QAAQ;AACxC,YAAM,KAAK;AAAA,QACT;AAAA,QACA;AAAA,QACA,MAAM;AAAA,QACN,GAAI,UAAU,EAAE,OAAO;AAAA,MACzB,CAAC;AAAA,IACH;AAEA,YAAQ,KAAK;AAAA,MACX;AAAA,MACA,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,IAAI,KAAK,OAAO;AAAA,MACtB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,kBAAkB,OAAoD;AAC7E,QAAM,eAAe,oBAAI,IAAkC;AAE3D,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,KAAK,WAAW,GAAG,EAAG;AAE3B,UAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,GAAI;AACpC,QAAI,MAAM,SAAS,EAAG;AAEtB,UAAM,WAAW,MAAM,CAAC,EAAE,MAAM,KAAK;AACrC,UAAM,cAAc,SAAS,CAAC,KAAK;AACnC,UAAM,SAAS,gBAAgB,WAAW;AAC1C,UAAM,OAAO,WAAW,aAAa,WAAW,WAC5C,MAAM,CAAC,KAAK,MAAM,CAAC,IACnB,MAAM,CAAC;AAEX,QAAI,MAAM;AACR,mBAAa,IAAI,MAAM,MAAM;AAAA,IAC/B;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,gBAAgB,aAA2C;AAClE,QAAM,OAAO,YAAY,CAAC;AAC1B,MAAI,SAAS,IAAK,QAAO;AACzB,MAAI,SAAS,IAAK,QAAO;AACzB,MAAI,SAAS,IAAK,QAAO;AACzB,MAAI,SAAS,IAAK,QAAO;AACzB,MAAI,SAAS,IAAK,QAAO;AACzB,SAAO;AACT;AAKA,eAAe,cAAc,UAAmC;AAC9D,QAAM,iBAAiB,GAAG;AAE1B,MAAI;AACF,UAAM,UAAU,MAAM,SAASA,MAAK,UAAU,YAAY,GAAG,OAAO;AACpE,mBAAe,IAAI,OAAO;AAAA,EAC5B,QAAQ;AAAA,EAER;AAGA,iBAAe,IAAI;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO;AACT;;;ACrLA,SAAS,WAAAC,gBAAe;;;ACZxB,SAAS,YAAAC,iBAAgB;AACzB,SAAS,QAAAC,aAAY;AACrB,SAAS,kBAAkB;AAEpB,SAAS,iBAAiB,QAA8B;AAC7D,SAAO,gBAAgB,MAAM,EAAE;AACjC;AAEO,SAAS,gBAAgB,QAAyC;AACvE,MAAI,QAAQ;AACZ,QAAM,UAAoB,CAAC;AAC3B,QAAM,aAAa,OAAO,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AACnE,QAAM,aAAa,OAAO,MAAM,OAAO,CAAC,SAAS,KAAK,WAAW,OAAO,EAAE;AAE1E,MAAI,eAAe,OAAO,OAAO,KAAK,eAAe,OAAO,QAAQ,EAAE,GAAG;AACvE,aAAS;AACT,YAAQ,KAAK,mCAAU;AAAA,EACzB;AAEA,MAAI,wBAAwB,OAAO,OAAO,KAAK,wBAAwB,OAAO,QAAQ,EAAE,GAAG;AACzF,aAAS;AACT,YAAQ,KAAK,4CAAS;AAAA,EACxB;AAEA,MAAI,aAAa,OAAO,OAAO,MAAM,UAAU,KAAK,aAAa,OAAO,MAAM,SAAS,KAAK;AAC1F,aAAS;AACT,YAAQ,KAAK,4CAAS;AAAA,EACxB;AAEA,MAAI,aAAa,MAAM,OAAO,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,SAAS,CAAC,KAAK,aAAa,MAAM;AAC/F,aAAS;AACT,YAAQ,KAAK,4CAAS;AAAA,EACxB;AAEA,MAAI,aAAa,OAAQ,OAAO,MAAM,SAAS,MAAM,iBAAiB,OAAO,OAAO,GAAG;AACrF,aAAS;AACT,YAAQ,KAAK,sCAAQ;AAAA,EACvB,WAAW,aAAa,KAAK;AAC3B,aAAS;AACT,YAAQ,KAAK,0BAAM;AAAA,EACrB;AAEA,QAAM,iBAAiB,OAAO,MAAM,OAAO,OAAK,mBAAmB,EAAE,IAAI,CAAC;AAC1E,MAAI,eAAe,SAAS,GAAG;AAC7B,aAAS,KAAK,IAAI,IAAK,eAAe,SAAS,OAAO,MAAM,SAAU,EAAE;AACxE,YAAQ,KAAK,0BAAM;AAAA,EACrB;AAEA,SAAO,EAAE,OAAO,KAAK,IAAI,KAAK,KAAK,GAAG,QAAQ;AAChD;AAEA,eAAsB,aACpB,SACA,UAC4B;AAC5B,QAAM,kBAAoC,CAAC;AAE3C,aAAW,UAAU,SAAS;AAC5B,UAAM,aAAa,OAAO,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AAEnE,QAAI,aAAa,OAAQ,OAAO,MAAM,SAAS,IAAI;AACjD,UAAI,iBAAiB,OAAO,OAAO,GAAG;AACpC,wBAAgB,KAAK;AAAA,UACnB,QAAQ,OAAO;AAAA,UACf,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,aAAa,iCAAQ,UAAU;AAAA,QACjC,CAAC;AAAA,MACH;AAAA,IACF;AAEA,eAAW,QAAQ,OAAO,OAAO;AAC/B,UAAI,mBAAmB,KAAK,IAAI,GAAG;AACjC,wBAAgB,KAAK;AAAA,UACnB,MAAM,KAAK;AAAA,UACX,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,aAAa;AAAA,QACf,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAEA,QAAM,cAAc,IAAI,IAAI,QAAQ,QAAQ,OAAK,EAAE,MAAM,IAAI,OAAK,EAAE,IAAI,CAAC,CAAC;AAC1E,QAAM,eAAe,MAAM,KAAK,WAAW,EAAE,MAAM,GAAG,EAAE;AAExD,aAAW,YAAY,cAAc;AACnC,UAAM,WAAWA,MAAK,UAAU,QAAQ;AACxC,QAAI,CAAC,WAAW,QAAQ,EAAG;AAE3B,QAAI;AACF,YAAM,UAAU,MAAMD,UAAS,UAAU,OAAO;AAChD,YAAM,iBAAiB,wBAAwB,OAAO;AAEtD,UAAI,iBAAiB,KAAK;AACxB,wBAAgB,KAAK;AAAA,UACnB,MAAM;AAAA,UACN,QAAQ;AAAA,UACR,OAAO;AAAA,UACP,aAAa,8CAAW,iBAAiB,KAAK,QAAQ,CAAC,CAAC;AAAA,QAC1D,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,mBAAmB,MAAM;AAAA,IAC7B,IAAI,IAAI,gBAAgB,IAAI,OAAK,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO;AAAA,EACpE;AAEA,SAAO;AAAA,IACL,iBAAiB,iBAAiB,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE;AAAA,IAC/E,iBAAiB,iBAAiB;AAAA,EACpC;AACF;AAEA,SAAS,iBAAiB,SAA0B;AAClD,QAAM,kBAAkB;AAAA,IACtB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,QAAM,aAAa,QAAQ,KAAK,EAAE,YAAY;AAC9C,SAAO,gBAAgB,KAAK,aAAW,QAAQ,KAAK,UAAU,CAAC;AACjE;AAEA,SAAS,eAAe,MAAuB;AAC7C,QAAM,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,WAAW,KAAK,aAAW,QAAQ,KAAK,IAAI,CAAC;AACtD;AAEA,SAAS,wBAAwB,MAAuB;AACtD,SAAO,qDAAqD,KAAK,IAAI;AACvE;AAEA,SAAS,mBAAmB,MAAuB;AACjD,QAAM,oBAAoB;AAAA,IACxB;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,kBAAkB,KAAK,aAAW,QAAQ,KAAK,IAAI,CAAC;AAC7D;AAEA,SAAS,wBAAwB,SAAyB;AACxD,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,MAAI,eAAe;AACnB,MAAI,YAAY;AAEhB,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,CAAC,QAAS;AAEd,QACE,QAAQ,WAAW,IAAI,KACvB,QAAQ,WAAW,IAAI,KACvB,QAAQ,WAAW,GAAG,KACtB,QAAQ,WAAW,GAAG,GACtB;AACA;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAa,eAAe;AAClC,SAAO,aAAa,IAAI,eAAe,aAAa;AACtD;;;ACrLO,SAAS,mBAAmB,SAKjC;AACA,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,MACL,WAAW,EAAE,cAAc,GAAG,YAAY,GAAG,cAAc,GAAG,mBAAmB,GAAG,eAAe,CAAC,EAAE;AAAA,MACtG,eAAe,CAAC;AAAA,MAChB,kBAAkB,CAAC;AAAA,MACnB,UAAU,CAAC;AAAA,IACb;AAAA,EACF;AAEA,QAAM,YAAwB,CAAC;AAC/B,QAAM,cAAc,oBAAI,IAAqD;AAC7E,QAAM,iBAAiB,oBAAI,IAA+F;AAC1H,QAAM,cAAc,oBAAI,IAAqD;AAE7E,MAAI,eAAe;AACnB,MAAI,aAAa;AACjB,MAAI,oBAAoB;AAExB,aAAW,UAAU,SAAS;AAC5B,UAAM,eAAe,gBAAgB,MAAM;AAC3C,UAAM,UAAU,aAAa;AAC7B,UAAM,cAAc,OAAO,MAAM,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,CAAC;AACpE,UAAM,mBAAmB,KAAK,MAAO,cAAc,UAAW,GAAG;AAEjE,kBAAc;AACd,oBAAgB;AAEhB,QAAI,UAAU,IAAI;AAChB;AACA,gBAAU,KAAK;AAAA,QACb,MAAM,OAAO;AAAA,QACb,QAAQ,OAAO;AAAA,QACf,MAAM,OAAO;AAAA,QACb;AAAA,QACA;AAAA,QACA,YAAY;AAAA,QACZ,YAAY,OAAO,MAAM;AAAA,QACzB,SAAS,OAAO;AAAA,QAChB,SAAS,aAAa;AAAA,MACxB,CAAC;AAAA,IACH;AAEA,UAAM,YAAY,OAAO,MAAM,YAAY;AAC3C,UAAM,aAAa,YAAY,IAAI,SAAS,KAAK,EAAE,SAAS,GAAG,YAAY,EAAE;AAC7E,eAAW,WAAW;AACtB,eAAW,cAAc;AACzB,gBAAY,IAAI,WAAW,UAAU;AAErC,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,MAAM,gBAAgB,KAAK,IAAI;AACrC,YAAM,cAAc,KAAK,MAAO,KAAK,QAAQ,UAAW,GAAG;AAE3D,YAAM,UAAU,eAAe,IAAI,GAAG,KAAK;AAAA,QACzC,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,SAAS,oBAAI,IAAI;AAAA,QACjB,cAAc,OAAO;AAAA,MACvB;AACA,cAAQ,WAAW;AACnB,cAAQ,cAAc,KAAK;AAC3B,cAAQ,QAAQ,IAAI,OAAO,IAAI;AAC/B,UAAI,OAAO,OAAO,QAAQ,cAAc;AACtC,gBAAQ,eAAe,OAAO;AAAA,MAChC;AACA,qBAAe,IAAI,KAAK,OAAO;AAAA,IACjC;AAEA,UAAM,OAAO,WAAW,OAAO,IAAI;AACnC,UAAM,WAAW,YAAY,IAAI,IAAI,KAAK,EAAE,SAAS,GAAG,YAAY,EAAE;AACtE,aAAS,WAAW;AACpB,aAAS,cAAc;AACvB,gBAAY,IAAI,MAAM,QAAQ;AAAA,EAChC;AAEA,QAAM,YAAuB;AAAA,IAC3B;AAAA,IACA;AAAA,IACA,cAAc,aAAa,IAAK,eAAe,aAAc,MAAM;AAAA,IACnE;AAAA,IACA,eAAe,UAAU,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,EAAE;AAAA,EAC5E;AAEA,QAAM,gBAAiC,CAAC;AACxC,QAAM,YAAY,oBAAI,IAA6C;AACnE,aAAW,UAAU,SAAS;AAC5B,cAAU,IAAI,OAAO,MAAM,YAAY,GAAG,EAAE,MAAM,OAAO,QAAQ,OAAO,OAAO,MAAM,CAAC;AAAA,EACxF;AAEA,aAAW,CAAC,WAAW,IAAI,KAAK,aAAa;AAC3C,UAAM,SAAS,UAAU,IAAI,SAAS;AACtC,QAAI,QAAQ;AACV,oBAAc,KAAK;AAAA,QACjB,QAAQ,OAAO;AAAA,QACf,OAAO,OAAO;AAAA,QACd,SAAS,KAAK;AAAA,QACd,YAAY,KAAK;AAAA,QACjB,cAAc,KAAK,aAAa,IAAK,KAAK,UAAU,KAAK,aAAc,MAAM;AAAA,MAC/E,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,mBAAuC,CAAC;AAC9C,aAAW,CAAC,MAAM,IAAI,KAAK,gBAAgB;AACzC,UAAM,eAAe,KAAK,aAAa,IAAK,KAAK,UAAU,KAAK,aAAc,MAAM;AACpF,UAAM,aAAa,KAAK,QAAQ,OAAO,MAAM,eAAe;AAE5D,qBAAiB,KAAK;AAAA,MACpB;AAAA,MACA,aAAa;AAAA,MACb,SAAS,KAAK,QAAQ;AAAA,MACtB,SAAS,KAAK;AAAA,MACd,YAAY,KAAK;AAAA,MACjB;AAAA,MACA,cAAc,KAAK;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH;AAEA,QAAM,WAA2B,MAAM,KAAK,YAAY,QAAQ,CAAC,EAC9D,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO;AAAA,IACtB;AAAA,IACA,SAAS,KAAK;AAAA,IACd,YAAY,KAAK;AAAA,IACjB,cAAc,KAAK,aAAa,IAAK,KAAK,UAAU,KAAK,aAAc,MAAM;AAAA,EAC/E,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAE9C,SAAO;AAAA,IACL;AAAA,IACA,eAAe,cAAc,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY;AAAA,IAC3E,kBAAkB,iBAAiB,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY;AAAA,IACjF;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,UAA0B;AACjD,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,SAAO,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI;AACvC;AAEA,SAAS,WAAW,MAAoB;AACtC,QAAM,OAAO,KAAK,YAAY;AAC9B,QAAM,cAAc,IAAI,KAAK,MAAM,GAAG,CAAC;AACvC,QAAM,OAAO,KAAK,OAAO,KAAK,QAAQ,IAAI,YAAY,QAAQ,MAAM,KAAK,KAAK,KAAK,IAAK;AACxF,QAAM,OAAO,KAAK,MAAM,OAAO,YAAY,OAAO,IAAI,KAAK,CAAC;AAC5D,SAAO,GAAG,IAAI,KAAK,KAAK,SAAS,EAAE,SAAS,GAAG,GAAG,CAAC;AACrD;;;AClKO,SAASE,iBAAgB,UAA0B;AACxD,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,SAAO,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI;AACvC;AAGO,SAAS,cAAc,MAAoB;AAChD,QAAM,IAAI,KAAK,YAAY;AAC3B,QAAM,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG;AACrD,QAAM,IAAI,OAAO,KAAK,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG;AAChD,SAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB;;;ACGA,IAAM,iCAAiC;AACvC,IAAM,sBAAsB;AAKrB,SAAS,gCACd,SACwB;AACxB,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,MACL,SAAS,aAAa;AAAA,MACtB,eAAe;AAAA,MACf,kBAAkB;AAAA,MAClB,eAAe;AAAA,MACf,cAAc,CAAC;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,QAAkB,CAAC;AACzB,QAAM,aAA4B,CAAC;AAEnC,aAAW,UAAU,SAAS;AAC5B,QAAIC,SAAQ;AACZ,eAAW,KAAK,OAAO,MAAO,CAAAA,UAAS,EAAE,QAAQ,EAAE;AACnD,UAAM,KAAKA,MAAK;AAChB,eAAW,KAAK;AAAA,MACd,MAAM,OAAO;AAAA,MACb,QAAQ,OAAO;AAAA,MACf,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB,YAAYA;AAAA,MACZ,YAAY,OAAO,MAAM;AAAA,IAC3B,CAAC;AAAA,EACH;AAGA,QAAM,SAAS,EAAE,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,EAAE;AAChD,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,GAAI,QAAO;AAAA,aACb,OAAO,GAAI,QAAO;AAAA,aAClB,OAAO,IAAK,QAAO;AAAA,aACnB,OAAO,IAAM,QAAO;AAAA,QACxB,QAAO;AAAA,EACd;AAEA,QAAM,QAAQ,MAAM;AACpB,QAAM,UAA8B;AAAA,IAClC,EAAE,OAAO,MAAM,OAAO,OAAO,OAAO,OAAO,IAAI,YAAY,IAAI,OAAO,IAAI,KAAK,EAAE;AAAA,IACjF,EAAE,OAAO,KAAK,OAAO,SAAS,OAAO,OAAO,GAAG,YAAY,IAAI,OAAO,GAAG,KAAK,EAAE;AAAA,IAChF,EAAE,OAAO,KAAK,OAAO,UAAU,OAAO,OAAO,GAAG,YAAY,IAAI,OAAO,GAAG,KAAK,EAAE;AAAA,IACjF,EAAE,OAAO,KAAK,OAAO,WAAW,OAAO,OAAO,GAAG,YAAY,IAAI,OAAO,GAAG,KAAK,EAAE;AAAA,IAClF,EAAE,OAAO,MAAM,OAAO,cAAS,OAAO,OAAO,IAAI,YAAY,IAAI,OAAO,IAAI,KAAK,EAAE;AAAA,EACrF;AAEA,QAAM,SAAS,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC9C,QAAM,gBAAgB,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,MAAM;AAC/D,QAAM,mBAAmB,OAAO,KAAK,MAAM,OAAO,SAAS,CAAC,CAAC,KAAK;AAClE,QAAM,gBAAgB,OAAO,KAAK,MAAM,OAAO,SAAS,IAAI,CAAC,KAAK;AAElE,QAAM,eAAe,WAClB,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,EAAE,UAAU,EAC1C,MAAM,GAAG,EAAE;AAEd,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKO,SAAS,2BACd,SAC0B;AAC1B,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAC,GAAG,aAAa,CAAC,EAAE;AAAA,EAClD;AAGA,QAAM,iBAAiB,oBAAI,IAAoB;AAE/C,QAAM,UAAU,oBAAI,IAA2D;AAE/E,aAAW,UAAU,SAAS;AAC5B,UAAM,QAAQ,OAAO,MAAM,MAAM,GAAG,8BAA8B;AAClE,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,KAAK,MAAO,MAAK,IAAIC,iBAAgB,EAAE,IAAI,CAAC;AAEvD,UAAM,SAAS,MAAM,KAAK,IAAI,EAAE,KAAK;AACrC,eAAW,KAAK,QAAQ;AACtB,qBAAe,IAAI,IAAI,eAAe,IAAI,CAAC,KAAK,KAAK,CAAC;AAAA,IACxD;AAEA,aAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,eAAS,IAAI,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AAC1C,cAAM,MAAM,GAAG,OAAO,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC;AACvC,cAAM,WAAW,QAAQ,IAAI,GAAG,KAAK,EAAE,MAAM,OAAO,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,OAAO,EAAE;AAClF,iBAAS;AACT,gBAAQ,IAAI,KAAK,QAAQ;AAAA,MAC3B;AAAA,IACF;AAAA,EACF;AAGA,QAAM,QAAyB,MAAM,KAAK,QAAQ,OAAO,CAAC,EACvD,IAAI,CAAC,EAAE,MAAM,MAAM,MAAM,MAAM;AAC9B,UAAM,KAAK,eAAe,IAAI,IAAI,KAAK;AACvC,UAAM,KAAK,eAAe,IAAI,IAAI,KAAK;AACvC,UAAM,WAAW,QAAQ,KAAK,IAAI,IAAI,EAAE;AACxC,WAAO,EAAE,MAAM,MAAM,cAAc,OAAO,SAAS;AAAA,EACrD,CAAC,EACA,OAAO,CAAC,MAAM,EAAE,gBAAgB,CAAC,EACjC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,EACtC,MAAM,GAAG,EAAE;AAGd,QAAM,UAAU,MAAM,KAAK,eAAe,QAAQ,CAAC,EAChD,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1B,MAAM,GAAG,mBAAmB,EAC5B,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC;AAEjB,QAAM,SAAgC,CAAC;AACvC,aAAW,MAAM,SAAS;AACxB,eAAW,MAAM,SAAS;AACxB,UAAI,OAAO,IAAI;AACb,eAAO,KAAK,EAAE,MAAM,IAAI,MAAM,IAAI,OAAO,eAAe,IAAI,EAAE,KAAK,EAAE,CAAC;AAAA,MACxE,OAAO;AACL,cAAM,MAAM,KAAK,KAAK,GAAG,EAAE,MAAM,EAAE,KAAK,GAAG,EAAE,MAAM,EAAE;AACrD,eAAO,KAAK,EAAE,MAAM,IAAI,MAAM,IAAI,OAAO,QAAQ,IAAI,GAAG,GAAG,SAAS,EAAE,CAAC;AAAA,MACzE;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa;AAAA,EACf;AACF;AAKO,SAAS,uBAAuB,SAA+C;AACpF,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,MACL,OAAO,CAAC;AAAA,MACR,SAAS,CAAC;AAAA,MACV,SAAS,EAAE,iBAAiB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,eAAe,EAAE;AAAA,IACxF;AAAA,EACF;AAGA,QAAM,UAAU,oBAAI,IAGlB;AAEF,aAAW,UAAU,SAAS;AAC5B,UAAM,UAAU,iBAAiB,MAAM;AACvC,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,QAAQ,KAAK,QAAQ,KAAK;AAChC,YAAM,QAAQ,QAAQ,IAAI,KAAK,IAAI,KAAK;AAAA,QACtC,OAAO;AAAA,QACP,SAAS;AAAA,QACT,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,QAAQ;AAAA,MACV;AACA,YAAM,SAAS,KAAK;AACpB,YAAM,WAAW,KAAK;AACtB,YAAM;AAEN,YAAM,IAAI,KAAK,IAAI,OAAO,CAAC;AAC3B,YAAM,cAAc,UAAU;AAC9B,YAAM,UAAU;AAChB,cAAQ,IAAI,KAAK,MAAM,KAAK;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,QAA6B,CAAC;AACpC,QAAM,UAAmC,CAAC;AAC1C,MAAI,kBAAkB,GACpB,iBAAiB,GACjB,iBAAiB,GACjB,gBAAgB;AAElB,aAAW,CAAC,MAAM,IAAI,KAAK,SAAS;AAClC,UAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,aAAa,KAAK,SAAS;AAChE,UAAM,YAAY,KAAK,QAAQ,IAAI,KAAK,UAAU,KAAK,QAAQ;AAC/D,UAAM,aAAa,KAAK,QAAQ,KAAK;AAGrC,QAAI,KAAK,cAAc,KAAK,aAAa,GAAI;AAG7C,UAAM,YACH,QAAQ,MAAO,KAAK,IAAI,WAAW,CAAC,IAAI,KAAK,KAAK,KAAK,cAAc,CAAC,IAAI;AAE7E,UAAM,KAAK;AAAA,MACT;AAAA,MACA,SAAS;AAAA,MACT;AAAA,MACA,aAAa,KAAK;AAAA,MAClB;AAAA,MACA;AAAA,IACF,CAAC;AAED,YAAQ,KAAK,EAAE,MAAM,SAAS,OAAO,WAAW,aAAa,KAAK,YAAY,CAAC;AAE/E,UAAM,SAAS,QAAQ;AACvB,UAAM,YAAY,YAAY;AAC9B,QAAI,UAAU,UAAW;AAAA,aAChB,OAAQ;AAAA,aACR,UAAW;AAAA,QACf;AAAA,EACP;AAEA,QAAM,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAE9C,SAAO;AAAA,IACL,OAAO,MAAM,MAAM,GAAG,EAAE;AAAA,IACxB,SAAS,QAAQ,MAAM,GAAG,GAAG;AAAA;AAAA,IAC7B,SAAS,EAAE,iBAAiB,gBAAgB,gBAAgB,cAAc;AAAA,EAC5E;AACF;AAEA,SAAS,eAAmC;AAC1C,SAAO;AAAA,IACL,EAAE,OAAO,MAAM,OAAO,OAAO,OAAO,GAAG,YAAY,EAAE;AAAA,IACrD,EAAE,OAAO,KAAK,OAAO,SAAS,OAAO,GAAG,YAAY,EAAE;AAAA,IACtD,EAAE,OAAO,KAAK,OAAO,UAAU,OAAO,GAAG,YAAY,EAAE;AAAA,IACvD,EAAE,OAAO,KAAK,OAAO,WAAW,OAAO,GAAG,YAAY,EAAE;AAAA,IACxD,EAAE,OAAO,MAAM,OAAO,cAAS,OAAO,GAAG,YAAY,EAAE;AAAA,EACzD;AACF;AAEA,SAAS,IAAI,MAAc,OAAuB;AAChD,SAAO,QAAQ,IAAK,OAAO,QAAS,MAAM;AAC5C;;;ACnQA,SAAS,eAAe;AAmBjB,SAAS,uBAAuB,SAAyC;AAC9E,SAAO,QAAQ,IAAI,CAAC,WAAW;AAC7B,QAAI,aAAa;AACjB,QAAI,eAAe;AAEnB,eAAW,QAAQ,OAAO,OAAO;AAC/B,oBAAc,KAAK;AACnB,sBAAgB,KAAK;AAAA,IACvB;AAEA,WAAO;AAAA,MACL,MAAM,OAAO;AAAA,MACb,QAAQ,OAAO;AAAA,MACf,OAAO,OAAO;AAAA,MACd,UAAU;AAAA,MACV,MAAM,OAAO;AAAA,MACb,SAAS,OAAO;AAAA,MAChB;AAAA,MACA;AAAA,MACA,OAAO,OAAO,MAAM,IAAI,CAAC,UAAU,EAAE,GAAG,KAAK,EAAE;AAAA,IACjD;AAAA,EACF,CAAC;AACH;AAGO,SAAS,wBAAwB,SAAyC;AAC/E,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,oBAAoB;AAAA,EAC7B;AAEA,QAAM,aAAa,QAAQ,OAAO,CAAC,KAAK,WAAW,MAAM,OAAO,MAAM,QAAQ,CAAC;AAC/E,QAAM,oBAAoB,aAAa,QAAQ;AAE/C,QAAM,aAAa,QAAQ;AAAA,IACzB,CAAC,KAAK,WAAW,MAAM,OAAO,MAAM,OAAO,CAAC,GAAG,SAAS,IAAI,KAAK,QAAQ,KAAK,SAAS,CAAC;AAAA,IACxF;AAAA,EACF;AACA,QAAM,oBAAoB,aAAa,QAAQ;AAE/C,QAAM,aAAa,QAAQ;AAAA,IACzB,CAAC,KAAK,WAAW,MAAM,OAAO,MAAM,OAAO,CAAC,GAAG,SAAS,IAAI,KAAK,OAAO,CAAC;AAAA,IACzE;AAAA,EACF;AACA,QAAM,eAAe,QAAQ;AAAA,IAC3B,CAAC,KAAK,WAAW,MAAM,OAAO,MAAM,OAAO,CAAC,GAAG,SAAS,IAAI,KAAK,SAAS,CAAC;AAAA,IAC3E;AAAA,EACF;AACA,QAAM,YAAY,aAAa,IAAI,eAAe,aAAa;AAE/D,QAAM,gBAAgB,oBAAI,IAAqD;AAC/E,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,QAAQ,cAAc,IAAI,KAAK,IAAI,KAAK,EAAE,OAAO,GAAG,SAAS,oBAAI,IAAI,EAAE;AAC7E,YAAM;AACN,YAAM,QAAQ,IAAI,OAAO,MAAM;AAC/B,oBAAc,IAAI,KAAK,MAAM,KAAK;AAAA,IACpC;AAAA,EACF;AAEA,QAAM,WAAsB,MAAM,KAAK,cAAc,QAAQ,CAAC,EAC3D,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO;AAAA,IACtB;AAAA,IACA,aAAa,KAAK;AAAA,IAClB,SAAS,MAAM,KAAK,KAAK,OAAO;AAAA,EAClC,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,EAAE,WAAW,EAC5C,MAAM,GAAG,EAAE;AAEd,SAAO,EAAE,mBAAmB,mBAAmB,WAAW,SAAS;AACrE;AAGO,SAAS,sBAAsB,SAAuC;AAC3E,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,kBAAkB;AAAA,EAC3B;AAEA,QAAM,sBAAsB,IAAI,MAAc,CAAC,EAAE,KAAK,CAAC;AACvD,QAAM,kBAAoD,oBAAI,IAAI;AAElE,aAAW,UAAU,SAAS;AAC5B,UAAM,MAAM,OAAO,KAAK,OAAO;AAC/B,UAAM,MAAM,QAAQ,IAAI,IAAI,MAAM;AAClC,wBAAoB,GAAG;AAEvB,QAAI,CAAC,gBAAgB,IAAI,GAAG,GAAG;AAC7B,sBAAgB,IAAI,KAAK,oBAAI,IAAI,CAAC;AAAA,IACpC;AACA,UAAM,aAAa,gBAAgB,IAAI,GAAG;AAC1C,eAAW,IAAI,OAAO,SAAS,WAAW,IAAI,OAAO,MAAM,KAAK,KAAK,CAAC;AAAA,EACxE;AAEA,QAAM,kBACH,oBAAoB,CAAC,IAAI,oBAAoB,CAAC,KAAK,QAAQ;AAE9D,QAAM,SAAS,CAAC,GAAG,OAAO,EAAE;AAAA,IAC1B,CAAC,GAAG,MAAM,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,QAAQ;AAAA,EAC9C;AACA,MAAI,gBAAgB;AACpB,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,qBAAiB,OAAO,CAAC,EAAE,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ;AAAA,EACzE;AACA,QAAM,oBACJ,OAAO,SAAS,IAAI,iBAAiB,OAAO,SAAS,KAAK,OAAU;AAEtE,QAAM,EAAE,eAAe,cAAc,IAAI,iBAAiB,MAAM;AAEhE,QAAM,uBAAuB,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,CAAC,GAAG,QAAQ;AACjE,UAAM,YAAY,gBAAgB,IAAI,GAAG;AACzC,UAAM,UAAkC,CAAC;AACzC,QAAI,WAAW;AACb,gBAAU,QAAQ,CAAC,OAAO,WAAW;AACnC,gBAAQ,MAAM,IAAI;AAAA,MACpB,CAAC;AAAA,IACH;AACA,WAAO;AAAA,MACL,OAAO,oBAAoB,GAAG;AAAA,MAC9B;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,iBAAiB;AAAA,EACnB;AACF;AAGO,SAAS,gBAAgB,SAAoC;AAClE,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,eAAe;AAAA,EACxB;AAEA,QAAM,UAAU,oBAAI,IAAyB;AAC7C,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAOC,YAAW,OAAO,IAAI;AACnC,UAAM,QAAQ,QAAQ,IAAI,IAAI,KAAK;AAAA,MACjC;AAAA,MACA,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,cAAc;AAAA,IAChB;AACA,UAAM;AACN,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,cAAc,KAAK;AACzB,YAAM,gBAAgB,KAAK;AAAA,IAC7B;AACA,YAAQ,IAAI,MAAM,KAAK;AAAA,EACzB;AACA,QAAM,cAAc,MAAM,KAAK,QAAQ,OAAO,CAAC,EAAE;AAAA,IAAK,CAAC,GAAG,MACxD,EAAE,KAAK,cAAc,EAAE,IAAI;AAAA,EAC7B;AAEA,QAAM,WAAW,oBAAI,IAAoB;AACzC,aAAW,UAAU,SAAS;AAC5B,UAAM,UAAU,cAAc,OAAO,IAAI;AACzC,UAAM,MAAM,OAAO,MAAM,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,QAAQ,KAAK,SAAS,CAAC;AACjF,aAAS,IAAI,UAAU,SAAS,IAAI,OAAO,KAAK,KAAK,GAAG;AAAA,EAC1D;AAEA,MAAI,aAAa;AACjB,QAAM,kBAAqC,MAAM,KAAK,SAAS,QAAQ,CAAC,EACrE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,EACrC,IAAI,CAAC,CAAC,MAAM,GAAG,MAAM;AACpB,kBAAc;AACd,WAAO,EAAE,MAAM,UAAU,WAAW;AAAA,EACtC,CAAC;AAEH,SAAO,EAAE,aAAa,gBAAgB;AACxC;AAGO,SAAS,uBAAuB,SAA+C;AACpF,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,0BAA0B;AAAA,EACnC;AAEA,QAAM,cAAc,oBAAI,IAAyB;AACjD,QAAM,cAAc,oBAAI,IAAoB;AAE5C,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,UAAU,YAAY,IAAI,KAAK,IAAI,KAAK,oBAAI,IAAI;AACtD,cAAQ,IAAI,OAAO,MAAM,YAAY,CAAC;AACtC,kBAAY,IAAI,KAAK,MAAM,OAAO;AAClC,kBAAY,IAAI,KAAK,OAAO,YAAY,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC;AAAA,IAClE;AAAA,EACF;AAEA,QAAM,YAAwB,CAAC;AAC/B,QAAM,wBAAsC,CAAC;AAE7C,aAAW,CAAC,MAAM,OAAO,KAAK,aAAa;AACzC,UAAM,cAAc,YAAY,IAAI,IAAI,KAAK;AAC7C,QAAI,QAAQ,SAAS,KAAK,eAAe,GAAG;AAC1C,gBAAU,KAAK;AAAA,QACb;AAAA,QACA,QAAQ,MAAM,KAAK,OAAO,EAAE,CAAC;AAAA,QAC7B,SAAS;AAAA,MACX,CAAC;AAAA,IACH,WAAW,QAAQ,QAAQ,KAAK,eAAe,GAAG;AAChD,4BAAsB,KAAK;AAAA,QACzB;AAAA,QACA,aAAa,QAAQ;AAAA,QACrB,cAAc;AAAA,MAChB,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,WAAW,UAAU,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,EAAE;AAAA,IACtE,uBAAuB,sBACpB,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY,EAC9C,MAAM,GAAG,EAAE;AAAA,EAChB;AACF;AAGO,SAAS,sBAAsB,SAA6C;AACjF,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,kBAAkB;AAAA,EAC3B;AAEA,QAAM,mBAA2C,CAAC;AAClD,MAAI,cAAc;AAElB,QAAM,YACJ;AAEF,aAAW,UAAU,SAAS;AAC5B,mBAAe,OAAO,QAAQ;AAC9B,UAAM,QAAQ,OAAO,QAAQ,MAAM,SAAS;AAC5C,QAAI,OAAO;AACT,YAAM,OAAO,MAAM,CAAC,EAAE,YAAY;AAClC,uBAAiB,IAAI,KAAK,iBAAiB,IAAI,KAAK,KAAK;AAAA,IAC3D,OAAO;AACL,uBAAiB,OAAO,KAAK,iBAAiB,OAAO,KAAK,KAAK;AAAA,IACjE;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA,kBAAkB,cAAc,QAAQ;AAAA,EAC1C;AACF;AAGO,SAAS,qCACd,SAC8B;AAC9B,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,kBAAkB,oBAAI,IAAwC;AACpE,QAAM,iBAAiB,oBAAI,IAAyB;AAEpD,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,MAAM,QAAQ,KAAK,IAAI,EAAE,YAAY,KAAK;AAChD,YAAM,MAAM,GAAG,OAAO,MAAM,YAAY,CAAC,MAAM,GAAG;AAElD,UAAI,eAAe,gBAAgB,IAAI,GAAG;AAC1C,UAAI,CAAC,cAAc;AACjB,uBAAe;AAAA,UACb,QAAQ,OAAO;AAAA,UACf,OAAO,OAAO;AAAA,UACd,WAAW;AAAA,UACX,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,SAAS;AAAA,UACT,WAAW;AAAA,QACb;AACA,wBAAgB,IAAI,KAAK,YAAY;AACrC,uBAAe,IAAI,KAAK,oBAAI,IAAI,CAAC;AAAA,MACnC;AAEA,mBAAa,cAAc,KAAK;AAChC,mBAAa,gBAAgB,KAAK;AAClC,qBAAe,IAAI,GAAG,EAAG,IAAI,KAAK,IAAI;AAAA,IACxC;AAAA,EACF;AAEA,QAAM,iBAAiB,oBAAI,IAAyB;AACpD,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,MAAM,QAAQ,KAAK,IAAI,EAAE,YAAY,KAAK;AAChD,YAAM,MAAM,GAAG,OAAO,MAAM,YAAY,CAAC,MAAM,GAAG;AAElD,UAAI,CAAC,eAAe,IAAI,GAAG,GAAG;AAC5B,uBAAe,IAAI,KAAK,oBAAI,IAAI,CAAC;AAAA,MACnC;AACA,qBAAe,IAAI,GAAG,EAAG,IAAI,OAAO,IAAI;AAAA,IAC1C;AAAA,EACF;AAEA,aAAW,CAAC,KAAK,YAAY,KAAK,iBAAiB;AACjD,iBAAa,UAAU,eAAe,IAAI,GAAG,GAAG,QAAQ;AACxD,iBAAa,YAAY,eAAe,IAAI,GAAG,GAAG,QAAQ;AAAA,EAC5D;AAEA,SAAO,MAAM,KAAK,gBAAgB,OAAO,CAAC,EACvC,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,SAAS,EAAE,aAAa,EAAE;AAChC,UAAM,SAAS,EAAE,aAAa,EAAE;AAChC,WAAO,SAAS;AAAA,EAClB,CAAC,EACA,MAAM,GAAG,EAAE;AAChB;AAEO,SAAS,sBAAsC;AACpD,SAAO;AAAA,IACL,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,WAAW;AAAA,IACX,UAAU,CAAC;AAAA,EACb;AACF;AAEO,SAAS,oBAAkC;AAChD,SAAO;AAAA,IACL,qBAAqB,IAAI,MAAc,CAAC,EAAE,KAAK,CAAC;AAAA,IAChD,gBAAgB;AAAA,IAChB,mBAAmB;AAAA,IACnB,eAAe;AAAA,IACf,eAAe;AAAA,EACjB;AACF;AAEO,SAAS,iBAA4B;AAC1C,SAAO;AAAA,IACL,aAAa,CAAC;AAAA,IACd,iBAAiB,CAAC;AAAA,EACpB;AACF;AAEO,SAAS,4BAAkD;AAChE,SAAO;AAAA,IACL,WAAW,CAAC;AAAA,IACZ,uBAAuB,CAAC;AAAA,EAC1B;AACF;AAEO,SAAS,oBAAwC;AACtD,SAAO;AAAA,IACL,kBAAkB,CAAC;AAAA,IACnB,kBAAkB;AAAA,EACpB;AACF;AAGA,SAAS,iBAAiB,eAGxB;AACA,MAAI,cAAc,WAAW,GAAG;AAC9B,WAAO,EAAE,eAAe,GAAG,eAAe,EAAE;AAAA,EAC9C;AAEA,QAAM,cAAc,oBAAI,IAAY;AACpC,aAAW,UAAU,eAAe;AAClC,gBAAY,IAAI,cAAc,OAAO,IAAI,CAAC;AAAA,EAC5C;AAEA,QAAM,cAAc,MAAM,KAAK,WAAW,EAAE,KAAK;AACjD,MAAI,YAAY,WAAW,GAAG;AAC5B,WAAO,EAAE,eAAe,GAAG,eAAe,EAAE;AAAA,EAC9C;AAEA,MAAI,gBAAgB;AACpB,MAAI,qBAAqB;AACzB,MAAI,aAAa;AAEjB,WAAS,IAAI,GAAG,IAAI,YAAY,QAAQ,KAAK;AAC3C,UAAM,WAAW,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC;AAC5C,UAAM,WAAW,IAAI,KAAK,YAAY,CAAC,CAAC;AACxC,UAAM,WAAW,KAAK;AAAA,OACnB,SAAS,QAAQ,IAAI,SAAS,QAAQ,MAAM,MAAO,KAAK,KAAK;AAAA,IAChE;AAEA,QAAI,aAAa,GAAG;AAClB;AAAA,IACF,OAAO;AACL,mBAAa;AAAA,IACf;AAEA,oBAAgB,KAAK,IAAI,eAAe,UAAU;AAAA,EACpD;AAEA,QAAM,QAAQ,cAAc,oBAAI,KAAK,CAAC;AACtC,QAAM,iBAAiB,YAAY,YAAY,SAAS,CAAC;AACzD,QAAM,WAAW,IAAI,KAAK,cAAc;AACxC,QAAM,YAAY,IAAI,KAAK,KAAK;AAChC,QAAM,sBAAsB,KAAK;AAAA,KAC9B,UAAU,QAAQ,IAAI,SAAS,QAAQ,MAAM,MAAO,KAAK,KAAK;AAAA,EACjE;AAEA,MAAI,uBAAuB,GAAG;AAC5B,yBAAqB;AACrB,aAAS,IAAI,YAAY,SAAS,GAAG,KAAK,GAAG,KAAK;AAChD,YAAM,WAAW,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC;AAC5C,YAAM,WAAW,IAAI,KAAK,YAAY,CAAC,CAAC;AACxC,YAAM,WAAW,KAAK;AAAA,SACnB,SAAS,QAAQ,IAAI,SAAS,QAAQ,MAAM,MAAO,KAAK,KAAK;AAAA,MAChE;AAEA,UAAI,aAAa,GAAG;AAClB;AAAA,MACF,OAAO;AACL;AAAA,MACF;AAAA,IACF;AAAA,EACF,OAAO;AACL,yBAAqB;AAAA,EACvB;AAEA,SAAO,EAAE,eAAe,eAAe,mBAAmB;AAC5D;AAGA,SAASA,YAAW,MAAoB;AACtC,QAAM,IAAI,IAAI,KAAK,IAAI;AACvB,IAAE,SAAS,GAAG,GAAG,GAAG,CAAC;AACrB,IAAE,QAAQ,EAAE,QAAQ,IAAI,KAAK,EAAE,OAAO,KAAK,EAAE;AAC7C,QAAM,YAAY,IAAI,KAAK,EAAE,YAAY,GAAG,GAAG,CAAC;AAChD,QAAM,SAAS,KAAK;AAAA,MAChB,EAAE,QAAQ,IAAI,UAAU,QAAQ,KAAK,QAAW,KAAK;AAAA,EACzD;AACA,SAAO,GAAG,EAAE,YAAY,CAAC,KAAK,OAAO,MAAM,EAAE,SAAS,GAAG,GAAG,CAAC;AAC/D;;;AC1bO,SAAS,aAA0B;AACxC,SAAO;AAAA,IACL,cAAc;AAAA,IACd,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,cAAc;AAAA,IACd,iBAAiB,oBAAI,KAAK;AAAA,IAC1B,gBAAgB,oBAAI,KAAK;AAAA,IACzB,YAAY,EAAE,MAAM,IAAI,OAAO,EAAE;AAAA,IACjC,SAAS,CAAC;AAAA,IACV,WAAW,CAAC;AAAA,IACZ,aAAa,CAAC;AAAA,IACd,oBAAoB,IAAI,MAAc,EAAE,EAAE,KAAK,CAAC;AAAA,IAChD,cAAc,CAAC;AAAA,IACf,SAAS,oBAAoB;AAAA,IAC7B,cAAc,kBAAkB;AAAA,IAChC,QAAQ,eAAe;AAAA,IACvB,eAAe,0BAA0B;AAAA,IACzC,cAAc,kBAAkB;AAAA,IAChC,6BAA6B,CAAC;AAAA,IAC9B,eAAe,CAAC;AAAA,EAClB;AACF;;;ANGO,SAAS,eAAe,SAAsC;AACnE,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,WAAW;AAAA,EACpB;AAGA,QAAM,SAAS,CAAC,GAAG,OAAO,EAAE;AAAA,IAC1B,CAAC,GAAG,MAAM,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,QAAQ;AAAA,EAC9C;AAGA,MAAI,kBAAkB;AACtB,MAAI,oBAAoB;AACxB,QAAM,eAAe,oBAAI,IAAY;AAGrC,QAAM,YAAY,oBAAI,IAAyB;AAG/C,QAAM,cAAc,oBAAI,IAA2B;AAGnD,QAAM,eAAe,oBAAI,IAA4B;AACrD,QAAM,qBAAqB,oBAAI,IAAyB;AAGxD,QAAM,qBAAqB,IAAI,MAAc,EAAE,EAAE,KAAK,CAAC;AACvD,QAAM,eAAuC,CAAC;AAC9C,QAAM,iBAAmD,oBAAI,IAAI;AAGjE,QAAM,cAAc,oBAAI,IAAoB;AAE5C,aAAW,UAAU,QAAQ;AAE3B,UAAM,OAAO,OAAO,KAAK,SAAS;AAClC,uBAAmB,IAAI;AAGvB,QAAI,CAAC,eAAe,IAAI,IAAI,GAAG;AAC7B,qBAAe,IAAI,MAAM,oBAAI,IAAI,CAAC;AAAA,IACpC;AACA,UAAM,cAAc,eAAe,IAAI,IAAI;AAC3C,gBAAY,IAAI,OAAO,SAAS,YAAY,IAAI,OAAO,MAAM,KAAK,KAAK,CAAC;AAExE,UAAM,UAAU,cAAc,OAAO,IAAI;AACzC,iBAAa,OAAO,KAAK,aAAa,OAAO,KAAK,KAAK;AACvD,gBAAY,IAAI,UAAU,YAAY,IAAI,OAAO,KAAK,KAAK,CAAC;AAG5D,UAAM,YAAY,OAAO,MAAM,YAAY;AAC3C,QAAI,aAAa,UAAU,IAAI,SAAS;AACxC,QAAI,CAAC,YAAY;AACf,mBAAa;AAAA,QACX,MAAM,OAAO;AAAA,QACb,OAAO,OAAO;AAAA,QACd,SAAS;AAAA,QACT,YAAY;AAAA,QACZ,cAAc;AAAA,QACd,gBAAgB,OAAO;AAAA,MACzB;AACA,gBAAU,IAAI,WAAW,UAAU;AAAA,IACrC;AACA,eAAW;AACX,eAAW,iBAAiB,OAAO;AAGnC,eAAW,QAAQ,OAAO,OAAO;AAC/B,yBAAmB,KAAK;AACxB,2BAAqB,KAAK;AAC1B,mBAAa,IAAI,KAAK,IAAI;AAG1B,iBAAW,cAAc,KAAK;AAC9B,iBAAW,gBAAgB,KAAK;AAGhC,YAAM,MAAMC,SAAQ,KAAK,IAAI,EAAE,YAAY,KAAK;AAChD,UAAI,SAAS,YAAY,IAAI,GAAG;AAChC,UAAI,CAAC,QAAQ;AACX,iBAAS,EAAE,WAAW,KAAK,OAAO,GAAG,SAAS,GAAG,WAAW,EAAE;AAC9D,oBAAY,IAAI,KAAK,MAAM;AAAA,MAC7B;AACA,aAAO,SAAS,KAAK;AACrB,aAAO,WAAW,KAAK;AAGvB,YAAM,SAASC,iBAAgB,KAAK,IAAI;AACxC,UAAI,UAAU,aAAa,IAAI,MAAM;AACrC,UAAI,CAAC,SAAS;AACZ,kBAAU,EAAE,MAAM,QAAQ,SAAS,GAAG,cAAc,EAAE;AACtD,qBAAa,IAAI,QAAQ,OAAO;AAChC,2BAAmB,IAAI,QAAQ,oBAAI,IAAI,CAAC;AAAA,MAC1C;AACA,cAAQ,gBAAgB,KAAK,QAAQ,KAAK;AAC1C,yBAAmB,IAAI,MAAM,EAAG,IAAI,OAAO,IAAI;AAAA,IACjD;AAAA,EACF;AAGA,aAAW,CAAC,KAAK,SAAS,KAAK,oBAAoB;AACjD,UAAM,UAAU,aAAa,IAAI,GAAG;AACpC,QAAI,SAAS;AACX,cAAQ,UAAU,UAAU;AAAA,IAC9B;AAAA,EACF;AAGA,QAAM,iBAAiB,oBAAI,IAAyB;AACpD,aAAW,YAAY,cAAc;AACnC,UAAM,MAAMD,SAAQ,QAAQ,EAAE,YAAY,KAAK;AAC/C,QAAI,CAAC,eAAe,IAAI,GAAG,GAAG;AAC5B,qBAAe,IAAI,KAAK,oBAAI,IAAI,CAAC;AAAA,IACnC;AACA,mBAAe,IAAI,GAAG,EAAG,IAAI,QAAQ;AAAA,EACvC;AACA,aAAW,CAAC,KAAK,KAAK,KAAK,gBAAgB;AACzC,UAAM,SAAS,YAAY,IAAI,GAAG;AAClC,QAAI,QAAQ;AACV,aAAO,YAAY,MAAM;AAAA,IAC3B;AAAA,EACF;AAGA,MAAI,aAAyB,EAAE,MAAM,IAAI,OAAO,EAAE;AAClD,aAAW,CAAC,MAAM,KAAK,KAAK,aAAa;AACvC,QAAI,QAAQ,WAAW,OAAO;AAC5B,mBAAa,EAAE,MAAM,MAAM;AAAA,IAC7B;AAAA,EACF;AAGA,QAAM,UAAU,MAAM,KAAK,UAAU,OAAO,CAAC,EAAE;AAAA,IAC7C,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE;AAAA,EAC1B;AACA,QAAM,YAAY,MAAM,KAAK,YAAY,OAAO,CAAC,EAAE;AAAA,IACjD,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE;AAAA,EAC/C;AACA,QAAM,cAAc,MAAM,KAAK,aAAa,OAAO,CAAC,EACjD,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY,EAC9C,MAAM,GAAG,EAAE;AAGd,QAAM,sBAAsB,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,CAAC,GAAG,SAAS;AAClE,UAAME,aAAY,eAAe,IAAI,IAAI;AACzC,UAAMC,WAAkC,CAAC;AACzC,QAAID,YAAW;AACb,MAAAA,WAAU,QAAQ,CAAC,OAAO,WAAW;AACnC,QAAAC,SAAQ,MAAM,IAAI;AAAA,MACpB,CAAC;AAAA,IACH;AACA,WAAO;AAAA,MACL,OAAO,mBAAmB,IAAI;AAAA,MAC9B,SAAAA;AAAA,IACF;AAAA,EACF,CAAC;AAED,QAAM,UAAU,mBAAmB,MAAM;AAEzC,SAAO;AAAA,IACL,cAAc,OAAO;AAAA,IACrB,YAAY;AAAA,IACZ,cAAc;AAAA,IACd,cAAc,aAAa;AAAA,IAC3B,iBAAiB,OAAO,CAAC,EAAE;AAAA,IAC3B,gBAAgB,OAAO,OAAO,SAAS,CAAC,EAAE;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,gBAAgB;AAAA,IAChB,SAAS,wBAAwB,MAAM;AAAA,IACvC,cAAc,sBAAsB,MAAM;AAAA,IAC1C,QAAQ,gBAAgB,MAAM;AAAA,IAC9B,eAAe,uBAAuB,MAAM;AAAA,IAC5C,cAAc,sBAAsB,MAAM;AAAA,IAC1C,6BAA6B,qCAAqC,MAAM;AAAA,IACxE,eAAe,uBAAuB,MAAM;AAAA,IAC5C,WAAW,QAAQ;AAAA,IACnB,eAAe,QAAQ;AAAA,IACvB,kBAAkB,QAAQ;AAAA,IAC1B,UAAU,QAAQ;AAAA,IAClB,wBAAwB,gCAAgC,MAAM;AAAA,IAC9D,mBAAmB,2BAA2B,MAAM;AAAA,IACpD,eAAe,uBAAuB,MAAM;AAAA,EAC9C;AACF;AAKO,SAAS,WAAW,WAAuC;AAChE,MAAI,UAAU,WAAW,EAAG,QAAO,WAAW;AAC9C,MAAI,UAAU,WAAW,EAAG,QAAO,UAAU,CAAC;AAE9C,QAAM,SAAS,WAAW;AAE1B,aAAW,SAAS,WAAW;AAC7B,WAAO,gBAAgB,MAAM;AAC7B,WAAO,cAAc,MAAM;AAC3B,WAAO,gBAAgB,MAAM;AAC7B,WAAO,gBAAgB,MAAM;AAG7B,QACE,CAAC,OAAO,mBACR,MAAM,kBAAkB,OAAO,iBAC/B;AACA,aAAO,kBAAkB,MAAM;AAAA,IACjC;AACA,QACE,CAAC,OAAO,kBACR,MAAM,iBAAiB,OAAO,gBAC9B;AACA,aAAO,iBAAiB,MAAM;AAAA,IAChC;AAGA,aAAS,IAAI,GAAG,IAAI,IAAI,KAAK;AAC3B,aAAO,mBAAmB,CAAC,KAAK,MAAM,mBAAmB,CAAC;AAAA,IAC5D;AAGA,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,YAAY,GAAG;AAC9D,aAAO,aAAa,IAAI,KAAK,OAAO,aAAa,IAAI,KAAK,KAAK;AAAA,IACjE;AAGA,eAAW,UAAU,MAAM,SAAS;AAClC,YAAM,WAAW,OAAO,QAAQ;AAAA,QAC9B,CAAC,MAAM,EAAE,MAAM,YAAY,MAAM,OAAO,MAAM,YAAY;AAAA,MAC5D;AACA,UAAI,UAAU;AACZ,iBAAS,WAAW,OAAO;AAC3B,iBAAS,cAAc,OAAO;AAC9B,iBAAS,gBAAgB,OAAO;AAChC,YAAI,OAAO,iBAAiB,SAAS,gBAAgB;AACnD,mBAAS,iBAAiB,OAAO;AAAA,QACnC;AAAA,MACF,OAAO;AACL,eAAO,QAAQ,KAAK,EAAE,GAAG,OAAO,CAAC;AAAA,MACnC;AAAA,IACF;AAGA,eAAW,MAAM,MAAM,WAAW;AAChC,YAAM,WAAW,OAAO,UAAU;AAAA,QAChC,CAAC,MAAM,EAAE,cAAc,GAAG;AAAA,MAC5B;AACA,UAAI,UAAU;AACZ,iBAAS,SAAS,GAAG;AACrB,iBAAS,WAAW,GAAG;AACvB,iBAAS,aAAa,GAAG;AAAA,MAC3B,OAAO;AACL,eAAO,UAAU,KAAK,EAAE,GAAG,GAAG,CAAC;AAAA,MACjC;AAAA,IACF;AAGA,eAAW,OAAO,MAAM,aAAa;AACnC,YAAM,WAAW,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,IAAI;AACnE,UAAI,UAAU;AACZ,iBAAS,WAAW,IAAI;AACxB,iBAAS,gBAAgB,IAAI;AAAA,MAC/B,OAAO;AACL,eAAO,YAAY,KAAK,EAAE,GAAG,IAAI,CAAC;AAAA,MACpC;AAAA,IACF;AAGA,WAAO,QAAQ,qBAAqB,MAAM,QAAQ;AAClD,WAAO,QAAQ,qBAAqB,MAAM,QAAQ;AAClD,WAAO,QAAQ,aAAa,MAAM,QAAQ;AAC1C,eAAW,MAAM,MAAM,QAAQ,UAAU;AACvC,YAAM,WAAW,OAAO,QAAQ,SAAS,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;AACvE,UAAI,UAAU;AACZ,iBAAS,eAAe,GAAG;AAC3B,mBAAW,UAAU,GAAG,SAAS;AAC/B,cAAI,CAAC,SAAS,QAAQ,SAAS,MAAM,GAAG;AACtC,qBAAS,QAAQ,KAAK,MAAM;AAAA,UAC9B;AAAA,QACF;AAAA,MACF,OAAO;AACL,eAAO,QAAQ,SAAS,KAAK,EAAE,GAAG,IAAI,SAAS,CAAC,GAAG,GAAG,OAAO,EAAE,CAAC;AAAA,MAClE;AAAA,IACF;AAGA,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,aAAO,aAAa,oBAAoB,CAAC,KACvC,MAAM,aAAa,oBAAoB,CAAC;AAAA,IAC5C;AAGA,eAAW,MAAM,MAAM,OAAO,aAAa;AACzC,YAAM,WAAW,OAAO,OAAO,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;AACzE,UAAI,UAAU;AACZ,iBAAS,WAAW,GAAG;AACvB,iBAAS,cAAc,GAAG;AAC1B,iBAAS,gBAAgB,GAAG;AAAA,MAC9B,OAAO;AACL,eAAO,OAAO,YAAY,KAAK,EAAE,GAAG,GAAG,CAAC;AAAA,MAC1C;AAAA,IACF;AACA,eAAW,MAAM,MAAM,OAAO,iBAAiB;AAC7C,YAAM,WAAW,OAAO,OAAO,gBAAgB;AAAA,QAC7C,CAAC,MAAM,EAAE,SAAS,GAAG;AAAA,MACvB;AACA,UAAI,UAAU;AACZ,iBAAS,YAAY,GAAG;AAAA,MAC1B,OAAO;AACL,eAAO,OAAO,gBAAgB,KAAK,EAAE,GAAG,GAAG,CAAC;AAAA,MAC9C;AAAA,IACF;AAGA,eAAW,MAAM,MAAM,cAAc,WAAW;AAC9C,YAAM,WAAW,OAAO,cAAc,UAAU;AAAA,QAC9C,CAAC,MAAM,EAAE,SAAS,GAAG;AAAA,MACvB;AACA,UAAI,UAAU;AACZ,iBAAS,WAAW,GAAG;AAAA,MACzB,OAAO;AACL,eAAO,cAAc,UAAU,KAAK,EAAE,GAAG,GAAG,CAAC;AAAA,MAC/C;AAAA,IACF;AACA,eAAW,MAAM,MAAM,cAAc,uBAAuB;AAC1D,YAAM,WAAW,OAAO,cAAc,sBAAsB;AAAA,QAC1D,CAAC,MAAM,EAAE,SAAS,GAAG;AAAA,MACvB;AACA,UAAI,UAAU;AACZ,iBAAS,gBAAgB,GAAG;AAC5B,iBAAS,cAAc,KAAK,IAAI,SAAS,aAAa,GAAG,WAAW;AAAA,MACtE,OAAO;AACL,eAAO,cAAc,sBAAsB,KAAK,EAAE,GAAG,GAAG,CAAC;AAAA,MAC3D;AAAA,IACF;AAGA,eAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,MAAM,aAAa,gBAAgB,GAAG;AAC/E,aAAO,aAAa,iBAAiB,IAAI,KACtC,OAAO,aAAa,iBAAiB,IAAI,KAAK,KAAK;AAAA,IACxD;AACA,WAAO,aAAa,oBAAoB,MAAM,aAAa;AAE3D,WAAO,cAAc,KAAK,GAAG,MAAM,aAAa;AAAA,EAClD;AAGA,MAAI,aAAyB,EAAE,MAAM,IAAI,OAAO,EAAE;AAClD,aAAW,CAAC,MAAM,KAAK,KAAK,OAAO,QAAQ,OAAO,YAAY,GAAG;AAC/D,QAAI,QAAQ,WAAW,OAAO;AAC5B,mBAAa,EAAE,MAAM,MAAM;AAAA,IAC7B;AAAA,EACF;AACA,SAAO,aAAa;AAGpB,SAAO,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;AACnD,SAAO,UAAU;AAAA,IACf,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE;AAAA,EAC/C;AACA,SAAO,YAAY,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY;AACjE,SAAO,cAAc,OAAO,YAAY,MAAM,GAAG,EAAE;AAGnD,QAAM,YAAY,UAAU;AAC5B,SAAO,QAAQ,qBAAqB;AACpC,SAAO,QAAQ,qBAAqB;AACpC,SAAO,QAAQ,aAAa;AAC5B,SAAO,QAAQ,SAAS,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,EAAE,WAAW;AACpE,SAAO,QAAQ,WAAW,OAAO,QAAQ,SAAS,MAAM,GAAG,EAAE;AAG7D,QAAM,sBAAsB,OAAO,aAAa,oBAAoB;AAAA,IAClE,CAAC,GAAG,MAAM,IAAI;AAAA,IACd;AAAA,EACF;AACA,MAAI,sBAAsB,GAAG;AAC3B,WAAO,aAAa,kBACjB,OAAO,aAAa,oBAAoB,CAAC,IACxC,OAAO,aAAa,oBAAoB,CAAC,KAC3C;AAAA,EACJ;AAGA,SAAO,OAAO,YAAY,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AACrE,SAAO,OAAO,gBAAgB,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAGzE,MAAI,aAAa;AACjB,aAAW,SAAS,OAAO,OAAO,iBAAiB;AACjD,kBAAc,MAAM;AACpB,UAAM,WAAW;AAAA,EACnB;AAGA,SAAO,cAAc,UAAU,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;AACnE,SAAO,cAAc,YAAY,OAAO,cAAc,UAAU,MAAM,GAAG,EAAE;AAC3E,SAAO,cAAc,sBAAsB;AAAA,IACzC,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE;AAAA,EAC/B;AACA,SAAO,cAAc,wBACnB,OAAO,cAAc,sBAAsB,MAAM,GAAG,EAAE;AAGxD,SAAO,aAAa,oBAAoB;AAGxC,SAAO,cAAc,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,QAAQ,CAAC;AAEvE,eAAa,QAAQ,SAAS;AAG9B,QAAM,kBAAkB,oBAAI,IAAwC;AACpE,aAAW,SAAS,WAAW;AAC7B,eAAW,WAAW,MAAM,6BAA6B;AACvD,YAAM,MAAM,GAAG,QAAQ,MAAM,YAAY,CAAC,MAAM,QAAQ,SAAS;AACjE,YAAM,WAAW,gBAAgB,IAAI,GAAG;AACxC,UAAI,UAAU;AACZ,iBAAS,cAAc,QAAQ;AAC/B,iBAAS,gBAAgB,QAAQ;AACjC,iBAAS,WAAW,QAAQ;AAC5B,iBAAS,aAAa,QAAQ;AAAA,MAChC,OAAO;AACL,wBAAgB,IAAI,KAAK,EAAE,GAAG,QAAQ,CAAC;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AACA,SAAO,8BAA8B,MAAM,KAAK,gBAAgB,OAAO,CAAC,EACrE,KAAK,CAAC,GAAG,MAAM;AACd,UAAM,SAAS,EAAE,aAAa,EAAE;AAChC,UAAM,SAAS,EAAE,aAAa,EAAE;AAChC,WAAO,SAAS;AAAA,EAClB,CAAC,EACA,MAAM,GAAG,EAAE;AAiBd,SAAO;AACT;AAEA,SAAS,aAAa,QAAqB,WAAgC;AACzE,QAAM,cAAc,UAAU,OAAO,CAAC,UAAU,MAAM,SAAS;AAC/D,MAAI,YAAY,WAAW,EAAG;AAE9B,QAAM,gBAA4B,CAAC;AACnC,QAAM,YAAY,oBAAI,IAA2B;AACjD,QAAM,eAAe,oBAAI,IAA8B;AACvD,QAAM,WAAW,oBAAI,IAA0B;AAC/C,MAAI,eAAe;AACnB,MAAI,aAAa;AACjB,MAAI,oBAAoB;AAExB,aAAW,SAAS,aAAa;AAC/B,UAAM,UAAU,MAAM;AACtB,oBAAgB,QAAQ;AACxB,kBAAc,QAAQ;AACtB,yBAAqB,QAAQ;AAC7B,kBAAc,KAAK,GAAG,QAAQ,aAAa;AAE3C,eAAW,UAAU,MAAM,iBAAiB,CAAC,GAAG;AAC9C,YAAM,MAAM,OAAO,MAAM,YAAY;AACrC,YAAM,WAAW,UAAU,IAAI,GAAG;AAClC,UAAI,UAAU;AACZ,iBAAS,WAAW,OAAO;AAC3B,iBAAS,cAAc,OAAO;AAC9B,iBAAS,eAAe,oBAAoB,SAAS,SAAS,SAAS,UAAU;AAAA,MACnF,OAAO;AACL,kBAAU,IAAI,KAAK,EAAE,GAAG,OAAO,CAAC;AAAA,MAClC;AAAA,IACF;AAEA,eAAW,aAAa,MAAM,oBAAoB,CAAC,GAAG;AACpD,YAAM,MAAM,UAAU,WAAW,GAAG,UAAU,QAAQ,MAAM,UAAU,IAAI,KAAK,UAAU;AACzF,YAAM,cAAc,UAAU,WAC1B,GAAG,UAAU,QAAQ,MAAM,UAAU,IAAI,KACzC,UAAU,eAAe,UAAU;AACvC,YAAM,WAAW,aAAa,IAAI,GAAG;AACrC,UAAI,UAAU;AACZ,iBAAS,WAAW,UAAU;AAC9B,iBAAS,WAAW,UAAU;AAC9B,iBAAS,cAAc,UAAU;AACjC,iBAAS,eAAe,oBAAoB,SAAS,SAAS,SAAS,UAAU;AACjF,iBAAS,cAAc;AACvB,iBAAS,eACP,IAAI,KAAK,UAAU,YAAY,IAAI,IAAI,KAAK,SAAS,YAAY,IAC7D,UAAU,eACV,SAAS;AACf,iBAAS,aAAa,SAAS,UAAU,MAAM,SAAS,eAAe;AAAA,MACzE,OAAO;AACL,qBAAa,IAAI,KAAK,EAAE,GAAG,WAAW,YAAY,CAAC;AAAA,MACrD;AAAA,IACF;AAEA,eAAW,SAAS,MAAM,YAAY,CAAC,GAAG;AACxC,YAAM,WAAW,SAAS,IAAI,MAAM,IAAI;AACxC,UAAI,UAAU;AACZ,iBAAS,WAAW,MAAM;AAC1B,iBAAS,cAAc,MAAM;AAC7B,iBAAS,eAAe,oBAAoB,SAAS,SAAS,SAAS,UAAU;AAAA,MACnF,OAAO;AACL,iBAAS,IAAI,MAAM,MAAM,EAAE,GAAG,MAAM,CAAC;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAEA,SAAO,YAAY;AAAA,IACjB;AAAA,IACA;AAAA,IACA,cAAc,oBAAoB,cAAc,UAAU;AAAA,IAC1D;AAAA,IACA,eAAe,cAAc,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,EAAE;AAAA,EAChF;AACA,SAAO,gBAAgB,MAAM,KAAK,UAAU,OAAO,CAAC,EACjD,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY;AACjD,SAAO,mBAAmB,MAAM,KAAK,aAAa,OAAO,CAAC,EACvD,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY;AACjD,SAAO,WAAW,MAAM,KAAK,SAAS,OAAO,CAAC,EAC3C,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAChD;AAEA,SAAS,oBAAoB,MAAc,OAAuB;AAChE,SAAO,QAAQ,IAAK,OAAO,QAAS,MAAM;AAC5C;;;AOzjBO,SAAS,oBAAoB,SAA4C;AAE9E,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,gBAAgB;AAAA,EACzB;AAGA,QAAM,cAAc,oBAAoB,OAAO;AAC/C,QAAM,cAAc,oBAAoB,OAAO;AAG/C,MAAI,YAAY,SAAS,GAAG;AAC1B,WAAO,gBAAgB;AAAA,EACzB;AAGA,QAAM,eAAe,sBAAsB,aAAa,WAAW;AAGnE,QAAM,kBAAkB,aAAa,OAAO,CAAC,MAAM,EAAE,iBAAiB,EAAE;AAGxE,QAAM,mBAAmB,gBAAgB;AAAA,IACvC,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY;AAAA,IAChC;AAAA,EACF;AACA,QAAM,wBAAwB,IAAI,mBAAmB,YAAY;AAGjE,QAAM,YAAY,gBAAgB;AAClC,QAAM,YAAY,cAAc,IAAI,SAAS,aAAa,IAAI,WAAW;AAEzE,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,oBAAoB,SAAmD;AAC9E,QAAM,MAAM,oBAAI,IAAyB;AAEzC,aAAW,UAAU,SAAS;AAC5B,UAAM,YAAY,GAAG,OAAO,MAAM,KAAK,OAAO,KAAK;AACnD,QAAI,CAAC,IAAI,IAAI,SAAS,GAAG;AACvB,UAAI,IAAI,WAAW,oBAAI,IAAI,CAAC;AAAA,IAC9B;AACA,UAAM,QAAQ,IAAI,IAAI,SAAS;AAC/B,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,IAAI,KAAK,IAAI;AAAA,IACrB;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,oBAAoB,SAA2D;AACtF,QAAM,MAAM,oBAAI,IAAiC;AAEjD,aAAW,UAAU,SAAS;AAC5B,UAAM,YAAY,GAAG,OAAO,MAAM,KAAK,OAAO,KAAK;AACnD,eAAW,QAAQ,OAAO,OAAO;AAC/B,UAAI,CAAC,IAAI,IAAI,KAAK,IAAI,GAAG;AACvB,YAAI,IAAI,KAAK,MAAM,oBAAI,IAAI,CAAC;AAAA,MAC9B;AACA,YAAM,UAAU,IAAI,IAAI,KAAK,IAAI;AACjC,cAAQ,IAAI,YAAY,QAAQ,IAAI,SAAS,KAAK,KAAK,CAAC;AAAA,IAC1D;AAAA,EACF;AAEA,SAAO;AACT;AAKA,SAAS,sBACP,aACA,aACkB;AAClB,QAAM,SAA2B,CAAC;AAElC,aAAW,CAAC,WAAW,KAAK,KAAK,aAAa;AAC5C,UAAM,cAAwB,CAAC;AAC/B,UAAM,gBAA0B,CAAC;AAEjC,eAAW,YAAY,OAAO;AAC5B,YAAM,UAAU,YAAY,IAAI,QAAQ;AACxC,YAAM,cAAc,QAAQ;AAG5B,UAAI,gBAAgB,GAAG;AACrB,oBAAY,KAAK,QAAQ;AACzB;AAAA,MACF;AAGA,YAAM,gBAAgB,QAAQ,IAAI,SAAS,KAAK;AAChD,YAAM,eAAe,MAAM,KAAK,QAAQ,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC;AAC3E,UAAI,gBAAgB,eAAe,KAAK;AACtC,sBAAc,KAAK,QAAQ;AAAA,MAC7B;AAAA,IACF;AAGA,UAAM,kBACF,YAAY,SAAS,IAAI,cAAc,UAAU,YAAY,OAAQ;AAEzE,UAAM,CAAC,MAAM,KAAK,IAAI,eAAe,SAAS;AAC9C,WAAO,KAAK;AAAA,MACV;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKA,SAAS,eAAe,WAAqC;AAC3D,QAAM,QAAQ,UAAU,MAAM,eAAe;AAC7C,MAAI,OAAO;AACT,WAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC;AAAA,EAC5B;AACA,SAAO,CAAC,WAAW,EAAE;AACvB;AAEA,SAAS,kBAAqC;AAC5C,SAAO;AAAA,IACL,WAAW;AAAA,IACX,iBAAiB,CAAC;AAAA,IAClB,uBAAuB;AAAA,IACvB,WAAW;AAAA,EACb;AACF;;;AClJO,SAAS,mBAAmB,SAA2C;AAC5E,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,eAAe;AAAA,EACxB;AAGA,QAAM,YAAY,oBAAI,IAAqE;AAE3F,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAMC,QAAO,UAAU,IAAI,KAAK,IAAI,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,aAAa,EAAE;AAChF,MAAAA,MAAK,SAAS,KAAK;AACnB,MAAAA,MAAK,WAAW,KAAK;AACrB,MAAAA,MAAK;AACL,gBAAU,IAAI,KAAK,MAAMA,KAAI;AAAA,IAC/B;AAAA,EACF;AAGA,QAAM,gBAA6B,MAAM,KAAK,UAAU,QAAQ,CAAC,EAC9D,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,SAAS,YAAY,CAAC,OAAO;AAAA,IACjD;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,QAAQ,IAAI,UAAU,QAAQ;AAAA,IACzC;AAAA,IACA,YAAY,UAAU,KAAK,IAAI,OAAO,CAAC,IAAI;AAAA,EAC7C,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,EACxC,MAAM,GAAG,EAAE;AAGd,QAAM,WAAW,oBAAI,IAAoE;AAEzF,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,MAAMC,iBAAgB,KAAK,IAAI;AACrC,YAAMD,QAAO,SAAS,IAAI,GAAG,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,OAAO,oBAAI,IAAI,EAAE;AAC3E,MAAAA,MAAK,SAAS,KAAK;AACnB,MAAAA,MAAK,WAAW,KAAK;AACrB,MAAAA,MAAK,MAAM,IAAI,KAAK,IAAI;AACxB,eAAS,IAAI,KAAKA,KAAI;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,qBAAuC,MAAM,KAAK,SAAS,QAAQ,CAAC,EACvE,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,SAAS,MAAM,CAAC,OAAO;AAAA,IAC3C;AAAA,IACA,WAAW,QAAQ,IAAI,UAAU,QAAQ;AAAA,IACzC,cAAc,QAAQ;AAAA,IACtB,WAAW,MAAM;AAAA,EACnB,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS,EACxC,MAAM,GAAG,EAAE;AAGd,QAAM,gBAAgB,QAAQ,OAAO,CAAC,MAAM,mBAAmB,KAAK,EAAE,OAAO,CAAC,EAAE;AAChF,QAAM,aAAa,gBAAgB,QAAQ;AAG3C,QAAM,aAAa,QAAQ,OAAO,CAAC,MAAM,kBAAkB,KAAK,EAAE,OAAO,CAAC,EAAE;AAC5E,QAAM,gBAAgB,aAAa,QAAQ;AAG3C,QAAM,eACJ,cAAc,SAAS,IACnB,cAAc,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC,IAAI,cAAc,SACvE;AAEN,QAAM,iBAAiB,KAAK;AAAA,IAC1B;AAAA,IACA,KAAK,MAAM,OAAO,eAAe,KAAK,aAAa,MAAM,gBAAgB,GAAG;AAAA,EAC9E;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAY,KAAK,MAAM,aAAa,GAAG,IAAI;AAAA,IAC3C,eAAe,KAAK,MAAM,gBAAgB,GAAG,IAAI;AAAA,IACjD;AAAA,EACF;AACF;AAKA,SAASC,iBAAgB,UAA0B;AACjD,QAAM,QAAQ,SAAS,MAAM,GAAG;AAChC,SAAO,MAAM,SAAS,IAAI,MAAM,CAAC,IAAI;AACvC;AAEA,SAAS,iBAAmC;AAC1C,SAAO;AAAA,IACL,eAAe,CAAC;AAAA,IAChB,oBAAoB,CAAC;AAAA,IACrB,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,gBAAgB;AAAA,EAClB;AACF;;;ACnGO,SAAS,sBAAsB,SAA8C;AAClF,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,kBAAkB;AAAA,EAC3B;AAEA,MAAI,mBAAmB;AACvB,MAAI,sBAAsB;AAC1B,MAAI,iBAAiB;AAErB,QAAM,aAAa,oBAAI,IAA+C;AACtE,QAAM,WAAW,YAAY;AAE7B,aAAW,UAAU,SAAS;AAC5B,UAAM,OAAO,OAAO,KAAK,SAAS;AAClC,UAAM,MAAM,OAAO,KAAK,OAAO;AAC/B,UAAM,UAAU,WAAW,OAAO,IAAI;AAGtC,QAAI,QAAQ,MAAM,OAAO,GAAG;AAC1B;AAAA,IACF;AAGA,QAAI,QAAQ,KAAK,OAAO,GAAG;AACzB;AAAA,IACF;AAGA,QAAI,QAAQ,KAAK,QAAQ,GAAG;AAC1B;AAAA,IACF;AAGA,UAAM,UAAU,SAAS,IAAI,OAAO;AACpC,QAAI,SAAS;AACX,YAAM,QAAQ,WAAW,IAAI,OAAO,KAAK,EAAE,MAAM,SAAS,SAAS,EAAE;AACrE,YAAM;AACN,iBAAW,IAAI,SAAS,KAAK;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,iBAAkC,MAAM,KAAK,WAAW,QAAQ,CAAC,EACpE,IAAI,CAAC,CAAC,MAAM,EAAE,MAAM,SAAAC,SAAQ,CAAC,OAAO,EAAE,MAAM,aAAa,MAAM,SAAAA,SAAQ,EAAE,EACzE,KAAK,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,OAAO;AAGvC,QAAM,eAAe,eAAe,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,SAAS,CAAC;AACzE,QAAM,gBAAgB,mBAAmB,sBAAsB,iBAAiB;AAChF,QAAM,eAAe,gBAAgB,QAAQ;AAG7C,QAAM,kBAAmB,mBAAmB,QAAQ,SAAU;AAC9D,QAAM,qBAAsB,sBAAsB,QAAQ,SAAU;AACpE,QAAM,gBAAiB,iBAAiB,QAAQ,SAAU;AAC1D,QAAM,iBAAiB,eAAe,SAAS,IAAI,IAAI,KAAK;AAE5D,QAAM,gBAAgB,KAAK,MAAM,kBAAkB,qBAAqB,gBAAgB,aAAa;AAErG,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,KAAK,MAAM,eAAe,GAAI,IAAI;AAAA,EAClD;AACF;AAEA,SAAS,oBAAyC;AAChD,SAAO;AAAA,IACL,kBAAkB;AAAA,IAClB,qBAAqB;AAAA,IACrB,gBAAgB;AAAA,IAChB,gBAAgB,CAAC;AAAA,IACjB,eAAe;AAAA,IACf,cAAc;AAAA,EAChB;AACF;AAKA,SAAS,cAAmC;AAC1C,QAAM,WAAW,oBAAI,IAAoB;AAGzC,QAAM,YAAqC;AAAA,IACzC,CAAC,cAAc,cAAI;AAAA,IACnB,CAAC,cAAc,cAAI;AAAA,IAAG,CAAC,cAAc,cAAI;AAAA,IAAG,CAAC,cAAc,cAAI;AAAA,IAC/D,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAClE,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAClE,CAAC,cAAc,oBAAK;AAAA,IACpB,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAClE,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,EACpE;AAGA,QAAM,YAAqC;AAAA,IACzC,CAAC,cAAc,cAAI;AAAA,IACnB,CAAC,cAAc,cAAI;AAAA,IAAG,CAAC,cAAc,cAAI;AAAA,IAAG,CAAC,cAAc,cAAI;AAAA,IAC/D,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAClE,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAClE,CAAC,cAAc,oBAAK;AAAA,IACpB,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,EACpE;AAGA,QAAM,YAAqC;AAAA,IACzC,CAAC,cAAc,cAAI;AAAA,IACnB,CAAC,cAAc,cAAI;AAAA,IAAG,CAAC,cAAc,cAAI;AAAA,IAAG,CAAC,cAAc,cAAI;AAAA,IAC/D,CAAC,cAAc,oBAAK;AAAA,IACpB,CAAC,cAAc,oBAAK;AAAA,IACpB,CAAC,cAAc,oBAAK;AAAA,IACpB,CAAC,cAAc,oBAAK;AAAA,IACpB,CAAC,cAAc,oBAAK;AAAA,IAAG,CAAC,cAAc,oBAAK;AAAA,EAC7C;AAEA,GAAC,GAAG,WAAW,GAAG,WAAW,GAAG,SAAS,EAAE,QAAQ,CAAC,CAAC,MAAM,IAAI,MAAM;AACnE,aAAS,IAAI,MAAM,IAAI;AAAA,EACzB,CAAC;AAED,SAAO;AACT;AAKA,SAAS,WAAW,MAAoB;AACtC,SAAO,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AACxC;;;AC7HO,SAAS,0BAA0B,SAAkD;AAC1F,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,sBAAsB;AAAA,EAC/B;AAGA,QAAM,mBAAmB,oBAAI,IAM1B;AAEH,aAAW,UAAU,SAAS;AAC5B,UAAM,MAAM,OAAO,MAAM,YAAY;AACrC,UAAM,WAAW,iBAAiB,IAAI,GAAG;AAEzC,QAAI,CAAC,UAAU;AACb,uBAAiB,IAAI,KAAK;AAAA,QACxB,MAAM,OAAO;AAAA,QACb,OAAO,OAAO;AAAA,QACd,UAAU,OAAO;AAAA,QACjB,WAAW,OAAO;AAAA,QAClB,cAAc;AAAA,MAChB,CAAC;AAAA,IACH,OAAO;AACL,UAAI,OAAO,OAAO,SAAS,UAAU;AACnC,iBAAS,WAAW,OAAO;AAAA,MAC7B;AACA,UAAI,OAAO,OAAO,SAAS,WAAW;AACpC,iBAAS,YAAY,OAAO;AAAA,MAC9B;AACA,eAAS;AAAA,IACX;AAAA,EACF;AAGA,QAAM,MAAM,oBAAI,KAAK;AACrB,QAAM,SAAyB,CAAC;AAChC,QAAM,aAA6B,CAAC;AACpC,QAAM,UAA0B,CAAC;AACjC,QAAM,OAAuB,CAAC;AAC9B,QAAM,aAA6B,CAAC;AAEpC,aAAW,CAAC,EAAE,MAAM,KAAK,kBAAkB;AACzC,UAAM,gBAAgB,KAAK,OAAO,IAAI,QAAQ,IAAI,OAAO,SAAS,QAAQ,MAAM,MAAO,KAAK,KAAK,GAAG;AACpG,UAAM,iBAAiB,KAAK,OAAO,IAAI,QAAQ,IAAI,OAAO,UAAU,QAAQ,MAAM,MAAO,KAAK,KAAK,GAAG;AAEtG,UAAM,SAAuB;AAAA,MAC3B,MAAM,OAAO;AAAA,MACb,OAAO,OAAO;AAAA,MACd,gBAAgB,OAAO;AAAA,MACvB,qBAAqB;AAAA,MACrB,cAAc,OAAO;AAAA,IACvB;AAGA,QAAI,iBAAiB,IAAI;AACvB,iBAAW,KAAK,MAAM;AAAA,IACxB;AAGA,QAAI,gBAAgB,IAAI;AACtB,aAAO,KAAK,MAAM;AAAA,IACpB,WAAW,gBAAgB,IAAI;AAC7B,iBAAW,KAAK,MAAM;AAAA,IACxB,WAAW,gBAAgB,KAAK;AAC9B,cAAQ,KAAK,MAAM;AAAA,IACrB,OAAO;AACL,WAAK,KAAK,MAAM;AAAA,IAClB;AAAA,EACF;AAGA,QAAM,eAAe,iBAAiB;AACtC,QAAM,YAAY,eAAe,IAAI,KAAK,SAAS,eAAe;AAClE,QAAM,gBAAgB,eAAe,IAAI,OAAO,SAAS,eAAe;AACxE,QAAM,aAAa,eAAe,IAAI,WAAW,SAAS,eAAe;AAEzE,SAAO;AAAA,IACL,QAAQ,OAAO,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY;AAAA,IAC7D,YAAY,WAAW,KAAK,CAAC,GAAG,MAAM,EAAE,sBAAsB,EAAE,mBAAmB;AAAA,IACnF,SAAS,QAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,sBAAsB,EAAE,mBAAmB;AAAA,IAC7E,MAAM,KAAK,KAAK,CAAC,GAAG,MAAM,EAAE,eAAe,EAAE,YAAY;AAAA,IACzD,YAAY,WAAW,KAAK,CAAC,GAAG,MAAM,EAAE,sBAAsB,EAAE,mBAAmB;AAAA,IACnF,WAAW,KAAK,MAAM,YAAY,GAAI,IAAI;AAAA,IAC1C,eAAe,KAAK,MAAM,gBAAgB,GAAI,IAAI;AAAA,IAClD,YAAY,KAAK,MAAM,aAAa,GAAI,IAAI;AAAA,EAC9C;AACF;AAEA,SAAS,wBAAiD;AACxD,SAAO;AAAA,IACL,QAAQ,CAAC;AAAA,IACT,YAAY,CAAC;AAAA,IACb,SAAS,CAAC;AAAA,IACV,MAAM,CAAC;AAAA,IACP,YAAY,CAAC;AAAA,IACb,WAAW;AAAA,IACX,eAAe;AAAA,IACf,YAAY;AAAA,EACd;AACF;;;AC9GA,IAAM,uBAAuB;AAKtB,SAAS,+BAA+B,SAAuD;AACpG,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,mBAAmB;AAAA,EAC5B;AAGA,QAAM,kBAAkB,oBAAI,IAAoB;AAChD,aAAW,UAAU,SAAS;AAC5B,WAAO,MAAM,QAAQ,OAAK;AACxB,sBAAgB,IAAI,EAAE,OAAO,gBAAgB,IAAI,EAAE,IAAI,KAAK,KAAK,CAAC;AAAA,IACpE,CAAC;AAAA,EACH;AAGA,QAAM,gBAAgB,oBAAoB,SAAS,eAAe;AAGlE,QAAM,kBAAkB,sBAAsB,OAAO;AAGrD,QAAM,cAAc,cAAc,SAAS,IACvC,cAAc,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,UAAU,CAAC,IAAI,cAAc,SACtE;AACJ,QAAM,gBAAgB,KAAK,IAAI,KAAK,KAAK,MAAM,cAAc,GAAG,CAAC;AAEjE,SAAO;AAAA,IACL;AAAA,IACA,eAAe,CAAC;AAAA;AAAA,IAChB;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,oBACP,SACA,iBACY;AACZ,QAAM,gBAAgB,oBAAI,IAA6D;AAEvF,aAAW,UAAU,SAAS;AAE5B,UAAM,QAAQ,OAAO,MAClB,IAAI,OAAK,EAAE,IAAI,EACf,MAAM,GAAG,oBAAoB,EAC7B,KAAK;AAGR,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,eAAS,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACzC,cAAM,UAAU,GAAG,MAAM,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC;AACzC,cAAM,WAAW,cAAc,IAAI,OAAO,KAAK;AAAA,UAC7C,OAAO;AAAA,UACP,OAAO,MAAM,CAAC;AAAA,UACd,OAAO,MAAM,CAAC;AAAA,QAChB;AACA,iBAAS;AACT,sBAAc,IAAI,SAAS,QAAQ;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,gBAA4B,MAAM,KAAK,cAAc,OAAO,CAAC,EAChE,IAAI,CAAC,EAAE,OAAO,OAAO,MAAM,MAAM;AAChC,UAAM,SAAS,gBAAgB,IAAI,KAAK,KAAK;AAC7C,UAAM,SAAS,gBAAgB,IAAI,KAAK,KAAK;AAC7C,UAAM,WAAW,QAAQ,KAAK,IAAI,QAAQ,MAAM;AAEhD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,cAAc;AAAA,MACd;AAAA,IACF;AAAA,EACF,CAAC,EACA,OAAO,OAAK,EAAE,gBAAgB,CAAC,EAC/B,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,EACtC,MAAM,GAAG,EAAE;AAEd,SAAO;AACT;AAKA,SAAS,sBAAsB,SAAuC;AAEpE,QAAM,cAAc,oBAAI,IAAiC;AAEzD,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,UAAI,CAAC,YAAY,IAAI,KAAK,IAAI,GAAG;AAC/B,oBAAY,IAAI,KAAK,MAAM,oBAAI,IAAI,CAAC;AAAA,MACtC;AACA,YAAM,YAAY,YAAY,IAAI,KAAK,IAAI;AAC3C,YAAM,QAAQ,OAAO,MAAM,YAAY;AACvC,gBAAU,IAAI,QAAQ,UAAU,IAAI,KAAK,KAAK,KAAK,CAAC;AAAA,IACtD;AAAA,EACF;AAGA,QAAM,gBAAgB,oBAAI,IAKvB;AAEH,aAAW,CAAC,UAAU,SAAS,KAAK,aAAa;AAC/C,QAAI,UAAU,QAAQ,GAAG;AAEvB,YAAM,gBAAgB,MAAM,KAAK,UAAU,QAAQ,CAAC,EACjD,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,EAC1B,MAAM,GAAG,CAAC;AAEb,UAAI,cAAc,WAAW,GAAG;AAE9B,cAAM,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK;AACjE,cAAM,UAAU,GAAG,EAAE,MAAM,EAAE;AAE7B,cAAM,WAAW,cAAc,IAAI,OAAO,KAAK;AAAA,UAC7C,SAAS;AAAA,UACT,SAAS;AAAA,UACT,OAAO,oBAAI,IAAY;AAAA,UACvB,OAAO;AAAA,QACT;AAEA,iBAAS,MAAM,IAAI,QAAQ;AAC3B,iBAAS;AACT,sBAAc,IAAI,SAAS,QAAQ;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AAGA,QAAM,kBAAgC,MAAM,KAAK,cAAc,OAAO,CAAC,EACpE,OAAO,OAAK,EAAE,MAAM,QAAQ,CAAC,EAC7B,IAAI,QAAM;AAAA,IACT,SAAS,EAAE;AAAA,IACX,SAAS,EAAE;AAAA,IACX,aAAa,MAAM,KAAK,EAAE,KAAK;AAAA,IAC/B,oBAAoB,EAAE;AAAA,EACxB,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,qBAAqB,EAAE,kBAAkB,EAC1D,MAAM,GAAG,EAAE;AAEd,SAAO;AACT;AAEA,SAAS,qBAAmD;AAC1D,SAAO;AAAA,IACL,eAAe,CAAC;AAAA,IAChB,eAAe,CAAC;AAAA,IAChB,iBAAiB,CAAC;AAAA,IAClB,eAAe;AAAA,EACjB;AACF;;;ACzIO,SAAS,uBAAuB,SAAwC;AAC7E,SAAO;AAAA,IACL,YAAY,oBAAoB,OAAO;AAAA,IACvC,WAAW,mBAAmB,OAAO;AAAA,IACrC,cAAc,sBAAsB,OAAO;AAAA,IAC3C,kBAAkB,0BAA0B,OAAO;AAAA,IACnD,uBAAuB,+BAA+B,OAAO;AAAA,EAC/D;AACF;;;ACnCO,SAAS,oBAAoB,SAAqC;AACvE,MAAI,QAAQ,WAAW,EAAG,QAAO,CAAC;AAElC,QAAM,UAAU,oBAAI,IAA0B;AAE9C,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,UAAI,CAAC,QAAQ,IAAI,KAAK,IAAI,GAAG;AAC3B,gBAAQ,IAAI,KAAK,MAAM;AAAA,UACrB,MAAM,KAAK;AAAA,UACX,SAAS,CAAC;AAAA,UACV,SAAS,oBAAI,IAAI;AAAA,UACjB,YAAY;AAAA,UACZ,cAAc;AAAA,UACd,cAAc,OAAO;AAAA,QACvB,CAAC;AAAA,MACH;AAEA,YAAM,OAAO,QAAQ,IAAI,KAAK,IAAI;AAClC,WAAK,QAAQ,KAAK,MAAM;AACxB,WAAK,QAAQ,IAAI,OAAO,MAAM;AAC9B,WAAK,cAAc,KAAK;AACxB,WAAK,gBAAgB,KAAK;AAC1B,UAAI,OAAO,OAAO,KAAK,cAAc;AACnC,aAAK,eAAe,OAAO;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAwB,CAAC;AAE/B,aAAW,CAAC,MAAM,IAAI,KAAK,SAAS;AAClC,UAAM,aAAa,oBAAoB,IAAI;AAC3C,UAAM,YAAY,mBAAmB,MAAM,QAAQ,MAAM;AACzD,UAAM,eAAe,qBAAqB,MAAM,OAAO;AACvD,UAAM,gBAAgB,uBAAuB,IAAI;AAEjD,UAAM,YACJ,aAAa,MACb,YAAY,MAAM,QACjB,MAAM,gBAAgB,MACvB,gBAAgB,QACf,KAAK,aAAa,MAAM,KAAK;AAEhC,cAAU,KAAK;AAAA,MACb;AAAA,MACA,WAAW,KAAK,IAAI,WAAW,GAAG;AAAA,MAClC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe,oBAAoB,IAAI;AAAA,MACvC,cAAc,KAAK;AAAA,IACrB,CAAC;AAAA,EACH;AAEA,SAAO,UAAU,KAAK,CAAC,GAAG,MAAM,EAAE,YAAY,EAAE,SAAS;AAC3D;AAWA,SAAS,oBAAoB,MAA4B;AACvD,QAAM,cAAc,KAAK;AAEzB,MAAI,cAAc,IAAM,QAAO;AAC/B,MAAI,cAAc,IAAK,QAAO;AAC9B,MAAI,cAAc,IAAK,QAAO;AAC9B,MAAI,cAAc,IAAK,QAAO;AAC9B,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAoB,cAA8B;AAC5E,SAAO,KAAK,QAAQ,SAAS;AAC/B;AAEA,SAAS,qBAAqB,MAAc,SAA4C;AACtF,QAAM,eAAe,CAAC,UAAU,UAAU,aAAa,UAAU,SAAS;AAC1E,QAAM,aAAa,aAAa,KAAK,aAAW,KAAK,SAAS,OAAO,CAAC;AAEtE,MAAI,WAAY,QAAO;AAEvB,QAAM,WAAW,KAAK,QAAQ,sBAAsB,EAAE;AACtD,QAAM,oBAAoB;AAAA,IACxB,GAAG,QAAQ;AAAA,IACX,GAAG,QAAQ;AAAA,IACX,GAAG,QAAQ;AAAA,IACX,GAAG,QAAQ;AAAA,EACb;AAEA,aAAW,YAAY,mBAAmB;AACxC,QAAI,QAAQ,IAAI,QAAQ,GAAG;AACzB,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,MAAM,KAAK,MAAM,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,KAAK,GAAG;AACjD,aAAW,CAAC,QAAQ,KAAK,SAAS;AAChC,QAAI,SAAS,WAAW,GAAG,KAAK,aAAa,KAAK,OAAK,SAAS,SAAS,CAAC,CAAC,GAAG;AAC5E,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,uBAAuB,MAA4B;AAC1D,QAAM,cAAc,KAAK,QAAQ;AAEjC,MAAI,gBAAgB,EAAG,QAAO;AAC9B,MAAI,gBAAgB,EAAG,QAAO;AAC9B,MAAI,gBAAgB,EAAG,QAAO;AAC9B,SAAO;AACT;AAEA,SAAS,oBAAoB,MAA4B;AACvD,QAAM,gBAAgB,oBAAI,IAAoB;AAE9C,aAAW,UAAU,KAAK,SAAS;AACjC,kBAAc,IAAI,OAAO,SAAS,cAAc,IAAI,OAAO,MAAM,KAAK,KAAK,CAAC;AAAA,EAC9E;AAEA,MAAI,YAAY;AAChB,MAAI,aAAa;AAEjB,aAAW,CAAC,QAAQ,KAAK,KAAK,eAAe;AAC3C,QAAI,QAAQ,YAAY;AACtB,mBAAa;AACb,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AACT;;;AC5IA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,QAAAC,aAAY;AACrB,SAAqB,aAAa,gBAAgB;AAClD,SAAS,kBAAkB;AAE3B,eAAsB,kBAAkB,UAA8C;AACpF,QAAM,YAAY,cAAc,QAAQ;AACxC,QAAM,aAAa,oBAAI,IAAsB;AAC7C,QAAM,YAAY,oBAAI,IAAoB;AAE1C,aAAW,QAAQ,UAAU,MAAM,GAAG,GAAG,GAAG;AAC1C,QAAI;AACF,YAAM,UAAU,MAAMD,UAAS,MAAM,OAAO;AAC5C,YAAM,aAAa,cAAc,OAAO;AACxC,YAAM,OAAO,WAAW,UAAU;AAElC,UAAI,CAAC,WAAW,IAAI,IAAI,GAAG;AACzB,mBAAW,IAAI,MAAM,CAAC,CAAC;AAAA,MACzB;AACA,iBAAW,IAAI,IAAI,EAAG,KAAK,KAAK,QAAQ,UAAU,EAAE,EAAE,QAAQ,UAAU,EAAE,CAAC;AAC3E,gBAAU,IAAI,MAAM,QAAQ,MAAM,IAAI,EAAE,MAAM;AAAA,IAChD,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,WAAiC,CAAC;AACxC,aAAW,CAAC,MAAM,KAAK,KAAK,YAAY;AACtC,QAAI,MAAM,SAAS,GAAG;AACpB,YAAM,QAAQ,UAAU,IAAIC,MAAK,UAAU,MAAM,CAAC,CAAC,CAAC,KAAK;AACzD,eAAS,KAAK;AAAA,QACZ;AAAA,QACA,YAAY;AAAA,QACZ;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAEA,QAAM,aAAa,oBAAI,IAAoB;AAC3C,aAAW,WAAW,UAAU;AAC9B,eAAW,QAAQ,QAAQ,OAAO;AAChC,iBAAW,IAAI,OAAO,WAAW,IAAI,IAAI,KAAK,KAAK,QAAQ,KAAK;AAAA,IAClE;AAAA,EACF;AAEA,QAAM,eAAuC,MAAM,KAAK,WAAW,QAAQ,CAAC,EACzE,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,EAAE,MAAM,MAAM,EAAE,EACxC,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAEnC,SAAO;AAAA,IACL,UAAU,SAAS,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,GAAG,EAAE;AAAA,IAChE,YAAY,aAAa,MAAM,GAAG,EAAE;AAAA,EACtC;AACF;AAEA,SAAS,cAAc,KAAa,WAAW,GAAG,eAAe,GAAa;AAC5E,MAAI,eAAe,SAAU,QAAO,CAAC;AAErC,QAAM,QAAkB,CAAC;AACzB,QAAM,iBAAiB,CAAC,gBAAgB,QAAQ,QAAQ,SAAS,YAAY,OAAO;AAEpF,MAAI;AACF,UAAM,UAAU,YAAY,GAAG;AAE/B,eAAW,SAAS,SAAS;AAC3B,UAAI,eAAe,SAAS,KAAK,EAAG;AAEpC,YAAM,WAAWA,MAAK,KAAK,KAAK;AAChC,UAAI;AACF,cAAMC,QAAO,SAAS,QAAQ;AAE9B,YAAIA,MAAK,YAAY,GAAG;AACtB,gBAAM,KAAK,GAAG,cAAc,UAAU,UAAU,eAAe,CAAC,CAAC;AAAA,QACnE,WAAWA,MAAK,OAAO,KAAK,WAAW,KAAK,GAAG;AAC7C,gBAAM,KAAK,QAAQ;AAAA,QACrB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAEA,SAAS,WAAW,UAA2B;AAC7C,QAAM,iBAAiB,CAAC,OAAO,OAAO,QAAQ,QAAQ,OAAO,SAAS,OAAO,OAAO,QAAQ,IAAI;AAChG,SAAO,eAAe,KAAK,SAAO,SAAS,SAAS,GAAG,CAAC;AAC1D;AAEA,SAAS,cAAc,SAAyB;AAC9C,SAAO,QACJ,QAAQ,aAAa,EAAE,EACvB,QAAQ,qBAAqB,EAAE,EAC/B,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACV;AAEA,SAAS,WAAW,SAAyB;AAC3C,SAAO,WAAW,KAAK,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AACvD;;;ACrGO,SAAS,kBAAkB,WAAqC;AACrE,QAAM,cAA4B,UAAU,IAAI,UAAQ;AACtD,UAAM,SAAS,gBAAgB,IAAI;AACnC,UAAM,SAAS,eAAe,IAAI;AAClC,UAAM,WAAY,KAAK,YAAY,SAAU,KAAK,IAAI,QAAQ,CAAC;AAE/D,WAAO;AAAA,MACL,MAAM,KAAK;AAAA,MACX,WAAW,aAAa,KAAK,SAAS;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA,iBAAiB,mBAAmB,IAAI;AAAA,MACxC,OAAO,KAAK;AAAA,IACd;AAAA,EACF,CAAC;AAED,SAAO,YAAY,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE;AACxE;AAEA,SAAS,gBAAgB,MAAwB;AAC/C,MAAI,SAAS;AAEb,MAAI,KAAK,YAAY,IAAK,WAAU;AACpC,MAAI,KAAK,gBAAgB,GAAI,WAAU;AACvC,MAAI,KAAK,eAAe,GAAI,WAAU;AAEtC,SAAO,KAAK,IAAI,QAAQ,GAAG;AAC7B;AAEA,SAAS,eAAe,MAAwB;AAC9C,MAAI,SAAS;AAEb,MAAI,KAAK,aAAa,GAAI,WAAU;AACpC,MAAI,KAAK,aAAa,GAAI,WAAU;AACpC,MAAI,KAAK,gBAAgB,GAAI,WAAU;AAEvC,SAAO;AACT;AAEA,SAAS,aAAa,OAAuD;AAC3E,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,MAAI,SAAS,GAAI,QAAO;AACxB,SAAO;AACT;AAEA,SAAS,mBAAmB,MAAwB;AAClD,QAAM,cAAwB,CAAC;AAE/B,MAAI,KAAK,aAAa,IAAI;AACxB,gBAAY,KAAK,kDAAU;AAAA,EAC7B;AAEA,MAAI,KAAK,eAAe,IAAI;AAC1B,gBAAY,KAAK,kDAAU;AAAA,EAC7B;AAEA,MAAI,KAAK,gBAAgB,IAAI;AAC3B,gBAAY,KAAK,oEAAa;AAAA,EAChC;AAEA,MAAI,KAAK,YAAY,KAAK;AACxB,gBAAY,KAAK,kDAAU;AAAA,EAC7B;AAEA,MAAI,YAAY,WAAW,GAAG;AAC5B,gBAAY,KAAK,wDAAW;AAAA,EAC9B;AAEA,SAAO,YAAY,KAAK,QAAG;AAC7B;;;AC3DA,eAAsB,kBACpB,SACA,UACwB;AACxB,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO,cAAc;AAAA,EACvB;AAEA,QAAM,YAAY,oBAAoB,OAAO;AAC7C,QAAM,cAAc,MAAM,aAAa,SAAS,QAAQ;AACxD,QAAM,cAAc,MAAM,kBAAkB,QAAQ;AACpD,QAAM,cAAc,kBAAkB,SAAS;AAE/C,QAAM,QAAQ,yBAAyB,WAAW,aAAa,WAAW;AAC1E,QAAM,SAASC,iBAAgB,OAAO;AAEtC,SAAO;AAAA,IACL;AAAA,IACA,eAAe,UAAU,MAAM,GAAG,EAAE;AAAA,IACpC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,yBACP,WACA,aACA,aACkB;AAClB,QAAM,gBAAgB,UAAU,SAAS,IACrC,UAAU,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,YAAY,CAAC,IAAI,UAAU,SAChE;AAEJ,QAAM,iBAAiB,YAAY,WAAW,SAAS,IACnD,YAAY,WAAW,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,OAAO,CAAC,IAAI,YAAY,WAAW,SAC3F;AAEJ,QAAM,kBAAkB,UAAU,SAAS,IACvC,UAAU,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,cAAc,CAAC,IAAI,UAAU,SAClE;AAEJ,QAAM,mBAAmB,UAAU,SAAS,IACxC,UAAU,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,eAAe,CAAC,IAAI,UAAU,SACnE;AAEJ,QAAM,eAAe,UAAU,SAAS,IACpC,UAAU,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC,IAAI,UAAU,SAC/D;AAEJ,SAAO;AAAA,IACL;AAAA,MACE,WAAW;AAAA,MACX,OAAO,KAAK,IAAI,eAAe,GAAG;AAAA,MAClC,WAAW,gBAAgB,KAAK,SAAS,gBAAgB,KAAK,WAAW;AAAA,MACzE,aAAa;AAAA,MACb,eAAe,UAAU,OAAO,OAAK,EAAE,aAAa,EAAE,EAAE;AAAA,IAC1D;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX,OAAO,KAAK,IAAI,iBAAiB,IAAI,GAAG;AAAA,MACxC,WAAW,iBAAiB,MAAM,SAAS,iBAAiB,MAAM,WAAW;AAAA,MAC7E,aAAa;AAAA,MACb,eAAe,YAAY,WAAW,OAAO,CAAC,SAAS,KAAK,QAAQ,GAAG,EAAE;AAAA,IAC3E;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX,OAAO,MAAM;AAAA,MACb,WAAW,kBAAkB,KAAK,SAAS,kBAAkB,KAAK,WAAW;AAAA,MAC7E,aAAa;AAAA,MACb,eAAe,UAAU,OAAO,OAAK,EAAE,eAAe,EAAE,EAAE;AAAA,IAC5D;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX,OAAO,YAAY,gBAAgB,OAAO,CAAC,SAAS,KAAK,WAAW,oBAAoB,EAAE,SAAS;AAAA,MACnG,WAAW,YAAY,gBAAgB,SAAS,KAAK,SAAS,YAAY,gBAAgB,SAAS,IAAI,WAAW;AAAA,MAClH,aAAa;AAAA,MACb,eAAe,YAAY,gBAAgB;AAAA,IAC7C;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX,OAAO,eAAe;AAAA,MACtB,WAAW,eAAe,MAAM,SAAS,eAAe,MAAM,WAAW;AAAA,MACzE,aAAa;AAAA,MACb,eAAe,UAAU,OAAO,OAAK,EAAE,YAAY,GAAG,EAAE;AAAA,IAC1D;AAAA,IACA;AAAA,MACE,WAAW;AAAA,MACX,OAAO;AAAA,MACP,WAAW,mBAAmB,KAAK,SAAS,mBAAmB,KAAK,WAAW;AAAA,MAC/E,aAAa;AAAA,MACb,eAAe,UAAU,OAAO,OAAK,EAAE,gBAAgB,EAAE,EAAE;AAAA,IAC7D;AAAA,EACF;AACF;AAEA,SAASA,iBAAgB,SAAuC;AAC9D,QAAM,SAAS,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,QAAQ,IAAI,EAAE,KAAK,QAAQ,CAAC;AAC9E,QAAM,SAAS,CAAC;AAChB,QAAM,WAAW,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,SAAS,EAAE,CAAC;AAE3D,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK,UAAU;AAChD,UAAM,QAAQ,OAAO,MAAM,KAAK,IAAI,GAAG,IAAI,QAAQ,GAAG,IAAI,CAAC;AAC3D,UAAM,OAAO,MAAM,OAAO,CAAC,KAAK,MAAM;AACpC,YAAM,aAAa,EAAE,MAAM,OAAO,OAAK,EAAE,QAAQ,GAAG,EAAE;AACtD,aAAO,MAAM;AAAA,IACf,GAAG,CAAC;AAEJ,WAAO,KAAK;AAAA,MACV,MAAM,OAAO,CAAC,EAAE,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,MAC/C;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,gBAA+B;AACtC,SAAO;AAAA,IACL,OAAO,CAAC;AAAA,IACR,eAAe,CAAC;AAAA,IAChB,aAAa,EAAE,iBAAiB,CAAC,GAAG,iBAAiB,EAAE;AAAA,IACvD,aAAa,EAAE,UAAU,CAAC,GAAG,YAAY,CAAC,EAAE;AAAA,IAC5C,QAAQ,CAAC;AAAA,IACT,aAAa,CAAC;AAAA,EAChB;AACF;;;AC7IA,SAAS,gBAAAC,qBAAoB;AAgB7B,IAAM,kBACJ;AACF,IAAM,yBACJ;AACF,IAAM,SAAS;AACf,IAAM,oBACJ;AACF,IAAM,+BAA+B;AACrC,IAAM,uBAAuB,oBAAI,IAAI;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAOM,SAAS,4BACd,SACA,UACoB;AACpB,SAAO;AAAA,IACL,GAAI,YAAY;AAAA,MACd,eAAe,uBAAuB,UAAU;AAAA,QAC9C,gBAAgB,0BAA0B,OAAO;AAAA,MACnD,CAAC;AAAA,IACH;AAAA,IACA,gBAAgB,wBAAwB,OAAO;AAAA,IAC/C,eAAe,uBAAuB,OAAO;AAAA,IAC7C,eAAe,uBAAuB,OAAO;AAAA,IAC7C,WAAW,mBAAmB,OAAO;AAAA,EACvC;AACF;AAEO,SAAS,uBACd,UACA,UAAgC,CAAC,GACX;AACtB,QAAM,eAAe,iBAAiB,QAAQ;AAC9C,QAAM,aAAa,IAAI,IAAI,YAAY;AACvC,QAAM,WAAW,QAAQ,YAAY;AACrC,QAAM,aAAa,QAAQ,iBACvB,QAAQ,eAAe,OAAO,CAAC,SAAS,WAAW,IAAI,IAAI,CAAC,IAC5D;AACJ,QAAM,eAAe,WAClB,OAAO,qBAAqB,EAC5B,MAAM,GAAG,QAAQ;AACpB,QAAM,QAAyB,CAAC;AAEhC,aAAW,YAAY,cAAc;AACnC,UAAM,YAAY,uBAAuB,UAAU,QAAQ;AAC3D,QAAI,WAAW;AACb,YAAM,KAAK,SAAS;AAAA,IACtB;AAAA,EACF;AAEA,QAAM,KAAK,CAAC,GAAG,MAAM;AACnB,QAAI,EAAE,eAAe,EAAE,WAAY,QAAO,EAAE,aAAa,EAAE;AAC3D,WAAO,EAAE,iBAAiB,EAAE;AAAA,EAC9B,CAAC;AAED,SAAO;AAAA,IACL,YAAY,MAAM;AAAA,IAClB;AAAA,EACF;AACF;AAEA,SAAS,wBAAwB,SAAyC;AACxE,QAAM,aAAa,oBAAI,IAGrB;AACF,MAAI,iBAAiB;AAErB,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,OAAO,KAAK,OAAO,OAAO,EAAG;AAElC;AACA,eAAW,QAAQ,OAAO,OAAO;AAC/B,YAAM,QAAQ,WAAW,IAAI,KAAK,IAAI,KAAK;AAAA,QACzC,UAAU;AAAA,QACV,aAAa,OAAO;AAAA,QACpB,YAAY,oBAAI,IAAY;AAAA,MAC9B;AACA,YAAM;AACN,YAAM,WAAW,IAAI,OAAO,MAAM;AAClC,UAAI,OAAO,OAAO,MAAM,aAAa;AACnC,cAAM,cAAc,OAAO;AAAA,MAC7B;AACA,iBAAW,IAAI,KAAK,MAAM,KAAK;AAAA,IACjC;AAAA,EACF;AAEA,QAAM,WAA4B,MAAM,KAAK,WAAW,QAAQ,CAAC,EAC9D,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO;AAAA,IACtB;AAAA,IACA,UAAU,KAAK;AAAA,IACf,aAAa,KAAK;AAAA,IAClB,YAAY,MAAM,KAAK,KAAK,UAAU;AAAA,EACxC,EAAE,EACD,KAAK,CAAC,GAAG,MAAM;AACd,QAAI,EAAE,aAAa,EAAE,SAAU,QAAO,EAAE,WAAW,EAAE;AACrD,WAAO,EAAE,YAAY,QAAQ,IAAI,EAAE,YAAY,QAAQ;AAAA,EACzD,CAAC,EACA,MAAM,GAAG,EAAE;AAEd,SAAO,EAAE,gBAAgB,SAAS;AACpC;AAEA,SAAS,uBAAuB,SAA+C;AAC7E,QAAM,cAAc,oBAAI,IAGtB;AACF,MAAI,mBAAmB;AACvB,MAAI,qBAAqB;AACzB,MAAI,QAAQ;AAEZ,aAAW,UAAU,SAAS;AAC5B,QAAI,CAAC,cAAc,MAAM,EAAG;AAE5B;AACA,UAAMC,aAAY,eAAe,OAAO,QAAQ,OAAO,OAAO;AAC9D,QAAIA,WAAU,SAAS,GAAG;AACxB;AAAA,IACF;AAEA,eAAW,YAAYA,YAAW;AAChC,YAAM,MAAM,SAAS,MAAM,YAAY;AACvC,YAAM,WAAW,YAAY,IAAI,GAAG;AACpC,UAAI,UAAU;AACZ,iBAAS;AAAA,MACX,OAAO;AACL,oBAAY,IAAI,KAAK;AAAA,UACnB,GAAG;AAAA,UACH,SAAS;AAAA,UACT,gBAAgB;AAAA,QAClB,CAAC;AACD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,MAAM,KAAK,YAAY,OAAO,CAAC,EAC9C,KAAK,CAAC,GAAG,MAAM;AACd,QAAI,EAAE,YAAY,EAAE,QAAS,QAAO,EAAE,UAAU,EAAE;AAClD,WAAO,EAAE,iBAAiB,EAAE;AAAA,EAC9B,CAAC,EACA,IAAI,CAAC,EAAE,gBAAgB,GAAG,SAAS,MAAM,QAAQ;AAEpD,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,yBACE,mBAAmB,IAAI,WAAW,qBAAqB,gBAAgB,IAAI;AAAA,IAC7E;AAAA,EACF;AACF;AAEA,SAAS,uBAAuB,SAA+C;AAC7E,MAAI,QAAQ,WAAW,GAAG;AACxB,WAAO;AAAA,MACL,OAAO;AAAA,MACP,kBAAkB;AAAA,MAClB,mBAAmB;AAAA,MACnB,sBAAsB;AAAA,MACtB,kBAAkB,CAAC;AAAA,IACrB;AAAA,EACF;AAEA,MAAI,oBAAoB;AACxB,MAAI,cAAc;AAClB,MAAI,cAAc;AAClB,QAAM,mBAA2C,CAAC;AAElD,aAAW,UAAU,SAAS;AAC5B,mBAAe,OAAO,QAAQ;AAE9B,UAAM,oBAAoB,OAAO,QAAQ,MAAM,eAAe;AAC9D,QAAI,mBAAmB;AACrB;AACA,YAAM,OAAO,kBAAkB,CAAC,EAAE,YAAY;AAC9C,uBAAiB,IAAI,KAAK,iBAAiB,IAAI,KAAK,KAAK;AAAA,IAC3D,OAAO;AACL,uBAAiB,SAAS,iBAAiB,SAAS,KAAK;AAAA,IAC3D;AAEA,QAAI,uBAAuB,KAAK,OAAO,OAAO,GAAG;AAC/C;AAAA,IACF;AAAA,EACF;AAEA,QAAM,uBAAuB,QAAQ,cAAc,QAAQ,QAAQ,CAAC;AACpE,QAAM,mBAAmB,WAAW,oBAAoB,QAAQ,MAAM;AACtE,QAAM,oBAAoB,WAAW,cAAc,QAAQ,MAAM;AACjE,QAAM,qBAAqB,KAAK,IAAI,uBAAuB,IAAI,CAAC;AAChE,QAAM,QAAQ,KAAK;AAAA,IACjB,mBAAmB,KAAK,oBAAoB,KAAK,qBAAqB;AAAA,EACxE;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,SAAS,mBAAmB,SAA2C;AACrE,MAAI,eAAe;AACnB,MAAI,eAAe;AACnB,MAAI,gBAAgB;AAEpB,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,UAAI,KAAK,WAAW,WAAW,KAAK,WAAW,UAAU;AACvD;AAAA,MACF,WAAW,KAAK,WAAW,WAAW;AACpC;AAAA,MACF,OAAO;AACL;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,QAAQ,eAAe,eAAe;AAE5C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,cAAc,QAAQ,IAAI,YAAY,eAAe,gBAAgB,KAAK,IAAI;AAAA,IAC9E,eAAe,QAAQ,IAAI,WAAW,gBAAgB,KAAK,IAAI;AAAA,EACjE;AACF;AAEA,SAAS,0BAA0B,SAAmC;AACpE,QAAM,aAAa,oBAAI,IAAoB;AAE3C,aAAW,UAAU,SAAS;AAC5B,eAAW,QAAQ,OAAO,OAAO;AAC/B,iBAAW,IAAI,KAAK,OAAO,WAAW,IAAI,KAAK,IAAI,KAAK,KAAK,CAAC;AAAA,IAChE;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,WAAW,QAAQ,CAAC,EACnC,KAAK,CAAC,GAAG,MAAM;AACd,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO,EAAE,CAAC,IAAI,EAAE,CAAC;AACpC,WAAO,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;AAAA,EAChC,CAAC,EACA,IAAI,CAAC,CAAC,IAAI,MAAM,IAAI;AACzB;AAEA,SAAS,iBAAiB,UAA4B;AACpD,MAAI,SAAS;AACb,MAAI;AACF,aAASD,cAAa,OAAO,CAAC,YAAY,IAAI,GAAG;AAAA,MAC/C,KAAK;AAAA,MACL,UAAU;AAAA,MACV,WAAW,MAAM,OAAO;AAAA,MACxB,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,IAClC,CAAC;AAAA,EACH,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,OACJ,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,QAAQ,CAAC,uBAAuB,IAAI,CAAC;AAC3D;AAEA,SAAS,uBACP,UACA,UACsB;AACtB,MAAI,SAAS;AACb,MAAI;AACF,aAASA,cAAa,OAAO,CAAC,SAAS,QAAQ,oBAAoB,MAAM,QAAQ,GAAG;AAAA,MAClF,KAAK;AAAA,MACL,UAAU;AAAA,MACV,WAAW,MAAM,OAAO;AAAA,MACxB,OAAO,CAAC,QAAQ,QAAQ,QAAQ;AAAA,IAClC,CAAC;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,eAAe,uBAAuB,MAAM;AAClD,QAAM,aAAa,MAAM,KAAK,aAAa,OAAO,CAAC,EAAE;AAAA,IACnD,CAAC,KAAK,gBAAgB,MAAM,YAAY;AAAA,IACxC;AAAA,EACF;AACA,MAAI,eAAe,EAAG,QAAO;AAE7B,QAAM,qBAA8C,MAAM;AAAA,IACxD,aAAa,OAAO;AAAA,EACtB,EACG,IAAI,CAAC,iBAAiB;AAAA,IACrB,GAAG;AAAA,IACH,OAAO,YAAY,QAAQ;AAAA,EAC7B,EAAE,EACD,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAEnC,QAAM,QAAQ,mBAAmB,CAAC;AAElC,SAAO;AAAA,IACL,MAAM;AAAA,IACN,WAAW,MAAM;AAAA,IACjB,YAAY,MAAM;AAAA,IAClB,YAAY,MAAM;AAAA,IAClB;AAAA,IACA,gBAAgB,MAAM;AAAA,IACtB,cAAc;AAAA,EAChB;AACF;AAEA,SAAS,uBACP,QAC6D;AAC7D,QAAM,eAAe,oBAAI,IAA4D;AACrF,MAAI,cAAc;AAElB,aAAW,QAAQ,OAAO,MAAM,IAAI,GAAG;AACrC,QAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,oBAAc,KAAK,MAAM,UAAU,MAAM;AAAA,IAC3C,WAAW,KAAK,WAAW,cAAc,GAAG;AAC1C,YAAM,QAAQ,KACX,MAAM,eAAe,MAAM,EAC3B,QAAQ,UAAU,EAAE,EACpB,YAAY;AACf,YAAM,MAAM,SAAS;AACrB,YAAM,WAAW,aAAa,IAAI,GAAG,KAAK;AAAA,QACxC,MAAM,eAAe;AAAA,QACrB;AAAA,QACA,OAAO;AAAA,MACT;AACA,eAAS;AACT,mBAAa,IAAI,KAAK,QAAQ;AAAA,IAChC;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,eAAe,MAAmC;AACzD,QAAM,YAAiC,CAAC;AACxC,QAAM,OAAO,oBAAI,IAAY;AAC7B,oBAAkB,YAAY;AAE9B,MAAI,QAAQ,kBAAkB,KAAK,IAAI;AACvC,SAAO,OAAO;AACZ,UAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,UAAM,QAAQ,MAAM,CAAC,EAAE,KAAK,EAAE,YAAY;AAC1C,QAAI,CAAC,KAAK,IAAI,KAAK,GAAG;AACpB,gBAAU,KAAK,EAAE,MAAM,OAAO,SAAS,EAAE,CAAC;AAC1C,WAAK,IAAI,KAAK;AAAA,IAChB;AACA,YAAQ,kBAAkB,KAAK,IAAI;AAAA,EACrC;AAEA,SAAO;AACT;AAEA,SAAS,cAAc,QAA+B;AACpD,UAAQ,OAAO,cAAc,UAAU,KAAK,KAAK,YAAY,KAAK,OAAO,OAAO;AAClF;AAEA,SAAS,uBAAuB,UAA2B;AACzD,SAAO,yDAAyD,KAAK,QAAQ;AAC/E;AAEA,SAAS,sBAAsB,UAA2B;AACxD,MAAI,uBAAuB,QAAQ,EAAG,QAAO;AAC7C,QAAM,YAAY,SAAS,SAAS,GAAG,IACnC,SAAS,MAAM,SAAS,YAAY,GAAG,CAAC,EAAE,YAAY,IACtD;AACJ,SAAO,qBAAqB,IAAI,SAAS;AAC3C;AAEA,SAAS,WAAW,OAAuB;AACzC,SAAO,QAAQ,OAAO,CAAC;AACzB;AAEA,SAAS,QAAQ,OAAe,QAAwB;AACtD,QAAM,SAAS,MAAM;AACrB,SAAO,KAAK,MAAM,QAAQ,MAAM,IAAI;AACtC;;;AnBtZA,eAAsB,aAAa,SAA+C;AAChF,QAAM,EAAE,OAAO,WAAW,OAAO,IAAI;AACrC,QAAM,UAAUE,KAAI,yCAAW,EAAE,MAAM;AACvC,QAAM,WAA0B,CAAC;AAEjC,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC;AACpB,YAAQ,OAAO,yCAAW,IAAI,CAAC,IAAI,MAAM,MAAM,OAAO,KAAK,IAAI;AAE/D,QAAI;AACF,YAAM,UAAU,MAAM,YAAY,KAAK,MAAM,WAAW,MAAM;AAE9D,UAAI,QAAQ,SAAS,KAAQ;AAC3B,gBAAQ;AAAA,UACN,MAAM,OAAO,GAAG,KAAK,IAAI,iBAAO,QAAQ,OAAO,eAAe,CAAC,0FAAoB;AAAA,QACrF;AACA,gBAAQ,MAAM;AAAA,MAChB;AAEA,YAAM,QAAQ,eAAe,OAAO;AAGpC,YAAM,gBAAgB,uBAAuB,OAAO;AAGpD,UAAI;AACJ,UAAI;AACJ,UAAI,MAAM,WAAW,GAAG;AACtB,gBAAQ,OAAO,oCAAW,KAAK,IAAI;AACnC,mBAAW,MAAM,kBAAkB,SAAS,KAAK,IAAI;AACrD,gBAAQ,OAAO,0CAAY,KAAK,IAAI;AACpC,sBAAc,4BAA4B,SAAS,KAAK,IAAI;AAAA,MAC9D;AAGA,YAAM,YAAyB;AAAA,QAC7B,GAAG;AAAA,QACH,GAAG;AAAA,QACH,GAAI,YAAY,EAAE,SAAS;AAAA,QAC3B,GAAI,eAAe,EAAE,YAAY;AAAA,MACnC;AACA,gBAAU,cAAc,QAAQ,CAAC,WAAW;AAC1C,eAAO,WAAW,KAAK;AAAA,MACzB,CAAC;AACD,gBAAU,WAAW,cAAc,QAAQ,CAAC,WAAW;AACrD,eAAO,WAAW,KAAK;AAAA,MACzB,CAAC;AACD,gBAAU,kBAAkB,QAAQ,CAAC,cAAc;AACjD,kBAAU,WAAW,KAAK;AAC1B,kBAAU,cAAc,MAAM,SAAS,IAAI,GAAG,KAAK,IAAI,MAAM,UAAU,IAAI,KAAK,UAAU;AAAA,MAC5F,CAAC;AAED,eAAS,KAAK,SAAS;AAAA,IACzB,SAAS,OAAO;AACd,cAAQ;AAAA,QACN,MAAM;AAAA,UACJ,4BAAQ,KAAK,IAAI,KAAK,iBAAiB,QAAQ,MAAM,UAAU,0BAAM;AAAA,QACvE;AAAA,MACF;AACA,cAAQ,MAAM;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,SAAS,WAAW,QAAQ;AAElC,UAAQ;AAAA,IACN,6BAAS,OAAO,aAAa,eAAe,CAAC,wBAC1C,OAAO,QAAQ,MAAM,yBACpB,OAAO,WAAW,eAAe,CAAC,OAAO,OAAO,aAAa,eAAe,CAAC;AAAA,EACnF;AAEA,SAAO;AACT;;;AoBpFA,SAAS,iBAAiB;AAC1B,SAAS,WAAAC,gBAAe;AACxB,OAAOC,YAAW;AAClB,OAAOC,UAAS;AAChB,OAAO,UAAU;;;ACJjB,SAAS,YAAAC,iBAAgB;AACzB,SAAS,SAAS,eAAe;AACjC,SAAS,qBAAqB;AAO9B,IAAM,sBAAsB;AAAA,EAC1B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAM,uBAAuB;AAAA,EAC3B;AAAA,EACA;AACF;AAKA,eAAsB,UACpB,OACA,SACiB;AACjB,QAAM,WAAW,MAAM,iBAAiB,aAAa;AACrD,QAAM,iBAAiB,MAAM,kBAAkB,oBAAoB;AACnE,QAAM,eAAe,MAAM,iBAAiB;AAE5C,QAAM,aAA6B;AAAA,IACjC,OAAO,eAAe,KAAK;AAAA,IAC3B,cAAa,oBAAI,KAAK,GAAE,eAAe,OAAO;AAAA,IAC9C,WAAW,QAAQ,YACf;AAAA,MACE,MAAM,QAAQ,UAAU,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,MACvD,IAAI,QAAQ,UAAU,GAAG,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AAAA,IACrD,IACA;AAAA,IACJ,OAAO,QAAQ;AAAA,EACjB;AAGA,QAAM,WAAW,KAAK,UAAU,UAAU,EACvC,QAAQ,MAAM,SAAS,EACvB,QAAQ,MAAM,SAAS,EACvB,QAAQ,MAAM,SAAS;AAE1B,SAAO,SACJ,QAAQ,mBAAmB,QAAQ,EACnC,QAAQ,uBAAuB,cAAc,EAC7C,QAAQ,qBAAqB,YAAY;AAC9C;AAMA,SAAS,eAAe,OAA6C;AACnE,QAAM,wBAAwB,CAAqC,YAAe;AAAA,IAChF,GAAG;AAAA,IACH,gBAAgB,OAAO,eAAe,YAAY;AAAA,EACpD;AAEA,SAAO;AAAA,IACL,GAAG;AAAA,IACH,iBAAiB,MAAM,gBAAgB,YAAY;AAAA,IACnD,gBAAgB,MAAM,eAAe,YAAY;AAAA,IACjD,SAAS,MAAM,QAAQ,IAAI,CAAC,OAAO;AAAA,MACjC,GAAG;AAAA,MACH,gBAAgB,EAAE,eAAe,YAAY;AAAA,IAC/C,EAAE;AAAA,IACF,eAAe,MAAM,cAAc,IAAI,CAAC,YAAY;AAAA,MAClD,GAAG;AAAA,MACH,MAAM,OAAO,KAAK,YAAY;AAAA,IAChC,EAAE;AAAA,IACF,kBAAkB,MAAM,mBACpB;AAAA,MACE,GAAG,MAAM;AAAA,MACT,QAAQ,MAAM,iBAAiB,OAAO,IAAI,qBAAqB;AAAA,MAC/D,YAAY,MAAM,iBAAiB,WAAW,IAAI,qBAAqB;AAAA,MACvE,SAAS,MAAM,iBAAiB,QAAQ,IAAI,qBAAqB;AAAA,MACjE,MAAM,MAAM,iBAAiB,KAAK,IAAI,qBAAqB;AAAA,MAC3D,YAAY,MAAM,iBAAiB,WAAW,IAAI,qBAAqB;AAAA,IACzE,IACA;AAAA,IACJ,wBAAwB,MAAM,yBAC1B;AAAA,MACE,GAAG,MAAM;AAAA,MACT,cAAc,MAAM,uBAAuB,aAAa,IAAI,CAAC,OAAO;AAAA,QAClE,GAAG;AAAA,QACH,MAAM,EAAE,KAAK,YAAY;AAAA,MAC3B,EAAE;AAAA,IACJ,IACA;AAAA,EACN;AACF;AAEA,eAAe,mBAAoC;AACjD,SAAO,kBAAkB,mBAAmB;AAC9C;AAEA,eAAe,kBAAkB,WAAsC;AACrE,QAAM,QAAQ,MAAM,QAAQ;AAAA,IAC1B,UAAU,IAAI,CAAC,aAAa,iBAAiB,QAAQ,CAAC;AAAA,EACxD;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAKA,eAAe,iBAAiB,UAAmC;AAEjE,QAAM,aAAa,QAAQ,cAAc,YAAY,GAAG,CAAC;AAIzD,QAAM,gBAAgB;AAAA,IACpB,QAAQ,YAAY,gBAAgB,QAAQ;AAAA,IAC5C,QAAQ,YAAY,mBAAmB,QAAQ;AAAA,IAC/C,QAAQ,YAAY,sBAAsB,QAAQ;AAAA,EACpD;AAEA,aAAW,gBAAgB,eAAe;AACxC,QAAI;AACF,aAAO,MAAMA,UAAS,cAAc,OAAO;AAAA,IAC7C,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,IAAI,MAAM,2DAAmB,QAAQ,EAAE;AAC/C;;;ADtIA,eAAsB,eACpB,OACA,SACe;AACf,QAAM,UAAUC,KAAI,6BAAS,EAAE,MAAM;AAErC,MAAI;AACF,UAAM,OAAO,MAAM,UAAU,OAAO,OAAO;AAC3C,UAAM,aAAaC,SAAQ,QAAQ,IAAI,GAAG,QAAQ,UAAU;AAE5D,UAAM,UAAU,YAAY,MAAM,OAAO;AACzC,YAAQ,QAAQ,mCAAUC,OAAM,KAAK,UAAU,CAAC,EAAE;AAElD,QAAI,QAAQ,UAAU;AACpB,YAAM,KAAK,UAAU;AACrB,cAAQ,IAAIA,OAAM,MAAM,yDAAY,CAAC;AAAA,IACvC;AAAA,EACF,SAAS,OAAO;AACd,YAAQ,KAAK,sCAAQ;AACrB,UAAM;AAAA,EACR;AACF;;;AE7BA,IAAM,eAAe;AAMd,SAAS,YAAY,QAAkC;AAC5D,MAAI,WAAW,OAAO;AACpB,WAAO;AAAA,EACT;AAEA,QAAM,QAAQ,aAAa,KAAK,MAAM;AACtC,MAAI,CAAC,OAAO;AACV,UAAM,IAAI,MAAM,gDAAa,MAAM,0DAAiC;AAAA,EACtE;AAEA,QAAM,SAAS,SAAS,MAAM,CAAC,GAAG,EAAE;AACpC,QAAM,OAAO,MAAM,CAAC;AACpB,QAAM,KAAK,oBAAI,KAAK;AACpB,QAAM,OAAO,oBAAI,KAAK;AAEtB,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,WAAK,QAAQ,KAAK,QAAQ,IAAI,MAAM;AACpC;AAAA,IACF,KAAK;AACH,WAAK,SAAS,KAAK,SAAS,IAAI,MAAM;AACtC;AAAA,IACF,KAAK;AACH,WAAK,YAAY,KAAK,YAAY,IAAI,MAAM;AAC5C;AAAA,EACJ;AAEA,SAAO,EAAE,MAAM,GAAG;AACpB;AAKA,SAAS,UAAU,SAAuB;AACxC,QAAM,OAAO,IAAI,KAAK,OAAO;AAC7B,MAAI,MAAM,KAAK,QAAQ,CAAC,GAAG;AACzB,UAAM,IAAI,MAAM,gDAAa,OAAO,mDAAqB;AAAA,EAC3D;AACA,SAAO;AACT;AAOO,SAAS,iBAAiB,MAAoC;AACnE,MAAI,KAAK,QAAQ,KAAK,IAAI;AACxB,UAAM,KAAK,KAAK,KAAK,UAAU,KAAK,EAAE,IAAI,oBAAI,KAAK;AACnD,UAAM,OAAO,KAAK,OAAO,UAAU,KAAK,IAAI,KAAK,MAAM;AACrD,YAAM,IAAI,IAAI,KAAK,EAAE;AACrB,QAAE,SAAS,EAAE,SAAS,IAAI,CAAC;AAC3B,aAAO;AAAA,IACT,GAAG;AAEH,QAAI,OAAO,IAAI;AACb,YAAM,IAAI,MAAM,0EAAc;AAAA,IAChC;AAEA,WAAO,EAAE,MAAM,GAAG;AAAA,EACpB;AAEA,SAAO,YAAY,KAAK,MAAM;AAChC;;;AxB/DA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,eAAe,EACpB,YAAY,gGAA0B,EACtC,QAAQ,OAAO,EACf,SAAS,eAAe,oDAAY,QAAQ,IAAI,CAAC,EACjD,OAAO,yBAAyB,iDAA6B,KAAK,EAClE,OAAO,qBAAqB,uCAAmB,EAC/C,OAAO,mBAAmB,uCAAmB,EAC7C,OAAO,uBAAuB,0BAAM,EACpC,OAAO,uBAAuB,kCAAS,oBAAoB,EAC3D,OAAO,aAAa,kDAAU,EAC9B,OAAO,wBAAwB,wCAAU,IAAI,EAC7C,OAAO,OAAO,WAAmB,SAAqB;AACrD,MAAI;AACF,UAAM,IAAI,WAAW,IAAI;AAAA,EAC3B,SAAS,OAAO;AACd,QAAI,iBAAiB,SAAS,MAAM,YAAY,eAAe;AAC7D,cAAQ,IAAIC,OAAM,OAAO,kCAAS,CAAC;AACnC,cAAQ,KAAK,CAAC;AAAA,IAChB;AACA,YAAQ,MAAMA,OAAM,IAAI;AAAA,gBAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE,CAAC;AAC1F,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,eAAe,IAAI,WAAmB,MAAiC;AAErE,QAAM,kBAAkB;AAGxB,QAAM,YAAY,iBAAiB,IAAI;AAGvC,QAAM,QAAQ,MAAM,iBAAiB;AAAA,IACnC,WAAW;AAAA,IACX,UAAU,OAAO,KAAK,KAAK;AAAA,EAC7B,CAAC;AAED,MAAI,MAAM,WAAW,GAAG;AACtB,YAAQ,IAAIA,OAAM,IAAI,qCAAY,CAAC;AACnC,YAAQ,KAAK,CAAC;AAAA,EAChB;AAGA,MAAI;AAEJ,MAAI,MAAM,WAAW,GAAG;AACtB,oBAAgB;AAChB,YAAQ,IAAIA,OAAM,KAAK,2CAAkB,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC;AAAA,EAC3D,OAAO;AACL,YAAQ,IAAIA,OAAM,KAAK;AAAA,eAAQ,MAAM,MAAM;AAAA,CAAc,CAAC;AAE1D,UAAM,WAAW,MAAM,SAAiB;AAAA,MACtC,SAAS;AAAA,MACT,SAAS,MAAM,IAAI,CAAC,UAAU;AAAA,QAC5B,MAAM,GAAG,KAAK,IAAI,KAAK,KAAK,WAAW;AAAA,QACvC,OAAO,KAAK;AAAA,QACZ,SAAS;AAAA,MACX,EAAE;AAAA,IACJ,CAAC;AAED,QAAI,SAAS,WAAW,GAAG;AACzB,cAAQ,IAAIA,OAAM,OAAO,4CAAS,CAAC;AACnC,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,oBAAgB,MAAM,OAAO,CAAC,MAAM,SAAS,SAAS,EAAE,IAAI,CAAC;AAAA,EAC/D;AAEA,QAAM,gBAAgB,YAClB,GAAGC,YAAW,UAAU,IAAI,CAAC,MAAMA,YAAW,UAAU,EAAE,CAAC,KAC3D;AACJ,UAAQ;AAAA,IACND,OAAM;AAAA,MACJ;AAAA,qBAAS,cAAc,MAAM,0DAAa,aAAa;AAAA;AAAA,IACzD;AAAA,EACF;AAGA,QAAM,QAAQ,MAAM,aAAa;AAAA,IAC/B,OAAO;AAAA,IACP;AAAA,IACA,QAAQ,KAAK;AAAA,EACf,CAAC;AAED,MAAI,MAAM,iBAAiB,GAAG;AAC5B,YAAQ,IAAIA,OAAM,OAAO,wDAAW,CAAC;AAAA,EACvC;AAGA,QAAM,eAAe,OAAO;AAAA,IAC1B,YAAY,KAAK;AAAA,IACjB,UAAU,KAAK;AAAA,IACf;AAAA,IACA,WAAW,cAAc,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EAC5C,CAAC;AACH;AAEA,eAAe,oBAAmC;AAChD,QAAM,EAAE,UAAAE,UAAS,IAAI,MAAM,OAAO,eAAe;AACjD,MAAI;AACF,IAAAA,UAAS,iBAAiB,EAAE,OAAO,SAAS,CAAC;AAAA,EAC/C,QAAQ;AACN,YAAQ,MAAMF,OAAM,IAAI,8BAAU,CAAC;AACnC,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;AAEA,SAASC,YAAW,MAAoB;AACtC,SAAO,KAAK,YAAY,EAAE,MAAM,GAAG,EAAE,CAAC;AACxC;AAGA,QAAQ,GAAG,UAAU,MAAM;AACzB,UAAQ,IAAID,OAAM,OAAO,kCAAS,CAAC;AACnC,UAAQ,KAAK,CAAC;AAChB,CAAC;AAED,QAAQ,MAAM;","names":["chalk","ora","join","extname","readFile","join","getTopDirectory","total","getTopDirectory","getWeekKey","extname","getTopDirectory","authorMap","authors","stat","getTopDirectory","commits","readFile","join","stat","calculateTrends","execFileSync","reviewers","ora","resolve","chalk","ora","readFile","ora","resolve","chalk","chalk","formatDate","execSync"]}
|