archtracker-mcp 0.6.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/cli/index.ts","../../src/analyzer/analyze.ts","../../src/analyzer/engines/regex-engine.ts","../../src/analyzer/engines/cycle.ts","../../src/analyzer/engines/strip-comments.ts","../../src/analyzer/engines/detect.ts","../../src/analyzer/engines/languages.ts","../../src/analyzer/engines/types.ts","../../src/i18n/index.ts","../../src/analyzer/report.ts","../../src/analyzer/multi-layer.ts","../../src/storage/layers.ts","../../src/analyzer/resolve.ts","../../src/storage/snapshot.ts","../../src/types/schema.ts","../../src/storage/diff.ts","../../src/web/server.ts","../../src/web/styles.ts","../../src/web/viewer-html.ts","../../src/web/js-hierarchy.ts","../../src/web/js-diff.ts","../../src/utils/html-escape.ts","../../src/web/template.ts","../../src/utils/version.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { watch } from \"node:fs\";\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { AnalyzerError, formatAnalysisReport, resolveGraph } from \"../analyzer/index.js\";\nimport {\n saveSnapshot,\n loadSnapshot,\n computeDiff,\n formatDiffReport,\n StorageError,\n} from \"../storage/index.js\";\nimport { loadLayerConfig, saveLayerConfig } from \"../storage/layers.js\";\nimport { startViewer } from \"../web/server.js\";\nimport { t, setLocale } from \"../i18n/index.js\";\nimport type { Locale } from \"../i18n/index.js\";\nimport { VERSION } from \"../utils/version.js\";\nimport { LANGUAGE_IDS } from \"../analyzer/engines/types.js\";\nimport type { LanguageId } from \"../analyzer/engines/types.js\";\n\nconst VALID_LANGUAGES = LANGUAGE_IDS as readonly string[];\n\n/** CLI wrapper: resolveGraph always checks layers.json in projectRoot */\nasync function resolveGraphCli(opts: {\n target: string;\n root: string;\n exclude?: string[];\n language?: LanguageId;\n}) {\n return resolveGraph({\n targetDir: opts.target,\n projectRoot: opts.root,\n exclude: opts.exclude,\n language: opts.language,\n });\n}\n\nconst program = new Command();\n\nprogram\n .name(\"archtracker\")\n .description(\n \"Architecture & Dependency Tracker — Prevent missed architecture changes in AI-driven development\",\n )\n .version(VERSION)\n .option(\"--lang <locale>\", \"Language (en/ja, auto-detected from LANG env)\")\n .hook(\"preAction\", (thisCommand) => {\n const lang = thisCommand.opts().lang;\n if (lang === \"en\" || lang === \"ja\") {\n setLocale(lang as Locale);\n }\n });\n\n// ─── archtracker init ───────────────────────────────────────────\n\nprogram\n .command(\"init\")\n .description(\"Generate initial snapshot and save to .archtracker/\")\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\n \"-e, --exclude <patterns...>\",\n \"Exclude patterns (regex)\",\n )\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n console.log(t(\"cli.analyzing\"));\n const { graph, multiLayer } = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n exclude: opts.exclude,\n language,\n });\n\n const snapshot = await saveSnapshot(opts.root, graph, multiLayer);\n\n console.log(t(\"cli.snapshotSaved\"));\n console.log(t(\"cli.timestamp\", { ts: snapshot.timestamp }));\n console.log(t(\"cli.fileCount\", { count: graph.totalFiles }));\n console.log(t(\"cli.edgeCount\", { count: graph.totalEdges }));\n\n if (graph.circularDependencies.length > 0) {\n console.log(\n t(\"cli.circularCount\", { count: graph.circularDependencies.length }),\n );\n }\n\n // Show top 5 key components\n const top = Object.values(graph.files)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, 5);\n\n if (top.length > 0 && top[0].dependents.length > 0) {\n console.log(t(\"cli.keyComponents\"));\n for (const f of top) {\n if (f.dependents.length === 0) break;\n console.log(` ${t(\"cli.dependedBy\", { path: f.path, count: f.dependents.length })}`);\n }\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker analyze ─────────────────────────────────────\n\nprogram\n .command(\"analyze\")\n .description(\n \"Comprehensive architecture analysis for existing projects\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\n \"-e, --exclude <patterns...>\",\n \"Exclude patterns (regex)\",\n )\n .option(\"-n, --top <number>\", \"Number of top components to show\", \"10\")\n .option(\"--save\", \"Also save a snapshot after analysis\")\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n console.log(t(\"cli.analyzing\"));\n const { graph, multiLayer } = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n exclude: opts.exclude,\n language,\n });\n\n const report = formatAnalysisReport(graph, { topN: parseInt(opts.top, 10) });\n console.log(report);\n\n if (opts.save) {\n await saveSnapshot(opts.root, graph, multiLayer);\n console.log(t(\"analyze.snapshotSaved\"));\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker check ─────────────────────────────────────────\n\nprogram\n .command(\"check\")\n .description(\n \"Compare snapshot with current code and report change impacts\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\"--ci\", \"CI mode: exit code 1 if affected files exist\")\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n const existingSnapshot = await loadSnapshot(opts.root);\n\n if (!existingSnapshot) {\n console.log(t(\"cli.noSnapshot\"));\n process.exit(1);\n }\n\n console.log(t(\"cli.analyzing\"));\n const { graph: currentGraph } = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n language,\n });\n const diff = computeDiff(existingSnapshot.graph, currentGraph);\n const report = formatDiffReport(diff);\n\n console.log(report);\n\n // CI mode: exit with error if there are affected dependents\n if (opts.ci && diff.affectedDependents.length > 0) {\n console.log(t(\"cli.ciFailed\", { count: diff.affectedDependents.length }));\n process.exit(1);\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker context ────────────────────────────────────────\n\nprogram\n .command(\"context\")\n .description(\n \"Display current architecture context (for AI session initialization)\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\"--json\", \"Output in JSON format\")\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n let snapshot = await loadSnapshot(opts.root);\n\n if (!snapshot) {\n console.log(t(\"cli.autoGenerating\"));\n const result = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n language,\n });\n snapshot = await saveSnapshot(opts.root, result.graph, result.multiLayer);\n }\n\n const graph = snapshot.graph;\n\n if (opts.json) {\n const context = {\n validPaths: Object.keys(graph.files).sort(),\n snapshotTimestamp: snapshot.timestamp,\n totalFiles: graph.totalFiles,\n totalEdges: graph.totalEdges,\n circularDependencies: graph.circularDependencies.length,\n keyComponents: Object.values(graph.files)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, 20)\n .map((f) => ({\n path: f.path,\n dependentCount: f.dependents.length,\n dependencyCount: f.dependencies.length,\n })),\n };\n console.log(JSON.stringify(context, null, 2));\n return;\n }\n\n console.log(t(\"cli.project\", { path: graph.rootDir }));\n console.log(t(\"cli.fileCount\", { count: graph.totalFiles }));\n console.log(t(\"cli.edgeCount\", { count: graph.totalEdges }));\n console.log(t(\"cli.circularCount\", { count: graph.circularDependencies.length }));\n console.log(t(\"cli.snapshot\", { ts: snapshot.timestamp }));\n\n console.log(t(\"cli.validPaths\"));\n for (const f of Object.keys(graph.files).sort()) {\n console.log(` ${f}`);\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker serve ──────────────────────────────────────────\n\nprogram\n .command(\"serve\")\n .description(\n \"Start interactive architecture graph viewer in browser\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\"-p, --port <number>\", \"Port number\", \"3000\")\n .option(\n \"-e, --exclude <patterns...>\",\n \"Exclude patterns (regex)\",\n )\n .option(\"-w, --watch\", \"Watch for file changes and auto-reload\")\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n console.log(t(\"web.starting\"));\n console.log(t(\"cli.analyzing\"));\n\n // Use snapshot if available, otherwise analyze fresh\n let diff = null;\n const result = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n exclude: opts.exclude,\n language,\n });\n const snapshot = await loadSnapshot(opts.root);\n if (snapshot) {\n diff = computeDiff(snapshot.graph, result.graph);\n }\n\n const port = parseInt(opts.port, 10);\n const viewer = startViewer(result.graph, {\n port,\n diff,\n layerMetadata: result.layerMetadata,\n crossLayerEdges: result.crossLayerEdges,\n });\n\n console.log(t(\"web.listening\", { port }));\n console.log(t(\"web.stop\"));\n\n if (opts.watch) {\n console.log(t(\"web.watching\", { dir: opts.target }));\n let debounce: ReturnType<typeof setTimeout> | null = null;\n watch(opts.target, { recursive: true }, () => {\n if (debounce) clearTimeout(debounce);\n debounce = setTimeout(async () => {\n try {\n console.log(t(\"web.reloading\"));\n const newResult = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n exclude: opts.exclude,\n language,\n });\n viewer.close();\n startViewer(newResult.graph, {\n port,\n layerMetadata: newResult.layerMetadata,\n crossLayerEdges: newResult.crossLayerEdges,\n });\n console.log(t(\"web.reloaded\"));\n } catch { /* ignore transient errors during file saves */ }\n }, 500);\n });\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker ci-setup ────────────────────────────────────────\n\nprogram\n .command(\"ci-setup\")\n .description(\n \"Generate GitHub Actions workflow for architecture checks on PRs\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .action(async (opts) => {\n const workflow = `name: Architecture Check\n\non:\n pull_request:\n branches: [main, master]\n\njobs:\n arch-check:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: '20'\n - run: npm ci\n - run: npx archtracker check --target ${opts.target} --ci\n`;\n try {\n const dir = join(\".github\", \"workflows\");\n await mkdir(dir, { recursive: true });\n const path = join(dir, \"arch-check.yml\");\n await writeFile(path, workflow, \"utf-8\");\n console.log(t(\"ci.generated\", { path }));\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker layers ──────────────────────────────────────────\n\nconst layersCmd = program\n .command(\"layers\")\n .description(\"Manage multi-layer architecture configuration\");\n\nlayersCmd\n .command(\"init\")\n .description(\"Create a template .archtracker/layers.json\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .action(async (opts) => {\n try {\n const existing = await loadLayerConfig(opts.root);\n if (existing) {\n console.log(t(\"layers.alreadyExists\"));\n return;\n }\n\n const config = {\n version: \"1.0\" as const,\n layers: [\n { name: \"Frontend\", targetDir: \"frontend\", description: \"UI layer\" },\n { name: \"Backend\", targetDir: \"backend\", description: \"API layer\" },\n ],\n };\n\n await saveLayerConfig(opts.root, config);\n console.log(t(\"layers.created\"));\n } catch (error) {\n handleError(error);\n }\n });\n\nlayersCmd\n .command(\"list\")\n .description(\"List configured layers\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .action(async (opts) => {\n try {\n const config = await loadLayerConfig(opts.root);\n if (!config) {\n console.log(t(\"layers.notFound\"));\n return;\n }\n\n console.log(t(\"layers.header\", { count: config.layers.length }));\n for (const layer of config.layers) {\n const lang = layer.language ? ` [${layer.language}]` : \"\";\n const desc = layer.description ? ` — ${layer.description}` : \"\";\n console.log(` ${layer.name}: ${layer.targetDir}${lang}${desc}`);\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── Error handling ─────────────────────────────────────────────\n\nfunction validateLanguage(lang?: string): LanguageId | undefined {\n if (!lang) return undefined;\n if (VALID_LANGUAGES.includes(lang)) return lang as LanguageId;\n console.error(`Invalid language: ${lang}`);\n console.error(`Valid languages: ${LANGUAGE_IDS.join(\", \")}`);\n process.exit(1);\n}\n\nfunction handleError(error: unknown): never {\n if (error instanceof AnalyzerError) {\n console.error(t(\"error.cli.analyzer\", { message: error.message }));\n } else if (error instanceof StorageError) {\n console.error(t(\"error.cli.storage\", { message: error.message }));\n } else if (error instanceof Error) {\n console.error(t(\"error.cli.generic\", { message: error.message }));\n } else {\n console.error(t(\"error.cli.unexpected\", { message: String(error) }));\n }\n process.exit(1);\n}\n\nprogram.parse();\n","import { resolve } from \"node:path\";\nimport { stat } from \"node:fs/promises\";\nimport type { DependencyGraph } from \"../types/schema.js\";\nimport { RegexEngine } from \"./engines/regex-engine.js\";\nimport { detectLanguage } from \"./engines/detect.js\";\nimport { getLanguageConfig } from \"./engines/languages.js\";\nimport type { LanguageId } from \"./engines/types.js\";\n\n/** Options for the dependency analyzer */\nexport interface AnalyzeOptions {\n /** Regex patterns to exclude from analysis (e.g. [\"node_modules\", \"\\\\.test\\\\.ts$\"]) */\n exclude?: string[];\n /** Maximum recursion depth (0 = unlimited) */\n maxDepth?: number;\n /** Target language (auto-detected if omitted) */\n language?: LanguageId;\n}\n\n/**\n * Analyze project dependencies using regex-based import extraction.\n * Supports all 13 languages. Language is auto-detected if not specified.\n */\nexport async function analyzeProject(\n rootDir: string,\n options: AnalyzeOptions = {},\n): Promise<DependencyGraph> {\n const absRootDir = resolve(rootDir);\n\n // Validate directory exists\n try {\n const s = await stat(absRootDir);\n if (!s.isDirectory()) {\n throw new AnalyzerError(`Not a directory: ${absRootDir}`);\n }\n } catch (error) {\n if (error instanceof AnalyzerError) throw error;\n throw new AnalyzerError(`Directory not found: ${absRootDir}`, { cause: error });\n }\n\n const language = options.language ?? (await detectLanguage(absRootDir));\n\n try {\n const config = getLanguageConfig(language);\n if (!config) {\n throw new AnalyzerError(`No analyzer config for language: ${language}`);\n }\n\n return await new RegexEngine(config).analyze(absRootDir, options);\n } catch (error) {\n if (error instanceof AnalyzerError) throw error;\n const message = error instanceof Error ? error.message : String(error);\n throw new AnalyzerError(message, { cause: error });\n }\n}\n\n/** Custom error class for analyzer failures */\nexport class AnalyzerError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AnalyzerError\";\n }\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { join, relative, resolve } from \"node:path\";\nimport type {\n DependencyGraph,\n DependencyEdge,\n FileNode,\n} from \"../../types/schema.js\";\nimport type { AnalyzerEngine, LanguageConfig } from \"./types.js\";\nimport { detectCycles } from \"./cycle.js\";\nimport { stripComments } from \"./strip-comments.js\";\n\nexport class RegexEngine implements AnalyzerEngine {\n constructor(private config: LanguageConfig) {}\n\n async analyze(\n rootDir: string,\n options: { exclude?: string[]; maxDepth?: number } = {},\n ): Promise<DependencyGraph> {\n const absRootDir = resolve(rootDir);\n const excludePatterns = [\n ...(this.config.defaultExclude ?? []),\n ...(options.exclude ?? []),\n \"\\\\.archtracker\",\n ].map((p) => new RegExp(p));\n\n // 1. Collect all matching files\n const projectFiles = await this.collectFiles(\n absRootDir,\n absRootDir,\n excludePatterns,\n options.maxDepth ?? 0,\n );\n\n const projectFileSet = new Set(projectFiles);\n\n // 2. Extract imports and resolve edges\n const files: Record<string, FileNode> = {};\n const edges: DependencyEdge[] = [];\n const edgeSet = new Set<string>(); // \"source\\0target\" for dedup\n\n // Initialize all file nodes\n for (const filePath of projectFiles) {\n const relPath = relative(absRootDir, filePath);\n files[relPath] = {\n path: relPath,\n exists: true,\n dependencies: [],\n dependents: [],\n };\n }\n\n // Process each file for imports\n for (const filePath of projectFiles) {\n const relSource = relative(absRootDir, filePath);\n let content: string;\n try {\n content = await readFile(filePath, \"utf-8\");\n } catch {\n if (files[relSource]) files[relSource].exists = false;\n continue;\n }\n\n const stripped = stripComments(content, this.config.commentStyle);\n const imports = this.extractImports(stripped, filePath, absRootDir, projectFileSet);\n for (const importPath of imports) {\n const resolved = this.config.resolveImport(\n importPath,\n filePath,\n absRootDir,\n projectFileSet,\n );\n if (!resolved) continue;\n\n const relTarget = relative(absRootDir, resolved);\n if (!files[relTarget]) continue; // skip external\n if (relSource === relTarget) continue; // skip self-import\n\n const edgeKey = `${relSource}\\0${relTarget}`;\n if (edgeSet.has(edgeKey)) continue; // deduplicate\n edgeSet.add(edgeKey);\n\n edges.push({\n source: relSource,\n target: relTarget,\n type: \"static\",\n });\n\n files[relSource].dependencies.push(relTarget);\n files[relTarget].dependents.push(relSource);\n }\n }\n\n // 3. Detect cycles\n const circularDependencies = detectCycles(edges);\n\n return {\n rootDir: absRootDir,\n files,\n edges,\n circularDependencies,\n totalFiles: Object.keys(files).length,\n totalEdges: edges.length,\n };\n }\n\n private extractImports(\n content: string,\n filePath: string,\n rootDir: string,\n projectFiles: Set<string>,\n ): string[] {\n // Use custom extractor if defined (e.g., Rust grouped use, C# class refs)\n if (this.config.extractImports) {\n return this.config.extractImports(content, filePath, rootDir, projectFiles);\n }\n\n const imports: string[] = [];\n for (const pattern of this.config.importPatterns) {\n // Reset regex state, ensuring 'g' flag is present to prevent infinite loops\n const flags = pattern.regex.flags.includes('g') ? pattern.regex.flags : pattern.regex.flags + 'g';\n const regex = new RegExp(pattern.regex.source, flags);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(content)) !== null) {\n // Use the first capturing group as the import path\n if (match[1]) {\n imports.push(match[1]);\n }\n }\n }\n return imports;\n }\n\n private async collectFiles(\n dir: string,\n absRootDir: string,\n excludePatterns: RegExp[],\n maxDepth: number,\n currentDepth: number = 0,\n ): Promise<string[]> {\n if (maxDepth > 0 && currentDepth >= maxDepth) return [];\n\n const results: string[] = [];\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n const relPath = relative(absRootDir, fullPath);\n\n // Check excludes against the entry name and relative path\n if (\n excludePatterns.some(\n (p) => p.test(entry.name) || p.test(relPath) || p.test(fullPath),\n )\n ) {\n continue;\n }\n\n if (entry.isDirectory()) {\n if (entry.name.startsWith(\".\")) continue;\n const sub = await this.collectFiles(\n fullPath,\n absRootDir,\n excludePatterns,\n maxDepth,\n currentDepth + 1,\n );\n results.push(...sub);\n } else if (entry.isFile()) {\n const dotIdx = entry.name.lastIndexOf(\".\");\n if (dotIdx > 0) {\n const ext = entry.name.slice(dotIdx);\n if (this.config.extensions.includes(ext)) {\n results.push(fullPath);\n }\n }\n }\n }\n\n return results;\n }\n}\n","import type { DependencyEdge, CircularDependency } from \"../../types/schema.js\";\n\nexport function detectCycles(edges: DependencyEdge[]): CircularDependency[] {\n const adj = new Map<string, string[]>();\n for (const edge of edges) {\n if (!adj.has(edge.source)) adj.set(edge.source, []);\n adj.get(edge.source)!.push(edge.target);\n }\n\n const visited = new Set<string>();\n const inStack = new Set<string>();\n const cycles: CircularDependency[] = [];\n const cycleKeys = new Set<string>();\n\n function dfs(node: string, path: string[]): void {\n if (inStack.has(node)) {\n const cycleStart = path.indexOf(node);\n if (cycleStart !== -1) {\n const cycle = path.slice(cycleStart);\n const key = [...cycle].sort().join(\"\\u2192\");\n if (!cycleKeys.has(key)) {\n cycleKeys.add(key);\n cycles.push({ cycle });\n }\n }\n return;\n }\n if (visited.has(node)) return;\n\n visited.add(node);\n inStack.add(node);\n path.push(node);\n\n for (const neighbor of adj.get(node) ?? []) {\n dfs(neighbor, path);\n }\n\n path.pop();\n inStack.delete(node);\n }\n\n for (const node of adj.keys()) {\n if (!visited.has(node)) {\n dfs(node, []);\n }\n }\n\n return cycles;\n}\n","import type { CommentStyle } from \"./types.js\";\n\n/**\n * Strip comments and string literals from source code.\n * Replaces them with whitespace to preserve line numbers and positions.\n */\nexport function stripComments(content: string, style: CommentStyle): string {\n switch (style) {\n case \"c-style\":\n return stripCStyle(content);\n case \"hash\":\n return stripHash(content);\n case \"python\":\n return stripPython(content);\n case \"ruby\":\n return stripRuby(content);\n case \"php\":\n return stripPhp(content);\n default: {\n const _exhaustive: never = style;\n throw new Error(`Unknown comment style: ${_exhaustive}`);\n }\n }\n}\n\n/**\n * C-style comments: // line comments and /* block comments *\\/\n * Used by: Rust, Go, Java, C/C++, Swift, Kotlin\n * Also strips string literals (\"...\" and '...' for char literals)\n * Handles Rust raw strings: r\"...\", r#\"...\"#, r##\"...\"## etc.\n */\nfunction stripCStyle(content: string): string {\n let result = \"\";\n let i = 0;\n while (i < content.length) {\n // Rust raw strings: r\"...\", r#\"...\"#, r##\"...\"##, etc.\n if (content[i] === \"r\" && i + 1 < content.length) {\n let hashes = 0;\n let j = i + 1;\n while (j < content.length && content[j] === \"#\") {\n hashes++;\n j++;\n }\n if (j < content.length && content[j] === '\"') {\n // This is a raw string: r\"...\", r#\"...\"#, etc.\n // Replace entire raw string content with spaces\n // Replace r, hashes, and opening quote with spaces\n for (let k = i; k <= j; k++) {\n result += \" \";\n }\n i = j + 1; // move past opening quote\n // Scan for closing pattern\n while (i < content.length) {\n if (content[i] === '\"') {\n // Check if followed by correct number of hashes\n let matchHashes = 0;\n let m = i + 1;\n while (m < content.length && content[m] === \"#\" && matchHashes < hashes) {\n matchHashes++;\n m++;\n }\n if (matchHashes === hashes) {\n // Found the closing delimiter\n for (let k = i; k < m; k++) {\n result += \" \";\n }\n i = m;\n break;\n }\n }\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n continue;\n }\n // Not a raw string, fall through to normal character handling\n }\n\n // Single-line comment\n if (content[i] === \"/\" && content[i + 1] === \"/\") {\n // Skip to end of line, preserve newline\n while (i < content.length && content[i] !== \"\\n\") {\n result += \" \";\n i++;\n }\n }\n // Block comment\n else if (content[i] === \"/\" && content[i + 1] === \"*\") {\n result += \" \";\n i++; // /\n result += \" \";\n i++; // *\n while (i < content.length) {\n if (content[i] === \"*\" && content[i + 1] === \"/\") {\n result += \" \";\n i++;\n result += \" \";\n i++;\n break;\n }\n // Preserve newlines for line-number accuracy\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n }\n // Double-quoted string\n else if (content[i] === '\"') {\n result += content[i]; // keep the quote\n i++;\n while (i < content.length && content[i] !== '\"') {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i]; // backslash\n i++;\n result += content[i]; // escaped char\n i++;\n } else if (content[i] === \"\\n\") {\n result += \"\\n\";\n i++;\n } else {\n result += content[i];\n i++;\n }\n }\n if (i < content.length) {\n result += content[i]; // closing quote\n i++;\n }\n }\n // Single-quoted char literal (e.g. '/', '\\'', '\\\\', '\\n')\n else if (content[i] === \"'\") {\n result += content[i]; // opening quote\n i++;\n while (i < content.length && content[i] !== \"'\") {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i]; // backslash\n i++;\n result += content[i]; // escaped char\n i++;\n } else {\n result += content[i];\n i++;\n }\n }\n if (i < content.length) {\n result += content[i]; // closing quote\n i++;\n }\n }\n // Raw/backtick string (Go)\n else if (content[i] === \"`\") {\n result += \" \"; // replace backtick string content\n i++;\n while (i < content.length && content[i] !== \"`\") {\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n if (i < content.length) {\n result += \" \";\n i++;\n }\n }\n // Normal character\n else {\n result += content[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Hash-style comments: # line comments\n * Used by: (standalone, though mostly via python/ruby)\n */\nfunction stripHash(content: string): string {\n let result = \"\";\n let i = 0;\n while (i < content.length) {\n if (content[i] === \"#\") {\n while (i < content.length && content[i] !== \"\\n\") {\n result += \" \";\n i++;\n }\n } else if (content[i] === '\"') {\n result += content[i];\n i++;\n while (i < content.length && content[i] !== '\"') {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else {\n result += content[i++];\n }\n }\n if (i < content.length) { result += content[i]; i++; }\n } else if (content[i] === \"'\") {\n result += content[i];\n i++;\n while (i < content.length && content[i] !== \"'\") {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else {\n result += content[i++];\n }\n }\n if (i < content.length) { result += content[i]; i++; }\n } else {\n result += content[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Python comments: # line comments + triple-quoted strings (\"\"\" and ''')\n * Handles string prefixes: r, b, f, rb, fr, br (case-insensitive)\n * For raw strings (r prefix), backslash is NOT treated as escape.\n */\nfunction stripPython(content: string): string {\n let result = \"\";\n let i = 0;\n while (i < content.length) {\n // Check for string prefix (r, b, f, rb, fr, br) before quotes\n let prefixLen = 0;\n let isRaw = false;\n if (i < content.length) {\n const c0 = content[i];\n const c1 = i + 1 < content.length ? content[i + 1] : \"\";\n const c2 = i + 2 < content.length ? content[i + 2] : \"\";\n // Two-char prefixes: rb, br, fr, rf\n if (\n (c0 === \"r\" || c0 === \"R\" || c0 === \"b\" || c0 === \"B\" || c0 === \"f\" || c0 === \"F\") &&\n (c1 === \"r\" || c1 === \"R\" || c1 === \"b\" || c1 === \"B\" || c1 === \"f\" || c1 === \"F\") &&\n (c2 === '\"' || c2 === \"'\")\n ) {\n // Validate it's a real two-char prefix (rb, br, fr, rf)\n const pair = (c0 + c1).toLowerCase();\n if (pair === \"rb\" || pair === \"br\" || pair === \"fr\" || pair === \"rf\") {\n prefixLen = 2;\n isRaw = pair.includes(\"r\");\n }\n }\n // Single-char prefix: r, b, f\n if (\n prefixLen === 0 &&\n (c0 === \"r\" || c0 === \"R\" || c0 === \"b\" || c0 === \"B\" || c0 === \"f\" || c0 === \"F\") &&\n (c1 === '\"' || c1 === \"'\")\n ) {\n prefixLen = 1;\n isRaw = c0 === \"r\" || c0 === \"R\";\n }\n }\n\n const quoteStart = i + prefixLen;\n\n // Triple-quoted strings (with or without prefix)\n if (\n quoteStart + 2 < content.length &&\n (content[quoteStart] === '\"' || content[quoteStart] === \"'\") &&\n content[quoteStart + 1] === content[quoteStart] &&\n content[quoteStart + 2] === content[quoteStart]\n ) {\n const quote = content[quoteStart];\n // Output prefix chars\n for (let k = 0; k < prefixLen; k++) {\n result += \" \";\n }\n // Replace triple-quoted content with spaces\n result += \" \";\n i = quoteStart + 3;\n while (i < content.length) {\n if (content[i] === quote && content[i + 1] === quote && content[i + 2] === quote) {\n result += \" \";\n i += 3;\n break;\n }\n if (!isRaw && content[i] === \"\\\\\" && i + 1 < content.length) {\n result += \" \";\n i++;\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n } else {\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n }\n }\n // Hash comment\n else if (prefixLen === 0 && content[i] === \"#\") {\n while (i < content.length && content[i] !== \"\\n\") {\n result += \" \";\n i++;\n }\n }\n // Single/double quoted strings (non-triple), with or without prefix\n else if (\n quoteStart < content.length &&\n (content[quoteStart] === '\"' || content[quoteStart] === \"'\") &&\n (prefixLen > 0 || content[i] === '\"' || content[i] === \"'\")\n ) {\n const quote = content[quoteStart];\n // Output prefix\n for (let k = i; k < quoteStart; k++) {\n result += content[k];\n }\n result += content[quoteStart]; // opening quote\n i = quoteStart + 1;\n while (i < content.length && content[i] !== quote) {\n if (!isRaw && content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else if (content[i] === \"\\n\") {\n result += \"\\n\";\n i++;\n break; // unterminated string\n } else {\n result += content[i++];\n }\n }\n if (i < content.length && content[i] === quote) { result += content[i]; i++; }\n }\n else {\n result += content[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Ruby comments: # line comments + =begin/=end block comments\n * Handles string interpolation #{} inside double-quoted strings.\n */\nfunction stripRuby(content: string): string {\n const lines = content.split(\"\\n\");\n const result: string[] = [];\n let inBlock = false;\n\n for (const line of lines) {\n if (!inBlock && /^=begin(\\s|$)/.test(line)) {\n inBlock = true;\n result.push(\" \".repeat(line.length));\n continue;\n }\n if (inBlock) {\n if (/^=end(\\s|$)/.test(line)) {\n inBlock = false;\n }\n result.push(\" \".repeat(line.length));\n continue;\n }\n\n // Process line for # comments (respecting strings)\n let processed = \"\";\n let i = 0;\n while (i < line.length) {\n if (line[i] === \"#\") {\n // Outside of any string, this is a comment\n processed += \" \".repeat(line.length - i);\n break;\n } else if (line[i] === '\"') {\n processed += line[i]; i++;\n processed = scanRubyDoubleQuotedString(line, i, processed);\n i = (scanRubyDoubleQuotedStringIndex(line, i));\n } else if (line[i] === \"'\") {\n processed += line[i]; i++;\n while (i < line.length && line[i] !== \"'\") {\n if (line[i] === \"\\\\\" && i + 1 < line.length) {\n processed += line[i++];\n processed += line[i++];\n } else {\n processed += line[i++];\n }\n }\n if (i < line.length) { processed += line[i]; i++; }\n } else {\n processed += line[i]; i++;\n }\n }\n result.push(processed);\n }\n\n return result.join(\"\\n\");\n}\n\n/**\n * Scans a Ruby double-quoted string body starting at position i (after the opening \"),\n * handling #{} interpolation. Returns the resulting processed string content appended to `processed`.\n */\nfunction scanRubyDoubleQuotedString(line: string, startI: number, processed: string): string {\n let i = startI;\n while (i < line.length && line[i] !== '\"') {\n if (line[i] === \"\\\\\" && i + 1 < line.length) {\n processed += line[i++];\n processed += line[i++];\n } else if (line[i] === \"#\" && i + 1 < line.length && line[i + 1] === \"{\") {\n // String interpolation: #{...}\n processed += line[i++]; // #\n processed += line[i++]; // {\n let depth = 1;\n while (i < line.length && depth > 0) {\n if (line[i] === \"{\") {\n depth++;\n processed += line[i++];\n } else if (line[i] === \"}\") {\n depth--;\n processed += line[i++];\n } else if (line[i] === \"\\\\\" && i + 1 < line.length) {\n processed += line[i++];\n processed += line[i++];\n } else {\n processed += line[i++];\n }\n }\n } else {\n processed += line[i++];\n }\n }\n if (i < line.length) { processed += line[i]; /* closing \" */ }\n return processed;\n}\n\n/**\n * Returns the index after scanning a Ruby double-quoted string body starting at startI.\n */\nfunction scanRubyDoubleQuotedStringIndex(line: string, startI: number): number {\n let i = startI;\n while (i < line.length && line[i] !== '\"') {\n if (line[i] === \"\\\\\" && i + 1 < line.length) {\n i += 2;\n } else if (line[i] === \"#\" && i + 1 < line.length && line[i + 1] === \"{\") {\n i += 2; // skip #{\n let depth = 1;\n while (i < line.length && depth > 0) {\n if (line[i] === \"{\") {\n depth++;\n i++;\n } else if (line[i] === \"}\") {\n depth--;\n i++;\n } else if (line[i] === \"\\\\\" && i + 1 < line.length) {\n i += 2;\n } else {\n i++;\n }\n }\n } else {\n i++;\n }\n }\n if (i < line.length) { i++; /* skip closing \" */ }\n return i;\n}\n\n/**\n * PHP comments: // line, /* block *\\/, and # line\n * Also handles heredoc (<<<IDENTIFIER) and nowdoc (<<<'IDENTIFIER').\n */\nfunction stripPhp(content: string): string {\n let result = \"\";\n let i = 0;\n while (i < content.length) {\n // Heredoc / Nowdoc: <<<IDENTIFIER or <<<'IDENTIFIER'\n if (content[i] === \"<\" && content[i + 1] === \"<\" && content[i + 2] === \"<\") {\n const heredocStart = i;\n let j = i + 3;\n let isNowdoc = false;\n // Skip optional whitespace after <<<\n // Check for nowdoc (single-quoted identifier)\n if (j < content.length && content[j] === \"'\") {\n isNowdoc = true;\n j++; // skip opening '\n }\n // Read identifier\n const identStart = j;\n while (j < content.length && /[A-Za-z0-9_]/.test(content[j])) {\n j++;\n }\n const identifier = content.slice(identStart, j);\n if (identifier.length > 0) {\n // For nowdoc, skip the closing '\n if (isNowdoc && j < content.length && content[j] === \"'\") {\n j++;\n }\n // Check for optional semicolon and newline (or just newline) to validate this is heredoc syntax\n // We need at least a newline after the identifier line\n let validHeredoc = false;\n let lineEnd = j;\n // There might be a semicolon and/or other chars before newline, but typically just newline\n if (lineEnd < content.length && content[lineEnd] === \"\\n\") {\n validHeredoc = true;\n }\n if (validHeredoc) {\n // Output the heredoc opening line as-is (<<<IDENTIFIER or <<<'IDENTIFIER')\n for (let k = heredocStart; k <= lineEnd; k++) {\n if (content[k] === \"\\n\") {\n result += \"\\n\";\n } else {\n result += content[k];\n }\n }\n i = lineEnd + 1; // move past the newline\n // Now scan for the closing identifier on its own line\n // The closing identifier may optionally be followed by ; and must be at end of line\n let found = false;\n while (i < content.length && !found) {\n // Check if current line starts with the identifier\n const lineStart = i;\n // Read the current line\n let lineEndIdx = i;\n while (lineEndIdx < content.length && content[lineEndIdx] !== \"\\n\") {\n lineEndIdx++;\n }\n const currentLine = content.slice(lineStart, lineEndIdx);\n // PHP 7.3+ allows indented heredoc closing, but we check trimmed\n const trimmedLine = currentLine.trimStart();\n if (\n trimmedLine === identifier ||\n trimmedLine === identifier + \";\" ||\n trimmedLine === identifier + \",\" ||\n trimmedLine === identifier + \");\" ||\n trimmedLine === identifier + \")\" ||\n trimmedLine.startsWith(identifier + \";\") ||\n trimmedLine === identifier\n ) {\n // This is the closing line - output it as-is\n for (let k = lineStart; k < lineEndIdx; k++) {\n result += content[k];\n }\n i = lineEndIdx;\n if (i < content.length && content[i] === \"\\n\") {\n result += \"\\n\";\n i++;\n }\n found = true;\n } else {\n // Body line: replace content with spaces, preserve newlines\n for (let k = lineStart; k < lineEndIdx; k++) {\n result += \" \";\n }\n i = lineEndIdx;\n if (i < content.length && content[i] === \"\\n\") {\n result += \"\\n\";\n i++;\n }\n }\n }\n continue;\n }\n }\n // If not a valid heredoc, just output the < and continue\n result += content[i];\n i++;\n continue;\n }\n\n // Single-line comments (// or #)\n if ((content[i] === \"/\" && content[i + 1] === \"/\") || content[i] === \"#\") {\n while (i < content.length && content[i] !== \"\\n\") {\n result += \" \";\n i++;\n }\n }\n // Block comments\n else if (content[i] === \"/\" && content[i + 1] === \"*\") {\n result += \" \"; i++;\n result += \" \"; i++;\n while (i < content.length) {\n if (content[i] === \"*\" && content[i + 1] === \"/\") {\n result += \" \"; i++;\n result += \" \"; i++;\n break;\n }\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n }\n // Double-quoted string\n else if (content[i] === '\"') {\n result += content[i]; i++;\n while (i < content.length && content[i] !== '\"') {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else {\n result += content[i++];\n }\n }\n if (i < content.length) { result += content[i]; i++; }\n }\n // Single-quoted string\n else if (content[i] === \"'\") {\n result += content[i]; i++;\n while (i < content.length && content[i] !== \"'\") {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else {\n result += content[i++];\n }\n }\n if (i < content.length) { result += content[i]; i++; }\n }\n else {\n result += content[i]; i++;\n }\n }\n return result;\n}\n","import { readdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { LanguageId } from \"./types.js\";\n\n/** Marker files that identify a project's primary language */\nconst MARKERS: Array<{ file: string; language: LanguageId }> = [\n { file: \"Cargo.toml\", language: \"rust\" },\n { file: \"go.mod\", language: \"go\" },\n { file: \"pyproject.toml\", language: \"python\" },\n { file: \"setup.py\", language: \"python\" },\n { file: \"requirements.txt\", language: \"python\" },\n { file: \"Pipfile\", language: \"python\" },\n { file: \"pom.xml\", language: \"java\" },\n { file: \"build.gradle\", language: \"java\" },\n { file: \"build.gradle.kts\", language: \"kotlin\" },\n { file: \"build.sbt\", language: \"scala\" },\n { file: \"build.sc\", language: \"scala\" },\n { file: \"Package.swift\", language: \"swift\" },\n { file: \"Gemfile\", language: \"ruby\" },\n { file: \"composer.json\", language: \"php\" },\n { file: \"pubspec.yaml\", language: \"dart\" },\n { file: \"CMakeLists.txt\", language: \"c-cpp\" },\n { file: \"Makefile\", language: \"c-cpp\" },\n { file: \"package.json\", language: \"javascript\" },\n { file: \"tsconfig.json\", language: \"javascript\" },\n];\n\n/** Extension-based markers for languages without fixed-name marker files */\nconst EXT_MARKERS: Array<[string, LanguageId]> = [\n [\".sln\", \"c-sharp\"],\n [\".csproj\", \"c-sharp\"],\n];\n\n/** Extension-to-language mapping for fallback detection */\nconst EXT_MAP: Record<string, LanguageId> = {\n \".ts\": \"javascript\",\n \".tsx\": \"javascript\",\n \".js\": \"javascript\",\n \".jsx\": \"javascript\",\n \".mjs\": \"javascript\",\n \".cjs\": \"javascript\",\n \".py\": \"python\",\n \".rs\": \"rust\",\n \".go\": \"go\",\n \".java\": \"java\",\n \".c\": \"c-cpp\",\n \".cpp\": \"c-cpp\",\n \".cc\": \"c-cpp\",\n \".cxx\": \"c-cpp\",\n \".h\": \"c-cpp\",\n \".hpp\": \"c-cpp\",\n \".rb\": \"ruby\",\n \".php\": \"php\",\n \".swift\": \"swift\",\n \".kt\": \"kotlin\",\n \".kts\": \"kotlin\",\n \".cs\": \"c-sharp\",\n \".dart\": \"dart\",\n \".scala\": \"scala\",\n \".sc\": \"scala\",\n};\n\n/**\n * Auto-detect the primary language of a project directory.\n * 1. Check for marker files (Cargo.toml, go.mod, etc.)\n * 2. Fallback: count file extensions in the top-level directory\n */\nexport async function detectLanguage(rootDir: string): Promise<LanguageId> {\n // Phase 1: marker files\n for (const marker of MARKERS) {\n try {\n const s = await stat(join(rootDir, marker.file));\n if (s.isFile() || s.isDirectory()) {\n return marker.language;\n }\n } catch {\n // file doesn't exist, continue\n }\n }\n\n // Phase 1.5: extension-based markers (e.g. *.sln, *.csproj for C#)\n try {\n const topEntries = await readdir(rootDir, { withFileTypes: true });\n for (const entry of topEntries) {\n if (!entry.isFile()) continue;\n for (const [ext, lang] of EXT_MARKERS) {\n if (entry.name.endsWith(ext)) return lang;\n }\n }\n } catch {\n // ignore\n }\n\n // Phase 2: extension frequency (shallow scan)\n const counts = new Map<LanguageId, number>();\n try {\n await scanExtensions(rootDir, counts, 2, 0);\n } catch {\n // if scan fails, default to JS\n }\n\n if (counts.size > 0) {\n let maxLang: LanguageId = \"javascript\";\n let maxCount = 0;\n for (const [lang, count] of counts) {\n if (count > maxCount) {\n maxCount = count;\n maxLang = lang;\n }\n }\n return maxLang;\n }\n\n return \"javascript\";\n}\n\nasync function scanExtensions(\n dir: string,\n counts: Map<LanguageId, number>,\n maxDepth: number,\n currentDepth: number,\n): Promise<void> {\n if (currentDepth >= maxDepth) return;\n\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n\n if (entry.isDirectory() && currentDepth < maxDepth - 1) {\n await scanExtensions(\n join(dir, entry.name),\n counts,\n maxDepth,\n currentDepth + 1,\n );\n } else if (entry.isFile()) {\n const dotIdx = entry.name.lastIndexOf(\".\");\n if (dotIdx > 0) {\n const ext = entry.name.slice(dotIdx);\n const lang = EXT_MAP[ext];\n if (lang) {\n counts.set(lang, (counts.get(lang) ?? 0) + 1);\n }\n }\n }\n }\n}\n","import { readFileSync } from \"node:fs\";\nimport { join, dirname, resolve } from \"node:path\";\nimport { LANGUAGE_IDS } from \"./types.js\";\nimport type { LanguageConfig, LanguageId } from \"./types.js\";\n\n// ─── Python ──────────────────────────────────────────\nconst python: LanguageConfig = {\n id: \"python\",\n extensions: [\".py\"],\n commentStyle: \"python\",\n importPatterns: [\n // from package.module import something (including indented, e.g. inside try/except)\n { regex: /^\\s*from\\s+(\\.[\\w.]*|\\w[\\w.]*)\\s+import\\b/gm },\n // import package.module (handled by extractImports for multi-module case)\n ],\n // Bug #1 fix: custom extractImports to handle `import a, b, c`\n // Bug #12 fix: allow leading whitespace to catch try/except indented imports\n extractImports(content: string): string[] {\n const imports: string[] = [];\n\n // from package.module import something (including indented)\n const fromRegex = /^\\s*from\\s+(\\.[\\w.]*|\\w[\\w.]*)\\s+import\\b/gm;\n let match: RegExpExecArray | null;\n while ((match = fromRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // import a, b, c — captures all comma-separated modules (including indented)\n const importRegex = /^\\s*import\\s+([\\w.]+(?:\\s*,\\s*[\\w.]+)*)/gm;\n while ((match = importRegex.exec(content)) !== null) {\n const modules = match[1].split(\",\");\n for (const mod of modules) {\n const trimmed = mod.trim();\n if (trimmed) imports.push(trimmed);\n }\n }\n\n return imports;\n },\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Relative imports (starts with .)\n if (importPath.startsWith(\".\")) {\n const dots = importPath.match(/^\\.+/)?.[0].length ?? 1;\n let base = dirname(sourceFile);\n for (let i = 1; i < dots; i++) base = dirname(base);\n const rest = importPath.slice(dots).replace(/\\./g, \"/\");\n return tryPythonResolve(join(base, rest), projectFiles);\n }\n // Absolute imports\n const parts = importPath.replace(/\\./g, \"/\");\n return tryPythonResolve(join(rootDir, parts), projectFiles);\n },\n defaultExclude: [\"__pycache__\", \"\\\\.venv\", \"venv\", \"\\\\.egg-info\", \"dist\", \"build\"],\n};\n\nfunction tryPythonResolve(base: string, projectFiles: Set<string>): string | null {\n // Try as module file\n if (projectFiles.has(base + \".py\")) return base + \".py\";\n // Try as package __init__.py\n if (projectFiles.has(join(base, \"__init__.py\"))) return join(base, \"__init__.py\");\n return null;\n}\n\n// ─── Rust ────────────────────────────────────────────\nconst rust: LanguageConfig = {\n id: \"rust\",\n extensions: [\".rs\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports\n extractImports(content: string): string[] {\n const imports: string[] = [];\n\n // mod declarations: mod child;\n const modRegex = /\\bmod\\s+(\\w+)\\s*;/gm;\n let match: RegExpExecArray | null;\n while ((match = modRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // use crate::... (simple and grouped)\n const useRegex = /\\buse\\s+crate::([\\s\\S]*?);/gm;\n while ((match = useRegex.exec(content)) !== null) {\n const body = match[1].trim();\n extractRustUsePaths(body, \"\", imports);\n }\n\n // Bug #2 fix: use super::... imports\n const useSuperRegex = /\\buse\\s+super::([\\s\\S]*?);/gm;\n while ((match = useSuperRegex.exec(content)) !== null) {\n const body = match[1].trim();\n extractRustUsePaths(body, \"\", imports, \"super\");\n }\n\n // Bug #2 fix: use self::... imports\n const useSelfRegex = /\\buse\\s+self::([\\s\\S]*?);/gm;\n while ((match = useSelfRegex.exec(content)) !== null) {\n const body = match[1].trim();\n extractRustUsePaths(body, \"\", imports, \"self\");\n }\n\n return imports;\n },\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n const srcDir = join(rootDir, \"src\");\n\n if (!importPath.includes(\"::\")) {\n // mod declaration: look for child module\n const parentDir = dirname(sourceFile);\n const asFile = join(parentDir, importPath + \".rs\");\n if (projectFiles.has(asFile)) return asFile;\n const asDir = join(parentDir, importPath, \"mod.rs\");\n if (projectFiles.has(asDir)) return asDir;\n return null;\n }\n\n // Bug #3 fix: use super::x::y — resolve from parent directory of source file\n if (importPath.startsWith(\"super::\")) {\n const parentDir = dirname(dirname(sourceFile));\n const segments = importPath.slice(\"super::\".length).split(\"::\");\n for (let i = segments.length; i > 0; i--) {\n const path = segments.slice(0, i).join(\"/\");\n const asFile = join(parentDir, path + \".rs\");\n if (projectFiles.has(asFile)) return asFile;\n const asDir = join(parentDir, path, \"mod.rs\");\n if (projectFiles.has(asDir)) return asDir;\n }\n return null;\n }\n\n // Bug #4 fix: use self::x::y — resolve from same directory as source file\n if (importPath.startsWith(\"self::\")) {\n const selfDir = dirname(sourceFile);\n const segments = importPath.slice(\"self::\".length).split(\"::\");\n for (let i = segments.length; i > 0; i--) {\n const path = segments.slice(0, i).join(\"/\");\n const asFile = join(selfDir, path + \".rs\");\n if (projectFiles.has(asFile)) return asFile;\n const asDir = join(selfDir, path, \"mod.rs\");\n if (projectFiles.has(asDir)) return asDir;\n }\n return null;\n }\n\n // use crate::x::y — resolve from src/\n const segments = importPath.split(\"::\");\n // Try progressively shorter prefixes as directory/file\n for (let i = segments.length; i > 0; i--) {\n const path = segments.slice(0, i).join(\"/\");\n const asFile = join(srcDir, path + \".rs\");\n if (projectFiles.has(asFile)) return asFile;\n const asDir = join(srcDir, path, \"mod.rs\");\n if (projectFiles.has(asDir)) return asDir;\n }\n return null;\n },\n defaultExclude: [\"target\"],\n};\n\n/**\n * Recursively extract Rust use paths, handling grouped imports.\n * e.g. \"foo::{bar, baz::qux}\" → [\"foo::bar\", \"foo::baz::qux\"]\n * e.g. \"foo::bar\" → [\"foo::bar\"]\n * The optional rootPrefix parameter prepends \"super\" or \"self\" to all paths.\n */\nfunction extractRustUsePaths(body: string, prefix: string, results: string[], rootPrefix?: string): void {\n const trimmed = body.trim();\n\n // Check for grouped import: path::{...}\n const braceStart = trimmed.indexOf(\"{\");\n if (braceStart === -1) {\n // Simple path like \"foo::bar::Baz\" or just \"foo\"\n let path = prefix ? `${prefix}::${trimmed}` : trimmed;\n if (rootPrefix) {\n path = `${rootPrefix}::${path}`;\n }\n // Remove trailing items after last :: that aren't module names\n // Keep the full path — resolver handles progressively shorter prefixes\n if (path && !path.includes(\"{\")) {\n results.push(path);\n }\n return;\n }\n\n // Extract the prefix before the brace\n let pathPrefix = trimmed.slice(0, braceStart).trim();\n if (pathPrefix.endsWith(\"::\")) {\n pathPrefix = pathPrefix.slice(0, -2);\n }\n const fullPrefix = prefix ? `${prefix}::${pathPrefix}` : pathPrefix;\n\n // Find matching closing brace\n let depth = 0;\n let braceEnd = -1;\n for (let i = braceStart; i < trimmed.length; i++) {\n if (trimmed[i] === \"{\") depth++;\n else if (trimmed[i] === \"}\") {\n depth--;\n if (depth === 0) { braceEnd = i; break; }\n }\n }\n if (braceEnd === -1) return;\n\n // Split the brace content by commas (respecting nested braces)\n const inner = trimmed.slice(braceStart + 1, braceEnd).trim();\n const items = splitByTopLevelComma(inner);\n\n for (const item of items) {\n const cleaned = item.trim();\n if (cleaned === \"self\") {\n const selfPath = rootPrefix ? `${rootPrefix}::${fullPrefix}` : fullPrefix;\n results.push(selfPath);\n } else if (cleaned) {\n extractRustUsePaths(cleaned, fullPrefix, results, rootPrefix);\n }\n }\n}\n\n/** Split string by commas, respecting nested braces */\nfunction splitByTopLevelComma(s: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < s.length; i++) {\n if (s[i] === \"{\") depth++;\n else if (s[i] === \"}\") depth--;\n else if (s[i] === \",\" && depth === 0) {\n parts.push(s.slice(start, i));\n start = i + 1;\n }\n }\n parts.push(s.slice(start));\n return parts;\n}\n\n// ─── Go ──────────────────────────────────────────────\nconst go: LanguageConfig = {\n id: \"go\",\n extensions: [\".go\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports\n extractImports(content: string): string[] {\n const imports: string[] = [];\n\n // Single import: import \"path\" or import alias \"path\"\n const singleRegex = /\\bimport\\s+(?:\\w+\\s+)?\"([^\"]+)\"/gm;\n let match: RegExpExecArray | null;\n while ((match = singleRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // Import block: import ( ... )\n const blockRegex = /\\bimport\\s*\\(([^)]*)\\)/gms;\n while ((match = blockRegex.exec(content)) !== null) {\n const block = match[1];\n const entryRegex = /(?:\\w+\\s+)?\"([^\"]+)\"/g;\n let entry: RegExpExecArray | null;\n while ((entry = entryRegex.exec(block)) !== null) {\n imports.push(entry[1]);\n }\n }\n\n return imports;\n },\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Only resolve project-internal imports\n // Read go.mod module path to strip prefix\n const modPrefix = goModulePrefix(rootDir);\n if (!modPrefix || !importPath.startsWith(modPrefix)) return null;\n\n const relPath = importPath.slice(modPrefix.length + 1); // +1 for /\n // Go packages are directories; find any .go file in the directory\n const pkgDir = join(rootDir, relPath);\n for (const f of projectFiles) {\n if (f.startsWith(pkgDir + \"/\") && f.endsWith(\".go\")) return f;\n }\n return null;\n },\n defaultExclude: [\"vendor\"],\n};\n\n// Bug #11: goModCache is a per-process cache of go.mod module prefixes, keyed by rootDir.\n// It is intentionally never cleared because the module path for a given rootDir does not\n// change during the lifetime of a single process. If a long-running process needs to pick\n// up go.mod changes, restart the process.\nconst goModCache = new Map<string, string | null>();\nfunction goModulePrefix(rootDir: string): string | null {\n if (goModCache.has(rootDir)) return goModCache.get(rootDir)!;\n try {\n const content = readFileSync(join(rootDir, \"go.mod\"), \"utf-8\");\n const match = content.match(/^module\\s+(.+)$/m);\n const prefix = match ? match[1].trim() : null;\n goModCache.set(rootDir, prefix);\n return prefix;\n } catch {\n goModCache.set(rootDir, null);\n return null;\n }\n}\n\n// ─── Java ────────────────────────────────────────────\nconst java: LanguageConfig = {\n id: \"java\",\n extensions: [\".java\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // Bug #5 fix: import com.example.ClassName; and import com.example.*; (wildcard)\n // Bug #6: static imports also captured here\n { regex: /^import\\s+(?:static\\s+)?([\\w.*]+);/gm },\n ],\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Bug #5 fix: wildcard imports — can't resolve to a single file, return null\n if (importPath.endsWith(\".*\")) {\n return null;\n }\n\n // Bug #6 fix: static imports — try progressively shorter paths\n // e.g. com.example.Class.method → try com/example/Class/method.java,\n // then com/example/Class.java, then com/example.java, etc.\n const segments = importPath.split(\".\");\n for (let i = segments.length; i > 0; i--) {\n const filePath = segments.slice(0, i).join(\"/\") + \".java\";\n for (const srcRoot of [\"\", \"src/main/java/\", \"src/\", \"app/src/main/java/\"]) {\n const full = join(rootDir, srcRoot, filePath);\n if (projectFiles.has(full)) return full;\n }\n }\n // Suffix fallback: try right-side segments for non-Maven layouts\n // e.g. com.example.analytics.processors.TraceProcessor → processors/TraceProcessor.java\n for (let i = 1; i < segments.length; i++) {\n const filePath = segments.slice(i).join(\"/\") + \".java\";\n for (const srcRoot of [\"\", \"src/main/java/\", \"src/\", \"app/src/main/java/\"]) {\n const full = join(rootDir, srcRoot, filePath);\n if (projectFiles.has(full)) return full;\n }\n }\n return null;\n },\n defaultExclude: [\"build\", \"target\", \"\\\\.gradle\", \"\\\\.idea\"],\n};\n\n// ─── C/C++ ───────────────────────────────────────────\nconst cCpp: LanguageConfig = {\n id: \"c-cpp\",\n extensions: [\".c\", \".cpp\", \".cc\", \".cxx\", \".h\", \".hpp\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // #include \"file.h\" (skip <system> includes)\n { regex: /^#include\\s+\"([^\"]+)\"/gm },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Resolve relative to source file first\n const fromSource = resolve(dirname(sourceFile), importPath);\n if (projectFiles.has(fromSource)) return fromSource;\n // Then relative to root\n const fromRoot = join(rootDir, importPath);\n if (projectFiles.has(fromRoot)) return fromRoot;\n // Try common include dirs\n for (const incDir of [\"include\", \"src\"]) {\n const full = join(rootDir, incDir, importPath);\n if (projectFiles.has(full)) return full;\n }\n return null;\n },\n defaultExclude: [\"build\", \"cmake-build\", \"\\\\.o$\", \"\\\\.obj$\"],\n};\n\n// ─── Ruby ────────────────────────────────────────────\nconst ruby: LanguageConfig = {\n id: \"ruby\",\n extensions: [\".rb\"],\n commentStyle: \"ruby\",\n importPatterns: [\n // require_relative 'path'\n { regex: /\\brequire_relative\\s+['\"]([^'\"]+)['\"]/gm },\n // require 'path' (for project-internal requires)\n { regex: /\\brequire\\s+['\"]([^'\"]+)['\"]/gm },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n const withExt = importPath.endsWith(\".rb\") ? importPath : importPath + \".rb\";\n // require_relative: relative to source\n const fromSource = resolve(dirname(sourceFile), withExt);\n if (projectFiles.has(fromSource)) return fromSource;\n // require: relative to root or lib/\n const fromRoot = join(rootDir, withExt);\n if (projectFiles.has(fromRoot)) return fromRoot;\n const fromLib = join(rootDir, \"lib\", withExt);\n if (projectFiles.has(fromLib)) return fromLib;\n return null;\n },\n defaultExclude: [\"vendor\", \"\\\\.bundle\"],\n};\n\n// ─── PHP ─────────────────────────────────────────────\nconst php: LanguageConfig = {\n id: \"php\",\n extensions: [\".php\"],\n commentStyle: \"php\",\n importPatterns: [\n // require/include/require_once/include_once 'path'\n { regex: /\\b(?:require|include)(?:_once)?\\s+['\"]([^'\"]+)['\"]/gm },\n // require_once __DIR__ . '/path' (common PHP pattern)\n { regex: /\\b(?:require|include)(?:_once)?\\s+__DIR__\\s*\\.\\s*['\"]([^'\"]+)['\"]/gm },\n // Bug #9 fix: use Namespace\\Class — skip `function` and `const` keywords\n { regex: /^use\\s+(?:function\\s+|const\\s+)?([\\w\\\\]+)/gm },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Handle __DIR__ relative paths: strip leading /\n let normalizedPath = importPath;\n if (normalizedPath.startsWith(\"/\")) {\n normalizedPath = normalizedPath.slice(1);\n }\n // Direct file path\n if (normalizedPath.includes(\"/\") || normalizedPath.endsWith(\".php\")) {\n const withExt = normalizedPath.endsWith(\".php\") ? normalizedPath : normalizedPath + \".php\";\n const fromSource = resolve(dirname(sourceFile), withExt);\n if (projectFiles.has(fromSource)) return fromSource;\n const fromRoot = join(rootDir, withExt);\n if (projectFiles.has(fromRoot)) return fromRoot;\n return null;\n }\n // PSR-4: Namespace\\Class → Namespace/Class.php\n const filePath = importPath.replace(/\\\\/g, \"/\") + \".php\";\n const fromRoot = join(rootDir, filePath);\n if (projectFiles.has(fromRoot)) return fromRoot;\n const fromSrc = join(rootDir, \"src\", filePath);\n if (projectFiles.has(fromSrc)) return fromSrc;\n return null;\n },\n defaultExclude: [\"vendor\"],\n};\n\n// ─── Swift ───────────────────────────────────────────\n/** Files that don't define referenceable Swift types */\nconst SWIFT_SKIP_FILES = new Set([\"Package\", \"main\", \"AppDelegate\", \"SceneDelegate\"]);\n\nconst swift: LanguageConfig = {\n id: \"swift\",\n extensions: [\".swift\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports\n extractImports(content: string, filePath: string, _rootDir: string, projectFiles: Set<string>): string[] {\n const imports: string[] = [];\n\n // 1. Module imports: import ModuleName / @testable import ModuleName\n const moduleRegex = /^(?:@testable\\s+)?import\\s+(?:class\\s+|struct\\s+|enum\\s+|protocol\\s+|func\\s+|var\\s+|let\\s+|typealias\\s+)?(\\w+)/gm;\n let match: RegExpExecArray | null;\n while ((match = moduleRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // 2. Intra-module type reference scanning (like C# engine)\n // Swift files in the same module can reference each other without imports\n const typeMap = new Map<string, string>();\n for (const f of projectFiles) {\n if (f === filePath || !f.endsWith(\".swift\")) continue;\n const basename = f.split(\"/\").pop()!.replace(/\\.swift$/, \"\");\n if (!basename || SWIFT_SKIP_FILES.has(basename)) continue;\n typeMap.set(basename, f);\n }\n\n if (typeMap.size > 0) {\n const escaped = [...typeMap.keys()].map((n) =>\n n.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"),\n );\n const combined = new RegExp(`\\\\b(${escaped.join(\"|\")})\\\\b`, \"g\");\n const matched = new Set<string>();\n while ((match = combined.exec(content)) !== null) {\n const typeName = match[1];\n const targetPath = typeMap.get(typeName);\n if (targetPath && !matched.has(targetPath)) {\n matched.add(targetPath);\n imports.push(targetPath); // full path — resolveImport passes through\n }\n }\n }\n\n return imports;\n },\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Direct file path (from type reference scanning)\n if (projectFiles.has(importPath)) return importPath;\n\n // Cross-module: import Module → find Sources/Module/*.swift\n const spmDir = join(rootDir, \"Sources\", importPath);\n for (const f of projectFiles) {\n if (f.startsWith(spmDir + \"/\") && f.endsWith(\".swift\")) return f;\n }\n return null;\n },\n defaultExclude: [\"\\\\.build\", \"DerivedData\"],\n};\n\n// ─── Kotlin ──────────────────────────────────────────\nconst kotlin: LanguageConfig = {\n id: \"kotlin\",\n extensions: [\".kt\", \".kts\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // Bug #7/#8 fix: import com.example.ClassName and import com.example.*\n { regex: /^import\\s+([\\w.*]+)/gm },\n ],\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Bug #8 fix: wildcard imports — can't resolve to a single file, return null\n if (importPath.endsWith(\".*\")) {\n return null;\n }\n\n // Bug #7 fix: strip trailing dot if present (from previous regex issues)\n let cleanPath = importPath;\n if (cleanPath.endsWith(\".\")) {\n cleanPath = cleanPath.slice(0, -1);\n }\n\n // Convert com.example.Class to com/example/Class.kt\n const segments = cleanPath.split(\".\");\n const filePath = segments.join(\"/\");\n for (const ext of [\".kt\", \".kts\"]) {\n for (const srcRoot of [\n \"\",\n \"src/main/kotlin/\",\n \"src/main/java/\",\n \"src/\",\n \"app/src/main/kotlin/\",\n \"app/src/main/java/\",\n ]) {\n const full = join(rootDir, srcRoot, filePath + ext);\n if (projectFiles.has(full)) return full;\n }\n }\n // Suffix fallback: try right-side segments for non-standard layouts\n for (let i = 1; i < segments.length; i++) {\n const suffixPath = segments.slice(i).join(\"/\");\n for (const ext of [\".kt\", \".kts\"]) {\n for (const srcRoot of [\n \"\",\n \"src/main/kotlin/\",\n \"src/main/java/\",\n \"src/\",\n \"app/src/main/kotlin/\",\n \"app/src/main/java/\",\n ]) {\n const full = join(rootDir, srcRoot, suffixPath + ext);\n if (projectFiles.has(full)) return full;\n }\n }\n }\n return null;\n },\n defaultExclude: [\"build\", \"\\\\.gradle\", \"\\\\.idea\"],\n};\n\n// ─── C# ──────────────────────────────────────────────\n/** Files that don't define referenceable classes */\nconst CS_SKIP_CLASSNAMES = new Set([\"AssemblyInfo\", \"GlobalUsings\"]);\n\nconst cSharp: LanguageConfig = {\n id: \"c-sharp\",\n extensions: [\".cs\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports\n extractImports(content, filePath, _rootDir, projectFiles) {\n const imports: string[] = [];\n\n // 1. using statements (indented, global, static)\n const usingRegex = /^\\s*(?:global\\s+)?using\\s+(?:static\\s+)?([\\w.]+)\\s*;/gm;\n let match: RegExpExecArray | null;\n while ((match = usingRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // 2. Class-name reference scanning (same-namespace dependencies)\n // C# convention: class name = file name → scan for references\n const classMap = new Map<string, string>();\n for (const f of projectFiles) {\n if (f === filePath) continue;\n if (!f.endsWith(\".cs\")) continue;\n const basename = f.split(\"/\").pop()!;\n const className = basename.replace(/\\.xaml\\.cs$/i, \"\").replace(/\\.cs$/i, \"\");\n if (!className || CS_SKIP_CLASSNAMES.has(className)) continue;\n classMap.set(className, f);\n }\n\n if (classMap.size > 0) {\n // Build combined regex for efficient single-pass scanning\n const escaped = [...classMap.keys()].map((n) =>\n n.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"),\n );\n const combined = new RegExp(`\\\\b(${escaped.join(\"|\")})\\\\b`, \"g\");\n const matched = new Set<string>();\n while ((match = combined.exec(content)) !== null) {\n const className = match[1];\n const targetPath = classMap.get(className);\n if (targetPath && !matched.has(targetPath)) {\n matched.add(targetPath);\n imports.push(targetPath); // full path — resolveImport passes through\n }\n }\n }\n\n return imports;\n },\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Direct file path (from class name scanning)\n if (projectFiles.has(importPath)) return importPath;\n\n // Namespace-based resolution (using statements)\n const segments = importPath.split(\".\");\n\n // 1. Try as direct file path (progressively shorter, like Java static imports)\n for (let i = segments.length; i > 0; i--) {\n const filePath = segments.slice(0, i).join(\"/\") + \".cs\";\n for (const srcRoot of [\"\", \"src/\", \"lib/\"]) {\n const full = join(rootDir, srcRoot, filePath);\n if (projectFiles.has(full)) return full;\n }\n }\n\n // 2. C# using = namespace import; try as directory, find first .cs file\n // Strip leading segments progressively (handles root namespace prefix)\n for (let start = 0; start < segments.length; start++) {\n const dirPath = segments.slice(start).join(\"/\");\n for (const srcRoot of [\"\", \"src/\", \"lib/\"]) {\n const prefix = join(rootDir, srcRoot, dirPath) + \"/\";\n for (const f of projectFiles) {\n if (f.startsWith(prefix) && f.endsWith(\".cs\")) return f;\n }\n }\n }\n\n return null;\n },\n defaultExclude: [\"bin\", \"obj\", \"\\\\.vs\", \"packages\", \"TestResults\"],\n};\n\n// ─── Dart ────────────────────────────────────────────\nconst dart: LanguageConfig = {\n id: \"dart\",\n extensions: [\".dart\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // import 'package:pkg/file.dart'; or import 'relative/path.dart';\n { regex: /^import\\s+['\"]([^'\"]+)['\"]/gm },\n // export 'file.dart'; (re-exports create dependencies too)\n { regex: /^export\\s+['\"]([^'\"]+)['\"]/gm },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Skip dart: stdlib imports\n if (importPath.startsWith(\"dart:\")) return null;\n\n // package: imports — resolve own package to lib/\n if (importPath.startsWith(\"package:\")) {\n const ownPackage = dartPackageName(rootDir);\n if (!ownPackage) return null;\n const prefix = `package:${ownPackage}/`;\n if (!importPath.startsWith(prefix)) return null; // external package\n const relPath = importPath.slice(prefix.length);\n // Try lib/ first (standard pub.dev packages)\n const libPath = join(rootDir, \"lib\", relPath);\n if (projectFiles.has(libPath)) return libPath;\n // Fallback to root level (Flutter apps without lib/)\n const rootPath = join(rootDir, relPath);\n if (projectFiles.has(rootPath)) return rootPath;\n return null;\n }\n\n // Relative imports\n const resolved = resolve(dirname(sourceFile), importPath);\n if (projectFiles.has(resolved)) return resolved;\n return null;\n },\n defaultExclude: [\"\\\\.dart_tool\", \"build\", \"\\\\.packages\"],\n};\n\nconst dartPackageCache = new Map<string, string | null>();\nfunction dartPackageName(rootDir: string): string | null {\n if (dartPackageCache.has(rootDir)) return dartPackageCache.get(rootDir)!;\n try {\n const content = readFileSync(join(rootDir, \"pubspec.yaml\"), \"utf-8\");\n const match = content.match(/^name:\\s*(\\S+)/m);\n const name = match ? match[1] : null;\n dartPackageCache.set(rootDir, name);\n return name;\n } catch {\n dartPackageCache.set(rootDir, null);\n return null;\n }\n}\n\n// ─── Scala ───────────────────────────────────────────\nconst scala: LanguageConfig = {\n id: \"scala\",\n extensions: [\".scala\", \".sc\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports for grouped syntax\n extractImports(content: string): string[] {\n const imports: string[] = [];\n // import pkg.Class\n // import pkg.{A, B, C}\n // import pkg._ (wildcard)\n const importRegex = /\\bimport\\s+([\\w.]+(?:\\.\\{[^}]+\\}|\\.\\w+|\\._))/gm;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n const full = match[1];\n // Check for grouped imports: import pkg.{A, B}\n const braceMatch = full.match(/^([\\w.]+)\\.\\{([^}]+)\\}$/);\n if (braceMatch) {\n const prefix = braceMatch[1];\n const items = braceMatch[2].split(\",\");\n for (const item of items) {\n const trimmed = item.trim().split(/\\s+/)[0]; // handle \"A => B\" rename\n if (trimmed === \"_\") continue; // wildcard in group\n imports.push(`${prefix}.${trimmed}`);\n }\n } else if (full.endsWith(\"._\")) {\n // Wildcard import — skip\n continue;\n } else {\n imports.push(full);\n }\n }\n return imports;\n },\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n const segments = importPath.split(\".\");\n // Try progressively shorter left-prefix paths\n for (let i = segments.length; i > 0; i--) {\n const filePath = segments.slice(0, i).join(\"/\");\n for (const ext of [\".scala\", \".sc\"]) {\n for (const srcRoot of [\"\", \"src/main/scala/\", \"src/\", \"app/\"]) {\n const full = join(rootDir, srcRoot, filePath + ext);\n if (projectFiles.has(full)) return full;\n }\n }\n }\n // Suffix fallback: try right-side segments for non-SBT layouts\n for (let i = 1; i < segments.length; i++) {\n const suffixPath = segments.slice(i).join(\"/\");\n for (const ext of [\".scala\", \".sc\"]) {\n for (const srcRoot of [\"\", \"src/main/scala/\", \"src/\", \"app/\"]) {\n const full = join(rootDir, srcRoot, suffixPath + ext);\n if (projectFiles.has(full)) return full;\n }\n }\n }\n return null;\n },\n defaultExclude: [\"target\", \"\\\\.bsp\", \"\\\\.metals\", \"\\\\.bloop\"],\n};\n\n// ─── JavaScript / TypeScript ──────────────────────────\nconst javascript: LanguageConfig = {\n id: \"javascript\",\n extensions: [\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // ES6: import [type] [stuff from] \"path\"\n { regex: /import\\s+(?:type\\s+)?(?:[\\w*{}\\s,]+\\s+from\\s+)?[\"']([^\"']+)[\"']/g },\n // Dynamic: import(\"path\")\n { regex: /\\bimport\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g },\n // Re-export: export [type] { stuff } from \"path\" / export * from \"path\"\n { regex: /export\\s+(?:type\\s+)?(?:\\{[^}]*\\}|\\*(?:\\s+as\\s+\\w+)?)\\s+from\\s+[\"']([^\"']+)[\"']/g },\n // CommonJS: require(\"path\")\n { regex: /\\brequire\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Skip external modules: node:, @scope/pkg, bare specifiers without ./ or ../\n if (importPath.startsWith(\"node:\")) return null;\n if (!importPath.startsWith(\".\")) return null;\n\n // Resolve relative to source file\n const resolved = resolve(dirname(sourceFile), importPath);\n\n // 1. Exact match (e.g., \"./foo.js\" where foo.js actually exists)\n if (projectFiles.has(resolved)) return resolved;\n\n // 2. ESM convention: .js → .ts / .tsx (TypeScript emits .js in import paths)\n if (resolved.endsWith(\".js\")) {\n const tsPath = resolved.slice(0, -3) + \".ts\";\n if (projectFiles.has(tsPath)) return tsPath;\n const tsxPath = resolved.slice(0, -3) + \".tsx\";\n if (projectFiles.has(tsxPath)) return tsxPath;\n }\n if (resolved.endsWith(\".jsx\")) {\n const tsxPath = resolved.slice(0, -4) + \".tsx\";\n if (projectFiles.has(tsxPath)) return tsxPath;\n }\n\n // 3. Try adding extensions\n for (const ext of [\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\"]) {\n if (projectFiles.has(resolved + ext)) return resolved + ext;\n }\n\n // 4. Try as directory with index file\n for (const idx of [\"/index.ts\", \"/index.tsx\", \"/index.js\", \"/index.jsx\"]) {\n if (projectFiles.has(resolved + idx)) return resolved + idx;\n }\n\n return null;\n },\n defaultExclude: [\"node_modules\", \"\\\\.d\\\\.ts$\", \"dist\", \"build\", \"coverage\"],\n};\n\n// ─── Registry ────────────────────────────────────────\nconst LANGUAGE_CONFIGS: Record<LanguageId, LanguageConfig | null> = {\n javascript,\n python,\n rust,\n go,\n java,\n \"c-cpp\": cCpp,\n \"c-sharp\": cSharp,\n ruby,\n php,\n swift,\n kotlin,\n dart,\n scala,\n};\n\nexport function getLanguageConfig(id: LanguageId): LanguageConfig | null {\n return LANGUAGE_CONFIGS[id] ?? null;\n}\n\nexport function getAllLanguageIds(): LanguageId[] {\n return [...LANGUAGE_IDS];\n}\n","import type { DependencyGraph } from \"../../types/schema.js\";\n\n/** Single source of truth for supported language IDs */\nexport const LANGUAGE_IDS = [\n \"javascript\",\n \"python\",\n \"rust\",\n \"go\",\n \"java\",\n \"c-cpp\",\n \"c-sharp\",\n \"ruby\",\n \"php\",\n \"swift\",\n \"kotlin\",\n \"dart\",\n \"scala\",\n] as const;\n\nexport type LanguageId = (typeof LANGUAGE_IDS)[number];\n\nexport interface AnalyzerEngine {\n analyze(\n rootDir: string,\n options: { exclude?: string[]; maxDepth?: number },\n ): Promise<DependencyGraph>;\n}\n\nexport interface ImportPattern {\n regex: RegExp;\n}\n\nexport type ImportResolver = (\n importPath: string,\n sourceFile: string,\n rootDir: string,\n projectFiles: Set<string>,\n) => string | null;\n\nexport type CommentStyle =\n | \"c-style\" // // and /* */\n | \"hash\" // #\n | \"python\" // # and \"\"\" / '''\n | \"ruby\" // # and =begin/=end\n | \"php\"; // //, /* */, and #\n\nexport interface LanguageConfig {\n id: LanguageId;\n extensions: string[];\n importPatterns: ImportPattern[];\n resolveImport: ImportResolver;\n commentStyle: CommentStyle;\n defaultExclude?: string[];\n /** Custom import extractor for languages with complex syntax (e.g. Rust grouped use, C# class references) */\n extractImports?: (\n content: string,\n filePath: string,\n rootDir: string,\n projectFiles: Set<string>,\n ) => string[];\n}\n","export type Locale = \"en\" | \"ja\";\n\nlet currentLocale: Locale = detectLocale();\n\n/** Get the current locale */\nexport function getLocale(): Locale {\n return currentLocale;\n}\n\n/** Set the locale explicitly */\nexport function setLocale(locale: Locale): void {\n currentLocale = locale;\n}\n\n/** Auto-detect locale from LANG/LC_ALL environment variable */\nfunction detectLocale(): Locale {\n const env = process.env.LC_ALL || process.env.LANG || \"\";\n if (env.startsWith(\"ja\")) return \"ja\";\n return \"en\";\n}\n\n/** Get a translated message by key */\nexport function t(key: string, vars?: Record<string, string | number>): string {\n const messages = currentLocale === \"ja\" ? ja : en;\n let msg = (messages as Record<string, string>)[key] ?? (en as Record<string, string>)[key] ?? key;\n\n if (vars) {\n for (const [k, v] of Object.entries(vars)) {\n msg = msg.replaceAll(`{${k}}`, String(v));\n }\n }\n return msg;\n}\n\n// ─── Message catalogs ───────────────────────────────────────────\n\nconst en = {\n // Analyzer\n \"analyzer.failed\": \"dependency-cruiser failed: {message}\",\n \"analyzer.exitCode\": \"Analysis finished with error code {code}\",\n\n // Storage\n \"storage.parseFailed\": \"Failed to parse snapshot.json. File may be corrupted: {path}\",\n \"storage.readFailed\": \"Failed to read snapshot.json: {path}\",\n \"storage.invalidSchema\": \"snapshot.json schema is invalid. Please regenerate with `archtracker init`:\\n{issues}\",\n \"storage.versionMismatch\": \"snapshot.json version ({version}) is incompatible with current schema ({expected}). Regenerate with `archtracker init`.\",\n\n // Path guard\n \"pathGuard.traversal\": \"Path points outside project root: \\\"{input}\\\" → \\\"{resolved}\\\" (allowed: \\\"{boundary}\\\")\",\n\n // Diff report\n \"diff.title\": \"# Architecture Change Report\\n\",\n \"diff.noChanges\": \"No changes — snapshot matches current code.\\n\",\n \"diff.added\": \"## Added Files ({count})\",\n \"diff.removed\": \"## Removed Files ({count})\",\n \"diff.modified\": \"## Modified Dependencies ({count})\",\n \"diff.affected\": \"## Files Requiring Review ({count})\",\n \"diff.reasonRemoved\": \"Dependency \\\"{file}\\\" was removed\",\n \"diff.reasonModified\": \"Dependency \\\"{file}\\\" had its dependencies changed\",\n \"diff.reasonAdded\": \"New dependency \\\"{file}\\\" was added\",\n\n // Search\n \"search.pathMatch\": \"Path matches \\\"{pattern}\\\"\",\n \"search.affected\": \"May be affected by changes to \\\"{file}\\\" (via: {via})\",\n \"search.critical\": \"{count} files depend on this component\",\n \"search.orphan\": \"Orphan file (no dependencies, no dependents)\",\n \"search.noResults\": \"No results: \\\"{query}\\\" (mode: {mode})\",\n \"search.results\": \"Results: {count} (mode: {mode})\",\n\n // CLI\n \"cli.analyzing\": \"Analyzing...\",\n \"cli.snapshotSaved\": \"Snapshot saved\",\n \"cli.timestamp\": \" Timestamp: {ts}\",\n \"cli.fileCount\": \" Files: {count}\",\n \"cli.edgeCount\": \" Edges: {count}\",\n \"cli.circularCount\": \" Circular deps: {count}\",\n \"cli.keyComponents\": \"\\nKey components:\",\n \"cli.dependedBy\": \"{path} ({count} dependents)\",\n \"cli.noSnapshot\": \"No snapshot found. Run `archtracker init` first.\",\n \"cli.ciFailed\": \"\\nCI check failed: {count} file(s) require review\",\n \"cli.autoGenerating\": \"No snapshot found, auto-generating...\",\n \"cli.project\": \"Project: {path}\",\n \"cli.validPaths\": \"\\nValid file paths:\",\n \"cli.snapshot\": \"Snapshot: {ts}\",\n\n // MCP\n \"mcp.analyzeComplete\": \"Analysis complete: {files} files, {edges} edges\",\n \"mcp.circularFound\": \"Circular deps: {count} found\",\n \"mcp.circularNone\": \"Circular deps: none\",\n \"mcp.snapshotSaved\": \"Snapshot saved\",\n \"mcp.autoInit\": \"No snapshot existed. Initial snapshot auto-generated.\",\n \"mcp.nextCheckEnabled\": \"Diff checking will be active from the next run.\",\n \"mcp.queryRequired\": \"\\\"{mode}\\\" mode requires the query parameter\",\n\n // Analyze report\n \"analyze.title\": \"# Architecture Analysis Report\\n\",\n \"analyze.overview\": \"## Overview\",\n \"analyze.totalFiles\": \" Total files: {count}\",\n \"analyze.totalEdges\": \" Total edges: {count}\",\n \"analyze.totalCircular\": \" Circular dependencies: {count}\",\n \"analyze.criticalTitle\": \"\\n## Critical Components (Top {count})\",\n \"analyze.criticalItem\": \" {path} ({count} dependents)\",\n \"analyze.circularTitle\": \"\\n## Circular Dependencies ({count})\",\n \"analyze.circularItem\": \" {files}\",\n \"analyze.orphanTitle\": \"\\n## Orphan Files ({count})\",\n \"analyze.couplingTitle\": \"\\n## High Coupling (Top {count} by import count)\",\n \"analyze.couplingItem\": \" {path} ({count} imports)\",\n \"analyze.layerTitle\": \"\\n## Directory Breakdown\",\n \"analyze.layerItem\": \" {dir}/ — {count} files\",\n \"analyze.noIssues\": \"\\nNo architectural issues detected.\",\n \"analyze.snapshotSaved\": \"\\nSnapshot saved alongside analysis.\",\n\n // CI\n \"ci.generated\": \"GitHub Actions workflow generated: {path}\",\n\n // Layers\n \"layers.alreadyExists\": \"layers.json already exists. Edit it manually to modify.\",\n \"layers.created\": \"Created .archtracker/layers.json — edit it to configure your layers.\",\n \"layers.notFound\": \"No .archtracker/layers.json found. Run 'archtracker layers init' to create one.\",\n \"layers.header\": \"Configured layers ({count}):\",\n\n // Web viewer\n \"web.starting\": \"Starting architecture viewer...\",\n \"web.listening\": \"Architecture graph available at: http://localhost:{port}\",\n \"web.stop\": \"Press Ctrl+C to stop\",\n \"web.watching\": \"Watching {dir}/ for changes...\",\n \"web.reloading\": \"File change detected, reloading...\",\n \"web.reloaded\": \"Graph reloaded\",\n\n // Errors\n \"error.analyzer\": \"[Analysis Error] {message}\",\n \"error.storage\": \"[Storage Error] {message}\",\n \"error.pathTraversal\": \"[Security Error] {message}\",\n \"error.generic\": \"[Error] {message}\",\n \"error.unexpected\": \"[Error] Unexpected error: {message}\",\n \"error.cli.analyzer\": \"Analysis error: {message}\",\n \"error.cli.storage\": \"Storage error: {message}\",\n \"error.cli.generic\": \"Error: {message}\",\n \"error.cli.unexpected\": \"Unexpected error: {message}\",\n} as const;\n\nconst ja = {\n // Analyzer\n \"analyzer.failed\": \"dependency-cruiser の実行に失敗しました: {message}\",\n \"analyzer.exitCode\": \"解析がエラーコード {code} で終了しました\",\n\n // Storage\n \"storage.parseFailed\": \"snapshot.json のパースに失敗しました。ファイルが破損している可能性があります: {path}\",\n \"storage.readFailed\": \"snapshot.json の読み取りに失敗しました: {path}\",\n \"storage.invalidSchema\": \"snapshot.json のスキーマが不正です。archtracker init で再生成してください:\\n{issues}\",\n \"storage.versionMismatch\": \"snapshot.json のバージョン ({version}) が現在のスキーマ ({expected}) と互換性がありません。archtracker init で再生成してください。\",\n\n // Path guard\n \"pathGuard.traversal\": \"パスがプロジェクトルートの外部を指しています: \\\"{input}\\\" → \\\"{resolved}\\\" (許可範囲: \\\"{boundary}\\\")\",\n\n // Diff report\n \"diff.title\": \"# アーキテクチャ変更レポート\\n\",\n \"diff.noChanges\": \"変更なし — スナップショットと現在のコードは一致しています。\\n\",\n \"diff.added\": \"## 追加されたファイル ({count}件)\",\n \"diff.removed\": \"## 削除されたファイル ({count}件)\",\n \"diff.modified\": \"## 依存関係が変更されたファイル ({count}件)\",\n \"diff.affected\": \"## 確認が必要なファイル ({count}件)\",\n \"diff.reasonRemoved\": \"依存先 \\\"{file}\\\" が削除されました\",\n \"diff.reasonModified\": \"依存先 \\\"{file}\\\" の依存関係が変更されました\",\n \"diff.reasonAdded\": \"新しい依存先 \\\"{file}\\\" が追加されました\",\n\n // Search\n \"search.pathMatch\": \"パスが \\\"{pattern}\\\" にマッチ\",\n \"search.affected\": \"\\\"{file}\\\" の変更により影響を受ける可能性(経由: {via})\",\n \"search.critical\": \"{count}件のファイルが依存する重要コンポーネント\",\n \"search.orphan\": \"孤立ファイル(依存なし・被依存なし)\",\n \"search.noResults\": \"検索結果なし: \\\"{query}\\\" (モード: {mode})\",\n \"search.results\": \"検索結果: {count}件 (モード: {mode})\",\n\n // CLI\n \"cli.analyzing\": \"解析中...\",\n \"cli.snapshotSaved\": \"スナップショットを保存しました\",\n \"cli.timestamp\": \" タイムスタンプ: {ts}\",\n \"cli.fileCount\": \" ファイル数: {count}\",\n \"cli.edgeCount\": \" エッジ数: {count}\",\n \"cli.circularCount\": \" 循環参照: {count}件\",\n \"cli.keyComponents\": \"\\n主要コンポーネント:\",\n \"cli.dependedBy\": \"{path} ({count}件が依存)\",\n \"cli.noSnapshot\": \"スナップショットが見つかりません。`archtracker init` を先に実行してください。\",\n \"cli.ciFailed\": \"\\nCI チェック失敗: {count}件の要確認ファイルがあります\",\n \"cli.autoGenerating\": \"スナップショットが無いため自動生成します...\",\n \"cli.project\": \"プロジェクト: {path}\",\n \"cli.validPaths\": \"\\n有効なファイルパス:\",\n \"cli.snapshot\": \"スナップショット: {ts}\",\n\n // MCP\n \"mcp.analyzeComplete\": \"解析完了: {files}ファイル, {edges}エッジ\",\n \"mcp.circularFound\": \"循環参照: {count}件検出\",\n \"mcp.circularNone\": \"循環参照: なし\",\n \"mcp.snapshotSaved\": \"スナップショットを保存しました\",\n \"mcp.autoInit\": \"スナップショットが存在しなかったため、初期スナップショットを自動生成しました。\",\n \"mcp.nextCheckEnabled\": \"次回の実行時から差分チェックが有効になります。\",\n \"mcp.queryRequired\": \"\\\"{mode}\\\" モードでは query パラメータが必須です\",\n\n // Analyze report\n \"analyze.title\": \"# アーキテクチャ分析レポート\\n\",\n \"analyze.overview\": \"## 概要\",\n \"analyze.totalFiles\": \" 総ファイル数: {count}\",\n \"analyze.totalEdges\": \" 総エッジ数: {count}\",\n \"analyze.totalCircular\": \" 循環参照: {count}件\",\n \"analyze.criticalTitle\": \"\\n## 重要コンポーネント (上位{count}件)\",\n \"analyze.criticalItem\": \" {path} ({count}件が依存)\",\n \"analyze.circularTitle\": \"\\n## 循環参照 ({count}件)\",\n \"analyze.circularItem\": \" {files}\",\n \"analyze.orphanTitle\": \"\\n## 孤立ファイル ({count}件)\",\n \"analyze.couplingTitle\": \"\\n## 高結合ファイル (import数 上位{count}件)\",\n \"analyze.couplingItem\": \" {path} ({count}件をimport)\",\n \"analyze.layerTitle\": \"\\n## ディレクトリ構成\",\n \"analyze.layerItem\": \" {dir}/ — {count}ファイル\",\n \"analyze.noIssues\": \"\\nアーキテクチャ上の問題は検出されませんでした。\",\n \"analyze.snapshotSaved\": \"\\n分析と同時にスナップショットを保存しました。\",\n\n // CI\n \"ci.generated\": \"GitHub Actions ワークフローを生成しました: {path}\",\n\n // Layers\n \"layers.alreadyExists\": \"layers.json は既に存在します。直接編集してください。\",\n \"layers.created\": \".archtracker/layers.json を作成しました。レイヤーを設定してください。\",\n \"layers.notFound\": \".archtracker/layers.json が見つかりません。'archtracker layers init' で作成してください。\",\n \"layers.header\": \"設定済みレイヤー ({count}件):\",\n\n // Web viewer\n \"web.starting\": \"アーキテクチャビューアーを起動中...\",\n \"web.listening\": \"アーキテクチャグラフ: http://localhost:{port}\",\n \"web.stop\": \"Ctrl+C で停止\",\n \"web.watching\": \"{dir}/ を監視中...\",\n \"web.reloading\": \"ファイル変更を検出、リロード中...\",\n \"web.reloaded\": \"グラフを更新しました\",\n\n // Errors\n \"error.analyzer\": \"[解析エラー] {message}\",\n \"error.storage\": \"[ストレージエラー] {message}\",\n \"error.pathTraversal\": \"[セキュリティエラー] {message}\",\n \"error.generic\": \"[エラー] {message}\",\n \"error.unexpected\": \"[エラー] 予期しないエラーが発生しました: {message}\",\n \"error.cli.analyzer\": \"解析エラー: {message}\",\n \"error.cli.storage\": \"ストレージエラー: {message}\",\n \"error.cli.generic\": \"エラー: {message}\",\n \"error.cli.unexpected\": \"予期しないエラー: {message}\",\n} as const;\n","import type { DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\n/**\n * Generate a comprehensive architecture analysis report.\n *\n * Designed for onboarding into existing projects — provides\n * a full overview of dependencies, critical components, circular\n * references, orphan files, coupling hotspots, and directory breakdown.\n */\nexport function formatAnalysisReport(\n graph: DependencyGraph,\n options: { topN?: number } = {},\n): string {\n const topN = options.topN ?? 10;\n const lines: string[] = [];\n const files = Object.values(graph.files);\n\n // ─── Overview ────────────────────────────────────────────\n lines.push(t(\"analyze.title\"));\n lines.push(t(\"analyze.overview\"));\n lines.push(t(\"analyze.totalFiles\", { count: graph.totalFiles }));\n lines.push(t(\"analyze.totalEdges\", { count: graph.totalEdges }));\n lines.push(t(\"analyze.totalCircular\", { count: graph.circularDependencies.length }));\n\n // ─── Critical Components ─────────────────────────────────\n const critical = files\n .filter((f) => f.dependents.length > 0)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, topN);\n\n if (critical.length > 0) {\n lines.push(t(\"analyze.criticalTitle\", { count: critical.length }));\n for (const f of critical) {\n lines.push(t(\"analyze.criticalItem\", { path: f.path, count: f.dependents.length }));\n }\n }\n\n // ─── Circular Dependencies ──────────────────────────────\n if (graph.circularDependencies.length > 0) {\n lines.push(t(\"analyze.circularTitle\", { count: graph.circularDependencies.length }));\n for (const c of graph.circularDependencies) {\n lines.push(t(\"analyze.circularItem\", { files: c.cycle.join(\" → \") }));\n }\n }\n\n // ─── High Coupling (most imports) ───────────────────────\n const highCoupling = files\n .filter((f) => f.dependencies.length > 0)\n .sort((a, b) => b.dependencies.length - a.dependencies.length)\n .slice(0, topN);\n\n if (highCoupling.length > 0) {\n lines.push(t(\"analyze.couplingTitle\", { count: highCoupling.length }));\n for (const f of highCoupling) {\n lines.push(t(\"analyze.couplingItem\", { path: f.path, count: f.dependencies.length }));\n }\n }\n\n // ─── Orphan Files ───────────────────────────────────────\n const orphans = files.filter(\n (f) => f.dependents.length === 0 && f.dependencies.length === 0,\n );\n\n if (orphans.length > 0) {\n lines.push(t(\"analyze.orphanTitle\", { count: orphans.length }));\n for (const f of orphans) {\n lines.push(` ${f.path}`);\n }\n }\n\n // ─── Directory Breakdown ────────────────────────────────\n const dirCounts = new Map<string, number>();\n for (const f of files) {\n const dir = f.path.includes(\"/\") ? f.path.substring(0, f.path.lastIndexOf(\"/\")) : \".\";\n dirCounts.set(dir, (dirCounts.get(dir) ?? 0) + 1);\n }\n\n if (dirCounts.size > 1) {\n lines.push(t(\"analyze.layerTitle\"));\n const sorted = [...dirCounts.entries()].sort((a, b) => b[1] - a[1]);\n for (const [dir, count] of sorted) {\n lines.push(t(\"analyze.layerItem\", { dir, count }));\n }\n }\n\n // ─── Summary ────────────────────────────────────────────\n if (graph.circularDependencies.length === 0 && orphans.length === 0) {\n lines.push(t(\"analyze.noIssues\"));\n }\n\n return lines.join(\"\\n\");\n}\n","import { resolve, join } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport type {\n DependencyGraph,\n DependencyEdge,\n FileNode,\n LayerMetadata,\n MultiLayerGraph,\n} from \"../types/schema.js\";\nimport type { LayerDefinition, CrossLayerConnection } from \"../types/layers.js\";\nimport { analyzeProject } from \"./analyze.js\";\nimport { detectLanguage } from \"./engines/detect.js\";\n\nconst LAYER_COLORS = [\n \"#58a6ff\",\n \"#3fb950\",\n \"#d2a8ff\",\n \"#f0883e\",\n \"#79c0ff\",\n \"#56d4dd\",\n \"#db61a2\",\n \"#f778ba\",\n \"#ffa657\",\n \"#7ee787\",\n];\n\n/**\n * Analyze multiple layers and produce a MultiLayerGraph.\n *\n * Each layer is analyzed independently via analyzeProject().\n * Results are merged into a unified graph with layer-prefixed node paths.\n */\nexport async function analyzeMultiLayer(\n projectRoot: string,\n layerDefs: LayerDefinition[],\n): Promise<MultiLayerGraph> {\n const layers: Record<string, DependencyGraph> = {};\n const layerMetadata: LayerMetadata[] = [];\n\n for (let idx = 0; idx < layerDefs.length; idx++) {\n const def = layerDefs[idx];\n const targetDir = resolve(projectRoot, def.targetDir);\n\n const graph = await analyzeProject(targetDir, {\n exclude: def.exclude,\n language: def.language,\n });\n\n // Detect actual language used (may have been auto-detected)\n const language = def.language ?? (await detectLanguage(targetDir)) ?? \"javascript\";\n\n layers[def.name] = graph;\n\n layerMetadata.push({\n name: def.name,\n originalRootDir: graph.rootDir,\n language,\n color: def.color ?? LAYER_COLORS[idx % LAYER_COLORS.length],\n description: def.description,\n fileCount: graph.totalFiles,\n edgeCount: graph.totalEdges,\n });\n }\n\n const merged = mergeLayerGraphs(projectRoot, layers);\n\n return { layers, layerMetadata, merged };\n}\n\n/**\n * Auto-detect cross-layer connections by scanning file contents\n * for references to identifiers defined in other layers.\n *\n * Strategies:\n * 1. Unique type-name matching — PascalCase/snake_case names unique to exactly\n * one target layer, with context validation (not self-definitions, not\n * local imports, not ambiguous across layers).\n * 2. Typed reference patterns — \"StorageClient\", \"AuthService\", etc.\n * 3. Cross-layer import/URL patterns — import paths or URL patterns referencing\n * another layer's directory, with strict context for short names.\n *\n * Results are deduplicated to max 1 connection per (sourceLayer → targetLayer) pair,\n * keeping the highest-scoring match. A minimum score threshold is enforced.\n */\nexport function detectCrossLayerConnections(\n layers: Record<string, DependencyGraph>,\n layerDefs: LayerDefinition[],\n): CrossLayerConnection[] {\n const MIN_NAME_LENGTH = 6;\n const MIN_SCORE_THRESHOLD = 10;\n\n // Build identifier map per layer: { layerName → { identifierName → filePath } }\n const layerIdentifiers = new Map<string, Map<string, string>>();\n for (const [layerName, graph] of Object.entries(layers)) {\n const identifiers = new Map<string, string>();\n for (const filePath of Object.keys(graph.files)) {\n const basename = filePath.split(\"/\").pop()!;\n const nameNoExt = basename.replace(/\\.[^.]+$/, \"\");\n if (nameNoExt.length < MIN_NAME_LENGTH || GENERIC_BASENAMES.has(nameNoExt.toLowerCase())) continue;\n identifiers.set(nameNoExt, filePath);\n }\n layerIdentifiers.set(layerName, identifiers);\n }\n\n // Pre-compute: names that exist in 2+ layers → ambiguous, skip\n const nameLayerCount = new Map<string, number>();\n for (const [, ids] of layerIdentifiers) {\n for (const name of ids.keys()) {\n nameLayerCount.set(name, (nameLayerCount.get(name) ?? 0) + 1);\n }\n }\n\n // Track best connection per (source→target) layer pair, with score\n const pairBest = new Map<string, { conn: CrossLayerConnection; score: number }>();\n\n function tryAdd(pairKey: string, conn: CrossLayerConnection, score: number) {\n if (score < MIN_SCORE_THRESHOLD) return; // discard low-quality\n const existing = pairBest.get(pairKey);\n if (!existing || score > existing.score) {\n pairBest.set(pairKey, { conn, score });\n }\n }\n\n // Regex for detecting self-definitions (class Foo, struct Foo, etc.)\n function isSelfDefined(content: string, name: string): boolean {\n const defPatterns = [\n new RegExp(`\\\\b(?:class|struct|enum|interface|protocol|type|object)\\\\s+${escapeRegex(name)}\\\\b`),\n new RegExp(`\\\\b(?:def|func|fun|fn)\\\\s+${escapeRegex(name)}\\\\b`),\n new RegExp(`\\\\b${escapeRegex(name)}\\\\s*=\\\\s*(?:class|struct|type|interface)\\\\b`),\n ];\n return defPatterns.some((re) => re.test(content));\n }\n\n // Check if a name match appears only in local import context (not cross-layer)\n function isLocalImportOnly(content: string, name: string): boolean {\n const regex = new RegExp(`\\\\b${escapeRegex(name)}\\\\b`, \"g\");\n const lines = content.split(\"\\n\");\n let crossLayerRef = false;\n for (const line of lines) {\n if (!regex.test(line)) continue;\n regex.lastIndex = 0;\n // Local import patterns: from . / from .. / require('./ / import ./ / #include \"\n const isLocalImport = /^\\s*(?:from\\s+[.'\"]|import\\s+[.'\"]|require\\s*\\(\\s*['\"][.\\/]|#include\\s*\")/.test(line);\n if (!isLocalImport) {\n crossLayerRef = true;\n break;\n }\n }\n return !crossLayerRef;\n }\n\n for (const [sourceLayer, graph] of Object.entries(layers)) {\n const ownNames = layerIdentifiers.get(sourceLayer) ?? new Map();\n\n for (const filePath of Object.keys(graph.files)) {\n const absPath = join(graph.rootDir, filePath);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch { continue; }\n\n // Strategy 1: File-name matching across layers\n for (const [targetLayer, targetIds] of layerIdentifiers) {\n if (targetLayer === sourceLayer) continue;\n for (const [targetName, targetFile] of targetIds) {\n // Skip if name exists in source layer (own concept)\n if (ownNames.has(targetName)) continue;\n // Skip if name exists in 2+ layers (ambiguous — e.g. ProfileScreen in both Mobile and Android)\n if ((nameLayerCount.get(targetName) ?? 0) > 1) continue;\n if (!content.includes(targetName)) continue;\n const regex = new RegExp(`\\\\b${escapeRegex(targetName)}\\\\b`);\n if (!regex.test(content)) continue;\n // Skip if source file defines this name itself (e.g. class AuthService in auth_service.dart)\n if (isSelfDefined(content, targetName)) continue;\n // Skip if only appears in local import context (from ./foo import bar)\n if (isLocalImportOnly(content, targetName)) continue;\n\n const pairKey = `${sourceLayer}→${targetLayer}`;\n // Score: longer + PascalCase names are more specific\n const isPascalCase = /^[A-Z][a-z]/.test(targetName);\n const baseScore = targetName.length + (isPascalCase ? 5 : 0);\n tryAdd(pairKey, {\n fromLayer: sourceLayer,\n fromFile: filePath,\n toLayer: targetLayer,\n toFile: targetFile,\n type: \"auto\",\n label: targetName,\n }, baseScore);\n }\n }\n\n // Strategy 2: Typed reference patterns (StorageClient, AuthService, etc.)\n for (const def of layerDefs) {\n if (def.name === sourceLayer) continue;\n const pairKey = `${sourceLayer}→${def.name}`;\n const layerName = def.name;\n const suffixes = [\"Client\", \"Service\", \"API\", \"Handler\", \"Provider\", \"Manager\", \"Gateway\", \"Proxy\", \"Adapter\", \"Connector\"];\n const typedRe = new RegExp(`\\\\b${escapeRegex(layerName)}(?:${suffixes.join(\"|\")})\\\\b`);\n if (typedRe.test(content)) {\n const targetGraph = layers[def.name];\n if (!targetGraph) continue;\n const entryFile = findEntryPoint(targetGraph);\n if (entryFile) {\n tryAdd(pairKey, {\n fromLayer: sourceLayer,\n fromFile: filePath,\n toLayer: def.name,\n toFile: entryFile,\n type: \"auto\",\n label: `${layerName}*`,\n }, 25);\n }\n }\n }\n\n // Strategy 3: Cross-layer import/URL patterns\n for (const def of layerDefs) {\n if (def.name === sourceLayer) continue;\n const pairKey = `${sourceLayer}→${def.name}`;\n const dirName = def.targetDir.split(\"/\").pop()!;\n const isShortName = dirName.length <= 4;\n\n const patterns: { re: RegExp; score: number }[] = [];\n\n if (!isShortName) {\n // Longer dir names: moderate context needed\n // Import context: from <dir> import / require('<dir>/...')\n patterns.push({ re: new RegExp(`(?:from|require|import)\\\\s+['\"].*\\\\b${escapeRegex(dirName)}\\\\b`, \"i\"), score: 15 });\n // URL path: /<dir>/\n patterns.push({ re: new RegExp(`['\"\\`/]${escapeRegex(dirName)}/[\\\\w]`, \"i\"), score: 12 });\n } else {\n // Short dir names (api, auth, cms, ios): require strict context\n // Must appear in import/require with path separator\n patterns.push({ re: new RegExp(`(?:from|require|import)\\\\s+['\"].*/${escapeRegex(dirName)}/`, \"i\"), score: 13 });\n // URL path: must have leading / and trailing /\n patterns.push({ re: new RegExp(`['\"\\`]\\\\s*(?:https?://[^'\"]*)?/${escapeRegex(dirName)}/[\\\\w]`, \"i\"), score: 11 });\n }\n\n for (const { re, score } of patterns) {\n if (re.test(content)) {\n const targetGraph = layers[def.name];\n if (!targetGraph) continue;\n const entryFile = findEntryPoint(targetGraph);\n if (entryFile) {\n tryAdd(pairKey, {\n fromLayer: sourceLayer,\n fromFile: filePath,\n toLayer: def.name,\n toFile: entryFile,\n type: \"auto\",\n label: `→ ${def.name}`,\n }, score);\n }\n break;\n }\n }\n }\n }\n }\n\n return [...pairBest.values()].map((v) => v.conn);\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Find the most likely entry point file in a layer's graph */\nfunction findEntryPoint(graph: DependencyGraph): string | null {\n const files = Object.values(graph.files);\n if (files.length === 0) return null;\n\n // Prefer files with most dependents (highest fan-in)\n const sorted = files.sort((a, b) => b.dependents.length - a.dependents.length);\n if (sorted[0].dependents.length > 0) return sorted[0].path;\n\n // Fallback: common entry point names\n const entryNames = [\"main\", \"index\", \"app\", \"server\", \"lib\", \"mod\"];\n for (const name of entryNames) {\n const entry = files.find((f) => {\n const basename = f.path.split(\"/\").pop()!.replace(/\\.[^.]+$/, \"\").toLowerCase();\n return basename === name;\n });\n if (entry) return entry.path;\n }\n\n return files[0].path;\n}\n\n/** Generic basenames that would cause too many false positives */\nconst GENERIC_BASENAMES = new Set([\n // Build / project structure\n \"index\", \"main\", \"app\", \"config\", \"setup\", \"init\", \"mod\", \"package\",\n \"build\", \"makefile\", \"dockerfile\", \"rakefile\", \"gemfile\", \"podfile\",\n // Common modules\n \"utils\", \"helpers\", \"types\", \"models\", \"views\", \"controllers\", \"services\",\n \"lib\", \"src\", \"test\", \"spec\", \"tests\", \"bench\", \"example\", \"examples\",\n // Infrastructure / patterns\n \"server\", \"client\", \"routes\", \"middleware\", \"database\", \"engine\",\n \"error\", \"errors\", \"logger\", \"logging\", \"constants\", \"common\", \"base\",\n \"core\", \"data\", \"manager\", \"handler\", \"factory\", \"context\", \"state\",\n \"store\", \"cache\", \"queue\", \"task\", \"worker\", \"adapter\", \"bridge\",\n // UI / presentation\n \"event\", \"events\", \"model\", \"view\", \"home\", \"user\", \"page\", \"layout\",\n \"router\", \"provider\", \"component\", \"widget\", \"screen\", \"template\",\n \"header\", \"footer\", \"sidebar\", \"navbar\", \"dialog\", \"modal\", \"panel\",\n // Data / IO\n \"reader\", \"writer\", \"parser\", \"formatter\", \"serializer\", \"converter\",\n \"loader\", \"exporter\", \"importer\", \"transformer\", \"mapper\", \"reducer\",\n \"filter\", \"sorter\", \"validator\", \"checker\", \"scanner\", \"analyzer\",\n // Auth / Security (generic enough to exist in many layers)\n \"login\", \"register\", \"verify\", \"token\", \"session\", \"credential\",\n \"password\", \"permission\", \"profile\", \"account\", \"settings\",\n // Network / API\n \"request\", \"response\", \"endpoint\", \"controller\", \"service\", \"gateway\",\n \"proxy\", \"connector\", \"socket\", \"channel\", \"stream\", \"pipeline\",\n // Storage / DB\n \"schema\", \"migration\", \"seed\", \"fixture\", \"record\", \"entity\",\n \"repository\", \"storage\", \"driver\", \"connection\", \"pool\",\n // Testing\n \"mock\", \"stub\", \"fake\", \"helper\", \"fixture\", \"factory\",\n]);\n\n/**\n * Merge multiple layer graphs into a single DependencyGraph.\n * Node paths are prefixed: \"LayerName/original/path.ext\"\n */\nfunction mergeLayerGraphs(\n projectRoot: string,\n layers: Record<string, DependencyGraph>,\n): DependencyGraph {\n const mergedFiles: Record<string, FileNode> = {};\n const mergedEdges: DependencyEdge[] = [];\n const mergedCircular: { cycle: string[] }[] = [];\n\n for (const [layerName, graph] of Object.entries(layers)) {\n for (const [origPath, node] of Object.entries(graph.files)) {\n const prefixedPath = `${layerName}/${origPath}`;\n mergedFiles[prefixedPath] = {\n path: prefixedPath,\n exists: node.exists,\n dependencies: node.dependencies.map((d) => `${layerName}/${d}`),\n dependents: node.dependents.map((d) => `${layerName}/${d}`),\n };\n }\n\n for (const edge of graph.edges) {\n mergedEdges.push({\n source: `${layerName}/${edge.source}`,\n target: `${layerName}/${edge.target}`,\n type: edge.type,\n });\n }\n\n for (const circ of graph.circularDependencies) {\n mergedCircular.push({\n cycle: circ.cycle.map((f) => `${layerName}/${f}`),\n });\n }\n }\n\n return {\n rootDir: resolve(projectRoot),\n files: mergedFiles,\n edges: mergedEdges,\n circularDependencies: mergedCircular,\n totalFiles: Object.keys(mergedFiles).length,\n totalEdges: mergedEdges.length,\n };\n}\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport { LANGUAGE_IDS } from \"../analyzer/engines/types.js\";\nimport type { LayerConfig } from \"../types/layers.js\";\n\nconst ARCHTRACKER_DIR = \".archtracker\";\nconst LAYERS_FILE = \"layers.json\";\n\nconst LayerDefinitionSchema = z.object({\n name: z\n .string()\n .min(1)\n .regex(\n /^[a-zA-Z0-9_-]+$/,\n \"Layer name must be alphanumeric (hyphens/underscores allowed)\",\n ),\n targetDir: z.string().min(1),\n language: z.enum(LANGUAGE_IDS).optional(),\n exclude: z.array(z.string()).optional(),\n color: z.string().optional(),\n description: z.string().optional(),\n});\n\nconst CrossLayerConnectionSchema = z.object({\n fromLayer: z.string(),\n fromFile: z.string(),\n toLayer: z.string(),\n toFile: z.string(),\n type: z.enum([\"api-call\", \"event\", \"data-flow\", \"manual\"]),\n label: z.string().optional(),\n});\n\nexport const LayerConfigSchema = z.object({\n version: z.literal(\"1.0\"),\n layers: z\n .array(LayerDefinitionSchema)\n .min(1)\n .refine(\n (layers) => {\n const names = layers.map((l) => l.name);\n return new Set(names).size === names.length;\n },\n { message: \"Layer names must be unique\" },\n ),\n connections: z.array(CrossLayerConnectionSchema).optional(),\n});\n\n/**\n * Load layers.json from .archtracker/ if it exists.\n * Returns null if no config file found.\n */\nexport async function loadLayerConfig(\n projectRoot: string,\n): Promise<LayerConfig | null> {\n const filePath = join(projectRoot, ARCHTRACKER_DIR, LAYERS_FILE);\n\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (error: unknown) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return null;\n }\n throw new Error(`Failed to read ${filePath}`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`Invalid JSON in ${filePath}`);\n }\n\n const result = LayerConfigSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .slice(0, 5)\n .join(\"\\n\");\n throw new Error(`layers.json validation failed:\\n${issues}`);\n }\n\n return result.data as LayerConfig;\n}\n\n/**\n * Save a LayerConfig to .archtracker/layers.json.\n */\nexport async function saveLayerConfig(\n projectRoot: string,\n config: LayerConfig,\n): Promise<void> {\n const dirPath = join(projectRoot, ARCHTRACKER_DIR);\n const filePath = join(dirPath, LAYERS_FILE);\n await mkdir(dirPath, { recursive: true });\n await writeFile(filePath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n","/**\n * Shared graph resolution logic for CLI and MCP.\n * Centralizes multi-layer auto-detection to avoid duplicated code.\n */\nimport { analyzeProject } from \"./analyze.js\";\nimport { analyzeMultiLayer, detectCrossLayerConnections } from \"./multi-layer.js\";\nimport { loadLayerConfig } from \"../storage/layers.js\";\nimport type { DependencyGraph, MultiLayerGraph, LayerMetadata } from \"../types/schema.js\";\nimport type { CrossLayerConnection } from \"../types/layers.js\";\nimport type { LanguageId } from \"./engines/types.js\";\n\nexport interface ResolvedGraph {\n graph: DependencyGraph;\n multiLayer?: MultiLayerGraph;\n layerMetadata?: LayerMetadata[];\n crossLayerEdges?: CrossLayerConnection[];\n}\n\n/**\n * Resolve a dependency graph. Always checks for layers.json in projectRoot.\n * If layers.json exists, performs multi-layer analysis regardless of how\n * targetDir was specified. Otherwise falls back to single-dir analysis.\n */\nexport async function resolveGraph(opts: {\n targetDir: string;\n projectRoot: string;\n exclude?: string[];\n language?: LanguageId;\n}): Promise<ResolvedGraph> {\n const layerConfig = await loadLayerConfig(opts.projectRoot);\n if (layerConfig) {\n const multi = await analyzeMultiLayer(opts.projectRoot, layerConfig.layers);\n const autoConnections = detectCrossLayerConnections(multi.layers, layerConfig.layers);\n const manualConnections = layerConfig.connections ?? [];\n const manualKeys = new Set(manualConnections.map(\n (c) => `${c.fromLayer}/${c.fromFile}→${c.toLayer}/${c.toFile}`,\n ));\n const merged = [\n ...manualConnections,\n ...autoConnections.filter(\n (c) => !manualKeys.has(`${c.fromLayer}/${c.fromFile}→${c.toLayer}/${c.toFile}`),\n ),\n ];\n return {\n graph: multi.merged,\n multiLayer: multi,\n layerMetadata: multi.layerMetadata,\n crossLayerEdges: merged,\n };\n }\n\n const graph = await analyzeProject(opts.targetDir, {\n exclude: opts.exclude,\n language: opts.language,\n });\n return { graph };\n}\n","import { mkdir, writeFile, readFile, access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport type { ArchSnapshot, MultiLayerGraph } from \"../types/schema.js\";\nimport { SCHEMA_VERSION } from \"../types/schema.js\";\nimport type { DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\nconst ARCHTRACKER_DIR = \".archtracker\";\nconst SNAPSHOT_FILE = \"snapshot.json\";\n\n/** Zod schema for runtime validation of loaded snapshots */\nconst FileNodeSchema = z.object({\n path: z.string(),\n exists: z.boolean(),\n dependencies: z.array(z.string()),\n dependents: z.array(z.string()),\n});\n\nconst DependencyGraphSchema = z.object({\n rootDir: z.string(),\n files: z.record(z.string(), FileNodeSchema),\n edges: z.array(z.object({\n source: z.string(),\n target: z.string(),\n type: z.enum([\"static\", \"dynamic\", \"type-only\"]),\n })),\n circularDependencies: z.array(z.object({ cycle: z.array(z.string()) })),\n totalFiles: z.number(),\n totalEdges: z.number(),\n});\n\nconst SnapshotSchema = z.object({\n version: z.enum([SCHEMA_VERSION, \"1.0\"]),\n timestamp: z.string(),\n rootDir: z.string(),\n graph: DependencyGraphSchema,\n});\n\n/**\n * Save a dependency graph as a versioned snapshot.\n *\n * Creates .archtracker/ directory if it doesn't exist.\n * Writes snapshot.json with schema version and timestamp.\n */\nexport async function saveSnapshot(\n projectRoot: string,\n graph: DependencyGraph,\n multiLayer?: MultiLayerGraph,\n): Promise<ArchSnapshot> {\n const dirPath = join(projectRoot, ARCHTRACKER_DIR);\n const filePath = join(dirPath, SNAPSHOT_FILE);\n\n const snapshot: ArchSnapshot = {\n version: SCHEMA_VERSION,\n timestamp: new Date().toISOString(),\n rootDir: graph.rootDir,\n graph,\n ...(multiLayer ? { multiLayer } : {}),\n };\n\n await mkdir(dirPath, { recursive: true });\n await writeFile(filePath, JSON.stringify(snapshot, null, 2), \"utf-8\");\n\n return snapshot;\n}\n\n/**\n * Load the most recent snapshot from .archtracker/snapshot.json.\n *\n * Returns null if no snapshot exists.\n * Validates schema structure with Zod for data integrity.\n */\nexport async function loadSnapshot(\n projectRoot: string,\n): Promise<ArchSnapshot | null> {\n const filePath = join(projectRoot, ARCHTRACKER_DIR, SNAPSHOT_FILE);\n\n // Read file directly — no TOCTOU race with access() check\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (error: unknown) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return null;\n }\n throw new StorageError(\n t(\"storage.readFailed\", { path: filePath }),\n { cause: error },\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new StorageError(\n t(\"storage.parseFailed\", { path: filePath }),\n );\n }\n\n // Validate structure with Zod\n const result = SnapshotSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .slice(0, 5)\n .join(\"\\n\");\n throw new StorageError(\n t(\"storage.invalidSchema\", { issues }),\n );\n }\n\n return result.data as ArchSnapshot;\n}\n\n/** Check if .archtracker directory exists */\nexport async function hasArchtrackerDir(\n projectRoot: string,\n): Promise<boolean> {\n try {\n await access(join(projectRoot, ARCHTRACKER_DIR));\n return true;\n } catch {\n return false;\n }\n}\n\n/** Custom error class for storage failures */\nexport class StorageError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"StorageError\";\n }\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n","/**\n * ArchTracker core schema definitions.\n * Snapshot format is versioned for backward compatibility.\n */\n\nexport const SCHEMA_VERSION = \"1.1\" as const;\n\n/** A single dependency edge: source imports target */\nexport interface DependencyEdge {\n source: string;\n target: string;\n type: \"static\" | \"dynamic\" | \"type-only\";\n}\n\n/** A file node in the architecture graph */\nexport interface FileNode {\n path: string;\n exists: boolean;\n dependencies: string[];\n dependents: string[];\n}\n\n/** A detected circular dependency */\nexport interface CircularDependency {\n cycle: string[];\n}\n\n/** The full dependency graph */\nexport interface DependencyGraph {\n rootDir: string;\n files: Record<string, FileNode>;\n edges: DependencyEdge[];\n circularDependencies: CircularDependency[];\n totalFiles: number;\n totalEdges: number;\n}\n\n/** Metadata about a single layer within a multi-layer graph */\nexport interface LayerMetadata {\n name: string;\n originalRootDir: string;\n language: string;\n color: string;\n description?: string;\n fileCount: number;\n edgeCount: number;\n}\n\n/** A multi-layer dependency graph combining multiple single-layer analyses */\nexport interface MultiLayerGraph {\n /** Individual layer graphs (un-prefixed paths, original rootDirs) */\n layers: Record<string, DependencyGraph>;\n /** Layer metadata for rendering */\n layerMetadata: LayerMetadata[];\n /** Merged graph with layer-prefixed paths (\"LayerName/path.ts\") */\n merged: DependencyGraph;\n}\n\n/** Persisted snapshot with schema version */\nexport interface ArchSnapshot {\n version: typeof SCHEMA_VERSION | \"1.0\";\n timestamp: string;\n rootDir: string;\n graph: DependencyGraph;\n /** Present only when layers.json was used */\n multiLayer?: MultiLayerGraph;\n}\n\n/** Diff result between two snapshots */\nexport interface ArchDiff {\n added: string[];\n removed: string[];\n modified: string[];\n affectedDependents: Array<{\n file: string;\n reason: string;\n dependsOn: string;\n }>;\n}\n\n/** Context summary for AI session initialization */\nexport interface ArchContext {\n validPaths: string[];\n summary: string;\n snapshotExists: boolean;\n snapshotTimestamp?: string;\n keyComponents: Array<{\n path: string;\n dependentCount: number;\n dependencyCount: number;\n }>;\n}\n","import type { ArchDiff, DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\n/**\n * Compute the diff between an old and new dependency graph.\n *\n * Identifies:\n * - Added files (new in the codebase)\n * - Removed files (deleted from the codebase)\n * - Modified files (dependencies changed)\n * - Affected dependents (files that depend on changed/removed files and may need updates)\n */\nexport function computeDiff(\n oldGraph: DependencyGraph,\n newGraph: DependencyGraph,\n): ArchDiff {\n const oldFiles = new Set(Object.keys(oldGraph.files));\n const newFiles = new Set(Object.keys(newGraph.files));\n\n // Files that exist in new but not in old\n const added = [...newFiles].filter((f) => !oldFiles.has(f));\n\n // Files that existed in old but not in new\n const removed = [...oldFiles].filter((f) => !newFiles.has(f));\n\n // Files that exist in both but have different dependency sets\n const modified: string[] = [];\n for (const file of newFiles) {\n if (!oldFiles.has(file)) continue;\n\n const oldDeps = oldGraph.files[file].dependencies.slice().sort();\n const newDeps = newGraph.files[file].dependencies.slice().sort();\n\n if (!arraysEqual(oldDeps, newDeps)) {\n modified.push(file);\n }\n }\n\n // Find all files that depend on changed/removed files\n const removedSet = new Set(removed);\n const changedFiles = new Set([...removed, ...modified]);\n const affectedDependents: ArchDiff[\"affectedDependents\"] = [];\n const seenAffected = new Set<string>();\n\n for (const changedFile of changedFiles) {\n // Look up dependents in the NEW graph (for modified files)\n // and in the OLD graph (for removed files)\n const graph = removedSet.has(changedFile) ? oldGraph : newGraph;\n const node = graph.files[changedFile];\n if (!node) continue;\n\n for (const dependent of node.dependents) {\n const key = `${dependent}→${changedFile}`;\n if (seenAffected.has(key)) continue;\n seenAffected.add(key);\n\n const reason = removedSet.has(changedFile)\n ? t(\"diff.reasonRemoved\", { file: changedFile })\n : t(\"diff.reasonModified\", { file: changedFile });\n\n affectedDependents.push({\n file: dependent,\n reason,\n dependsOn: changedFile,\n });\n }\n }\n\n // Also check for files that depend on added files\n // (new dependencies that might need verification)\n for (const addedFile of added) {\n const node = newGraph.files[addedFile];\n if (!node) continue;\n\n for (const dependent of node.dependents) {\n const key = `${dependent}→${addedFile}`;\n if (seenAffected.has(key)) continue;\n seenAffected.add(key);\n\n affectedDependents.push({\n file: dependent,\n reason: t(\"diff.reasonAdded\", { file: addedFile }),\n dependsOn: addedFile,\n });\n }\n }\n\n return { added, removed, modified, affectedDependents };\n}\n\n/** Generate a human-readable report from an ArchDiff */\nexport function formatDiffReport(diff: ArchDiff): string {\n const lines: string[] = [];\n\n lines.push(t(\"diff.title\"));\n\n if (diff.added.length === 0 && diff.removed.length === 0 && diff.modified.length === 0) {\n lines.push(t(\"diff.noChanges\"));\n return lines.join(\"\\n\");\n }\n\n if (diff.added.length > 0) {\n lines.push(t(\"diff.added\", { count: diff.added.length }));\n for (const f of diff.added) {\n lines.push(` + ${f}`);\n }\n lines.push(\"\");\n }\n\n if (diff.removed.length > 0) {\n lines.push(t(\"diff.removed\", { count: diff.removed.length }));\n for (const f of diff.removed) {\n lines.push(` - ${f}`);\n }\n lines.push(\"\");\n }\n\n if (diff.modified.length > 0) {\n lines.push(t(\"diff.modified\", { count: diff.modified.length }));\n for (const f of diff.modified) {\n lines.push(` ~ ${f}`);\n }\n lines.push(\"\");\n }\n\n if (diff.affectedDependents.length > 0) {\n lines.push(t(\"diff.affected\", { count: diff.affectedDependents.length }));\n for (const a of diff.affectedDependents) {\n lines.push(` ! ${a.file}`);\n lines.push(` ${a.reason}`);\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n","import { createServer } from \"node:http\";\nimport type { DependencyGraph, ArchDiff, LayerMetadata } from \"../types/schema.js\";\nimport type { CrossLayerConnection } from \"../types/layers.js\";\nimport { buildGraphPage } from \"./template.js\";\nimport { getLocale } from \"../i18n/index.js\";\nimport type { Locale } from \"../i18n/index.js\";\n\n/**\n * Start a local web server to visualize the dependency graph.\n *\n * Serves:\n * - GET / → Interactive graph visualization (single HTML page)\n * - GET /api/graph → Raw graph data as JSON\n */\nexport function startViewer(\n graph: DependencyGraph,\n options: {\n port?: number;\n locale?: Locale;\n diff?: ArchDiff | null;\n layerMetadata?: LayerMetadata[];\n crossLayerEdges?: CrossLayerConnection[];\n } = {},\n): { port: number; close: () => void } {\n const port = options.port ?? 3000;\n const locale = options.locale ?? getLocale();\n\n const html = buildGraphPage(graph, {\n locale,\n diff: options.diff,\n layerMetadata: options.layerMetadata,\n crossLayerEdges: options.crossLayerEdges,\n });\n const graphJson = JSON.stringify(graph);\n\n const server = createServer((req, res) => {\n if (req.url === \"/api/graph\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(graphJson);\n return;\n }\n\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n });\n\n server.listen(port);\n\n return {\n port,\n close: () => server.close(),\n };\n}\n","/**\n * CSS styles for the architecture viewer.\n * Extracted from template.ts for maintainability.\n */\nexport function buildStyles(): string {\n return `<style>\n:root {\n --bg: #0d1117; --bg-card: #161b22; --bg-hover: #1c2129;\n --border: #30363d; --border-active: #58a6ff;\n --text: #c9d1d9; --text-dim: #8b949e; --text-muted: #484f58;\n --accent: #58a6ff; --green: #3fb950; --red: #f97583; --yellow: #f0e68c;\n --radius: 8px; --font-size: 13px;\n}\n[data-theme=\"light\"] {\n --bg: #ffffff; --bg-card: #f6f8fa; --bg-hover: #eef1f5;\n --border: #d0d7de; --border-active: #0969da;\n --text: #1f2328; --text-dim: #656d76; --text-muted: #8b949e;\n --accent: #0969da; --green: #1a7f37; --red: #cf222e; --yellow: #9a6700;\n}\n* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; font-size: 13px; overflow: hidden; transition: background 0.3s, color 0.3s; }\n\n/* ─── Tab bar ──────────────────────────────── */\n#tab-bar { position: fixed; top: 0; left: 0; right: 0; height: 44px; background: var(--bg-card); border-bottom: 1px solid var(--border); display: flex; align-items: center; z-index: 30; padding: 0 16px; gap: 2px; transition: background 0.3s; }\n#tab-bar .logo { font-weight: 700; font-size: 14px; color: var(--accent); margin-right: 16px; letter-spacing: -0.3px; outline: none; border-bottom: 1px dashed transparent; cursor: text; min-width: 40px; }\n#tab-bar .logo:hover { border-bottom-color: var(--text-muted); }\n#tab-bar .logo:focus { border-bottom-color: var(--accent); }\n.tab { padding: 8px 16px; font-size: 13px; color: var(--text-dim); cursor: pointer; border-radius: 6px 6px 0 0; border: 1px solid transparent; border-bottom: none; transition: all 0.15s; user-select: none; position: relative; top: 1px; }\n.tab:hover { color: var(--text); background: var(--bg-hover); }\n.tab.active { color: var(--text); background: var(--bg); border-color: var(--border); }\n.tab-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }\n.tab-stats { font-size: 12px; color: var(--text-muted); display: flex; gap: 14px; }\n.tab-stats span b { color: var(--text-dim); }\n.settings-btn { background: none; border: 1px solid var(--border); border-radius: 6px; padding: 4px 8px; cursor: pointer; color: var(--text-dim); font-size: 16px; transition: all 0.15s; }\n.settings-btn:hover { border-color: var(--accent); color: var(--text); }\n\n/* ─── Views ────────────────────────────────── */\n.view { position: fixed; top: 44px; left: 0; right: 0; bottom: 0; display: none; }\n.view.active { display: block; }\n.view svg { width: 100%; height: 100%; }\n\n/* ─── HUD ─────────────────────────────────── */\n#hud { position: absolute; top: 12px; left: 12px; z-index: 10; display: flex; flex-direction: column; gap: 8px; }\n.hud-panel { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px; font-size: 12px; backdrop-filter: blur(8px); transition: background 0.3s; }\n#search-box { display: flex; align-items: center; gap: 8px; }\n#search-box input { background: transparent; border: none; outline: none; color: var(--text); font-size: 13px; width: 180px; }\n#search-box input::placeholder { color: var(--text-muted); }\nkbd { background: #21262d; border: 1px solid var(--border); border-radius: 3px; padding: 1px 5px; font-size: 10px; color: var(--text-muted); font-family: inherit; }\n.legend-item { display: flex; align-items: center; gap: 6px; margin: 3px 0; color: var(--text-dim); }\n.legend-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }\n\n/* ─── Tooltip (interactive — mouse can enter) ─ */\n#tooltip { position: fixed; display: none; background: var(--bg-card); border: 1px solid var(--accent); border-radius: var(--radius); padding: 14px 16px; font-size: 13px; z-index: 40; max-width: 420px; pointer-events: auto; box-shadow: 0 8px 24px rgba(0,0,0,0.5); transition: background 0.3s; }\n#tooltip .tt-name { color: var(--accent); font-size: 14px; font-weight: 600; margin-bottom: 8px; word-break: break-all; }\n#tooltip .tt-badge { display: inline-block; background: var(--bg-hover); border-radius: 10px; padding: 1px 8px; font-size: 11px; margin: 0 2px; }\n#tooltip .tt-section { margin-top: 8px; font-size: 12px; color: var(--text-dim); max-height: 140px; overflow-y: auto; }\n#tooltip .tt-section div { padding: 2px 0; }\n#tooltip .tt-out { color: var(--accent); }\n#tooltip .tt-in { color: var(--green); }\n\n/* ─── Filter bar ──────────────────────────── */\n#filter-bar { position: absolute; bottom: 12px; left: 12px; right: 120px; z-index: 10; display: flex; flex-direction: column; gap: 6px; pointer-events: none; }\n#filter-bar > * { pointer-events: auto; }\n#filter-layer-row { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }\n#filter-dir-toggle { background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; padding: 3px 10px; font-size: 11px; cursor: pointer; user-select: none; color: var(--text-dim); transition: all 0.15s; flex-shrink: 0; }\n#filter-dir-toggle:hover { border-color: var(--text-dim); color: var(--text); }\n#filter-dir-toggle.open { border-color: var(--accent); color: var(--text); }\n#filter-dir-panel { display: none; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; max-height: 220px; overflow-y: auto; backdrop-filter: blur(8px); }\n#filter-dir-panel.open { display: block; }\n.dir-group { margin-bottom: 8px; }\n.dir-group:last-child { margin-bottom: 0; }\n.dir-group-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; display: flex; align-items: center; gap: 5px; cursor: pointer; user-select: none; }\n.dir-group-label .dg-dot { width: 6px; height: 6px; border-radius: 50%; }\n.dir-group-pills { display: flex; flex-wrap: wrap; gap: 3px; }\n.filter-pill { background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; padding: 2px 8px; font-size: 10px; cursor: pointer; user-select: none; transition: all 0.15s; display: flex; align-items: center; gap: 4px; }\n.filter-pill:hover { border-color: var(--text-dim); }\n.filter-pill.active { border-color: var(--accent); }\n.filter-pill .pill-dot { width: 5px; height: 5px; border-radius: 50%; }\n.filter-pill .pill-count { color: var(--text-muted); font-size: 9px; }\n\n/* ─── Zoom controls ───────────────────────── */\n#zoom-ctrl { position: absolute; bottom: 52px; right: 12px; z-index: 10; display: flex; flex-direction: column; gap: 2px; }\n#zoom-ctrl button { width: 32px; height: 32px; background: var(--bg-card); border: 1px solid var(--border); color: var(--text-dim); border-radius: 6px; cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center; transition: all 0.1s; }\n#zoom-ctrl button:hover { background: var(--bg-hover); color: var(--text); }\n\n/* ─── Detail panel ────────────────────────── */\n#detail { position: absolute; top: 12px; right: 12px; width: 280px; z-index: 10; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; font-size: 13px; display: none; max-height: calc(100vh - 100px); overflow-y: auto; transition: background 0.3s; }\n#detail.open { display: block; }\n#detail .detail-name { color: var(--accent); font-weight: 600; font-size: 14px; word-break: break-all; margin-bottom: 8px; }\n#detail .detail-meta { color: var(--text-dim); margin-bottom: 12px; }\n#detail .detail-section { margin-top: 10px; }\n#detail .detail-section h4 { font-size: 11px; text-transform: uppercase; color: var(--text-muted); letter-spacing: 0.5px; margin-bottom: 4px; }\n#detail .detail-list { list-style: none; }\n#detail .detail-list li { padding: 3px 0; font-size: 12px; color: var(--text-dim); cursor: pointer; }\n#detail .detail-list li:hover { color: var(--accent); }\n#detail .close-btn { position: absolute; top: 8px; right: 10px; background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 16px; }\n\n/* ─── Settings panel ──────────────────────── */\n#settings-panel { position: fixed; top: 44px; right: 0; width: 280px; height: calc(100vh - 44px); background: var(--bg-card); border-left: 1px solid var(--border); z-index: 25; padding: 20px; transform: translateX(100%); transition: transform 0.25s ease, background 0.3s; overflow-y: auto; }\n#settings-panel.open { transform: translateX(0); }\n#settings-panel h3 { font-size: 14px; color: var(--text); margin-bottom: 16px; }\n.setting-group { margin-bottom: 18px; }\n.setting-group label { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 6px; }\n.setting-group select, .setting-group input[type=range] { width: 100%; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--text); padding: 6px 8px; font-size: 13px; }\n.setting-group input[type=range] { padding: 4px 0; border: none; accent-color: var(--accent); }\n.setting-value { font-size: 11px; color: var(--text-muted); text-align: right; }\n.theme-toggle { display: flex; gap: 6px; }\n.theme-btn { flex: 1; padding: 8px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; cursor: pointer; text-align: center; font-size: 12px; color: var(--text-dim); transition: all 0.15s; }\n.theme-btn:hover { border-color: var(--text-dim); }\n.theme-btn.active { border-color: var(--accent); color: var(--accent); }\n\n/* ─── Hierarchy detail panel ──────────────── */\n#hier-detail { position: absolute; top: 12px; right: 12px; width: 280px; z-index: 10; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; font-size: 13px; display: none; max-height: calc(100vh - 100px); overflow-y: auto; transition: background 0.3s; }\n#hier-detail.open { display: block; }\n#hier-detail .detail-name { color: var(--accent); font-weight: 600; font-size: 14px; word-break: break-all; margin-bottom: 8px; }\n#hier-detail .detail-meta { color: var(--text-dim); margin-bottom: 12px; }\n#hier-detail .detail-section { margin-top: 10px; }\n#hier-detail .detail-section h4 { font-size: 11px; text-transform: uppercase; color: var(--text-muted); letter-spacing: 0.5px; margin-bottom: 4px; }\n#hier-detail .detail-list { list-style: none; }\n#hier-detail .detail-list li { padding: 3px 0; font-size: 12px; color: var(--text-dim); cursor: pointer; }\n#hier-detail .detail-list li:hover { color: var(--accent); }\n#hier-detail .close-btn { position: absolute; top: 8px; right: 10px; background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 16px; }\n\n/* ─── Hierarchy ───────────────────────────── */\n.hier-node { cursor: pointer; }\n.hier-node rect { rx: 6; ry: 6; stroke-width: 1.5; transition: stroke 0.15s; }\n.hier-node:hover rect { stroke: var(--accent) !important; stroke-width: 2; }\n.hier-node text { fill: var(--text); pointer-events: none; }\n.hier-link { fill: none; stroke: var(--border); stroke-width: 1; }\n.hier-layer-label { fill: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }\n\n/* ─── Impact mode ─────────────────────────── */\n#impact-btn.active { background: var(--accent) !important; color: #fff !important; border-color: var(--accent) !important; }\n#impact-badge { position: absolute; bottom: 52px; left: 12px; z-index: 10; display: none; background: var(--accent); color: #fff; font-size: 12px; font-weight: 600; padding: 6px 12px; border-radius: var(--radius); }\n\n/* ─── Diff focus button ──────────────────────── */\n#diff-focus-btn.active { background: var(--accent) !important; color: #fff !important; border-color: var(--accent) !important; }\n\n/* ─── Help bar ─────────────────────────────── */\n#help-bar { position: absolute; bottom: 12px; right: 12px; z-index: 10; font-size: 11px; color: var(--text-muted); background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 6px 10px; transition: background 0.3s; }\n\n/* ─── Layer hulls ─────────────────────────── */\n.layer-hull { fill-opacity: 0.06; stroke-width: 1.5; stroke-dasharray: 6,4; pointer-events: none; }\n.layer-hull-label { font-size: 13px; font-weight: 700; letter-spacing: 0.5px; pointer-events: none; opacity: 0.7; }\n\n/* ─── Layer tabs ──────────────────────────── */\n#layer-tabs { display: flex; gap: 2px; margin-left: 12px; padding-left: 12px; border-left: 1px solid var(--border); }\n.layer-tab { padding: 4px 10px; font-size: 11px; color: var(--text-dim); cursor: pointer; border-radius: 4px; border: 1px solid transparent; transition: all 0.15s; user-select: none; display: flex; align-items: center; gap: 5px; }\n.layer-tab:hover { color: var(--text); background: var(--bg-hover); }\n.layer-tab.active { border-color: var(--accent); color: var(--text); }\n.layer-tab .lt-dot { width: 6px; height: 6px; border-radius: 50%; }\n\n/* ─── Layer filter pills ─────────────────── */\n.layer-pill { background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; padding: 2px 9px; font-size: 11px; font-weight: 600; cursor: pointer; user-select: none; transition: all 0.15s; display: flex; align-items: center; gap: 5px; }\n.layer-pill:hover { border-color: var(--text-dim); }\n.layer-pill.active { border-color: var(--accent); }\n.layer-pill .lp-dot { width: 6px; height: 6px; border-radius: 50%; }\n.layer-pill .lp-count { color: var(--text-muted); font-size: 9px; font-weight: 400; }\n</style>`;\n}\n","/**\n * HTML body structure for the architecture viewer.\n * Extracted from template.ts for maintainability.\n */\nexport function buildViewerHtml(): string {\n return `\n<!-- Tab bar -->\n<div id=\"tab-bar\">\n <span class=\"logo\" id=\"project-title\" contenteditable=\"true\" spellcheck=\"false\" title=\"Click to edit project name\"></span>\n <div class=\"tab active\" data-view=\"graph-view\" data-i18n=\"tab.graph\">Graph</div>\n <div class=\"tab\" data-view=\"hier-view\" data-i18n=\"tab.hierarchy\">Hierarchy</div>\n <div class=\"tab\" data-view=\"diff-view\" id=\"diff-tab\" style=\"display:none\" data-i18n=\"tab.diff\">Diff</div>\n <div id=\"layer-tabs\"></div>\n <div class=\"tab-right\">\n <div class=\"tab-stats\">\n <span><span data-i18n=\"stats.files\">Files</span> <b id=\"s-files\">0</b></span>\n <span><span data-i18n=\"stats.edges\">Edges</span> <b id=\"s-edges\">0</b></span>\n <span><span data-i18n=\"stats.circular\">Circular</span> <b id=\"s-circular\">0</b></span>\n </div>\n <button class=\"settings-btn\" onclick=\"toggleSettings()\" title=\"Settings\">\\u2699</button>\n </div>\n</div>\n\n<!-- Settings panel -->\n<div id=\"settings-panel\">\n <h3 data-i18n=\"settings.title\">Settings</h3>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.theme\">Theme</label>\n <div class=\"theme-toggle\">\n <div class=\"theme-btn active\" data-theme-val=\"dark\" onclick=\"setTheme('dark')\">\\uD83C\\uDF19 Dark</div>\n <div class=\"theme-btn\" data-theme-val=\"light\" onclick=\"setTheme('light')\">\\u2600\\uFE0F Light</div>\n </div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.fontSize\">Font Size</label>\n <input type=\"range\" id=\"font-size-slider\" min=\"10\" max=\"18\" value=\"13\" oninput=\"setFontSize(this.value)\">\n <div class=\"setting-value\"><span id=\"font-size-val\">13</span>px</div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.nodeSize\">Node Size</label>\n <input type=\"range\" id=\"node-size-slider\" min=\"50\" max=\"200\" value=\"100\" oninput=\"setNodeScale(this.value)\">\n <div class=\"setting-value\"><span id=\"node-size-val\">100</span>%</div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.linkOpacity\">Link Opacity</label>\n <input type=\"range\" id=\"link-opacity-slider\" min=\"10\" max=\"100\" value=\"40\" oninput=\"setLinkOpacity(this.value)\">\n <div class=\"setting-value\"><span id=\"link-opacity-val\">40</span>%</div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.gravity\">Gravity</label>\n <input type=\"range\" id=\"gravity-slider\" min=\"10\" max=\"500\" value=\"150\" oninput=\"setGravity(this.value)\">\n <div class=\"setting-value\"><span id=\"gravity-val\">150</span></div>\n </div>\n <div id=\"layer-gravity-setting\" class=\"setting-group\" style=\"display:none\">\n <label>Layer Cohesion</label>\n <input type=\"range\" id=\"layer-gravity-slider\" min=\"1\" max=\"40\" value=\"12\" oninput=\"setLayerGravity(this.value)\">\n <div class=\"setting-value\"><span id=\"layer-gravity-val\">12</span></div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.language\">Language</label>\n <div class=\"theme-toggle\">\n <div class=\"theme-btn lang-btn\" data-lang=\"en\" onclick=\"setLang('en')\">English</div>\n <div class=\"theme-btn lang-btn\" data-lang=\"ja\" onclick=\"setLang('ja')\">\\u65E5\\u672C\\u8A9E</div>\n </div>\n </div>\n <div id=\"cross-layer-setting\" class=\"setting-group\" style=\"display:none\">\n <label>Cross-layer Links</label>\n <div class=\"theme-toggle\">\n <div class=\"theme-btn active\" id=\"cross-link-toggle\" onclick=\"toggleCrossLinks()\">ON</div>\n </div>\n </div>\n <div class=\"setting-group\" style=\"margin-top:12px;padding-top:12px;border-top:1px solid var(--border)\">\n <label data-i18n=\"settings.export\">Export</label>\n <div class=\"theme-toggle\">\n <div class=\"theme-btn\" onclick=\"exportSVG()\">SVG</div>\n <div class=\"theme-btn\" onclick=\"exportPNG()\">PNG</div>\n </div>\n </div>\n</div>\n\n<!-- Graph View -->\n<div id=\"graph-view\" class=\"view active\">\n <svg id=\"graph-svg\"></svg>\n <div id=\"hud\">\n <div class=\"hud-panel\" id=\"search-box\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 16 16\" fill=\"currentColor\" style=\"color:var(--text-muted)\"><path d=\"M11.5 7a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm-.82 4.74a6 6 0 1 1 1.06-1.06l3.04 3.04a.75.75 0 1 1-1.06 1.06l-3.04-3.04Z\"/></svg>\n <input id=\"search\" type=\"text\" data-i18n-placeholder=\"search.placeholder\" placeholder=\"Search files...\" autocomplete=\"off\">\n <kbd>/</kbd>\n </div>\n <div class=\"hud-panel\" id=\"legend-panel\">\n <div id=\"layer-legend\"></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--red)\"></div> <span data-i18n=\"legend.circular\">Circular dep</span></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--text-muted)\"></div> <span data-i18n=\"legend.orphan\">Orphan</span></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"border:2px solid var(--yellow);width:6px;height:6px\"></div> <span data-i18n=\"legend.highCoupling\">High coupling</span></div>\n <div class=\"legend-item\" style=\"margin-top:4px;font-size:11px;gap:3px\"><span style=\"color:var(--accent)\">\\u2014\\u2192</span> <span data-i18n=\"legend.imports\">imports</span> <span style=\"margin-left:6px;color:var(--green)\">\\u2190\\u2014</span> <span data-i18n=\"legend.importedBy\">imported by</span></div>\n </div>\n </div>\n <div id=\"detail\">\n <button class=\"close-btn\" onclick=\"closeDetail()\">\\u2715</button>\n <div class=\"detail-name\" id=\"d-name\"></div>\n <div class=\"detail-meta\" id=\"d-meta\"></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.importedBy\">Imported by</h4><ul class=\"detail-list\" id=\"d-dependents\"></ul></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.imports\">Imports</h4><ul class=\"detail-list\" id=\"d-deps\"></ul></div>\n </div>\n <div id=\"filter-bar\">\n <div id=\"filter-dir-panel\"></div>\n <div id=\"filter-layer-row\"></div>\n </div>\n <div id=\"zoom-ctrl\">\n <button onclick=\"zoomIn()\" title=\"Zoom in\">+</button>\n <button onclick=\"zoomOut()\" title=\"Zoom out\">\\u2212</button>\n <button onclick=\"zoomFit()\" title=\"Fit\">\\u229E</button>\n <button id=\"impact-btn\" onclick=\"toggleImpactMode()\" title=\"Impact simulation\" style=\"font-size:12px;margin-top:4px\" data-i18n=\"impact.btn\">Impact</button>\n </div>\n <div id=\"impact-badge\"></div>\n <div id=\"help-bar\" data-i18n=\"help.graph\">Scroll: zoom \\u00B7 Drag: pan \\u00B7 Click: select \\u00B7 / search</div>\n</div>\n\n<!-- Hierarchy View -->\n<div id=\"hier-view\" class=\"view\">\n <svg id=\"hier-svg\"></svg>\n <div id=\"hier-hud\" style=\"position:absolute;top:12px;left:12px;z-index:10;display:flex;flex-direction:column;gap:8px;\">\n <div class=\"hud-panel\" id=\"hier-legend\">\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--red)\"></div> <span data-i18n=\"legend.circular\">Circular dep</span></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--text-muted)\"></div> <span data-i18n=\"legend.orphan\">Orphan</span></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"border:2px solid var(--yellow);width:6px;height:6px\"></div> <span data-i18n=\"legend.highCoupling\">High coupling</span></div>\n </div>\n </div>\n <div id=\"hier-detail\">\n <button class=\"close-btn\" onclick=\"closeHierDetail()\">\\u2715</button>\n <div class=\"detail-name\" id=\"hd-name\"></div>\n <div class=\"detail-meta\" id=\"hd-meta\"></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.importedBy\">Imported by</h4><ul class=\"detail-list\" id=\"hd-dependents\"></ul></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.imports\">Imports</h4><ul class=\"detail-list\" id=\"hd-deps\"></ul></div>\n </div>\n <div id=\"hier-filter-bar\" style=\"position:absolute;bottom:12px;left:12px;right:120px;z-index:10;display:none;\">\n <div id=\"hier-filter-row\" style=\"display:flex;flex-wrap:wrap;gap:4px;\"></div>\n </div>\n <div id=\"help-bar\" style=\"position:absolute\" data-i18n=\"help.hierarchy\">Scroll to navigate \\u00B7 Click to highlight</div>\n</div>\n\n<!-- Diff View -->\n<div id=\"diff-view\" class=\"view\">\n <svg id=\"diff-svg\"></svg>\n <div id=\"diff-hud\" style=\"position:absolute;top:12px;left:12px;z-index:10;display:flex;flex-direction:column;gap:8px;\">\n <div class=\"hud-panel\">\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--green)\"></div> <span data-i18n=\"diff.addedLabel\">Added</span> <b id=\"diff-added-count\" style=\"margin-left:auto\">0</b></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--red)\"></div> <span data-i18n=\"diff.removedLabel\">Removed</span> <b id=\"diff-removed-count\" style=\"margin-left:auto\">0</b></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--yellow)\"></div> <span data-i18n=\"diff.modifiedLabel\">Modified</span> <b id=\"diff-modified-count\" style=\"margin-left:auto\">0</b></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--accent)\"></div> <span data-i18n=\"diff.affectedLabel\">Affected</span> <b id=\"diff-affected-count\" style=\"margin-left:auto\">0</b></div>\n <div style=\"margin-top:6px;border-top:1px solid var(--border);padding-top:6px;\">\n <button id=\"diff-focus-btn\" onclick=\"toggleDiffFocus()\" style=\"background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:4px 10px;cursor:pointer;color:var(--text-dim);font-size:11px;width:100%;transition:all 0.15s;\" data-i18n=\"diff.focusChanges\">Focus changes</button>\n </div>\n </div>\n </div>\n <div id=\"diff-detail\" style=\"position:absolute;top:12px;right:12px;width:280px;z-index:10;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:16px;font-size:13px;display:none;max-height:calc(100vh - 100px);overflow-y:auto;transition:background 0.3s;\">\n <button class=\"close-btn\" onclick=\"closeDiffDetail()\" style=\"position:absolute;top:8px;right:10px;background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:16px;\">\\u2715</button>\n <div class=\"detail-name\" id=\"dd-name\"></div>\n <div id=\"dd-status\" style=\"margin:6px 0;font-size:12px;font-weight:600;\"></div>\n <div class=\"detail-meta\" id=\"dd-meta\"></div>\n <div class=\"detail-section\"><h4 data-i18n=\"diff.affectedByChange\">Affected by this change</h4><ul class=\"detail-list\" id=\"dd-affected\"></ul></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.imports\">Imports</h4><ul class=\"detail-list\" id=\"dd-deps\"></ul></div>\n </div>\n <div id=\"help-bar\" style=\"position:absolute\" data-i18n=\"help.diff\">Green=added \\u00B7 Red=removed \\u00B7 Yellow=modified \\u00B7 Blue=affected \\u00B7 Click: impact chain</div>\n</div>\n\n<!-- Tooltip (shared, interactive) -->\n<div id=\"tooltip\">\n <div class=\"tt-name\" id=\"tt-name\"></div>\n <div>\n <span class=\"tt-badge tt-out\" id=\"tt-dep-count\"></span> <span data-i18n=\"tooltip.imports\">imports</span>\n <span class=\"tt-badge tt-in\" id=\"tt-dpt-count\" style=\"margin-left:6px\"></span> <span data-i18n=\"tooltip.importedBy\">imported by</span>\n </div>\n <div class=\"tt-section\" id=\"tt-details\"></div>\n</div>`;\n}\n","/**\n * Hierarchy view JavaScript for the architecture viewer.\n * Extracted from template.ts for maintainability.\n *\n * Dependencies (defined in core JS before this runs):\n * DATA, LAYERS, W, H, circularSet, nodeColor, nodeRadius, fileName,\n * dirColor, dirCounts, activeLayers,\n * showTooltip, positionTooltip, scheduleHideTooltip, tooltip, tooltipLocked,\n * hierRelayout, hierSyncFromTab (function pointers set by this module)\n */\nexport function buildHierarchyJs(): string {\n return `\n// ═══════════════════════════════════════════════\n// HIERARCHY VIEW\n// ═══════════════════════════════════════════════\nfunction buildHierarchy(){\n const hSvg=d3.select('#hier-svg');\n const hG=hSvg.append('g');\n const hZoom=d3.zoom().scaleExtent([0.1,4]).on('zoom',e=>hG.attr('transform',e.transform));\n hSvg.call(hZoom);\n\n const nodeMap={}; DATA.nodes.forEach(n=>nodeMap[n.id]=n);\n const importsMap={}; DATA.links.forEach(l=>{const s=l.source.id??l.source,t=l.target.id??l.target;if(!importsMap[s])importsMap[s]=[];importsMap[s].push(t);});\n\n const entryPoints=DATA.nodes.filter(n=>n.dependents===0).map(n=>n.id);\n const layers={};const visited=new Set();\n const queue=entryPoints.map(id=>({id,layer:0}));\n DATA.nodes.forEach(n=>{if(n.isOrphan)layers[n.id]=0;});\n\n while(queue.length>0){\n const{id,layer}=queue.shift();\n if(visited.has(id)&&(layers[id]??-1)>=layer)continue;\n layers[id]=Math.max(layers[id]??0,layer);visited.add(id);\n (importsMap[id]||[]).forEach(t=>queue.push({id:t,layer:layer+1}));\n }\n DATA.nodes.forEach(n=>{if(!(n.id in layers))layers[n.id]=0;});\n\n const maxLayer=Math.max(0,...Object.values(layers));\n const layerGroups={};\n for(let i=0;i<=maxLayer;i++)layerGroups[i]=[];\n Object.entries(layers).forEach(([id,l])=>layerGroups[l].push(id));\n Object.values(layerGroups).forEach(arr=>arr.sort((a,b)=>(nodeMap[a]?.dir||'').localeCompare(nodeMap[b]?.dir||'')||a.localeCompare(b)));\n\n const boxW=200,boxH=30,gapX=24,gapY=70,padY=60,padX=40;\n const positions={};let maxRowWidth=0;\n for(let layer=0;layer<=maxLayer;layer++){const items=layerGroups[layer];maxRowWidth=Math.max(maxRowWidth,items.length*(boxW+gapX)-gapX);}\n for(let layer=0;layer<=maxLayer;layer++){\n const items=layerGroups[layer],rowWidth=items.length*(boxW+gapX)-gapX,startX=padX+(maxRowWidth-rowWidth)/2;\n items.forEach((id,i)=>{positions[id]={x:startX+i*(boxW+gapX),y:padY+layer*(boxH+gapY)};});\n }\n\n const totalW=maxRowWidth+padX*2,totalH=padY*2+(maxLayer+1)*(boxH+gapY);\n hSvg.attr('width',Math.max(totalW,W)).attr('height',Math.max(totalH,H));\n\n const linkG=hG.append('g');\n DATA.links.forEach(l=>{\n const sId=l.source.id??l.source,tId=l.target.id??l.target;\n const s=positions[sId],t=positions[tId]; if(!s||!t)return;\n const x1=s.x+boxW/2,y1=s.y+boxH,x2=t.x+boxW/2,y2=t.y,midY=(y1+y2)/2;\n linkG.append('path').attr('class','hier-link')\n .attr('d',\\`M\\${x1},\\${y1} C\\${x1},\\${midY} \\${x2},\\${midY} \\${x2},\\${y2}\\`)\n .attr('stroke',l.type==='type-only'?'#1f3d5c':'var(--border)')\n .attr('stroke-dasharray',l.type==='type-only'?'4,3':null)\n .attr('data-source',sId).attr('data-target',tId);\n });\n\n hSvg.append('defs').append('marker').attr('id','harrow').attr('viewBox','0 -3 6 6')\n .attr('refX',6).attr('refY',0).attr('markerWidth',6).attr('markerHeight',6).attr('orient','auto')\n .append('path').attr('d','M0,-3L6,0L0,3Z').attr('fill','var(--border)');\n linkG.selectAll('path').attr('marker-end','url(#harrow)');\n\n for(let layer=0;layer<=maxLayer;layer++){\n if(!layerGroups[layer].length)continue;\n hG.append('text').attr('class','hier-layer-label').attr('font-size',11)\n .attr('data-depth-idx',layer)\n .attr('x',12).attr('y',padY+layer*(boxH+gapY)+boxH/2+4).text('L'+layer);\n }\n\n const nodeG=hG.append('g');\n DATA.nodes.forEach(n=>{\n const pos=positions[n.id]; if(!pos)return;\n const gn=nodeG.append('g').attr('class','hier-node').attr('transform',\\`translate(\\${pos.x},\\${pos.y})\\`);\n gn.append('rect').attr('width',boxW).attr('height',boxH)\n .attr('fill','var(--bg-card)').attr('stroke',nodeColor(n))\n .attr('stroke-width',circularSet.has(n.id)?2:1.5);\n gn.append('text').attr('x',8).attr('y',boxH/2+4).attr('font-size',11)\n .text(fileName(n.id).length>24?fileName(n.id).slice(0,22)+'\\\\u2026':fileName(n.id));\n gn.append('text').attr('x',boxW-8).attr('y',boxH/2+4)\n .attr('text-anchor','end').attr('font-size',10).attr('fill','var(--text-muted)')\n .text(n.dependents>0?'\\\\u2191'+n.dependents:'');\n gn.append('text').attr('x',8).attr('y',-4).attr('font-size',9)\n .attr('fill',dirColor(n.dir)).attr('opacity',0.7).text(n.dir);\n\n gn.node().__data_id=n.id;\n gn.on('mouseover',e=>{\n showTooltip(e,n);\n if (!hierPinned) hierHighlight(n.id);\n })\n .on('mousemove',e=>positionTooltip(e))\n .on('mouseout',()=>{\n scheduleHideTooltip();\n if (!hierPinned) hierResetHighlight();\n })\n .on('click',(e)=>{\n e.stopPropagation();\n hierPinned=n.id;\n hierHighlight(n.id);\n showHierDetail(n);\n });\n });\n\n // Hierarchy highlight helpers\n let hierPinned=null;\n function hierHighlight(nId){\n linkG.selectAll('path')\n .attr('stroke',function(){const s=this.getAttribute('data-source'),t=this.getAttribute('data-target');if(s===nId)return'#58a6ff';if(t===nId)return'#3fb950';return this.getAttribute('stroke-dasharray')?'#1f3d5c':'var(--border)';})\n .attr('stroke-width',function(){const s=this.getAttribute('data-source'),t=this.getAttribute('data-target');return(s===nId||t===nId)?2.5:1;})\n .attr('opacity',function(){const s=this.getAttribute('data-source'),t=this.getAttribute('data-target');return(s===nId||t===nId)?1:0.15;});\n nodeG.selectAll('.hier-node').attr('opacity',function(){\n const id=this.__data_id; if(id===nId)return 1;\n const connected=DATA.links.some(l=>{const s=l.source.id??l.source,t=l.target.id??l.target;return(s===nId&&t===id)||(t===nId&&s===id);});\n return connected?1:0.3;\n });\n }\n function hierResetHighlight(){\n hierPinned=null;\n linkG.selectAll('path')\n .attr('stroke',function(){return this.getAttribute('stroke-dasharray')?'#1f3d5c':'var(--border)';})\n .attr('stroke-width',1).attr('opacity',1);\n nodeG.selectAll('.hier-node').attr('opacity',1);\n }\n function showHierDetail(n){\n const p=document.getElementById('hier-detail');\n document.getElementById('hd-name').textContent=n.id;\n document.getElementById('hd-meta').innerHTML=i('detail.dir')+': '+esc(n.dir)+'<br>'+i('detail.dependencies')+': '+n.deps+' \\\\u00b7 '+i('detail.dependents')+': '+n.dependents;\n document.getElementById('hd-dependents').innerHTML=(n.dependentsList||[]).map(x=>'<li>\\\\u2190 '+esc(x)+'</li>').join('')||'<li style=\"color:var(--text-muted)\">'+i('detail.none')+'</li>';\n document.getElementById('hd-deps').innerHTML=(n.dependencies||[]).map(x=>'<li>\\\\u2192 '+esc(x)+'</li>').join('')||'<li style=\"color:var(--text-muted)\">'+i('detail.none')+'</li>';\n p.classList.add('open');\n }\n window.closeHierDetail=()=>{document.getElementById('hier-detail').classList.remove('open');hierResetHighlight();tooltip.style.display='none';tooltipLocked=false;};\n\n // Click on empty space to deselect\n hSvg.on('click',()=>{closeHierDetail();});\n\n // Hierarchy filters — layer pills or dir pills\n const hFilterRow=document.getElementById('hier-filter-row');\n const hFilterBar=document.getElementById('hier-filter-bar');\n if (hFilterBar) hFilterBar.style.display='';\n const hActiveLayers=new Set(); // empty = show all (same as graph view)\n\n function hierRelayoutInner() {\n function isVisible(nId) {\n var nd = nodeMap[nId];\n if (!nd) return false;\n if (LAYERS && nd.layer && hActiveLayers.size > 0 && !hActiveLayers.has(nd.layer)) return false;\n return true;\n }\n\n // Build visible layer groups and compact Y positions\n var visibleDepths = [];\n var visLayerGroups = {};\n for (var depth = 0; depth <= maxLayer; depth++) {\n var visItems = layerGroups[depth].filter(function(id) { return isVisible(id); });\n if (visItems.length > 0) {\n visLayerGroups[depth] = visItems;\n visibleDepths.push(depth);\n }\n }\n\n // Recalculate positions for visible nodes (compacted)\n var newPositions = {};\n var newMaxRowWidth = 0;\n visibleDepths.forEach(function(depth) {\n newMaxRowWidth = Math.max(newMaxRowWidth, visLayerGroups[depth].length * (boxW + gapX) - gapX);\n });\n visibleDepths.forEach(function(depth, yIdx) {\n var items = visLayerGroups[depth];\n var rowWidth = items.length * (boxW + gapX) - gapX;\n var startX = padX + (newMaxRowWidth - rowWidth) / 2;\n items.forEach(function(id, idx) {\n newPositions[id] = { x: startX + idx * (boxW + gapX), y: padY + yIdx * (boxH + gapY) };\n });\n });\n\n // Update SVG size\n var newTotalW = (newMaxRowWidth || 0) + padX * 2;\n var newTotalH = padY * 2 + Math.max(1, visibleDepths.length) * (boxH + gapY);\n hSvg.attr('width', Math.max(newTotalW, W)).attr('height', Math.max(newTotalH, H));\n\n // Update nodes: hide/show + transition positions\n nodeG.selectAll('.hier-node').each(function() {\n var nId = this.__data_id;\n var el = d3.select(this);\n if (!isVisible(nId) || !newPositions[nId]) {\n el.attr('display', 'none');\n } else {\n el.attr('display', null)\n .transition().duration(300)\n .attr('transform', 'translate(' + newPositions[nId].x + ',' + newPositions[nId].y + ')');\n }\n });\n\n // Update links: show only if both endpoints visible, recalculate bezier\n linkG.selectAll('path').each(function() {\n var sId = this.getAttribute('data-source');\n var tId = this.getAttribute('data-target');\n var el = d3.select(this);\n if (!isVisible(sId) || !isVisible(tId) || !newPositions[sId] || !newPositions[tId]) {\n el.attr('display', 'none');\n } else {\n var s = newPositions[sId], t = newPositions[tId];\n var x1 = s.x + boxW / 2, y1 = s.y + boxH;\n var x2 = t.x + boxW / 2, y2 = t.y;\n var midY = (y1 + y2) / 2;\n el.attr('display', null)\n .transition().duration(300)\n .attr('d', 'M' + x1 + ',' + y1 + ' C' + x1 + ',' + midY + ' ' + x2 + ',' + midY + ' ' + x2 + ',' + y2);\n }\n });\n\n // Update depth labels: hide empty depths, reposition visible ones\n hG.selectAll('.hier-layer-label').each(function() {\n var depthIdx = +this.getAttribute('data-depth-idx');\n var el = d3.select(this);\n var yIdx = visibleDepths.indexOf(depthIdx);\n if (yIdx === -1) {\n el.attr('display', 'none');\n } else {\n el.attr('display', null)\n .transition().duration(300)\n .attr('y', padY + yIdx * (boxH + gapY) + boxH / 2 + 4);\n }\n });\n\n // Close detail panel if pinned node became hidden\n if (hierPinned && !isVisible(hierPinned)) {\n closeHierDetail();\n }\n }\n\n function hierSyncFromTabInner() {\n if (!LAYERS) return;\n hActiveLayers.clear();\n activeLayers.forEach(function(name) { hActiveLayers.add(name); });\n // Sync pill UI\n hFilterRow.querySelectorAll('.layer-pill').forEach(function(p) {\n var ln = p.dataset.layer;\n if (ln === 'all') {\n p.classList.toggle('active', hActiveLayers.size === 0);\n } else {\n p.classList.toggle('active', hActiveLayers.has(ln));\n }\n });\n }\n\n if (LAYERS) {\n // \"All\" button\n const allPill=document.createElement('div');\n allPill.className='layer-pill active';\n allPill.style.fontWeight='400';\n allPill.textContent='All';\n allPill.dataset.layer='all';\n allPill.onclick=()=>{\n hActiveLayers.clear();\n hFilterRow.querySelectorAll('.layer-pill').forEach(p=>p.classList.remove('active'));\n allPill.classList.add('active');\n hierRelayoutInner();\n };\n hFilterRow.appendChild(allPill);\n\n LAYERS.forEach(layer => {\n const pill=document.createElement('div');\n pill.className='layer-pill';\n pill.dataset.layer=layer.name;\n const count=DATA.nodes.filter(n=>n.layer===layer.name).length;\n pill.innerHTML='<div class=\"lp-dot\" style=\"background:'+esc(layer.color)+'\"></div>'+esc(layer.name)+' <span class=\"lp-count\">'+count+'</span>';\n pill.onclick=(e)=>{\n if (e.shiftKey) {\n hActiveLayers.clear();\n hActiveLayers.add(layer.name);\n } else {\n if (hActiveLayers.has(layer.name)) hActiveLayers.delete(layer.name);\n else hActiveLayers.add(layer.name);\n }\n // Sync pill UI\n hFilterRow.querySelectorAll('.layer-pill').forEach(function(p) {\n var ln = p.dataset.layer;\n if (ln === 'all') p.classList.toggle('active', hActiveLayers.size === 0);\n else p.classList.toggle('active', hActiveLayers.has(ln));\n });\n hierRelayoutInner();\n };\n hFilterRow.appendChild(pill);\n });\n } else {\n const hActiveDirs=new Set(DATA.dirs);\n DATA.dirs.forEach(dir=>{\n const pill=document.createElement('div');\n pill.className='filter-pill active';\n pill.innerHTML='<div class=\"pill-dot\" style=\"background:'+dirColor(dir)+'\"></div>'+esc(dir||'.')+' <span class=\"pill-count\">'+(dirCounts[dir]||0)+'</span>';\n pill.onclick=()=>{\n if(hActiveDirs.has(dir)){hActiveDirs.delete(dir);pill.classList.remove('active');}\n else{hActiveDirs.add(dir);pill.classList.add('active');}\n nodeG.selectAll('.hier-node').attr('opacity',function(){const nId=this.__data_id;return hActiveDirs.has(nodeMap[nId]?.dir)?1:0.1;});\n };\n hFilterRow.appendChild(pill);\n });\n }\n\n // Assign function pointers for cross-view sync\n hierRelayout = hierRelayoutInner;\n hierSyncFromTab = hierSyncFromTabInner;\n\n hSvg.call(hZoom.transform,d3.zoomIdentity.translate(\n Math.max(0,(W-totalW)/2),20\n ).scale(Math.min(1,W/(totalW+40),H/(totalH+40))));\n\n // If layers were already filtered in graph view, sync hierarchy on first build\n if (activeLayers.size > 0) {\n hierSyncFromTabInner();\n hierRelayoutInner();\n }\n}\n`;\n}\n","/**\n * Diff view JavaScript for the architecture viewer.\n * Extracted from template.ts for maintainability.\n *\n * Dependencies (defined in core JS before this runs):\n * DATA, LAYERS, W, H, nodeRadius, nodeScale, fileName, i,\n * showTooltip, positionTooltip, scheduleHideTooltip\n */\nexport function buildDiffJs(diffData: string): string {\n return `\n// ═══════════════════════════════════════════════\n// DIFF VIEW\n// ═══════════════════════════════════════════════\nconst DIFF = ${diffData};\nif (DIFF) {\n document.getElementById('diff-tab').style.display = '';\n const addedSet = new Set(DIFF.added||[]);\n const removedSet = new Set(DIFF.removed||[]);\n const modifiedSet = new Set(DIFF.modified||[]);\n const affectedSet = new Set((DIFF.affectedDependents||[]).map(a=>a.file));\n\n // Populate summary counts\n document.getElementById('diff-added-count').textContent = addedSet.size;\n document.getElementById('diff-removed-count').textContent = removedSet.size;\n document.getElementById('diff-modified-count').textContent = modifiedSet.size;\n document.getElementById('diff-affected-count').textContent = affectedSet.size;\n\n function isDiffNode(id) {\n return addedSet.has(id) || removedSet.has(id) || modifiedSet.has(id) || affectedSet.has(id);\n }\n\n function diffStatus(id) {\n if (addedSet.has(id)) return 'Added';\n if (removedSet.has(id)) return 'Removed';\n if (modifiedSet.has(id)) return 'Modified';\n if (affectedSet.has(id)) return 'Affected';\n return 'Unchanged';\n }\n\n function diffStatusColor(id) {\n if (addedSet.has(id)) return 'var(--green)';\n if (removedSet.has(id)) return 'var(--red)';\n if (modifiedSet.has(id)) return 'var(--yellow)';\n if (affectedSet.has(id)) return 'var(--accent)';\n return 'var(--text-muted)';\n }\n\n // Build reverse dependency map for impact chain\n var diffRevMap = {};\n DATA.links.forEach(function(l) {\n var s = l.source.id ?? l.source, t = l.target.id ?? l.target;\n if (!diffRevMap[t]) diffRevMap[t] = [];\n diffRevMap[t].push(s);\n });\n\n function getImpactChain(startId) {\n var result = new Set();\n var queue = [startId];\n while (queue.length) {\n var id = queue.shift();\n if (result.has(id)) continue;\n result.add(id);\n (diffRevMap[id] || []).forEach(function(x) { queue.push(x); });\n }\n return result;\n }\n\n let diffFocusMode = false;\n var dNode, dLink, dSim, simNodes, simLinks;\n\n window.toggleDiffFocus = function() {\n diffFocusMode = !diffFocusMode;\n var btn = document.getElementById('diff-focus-btn');\n btn.classList.toggle('active', diffFocusMode);\n btn.textContent = diffFocusMode ? i('diff.showAll') : i('diff.focusChanges');\n if (!diffBuilt) return;\n applyDiffFilter();\n };\n\n window.closeDiffDetail = function() {\n document.getElementById('diff-detail').style.display = 'none';\n if (diffBuilt) resetDiffHighlight();\n };\n\n function applyDiffFilter() {\n dNode.attr('display', function(d) {\n if (!diffFocusMode) return null;\n return isDiffNode(d.id) ? null : 'none';\n });\n dNode.select('circle')\n .attr('opacity', function(d) {\n if (diffFocusMode) return isDiffNode(d.id) ? 1 : 0;\n return isDiffNode(d.id) ? 1 : 0.12;\n });\n dNode.select('text')\n .attr('opacity', function(d) {\n if (diffFocusMode) return isDiffNode(d.id) ? 1 : 0;\n return isDiffNode(d.id) ? 1 : 0.08;\n });\n dLink.attr('display', function(l) {\n if (!diffFocusMode) return null;\n var s = l.source.id ?? l.source, t = l.target.id ?? l.target;\n return (isDiffNode(s) && isDiffNode(t)) ? null : 'none';\n });\n dLink.attr('opacity', function(l) {\n var s = l.source.id ?? l.source, t = l.target.id ?? l.target;\n if (isDiffNode(s) && isDiffNode(t)) return 0.6;\n if (isDiffNode(s) || isDiffNode(t)) return 0.15;\n return diffFocusMode ? 0 : 0.05;\n });\n dLink.attr('stroke', function(l) {\n var s = l.source.id ?? l.source, t = l.target.id ?? l.target;\n if (isDiffNode(s) && isDiffNode(t)) return diffStatusColor(s);\n return '#30363d';\n });\n dNode.select('circle')\n .attr('stroke-width', function(d) { return isDiffNode(d.id) ? 3 : 1; });\n }\n\n function resetDiffHighlight() {\n applyDiffFilter();\n }\n\n function highlightDiffImpact(d) {\n var chain = getImpactChain(d.id);\n dNode.select('circle').transition().duration(200)\n .attr('opacity', function(n) { return chain.has(n.id) ? 1 : 0.04; })\n .attr('stroke-width', function(n) { return chain.has(n.id) && n.id !== d.id ? 3 : isDiffNode(n.id) ? 3 : 1; })\n .attr('stroke', function(n) { return chain.has(n.id) && n.id !== d.id ? 'var(--red)' : diffStatusColor(n.id); });\n dNode.select('text').transition().duration(200)\n .attr('opacity', function(n) { return chain.has(n.id) ? 1 : 0.03; });\n dLink.transition().duration(200)\n .attr('opacity', function(l) {\n var s = l.source.id ?? l.source, t = l.target.id ?? l.target;\n return (chain.has(s) && chain.has(t)) ? 0.8 : 0.03;\n })\n .attr('stroke', function(l) {\n var s = l.source.id ?? l.source, t = l.target.id ?? l.target;\n return (chain.has(s) && chain.has(t)) ? 'var(--red)' : '#30363d';\n });\n return chain;\n }\n\n function showDiffDetail(d) {\n var panel = document.getElementById('diff-detail');\n document.getElementById('dd-name').textContent = d.id;\n var statusEl = document.getElementById('dd-status');\n statusEl.textContent = diffStatus(d.id);\n statusEl.style.color = diffStatusColor(d.id);\n document.getElementById('dd-meta').innerHTML = i('detail.dir') + ': ' + esc(d.dir) + '<br>' + i('detail.dependencies') + ': ' + d.deps + ' \\\\u00b7 ' + i('detail.dependents') + ': ' + d.dependents;\n\n // Show impact chain\n var chain = getImpactChain(d.id);\n chain.delete(d.id);\n var affectedList = document.getElementById('dd-affected');\n if (chain.size > 0) {\n affectedList.innerHTML = Array.from(chain).map(function(id) {\n return '<li style=\"color:' + diffStatusColor(id) + '\">\\\\u2190 ' + esc(id) + ' <span style=\"font-size:10px;color:var(--text-muted)\">(' + diffStatus(id) + ')</span></li>';\n }).join('');\n } else {\n affectedList.innerHTML = '<li style=\"color:var(--text-muted)\">' + i('diff.noImpact') + '</li>';\n }\n\n // Show imports\n var depsList = document.getElementById('dd-deps');\n depsList.innerHTML = (d.dependencies || []).map(function(x) {\n return '<li style=\"color:' + diffStatusColor(x) + '\">\\\\u2192 ' + esc(x) + '</li>';\n }).join('') || '<li style=\"color:var(--text-muted)\">' + i('detail.none') + '</li>';\n\n panel.style.display = 'block';\n }\n\n function buildDiffView() {\n const dSvg = d3.select('#diff-svg').attr('width', W).attr('height', H);\n const dG = dSvg.append('g');\n const dZoom = d3.zoom().scaleExtent([0.05,10]).on('zoom', e=>dG.attr('transform',e.transform));\n dSvg.call(dZoom);\n\n function diffColor(d) {\n if (addedSet.has(d.id)) return 'var(--green)';\n if (removedSet.has(d.id)) return 'var(--red)';\n if (modifiedSet.has(d.id)) return 'var(--yellow)';\n if (affectedSet.has(d.id)) return 'var(--accent)';\n return '#30363d';\n }\n\n const dDefs = dSvg.append('defs');\n dDefs.append('marker').attr('id','darrow').attr('viewBox','0 -4 8 8')\n .attr('refX',8).attr('refY',0).attr('markerWidth',7).attr('markerHeight',7).attr('orient','auto')\n .append('path').attr('d','M0,-3.5L8,0L0,3.5Z').attr('fill','#30363d');\n // Colored arrow markers for diff edges\n [['var(--green)','darrow-g'],['var(--red)','darrow-r'],['var(--yellow)','darrow-y'],['var(--accent)','darrow-a']].forEach(function(pair) {\n dDefs.append('marker').attr('id',pair[1]).attr('viewBox','0 -4 8 8')\n .attr('refX',8).attr('refY',0).attr('markerWidth',7).attr('markerHeight',7).attr('orient','auto')\n .append('path').attr('d','M0,-3.5L8,0L0,3.5Z').attr('fill',pair[0]);\n });\n\n simNodes = DATA.nodes.map(d=>({...d, x:undefined, y:undefined, vx:undefined, vy:undefined}));\n simLinks = DATA.links.map(d=>({source:d.source.id??d.source,target:d.target.id??d.target,type:d.type}));\n\n dLink = dG.append('g').selectAll('line').data(simLinks).join('line')\n .attr('stroke','#30363d').attr('stroke-width',1).attr('marker-end','url(#darrow)').attr('opacity',0.05);\n\n dNode = dG.append('g').selectAll('g').data(simNodes).join('g').attr('cursor','pointer');\n dNode.append('circle')\n .attr('r', d=>nodeRadius(d)*nodeScale)\n .attr('fill', diffColor)\n .attr('stroke', diffColor).attr('stroke-width', d=>isDiffNode(d.id)?3:1)\n .attr('opacity', d=>isDiffNode(d.id)?1:0.12);\n dNode.append('text')\n .text(d=>fileName(d.id).replace(/\\\\.tsx?$/,''))\n .attr('dx', d=>nodeRadius(d)*nodeScale+4).attr('dy',3.5).attr('font-size',11)\n .attr('fill', d=>isDiffNode(d.id)?'var(--text)':'var(--text-muted)')\n .attr('opacity', d=>isDiffNode(d.id)?1:0.08)\n .attr('pointer-events','none');\n\n dSim = d3.forceSimulation(simNodes)\n .force('link', d3.forceLink(simLinks).id(d=>d.id).distance(70).strength(0.25))\n .force('charge', d3.forceManyBody().strength(-150).distanceMax(500))\n .force('center', d3.forceCenter(0,0))\n .force('collision', d3.forceCollide().radius(d=>nodeRadius(d)*nodeScale+4));\n\n // Layer-aware physics for diff view (same pattern as graph view)\n var dHullGroup = null;\n if (LAYERS && LAYERS.length > 0) {\n var dLayerCenters = {};\n var dLayerCount = LAYERS.length;\n var dBaseRadius = Math.max(60, Math.min(W, H) * 0.04 * Math.sqrt(dLayerCount));\n LAYERS.forEach(function(l, idx) {\n var angle = (2 * Math.PI * idx) / dLayerCount - Math.PI / 2;\n dLayerCenters[l.name] = { x: Math.cos(angle) * dBaseRadius, y: Math.sin(angle) * dBaseRadius };\n });\n dSim.force('center', null);\n dSim.force('layerX', d3.forceX(function(d) { return dLayerCenters[d.layer]?.x || 0; }).strength(function(d) { return d.layer ? 0.12 : 0.03; }));\n dSim.force('layerY', d3.forceY(function(d) { return dLayerCenters[d.layer]?.y || 0; }).strength(function(d) { return d.layer ? 0.12 : 0.03; }));\n dSim.force('link').strength(function(l) {\n var sL = l.source.layer ?? l.source, tL = l.target.layer ?? l.target;\n return sL === tL ? 0.4 : 0.1;\n });\n // Cluster force for diff view\n dSim.force('cluster', (function() {\n var ns;\n function f(alpha) {\n var centroids = {}, counts = {};\n ns.forEach(function(n) {\n if (!n.layer) return;\n if (!centroids[n.layer]) { centroids[n.layer] = {x:0,y:0}; counts[n.layer] = 0; }\n centroids[n.layer].x += n.x; centroids[n.layer].y += n.y; counts[n.layer]++;\n });\n Object.keys(centroids).forEach(function(k) { centroids[k].x /= counts[k]; centroids[k].y /= counts[k]; });\n ns.forEach(function(n) {\n if (!n.layer || !centroids[n.layer]) return;\n n.vx += (centroids[n.layer].x - n.x) * alpha * 0.2;\n n.vy += (centroids[n.layer].y - n.y) * alpha * 0.2;\n });\n }\n f.initialize = function(n) { ns = n; };\n return f;\n })());\n\n dHullGroup = dG.insert('g', ':first-child');\n }\n\n function updateDiffHulls() {\n if (!dHullGroup) return;\n dHullGroup.selectAll('*').remove();\n LAYERS.forEach(function(layer) {\n var layerNodes = simNodes.filter(function(n) { return n.layer === layer.name; });\n if (layerNodes.length === 0) return;\n if (diffFocusMode && !layerNodes.some(function(n) { return isDiffNode(n.id); })) return;\n var hasDiff = layerNodes.some(function(n) { return isDiffNode(n.id); });\n\n var points = [];\n layerNodes.forEach(function(n) {\n if (n.x == null || n.y == null) return;\n if (diffFocusMode && !isDiffNode(n.id)) return;\n var r = nodeRadius(n) * nodeScale + 30;\n for (var a = 0; a < Math.PI * 2; a += Math.PI / 4) {\n points.push([n.x + Math.cos(a) * r, n.y + Math.sin(a) * r]);\n }\n });\n\n var fillOp = hasDiff ? 0.15 : 0.06;\n var strokeOp = hasDiff ? 0.6 : 0.2;\n var sw = hasDiff ? 2.5 : 1;\n if (points.length < 6) {\n var cx = layerNodes.reduce(function(s, n) { return s + (n.x||0); }, 0) / layerNodes.length;\n var cy = layerNodes.reduce(function(s, n) { return s + (n.y||0); }, 0) / layerNodes.length;\n dHullGroup.append('circle').attr('cx', cx).attr('cy', cy).attr('r', 50)\n .attr('fill', layer.color).attr('fill-opacity', fillOp)\n .attr('stroke', layer.color).attr('stroke-opacity', strokeOp).attr('stroke-width', sw);\n } else {\n var hull = d3.polygonHull(points);\n if (hull) {\n dHullGroup.append('path')\n .attr('d', 'M' + hull.map(function(p) { return p.join(','); }).join('L') + 'Z')\n .attr('fill', layer.color).attr('fill-opacity', fillOp)\n .attr('stroke', layer.color).attr('stroke-opacity', strokeOp).attr('stroke-width', sw)\n .attr('stroke-dasharray', hasDiff ? null : '6,3');\n }\n }\n // Layer name label\n var visNodes = diffFocusMode ? layerNodes.filter(function(n) { return isDiffNode(n.id); }) : layerNodes;\n if (visNodes.length === 0) return;\n var lx = visNodes.reduce(function(s, n) { return s + (n.x||0); }, 0) / visNodes.length;\n var ly = Math.min.apply(null, visNodes.map(function(n) { return n.y||0; })) - 25;\n dHullGroup.append('text')\n .attr('x', lx).attr('y', ly).attr('text-anchor', 'middle')\n .attr('fill', layer.color).attr('fill-opacity', hasDiff ? 0.9 : 0.4)\n .attr('font-size', 12).attr('font-weight', 600).text(layer.name);\n });\n }\n\n var dTickCount = 0;\n dSim.on('tick', function() {\n dLink.each(function(d) {\n var dx=d.target.x-d.source.x, dy=d.target.y-d.source.y, dist=Math.sqrt(dx*dx+dy*dy)||1;\n var rT=nodeRadius(d.target)*nodeScale, rS=nodeRadius(d.source)*nodeScale;\n d3.select(this).attr('x1',d.source.x+(dx/dist)*rS).attr('y1',d.source.y+(dy/dist)*rS)\n .attr('x2',d.target.x-(dx/dist)*rT).attr('y2',d.target.y-(dy/dist)*rT);\n });\n dNode.attr('transform', function(d) { return 'translate('+d.x+','+d.y+')'; });\n if (++dTickCount % 5 === 0) updateDiffHulls();\n });\n\n // Click: show impact chain + detail panel\n dNode.on('click', function(e, d) {\n e.stopPropagation();\n highlightDiffImpact(d);\n showDiffDetail(d);\n });\n\n // Click on empty space to deselect\n dSvg.on('click', function() {\n closeDiffDetail();\n });\n\n dNode.on('mouseover',function(e,d) { showTooltip(e,d); }).on('mousemove',function(e) { positionTooltip(e); }).on('mouseout',function() { scheduleHideTooltip(); });\n\n // Apply initial filter (in case focus was toggled before build)\n applyDiffFilter();\n\n var dAutoFitDone = false;\n dSim.on('end', function() {\n if (dAutoFitDone) return;\n dAutoFitDone = true;\n var b=dG.node().getBBox(); if(!b.width) return;\n var s=Math.min(W/(b.width+80),H/(b.height+80))*0.9;\n dSvg.call(dZoom.transform,d3.zoomIdentity.translate(W/2-(b.x+b.width/2)*s,H/2-(b.y+b.height/2)*s).scale(s));\n });\n }\n\n}\n`;\n}\n","/**\n * Escape a string for safe insertion into HTML.\n * Handles the 5 characters that can break HTML context:\n * & < > \" '\n *\n * Used both server-side (Node.js) and mirrored in browser-side\n * inline JS (template.ts `esc()`) with identical logic.\n */\nexport function escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/**\n * Returns the `esc()` function body as an inline JS string.\n * Ensures template.ts and unit tests use the exact same logic.\n */\nexport const ESC_FUNCTION_JS =\n `function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/'/g,'&#39;'); }`;\n","import type { DependencyGraph, LayerMetadata } from \"../types/schema.js\";\nimport type { ArchDiff } from \"../types/schema.js\";\nimport type { Locale } from \"../i18n/index.js\";\nimport type { CrossLayerConnection } from \"../types/layers.js\";\nimport { buildStyles } from \"./styles.js\";\nimport { buildViewerHtml } from \"./viewer-html.js\";\nimport { buildHierarchyJs } from \"./js-hierarchy.js\";\nimport { buildDiffJs } from \"./js-diff.js\";\nimport { ESC_FUNCTION_JS } from \"../utils/html-escape.js\";\n\nexport interface ViewerOptions {\n locale?: Locale;\n diff?: ArchDiff | null;\n layerMetadata?: LayerMetadata[];\n crossLayerEdges?: CrossLayerConnection[];\n}\n\n/**\n * Build the complete HTML page for the interactive graph viewer.\n * Two views: Force-directed Graph + Hierarchical Diagram (draw.io style).\n */\nexport function buildGraphPage(graph: DependencyGraph, options: ViewerOptions = {}): string {\n const locale = options.locale ?? \"en\";\n const diff = options.diff ?? null;\n const layers = options.layerMetadata ?? null;\n const crossEdges = options.crossLayerEdges ?? null;\n const files = Object.values(graph.files);\n const nodes = files.map((f) => ({\n id: f.path,\n deps: f.dependencies.length,\n dependents: f.dependents.length,\n dependencies: f.dependencies,\n dependentsList: f.dependents,\n isOrphan: f.dependencies.length === 0 && f.dependents.length === 0,\n dir: f.path.includes(\"/\") ? f.path.substring(0, f.path.lastIndexOf(\"/\")) : \".\",\n layer: layers && f.path.includes(\"/\") ? f.path.substring(0, f.path.indexOf(\"/\")) : null,\n }));\n\n const links = graph.edges.map((e) => ({\n source: e.source,\n target: e.target,\n type: e.type,\n }));\n\n const circularFiles = new Set<string>();\n for (const c of graph.circularDependencies) {\n for (const f of c.cycle) circularFiles.add(f);\n }\n\n const dirs = [...new Set(nodes.map((n) => n.dir))].sort();\n const projectName = graph.rootDir.split(\"/\").filter(Boolean).pop() || \"Project\";\n const diffData = diff ? JSON.stringify(diff) : \"null\";\n const layersData = layers ? JSON.stringify(layers) : \"null\";\n const crossEdgesData = crossEdges ? JSON.stringify(crossEdges) : \"null\";\n const graphData = JSON.stringify({ nodes, links, circularFiles: [...circularFiles], dirs, projectName });\n\n return /* html */ `<!DOCTYPE html>\n<html lang=\"${locale}\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${projectName} — Architecture Viewer</title>\n${buildStyles()}\n</head>\n<body>\n${buildViewerHtml()}\n<script src=\"https://d3js.org/d3.v7.min.js\"></script>\n<script>\n// ═══════════════════════════════════════════════\n// i18n\n// ═══════════════════════════════════════════════\nconst I18N = {\n en: {\n 'tab.graph': 'Graph', 'tab.hierarchy': 'Hierarchy',\n 'stats.files': 'Files', 'stats.edges': 'Edges', 'stats.circular': 'Circular',\n 'settings.title': 'Settings', 'settings.theme': 'Theme', 'settings.fontSize': 'Font Size',\n 'settings.nodeSize': 'Node Size', 'settings.linkOpacity': 'Link Opacity', 'settings.gravity': 'Gravity', 'settings.language': 'Language', 'settings.export': 'Export',\n 'impact.title': 'Impact Simulation', 'impact.btn': 'Impact', 'impact.transitive': 'files affected',\n 'search.placeholder': 'Search files...',\n 'legend.circular': 'Circular dep', 'legend.orphan': 'Orphan', 'legend.highCoupling': 'High coupling',\n 'legend.imports': 'imports', 'legend.importedBy': 'imported by',\n 'detail.importedBy': 'Imported by', 'detail.imports': 'Imports',\n 'detail.none': 'none', 'detail.dir': 'Dir', 'detail.dependencies': 'Dependencies', 'detail.dependents': 'Dependents',\n 'tooltip.imports': 'imports', 'tooltip.importedBy': 'imported by',\n 'help.graph': 'Scroll: zoom · Drag: pan · Click: select · / search',\n 'help.hierarchy': 'Scroll to navigate · Click to highlight',\n 'help.diff': 'Green=added · Red=removed · Yellow=modified · Blue=affected',\n 'tab.diff': 'Diff',\n 'diff.addedLabel': 'Added', 'diff.removedLabel': 'Removed', 'diff.modifiedLabel': 'Modified', 'diff.affectedLabel': 'Affected',\n 'diff.showAll': 'Show all', 'diff.focusChanges': 'Focus changes', 'diff.noImpact': 'No downstream impact',\n 'diff.affectedByChange': 'Affected by this change',\n },\n ja: {\n 'tab.graph': 'グラフ', 'tab.hierarchy': '階層図',\n 'stats.files': 'ファイル', 'stats.edges': 'エッジ', 'stats.circular': '循環参照',\n 'settings.title': '設定', 'settings.theme': 'テーマ', 'settings.fontSize': 'フォントサイズ',\n 'settings.nodeSize': 'ノードサイズ', 'settings.linkOpacity': 'リンク透明度', 'settings.gravity': '重力', 'settings.language': '言語', 'settings.export': 'エクスポート',\n 'impact.title': '影響範囲シミュレーション', 'impact.btn': '影響', 'impact.transitive': 'ファイルに影響',\n 'search.placeholder': 'ファイル検索...',\n 'legend.circular': '循環参照', 'legend.orphan': '孤立', 'legend.highCoupling': '高結合',\n 'legend.imports': 'import先', 'legend.importedBy': 'import元',\n 'detail.importedBy': 'import元', 'detail.imports': 'import先',\n 'detail.none': 'なし', 'detail.dir': 'ディレクトリ', 'detail.dependencies': '依存先', 'detail.dependents': '被依存',\n 'tooltip.imports': 'import先', 'tooltip.importedBy': 'import元',\n 'help.graph': 'スクロール: ズーム · ドラッグ: 移動 · クリック: 選択 · / 検索',\n 'help.hierarchy': 'スクロールで移動 · クリックでハイライト',\n 'help.diff': '緑=追加 · 赤=削除 · 黄=変更 · 青=影響',\n 'tab.diff': '差分',\n 'diff.addedLabel': '追加', 'diff.removedLabel': '削除', 'diff.modifiedLabel': '変更', 'diff.affectedLabel': '影響',\n 'diff.showAll': '全表示', 'diff.focusChanges': '変更のみ表示', 'diff.noImpact': '下流への影響なし',\n 'diff.affectedByChange': 'この変更の影響範囲',\n }\n};\nlet currentLang = '${locale}';\nfunction applyI18n() {\n const msgs = I18N[currentLang] || I18N.en;\n document.querySelectorAll('[data-i18n]').forEach(el => {\n const key = el.getAttribute('data-i18n');\n if (msgs[key]) el.textContent = msgs[key];\n });\n document.querySelectorAll('[data-i18n-placeholder]').forEach(el => {\n const key = el.getAttribute('data-i18n-placeholder');\n if (msgs[key]) el.placeholder = msgs[key];\n });\n document.querySelectorAll('.lang-btn').forEach(b => b.classList.toggle('active', b.dataset.lang === currentLang));\n}\nwindow.setLang = (lang) => { currentLang = lang; applyI18n(); saveSettings(); };\nfunction i(key) { return (I18N[currentLang] || I18N.en)[key] || key; }\n${ESC_FUNCTION_JS}\n\n// ═══════════════════════════════════════════════\n// SETTINGS (persisted to localStorage)\n// ═══════════════════════════════════════════════\nconst STORAGE_KEY = 'archtracker-settings';\nfunction saveSettings() {\n const s = { theme: document.body.getAttribute('data-theme') || 'dark', fontSize: document.getElementById('font-size-val').textContent, nodeSize: document.getElementById('node-size-val').textContent, linkOpacity: document.getElementById('link-opacity-val').textContent, gravity: document.getElementById('gravity-val').textContent, layerGravity: document.getElementById('layer-gravity-val').textContent, lang: currentLang, projectTitle: document.getElementById('project-title').textContent };\n try { localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch(e) {}\n}\nfunction loadSettings() {\n try { return JSON.parse(localStorage.getItem(STORAGE_KEY)) || null; } catch(e) { return null; }\n}\n\nlet nodeScale = 1, baseLinkOpacity = 0.4;\nwindow.toggleSettings = () => document.getElementById('settings-panel').classList.toggle('open');\nwindow.setTheme = (theme) => {\n document.body.setAttribute('data-theme', theme === 'light' ? 'light' : '');\n document.querySelectorAll('.theme-btn[data-theme-val]').forEach(b => b.classList.toggle('active', b.dataset.themeVal === theme));\n saveSettings();\n};\nwindow.setFontSize = (v) => {\n document.getElementById('font-size-val').textContent = v;\n const scale = v / 13;\n if (typeof node !== 'undefined') {\n node.select('text').attr('font-size', d => (d.dependents>=3?12:10) * scale);\n }\n saveSettings();\n};\nwindow.setNodeScale = (v) => {\n nodeScale = v / 100;\n document.getElementById('node-size-val').textContent = v;\n if (typeof node !== 'undefined') {\n node.select('circle').attr('r', d => nodeRadius(d) * nodeScale);\n node.select('text').attr('dx', d => nodeRadius(d) * nodeScale + 4);\n simulation.force('collision', d3.forceCollide().radius(d => nodeRadius(d) * nodeScale + 4));\n simulation.alpha(0.3).restart();\n }\n saveSettings();\n};\nwindow.setLinkOpacity = (v) => {\n baseLinkOpacity = v / 100;\n document.getElementById('link-opacity-val').textContent = v;\n if (typeof link !== 'undefined') link.attr('opacity', baseLinkOpacity);\n saveSettings();\n};\nlet gravityStrength = 150;\nwindow.setGravity = (v) => {\n gravityStrength = +v;\n document.getElementById('gravity-val').textContent = v;\n if (typeof simulation !== 'undefined') {\n if (typeof updateLayerPhysics === 'function') {\n updateLayerPhysics();\n } else {\n simulation.force('charge', d3.forceManyBody().strength(-gravityStrength).distanceMax(500));\n }\n simulation.alpha(0.5).restart();\n }\n saveSettings();\n};\nlet layerGravity = 12;\nwindow.setLayerGravity = (v) => {\n layerGravity = +v;\n document.getElementById('layer-gravity-val').textContent = v;\n if (typeof simulation !== 'undefined' && typeof updateLayerPhysics === 'function') {\n updateLayerPhysics();\n simulation.alpha(0.5).restart();\n }\n saveSettings();\n};\n\n// ═══════════════════════════════════════════════\n// EXPORT\n// ═══════════════════════════════════════════════\nwindow.exportSVG = () => {\n const activeView = document.querySelector('.view.active svg');\n if (!activeView) return;\n const clone = activeView.cloneNode(true);\n clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');\n const blob = new Blob([clone.outerHTML], {type: 'image/svg+xml'});\n const a = document.createElement('a');\n a.href = URL.createObjectURL(blob);\n a.download = (document.getElementById('project-title').textContent || 'graph') + '.svg';\n a.click(); URL.revokeObjectURL(a.href);\n};\nwindow.exportPNG = () => {\n const activeView = document.querySelector('.view.active svg');\n if (!activeView) return;\n const clone = activeView.cloneNode(true);\n clone.setAttribute('xmlns', 'http://www.w3.org/2000/svg');\n const svgStr = new XMLSerializer().serializeToString(clone);\n const canvas = document.createElement('canvas');\n const bbox = activeView.getBoundingClientRect();\n canvas.width = bbox.width * 2; canvas.height = bbox.height * 2;\n const ctx = canvas.getContext('2d');\n ctx.scale(2, 2);\n const img = new Image();\n img.onload = () => { ctx.drawImage(img, 0, 0); const a = document.createElement('a'); a.href = canvas.toDataURL('image/png'); a.download = (document.getElementById('project-title').textContent || 'graph') + '.png'; a.click(); };\n img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svgStr)));\n};\n\n// ═══════════════════════════════════════════════\n// DATA\n// ═══════════════════════════════════════════════\nconst DATA = ${graphData};\nconst LAYERS = ${layersData};\nconst CROSS_EDGES = ${crossEdgesData};\nconst W = window.innerWidth, H = window.innerHeight - 44;\nconst circularSet = new Set(DATA.circularFiles);\n\n// Project title (editable)\nconst titleEl = document.getElementById('project-title');\ntitleEl.textContent = DATA.projectName;\ntitleEl.addEventListener('blur', () => { if (!titleEl.textContent.trim()) titleEl.textContent = DATA.projectName; document.title = titleEl.textContent + ' \\u2014 Architecture Viewer'; saveSettings(); });\ntitleEl.addEventListener('keydown', e => { if (e.key === 'Enter') { e.preventDefault(); titleEl.blur(); } });\n\n// Restore saved settings — phase 1: non-graph settings (before graph init)\nconst _savedSettings = loadSettings();\nif (_savedSettings) {\n if (_savedSettings.theme) setTheme(_savedSettings.theme);\n if (_savedSettings.lang) { currentLang = _savedSettings.lang; applyI18n(); }\n if (_savedSettings.projectTitle) { titleEl.textContent = _savedSettings.projectTitle; document.title = _savedSettings.projectTitle + ' \\u2014 Architecture Viewer'; }\n // Set slider positions (visual only — graph not built yet)\n if (_savedSettings.fontSize) { document.getElementById('font-size-slider').value = _savedSettings.fontSize; document.getElementById('font-size-val').textContent = _savedSettings.fontSize; }\n if (_savedSettings.nodeSize) { document.getElementById('node-size-slider').value = _savedSettings.nodeSize; document.getElementById('node-size-val').textContent = _savedSettings.nodeSize; nodeScale = _savedSettings.nodeSize / 100; }\n if (_savedSettings.linkOpacity) { document.getElementById('link-opacity-slider').value = _savedSettings.linkOpacity; document.getElementById('link-opacity-val').textContent = _savedSettings.linkOpacity; baseLinkOpacity = _savedSettings.linkOpacity / 100; }\n if (_savedSettings.gravity) { document.getElementById('gravity-slider').value = _savedSettings.gravity; document.getElementById('gravity-val').textContent = _savedSettings.gravity; gravityStrength = +_savedSettings.gravity; }\n if (_savedSettings.layerGravity) { document.getElementById('layer-gravity-slider').value = _savedSettings.layerGravity; document.getElementById('layer-gravity-val').textContent = _savedSettings.layerGravity; layerGravity = +_savedSettings.layerGravity; }\n}\n\ndocument.getElementById('s-files').textContent = DATA.nodes.length;\ndocument.getElementById('s-edges').textContent = DATA.links.length;\ndocument.getElementById('s-circular').textContent = DATA.circularFiles.length;\n\nconst dirColor = d3.scaleOrdinal()\n .domain(DATA.dirs)\n .range(['#58a6ff','#3fb950','#d2a8ff','#f0883e','#79c0ff','#56d4dd','#db61a2','#f778ba','#ffa657','#7ee787']);\n\n// Layer color map (from LAYERS metadata)\nconst layerColorMap = {};\nlet activeLayerFilter = null; // DEPRECATED — kept for backward compat, always null with multi-select tabs\nconst activeLayers = new Set(); // empty = no filter (show all); non-empty = show only selected\nif (LAYERS) {\n LAYERS.forEach(l => { layerColorMap[l.name] = l.color; });\n document.getElementById('layer-gravity-setting').style.display = '';\n}\n\nfunction nodeColor(d) {\n if (circularSet.has(d.id)) return '#f97583';\n if (d.isOrphan) return '#484f58';\n // Layer coloring: all-visible or multi-select → layer colors; single-select → dir colors\n if (LAYERS && d.layer && layerColorMap[d.layer] && activeLayers.size !== 1) return layerColorMap[d.layer];\n return dirColor(d.dir);\n}\nfunction nodeRadius(d) { return Math.max(5, Math.min(22, 4 + d.dependents * 1.8)); }\nfunction fileName(id) { return id.split('/').pop(); }\n\n// ═══════════════════════════════════════════════\n// TAB SWITCHING\n// ═══════════════════════════════════════════════\nlet hierBuilt = false;\nlet diffBuilt = false;\nlet hierRelayout = null;\nlet hierSyncFromTab = null;\ndocument.querySelectorAll('.tab').forEach(tab => {\n tab.addEventListener('click', () => {\n document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));\n document.querySelectorAll('.view').forEach(v => v.classList.remove('active'));\n tab.classList.add('active');\n document.getElementById(tab.dataset.view).classList.add('active');\n if (tab.dataset.view === 'hier-view') {\n if (!hierBuilt) { buildHierarchy(); hierBuilt = true; }\n if (hierSyncFromTab) { hierSyncFromTab(); hierRelayout(); }\n }\n if (tab.dataset.view === 'diff-view') {\n if (!diffBuilt) { buildDiffView(); diffBuilt = true; }\n }\n });\n});\n\n// ═══════════════════════════════════════════════\n// TOOLTIP — delayed hide + interactive\n// ═══════════════════════════════════════════════\nconst tooltip = document.getElementById('tooltip');\nlet tooltipHideTimer = null;\nlet tooltipLocked = false;\n\nfunction showTooltip(e, d) {\n clearTimeout(tooltipHideTimer);\n document.getElementById('tt-name').textContent = d.id;\n document.getElementById('tt-dep-count').textContent = d.deps;\n document.getElementById('tt-dpt-count').textContent = d.dependents;\n const out = (d.dependencies||[]).map(x => '<div class=\"tt-out\">→ '+esc(x)+'</div>');\n const inc = (d.dependentsList||[]).map(x => '<div class=\"tt-in\">← '+esc(x)+'</div>');\n document.getElementById('tt-details').innerHTML = [...out, ...inc].join('');\n tooltip.style.display = 'block';\n positionTooltip(e);\n}\nfunction positionTooltip(e) {\n const gap = 24;\n const tw = 420, th = tooltip.offsetHeight || 200;\n // Prefer placing to the right and above the cursor so it doesn't cover nodes below\n let x = e.clientX + gap;\n let y = e.clientY - th - 12;\n // If no room on the right, flip left\n if (x + tw > window.innerWidth) x = e.clientX - tw - gap;\n // If no room above, place below the cursor with gap\n if (y < 50) y = e.clientY + gap;\n // Final clamp\n if (y + th > window.innerHeight) y = window.innerHeight - th - 8;\n if (x < 8) x = 8;\n tooltip.style.left = x + 'px';\n tooltip.style.top = y + 'px';\n}\nfunction scheduleHideTooltip() {\n clearTimeout(tooltipHideTimer);\n tooltipHideTimer = setTimeout(() => {\n if (!tooltipLocked) {\n tooltip.style.display = 'none';\n if (!pinnedNode) resetGraphHighlight();\n }\n }, 250);\n}\n\n// Keep tooltip visible when mouse enters it\ntooltip.addEventListener('mouseenter', () => {\n clearTimeout(tooltipHideTimer);\n tooltipLocked = true;\n});\ntooltip.addEventListener('mouseleave', () => {\n tooltipLocked = false;\n scheduleHideTooltip();\n});\n\n// ═══════════════════════════════════════════════\n// GRAPH VIEW\n// ═══════════════════════════════════════════════\nconst svg = d3.select('#graph-svg').attr('width', W).attr('height', H);\nconst g = svg.append('g');\nconst zoom = d3.zoom().scaleExtent([0.05, 10]).on('zoom', e => g.attr('transform', e.transform));\nsvg.call(zoom);\nsvg.call(zoom.transform, d3.zoomIdentity.translate(W/2, H/2).scale(0.7));\n\nwindow.zoomIn = () => svg.transition().duration(300).call(zoom.scaleBy, 1.4);\nwindow.zoomOut = () => svg.transition().duration(300).call(zoom.scaleBy, 0.7);\nwindow.zoomFit = () => {\n const b = g.node().getBBox(); if (!b.width) return;\n const s = Math.min(W/(b.width+80), H/(b.height+80))*0.9;\n svg.transition().duration(500).call(zoom.transform, d3.zoomIdentity.translate(W/2-(b.x+b.width/2)*s, H/2-(b.y+b.height/2)*s).scale(s));\n};\n\n// Defs\nconst defs = svg.append('defs');\n['#30363d','#58a6ff','#3fb950'].forEach((c,i) => {\n defs.append('marker').attr('id','arrow-'+i).attr('viewBox','0 -4 8 8')\n .attr('refX',8).attr('refY',0).attr('markerWidth',7).attr('markerHeight',7).attr('orient','auto')\n .append('path').attr('d','M0,-3.5L8,0L0,3.5Z').attr('fill',c);\n});\n\n// Links\nconst link = g.append('g').selectAll('line').data(DATA.links).join('line')\n .attr('stroke', d => d.type==='type-only'?'#1f3d5c':'#30363d')\n .attr('stroke-width',1)\n .attr('stroke-dasharray', d => d.type==='type-only'?'4,3':d.type==='dynamic'?'6,3':null)\n .attr('marker-end','url(#arrow-0)')\n .attr('opacity', baseLinkOpacity);\n\n// Cross-layer links (from layers.json connections)\ndefs.append('marker').attr('id','arrow-cross').attr('viewBox','0 -4 8 8')\n .attr('refX',8).attr('refY',0).attr('markerWidth',7).attr('markerHeight',7).attr('orient','auto')\n .append('path').attr('d','M0,-3.5L8,0L0,3.5Z').attr('fill','#f0883e');\n\nconst crossLinkData = (CROSS_EDGES || []).map(e => ({\n source: e.fromLayer + '/' + e.fromFile,\n target: e.toLayer + '/' + e.toFile,\n sourceLayer: e.fromLayer,\n targetLayer: e.toLayer,\n type: e.type || 'api-call',\n label: e.label || e.type || '',\n})).filter(e => DATA.nodes.some(n => n.id === e.source) && DATA.nodes.some(n => n.id === e.target));\n\nconst crossLinkG = g.append('g');\nconst crossLink = crossLinkG.selectAll('line').data(crossLinkData).join('line')\n .attr('stroke', '#f0883e')\n .attr('stroke-width', 2)\n .attr('stroke-dasharray', '8,4')\n .attr('marker-end', 'url(#arrow-cross)')\n .attr('opacity', 0.7);\nconst crossLabel = crossLinkG.selectAll('text').data(crossLinkData).join('text')\n .text(d => d.label)\n .attr('font-size', 9)\n .attr('fill', '#f0883e')\n .attr('text-anchor', 'middle')\n .attr('opacity', 0.8)\n .attr('pointer-events', 'none');\n\n// Nodes\nconst node = g.append('g').selectAll('g').data(DATA.nodes).join('g')\n .attr('cursor','pointer')\n .call(d3.drag().on('start',dragStart).on('drag',dragging).on('end',dragEnd));\n\nnode.append('circle')\n .attr('r', d => nodeRadius(d) * nodeScale)\n .attr('fill', nodeColor)\n .attr('stroke', d => d.deps>=5?'var(--yellow)':nodeColor(d))\n .attr('stroke-width', d => d.deps>=5?2.5:1.5)\n .attr('stroke-opacity', d => d.deps>=5?0.8:0.3);\n\nnode.append('text')\n .text(d => fileName(d.id).replace(/\\\\.tsx?$/,''))\n .attr('dx', d => nodeRadius(d)*nodeScale+4)\n .attr('dy',3.5)\n .attr('font-size', d => d.dependents>=3?12:10)\n .attr('font-weight', d => d.dependents>=3?600:400)\n .attr('fill', d => d.dependents>=3?'var(--text)':'var(--text-dim)')\n .attr('opacity', d => d.dependents>=1||d.deps>=3?1:0.5)\n .attr('pointer-events','none');\n\n// Simulation\nconst simulation = d3.forceSimulation(DATA.nodes)\n .force('link', d3.forceLink(DATA.links).id(d=>d.id).distance(70).strength(0.25))\n .force('charge', d3.forceManyBody().strength(-gravityStrength).distanceMax(500))\n .force('center', d3.forceCenter(0,0))\n .force('collision', d3.forceCollide().radius(d=>nodeRadius(d)*nodeScale+4))\n .force('x', d3.forceX(0).strength(0.03))\n .force('y', d3.forceY(0).strength(0.03))\n .on('tick', () => {\n link.each(function(d) {\n const dx=d.target.x-d.source.x, dy=d.target.y-d.source.y;\n const dist=Math.sqrt(dx*dx+dy*dy)||1;\n const rT=nodeRadius(d.target)*nodeScale, rS=nodeRadius(d.source)*nodeScale;\n d3.select(this)\n .attr('x1',d.source.x+(dx/dist)*rS).attr('y1',d.source.y+(dy/dist)*rS)\n .attr('x2',d.target.x-(dx/dist)*rT).attr('y2',d.target.y-(dy/dist)*rT);\n });\n node.attr('transform', d=>\\`translate(\\${d.x},\\${d.y})\\`);\n });\n\n// ─── Layer convex hulls ─────────────────────\nlet hullGroup = null;\nconst activeDirs = new Set(DATA.dirs);\nconst dirCounts = {};\nDATA.nodes.forEach(n => dirCounts[n.dir] = (dirCounts[n.dir] || 0) + 1);\nvar applyLayerFilter = null; // hoisted for dir-filter integration\nvar updateLayerPhysics = null; // hoisted — updates charge/layer forces without visibility changes\n\nif (LAYERS && LAYERS.length > 0) {\n // ─── Water droplet physics: intra-layer cohesion + inter-layer separation ───\n const allLayerCount = LAYERS.length;\n const allBaseRadius = Math.max(60, Math.min(W, H) * 0.04 * Math.sqrt(allLayerCount));\n // Pre-compute full-circle positions for all layers (used when no filter)\n const allLayerCenters = {};\n LAYERS.forEach((l, idx) => {\n const angle = (2 * Math.PI * idx) / allLayerCount - Math.PI / 2;\n allLayerCenters[l.name] = { x: Math.cos(angle) * allBaseRadius, y: Math.sin(angle) * allBaseRadius };\n });\n\n // Dynamic center calculation: compact when multi-selecting, full spread when all\n function getLayerCenters() {\n if (activeLayers.size <= 1) return allLayerCenters; // 0 = all, 1 = single (centered)\n // Multi-select: arrange only selected layers compactly on a smaller circle\n const selected = LAYERS.filter(l => activeLayers.has(l.name));\n const count = selected.length;\n const compactRadius = Math.max(40, Math.min(W, H) * 0.03 * Math.sqrt(count));\n const centers = {};\n selected.forEach((l, idx) => {\n const angle = (2 * Math.PI * idx) / count - Math.PI / 2;\n centers[l.name] = { x: Math.cos(angle) * compactRadius, y: Math.sin(angle) * compactRadius };\n });\n return centers;\n }\n\n // Replace default centering forces with per-layer positioning\n const layerStrength = layerGravity / 100;\n simulation.force('x', null).force('y', null).force('center', null);\n simulation.force('layerX', d3.forceX(d => allLayerCenters[d.layer]?.x || 0).strength(d => d.layer ? layerStrength : 0.03));\n simulation.force('layerY', d3.forceY(d => allLayerCenters[d.layer]?.y || 0).strength(d => d.layer ? layerStrength : 0.03));\n\n // Custom clustering force — surface tension pulling nodes toward their layer centroid\n function clusterForce() {\n let nodes;\n function force(alpha) {\n const centroids = {};\n const counts = {};\n nodes.forEach(n => {\n if (!n.layer) return;\n if (!centroids[n.layer]) { centroids[n.layer] = {x: 0, y: 0}; counts[n.layer] = 0; }\n centroids[n.layer].x += n.x;\n centroids[n.layer].y += n.y;\n counts[n.layer]++;\n });\n Object.keys(centroids).forEach(k => {\n centroids[k].x /= counts[k];\n centroids[k].y /= counts[k];\n });\n // Pull each node toward its layer centroid (surface tension)\n const strength = 0.2;\n nodes.forEach(n => {\n if (!n.layer || !centroids[n.layer]) return;\n n.vx += (centroids[n.layer].x - n.x) * alpha * strength;\n n.vy += (centroids[n.layer].y - n.y) * alpha * strength;\n });\n }\n force.initialize = (n) => { nodes = n; };\n return force;\n }\n simulation.force('cluster', clusterForce());\n\n // Boost link strength for intra-layer edges (tighter connections within a layer)\n simulation.force('link').strength(l => {\n const sLayer = (l.source.layer ?? l.source);\n const tLayer = (l.target.layer ?? l.target);\n return sLayer === tLayer ? 0.4 : 0.1;\n });\n\n hullGroup = g.insert('g', ':first-child');\n\n function updateHulls() {\n if (!hullGroup) return;\n hullGroup.selectAll('*').remove();\n // Show hulls always (filter to selected layers when focused)\n\n LAYERS.forEach(layer => {\n if (activeLayers.size > 0 && !activeLayers.has(layer.name)) return;\n const layerNodes = DATA.nodes.filter(n => n.layer === layer.name);\n if (layerNodes.length === 0) return;\n\n const points = [];\n layerNodes.forEach(n => {\n if (n.x == null || n.y == null) return;\n const r = nodeRadius(n) * nodeScale + 30;\n // Add expanded points for a nicer hull shape\n for (let a = 0; a < Math.PI * 2; a += Math.PI / 4) {\n points.push([n.x + Math.cos(a) * r, n.y + Math.sin(a) * r]);\n }\n });\n\n if (points.length < 3) {\n // Fallback: circle for 1-2 nodes\n const cx = layerNodes.reduce((s, n) => s + (n.x || 0), 0) / layerNodes.length;\n const cy = layerNodes.reduce((s, n) => s + (n.y || 0), 0) / layerNodes.length;\n const maxR = Math.max(60, ...layerNodes.map(n => {\n const dx = (n.x || 0) - cx, dy = (n.y || 0) - cy;\n return Math.sqrt(dx*dx + dy*dy) + nodeRadius(n) * nodeScale + 30;\n }));\n hullGroup.append('circle')\n .attr('cx', cx).attr('cy', cy).attr('r', maxR)\n .attr('class', 'layer-hull')\n .attr('fill', layer.color).attr('stroke', layer.color);\n hullGroup.append('text')\n .attr('class', 'layer-hull-label')\n .attr('x', cx).attr('y', cy - maxR - 8)\n .attr('text-anchor', 'middle')\n .attr('fill', layer.color)\n .text(layer.name);\n return;\n }\n\n const hull = d3.polygonHull(points);\n if (!hull) return;\n\n // Smooth the hull with a cardinal closed curve\n hullGroup.append('path')\n .attr('class', 'layer-hull')\n .attr('d', d3.line().curve(d3.curveCatmullRomClosed.alpha(0.5))(hull))\n .attr('fill', layer.color).attr('stroke', layer.color);\n\n // Label at the top of the hull\n const topPt = hull.reduce((best, p) => p[1] < best[1] ? p : best, hull[0]);\n hullGroup.append('text')\n .attr('class', 'layer-hull-label')\n .attr('x', topPt[0]).attr('y', topPt[1] - 10)\n .attr('text-anchor', 'middle')\n .attr('fill', layer.color)\n .text(layer.name);\n });\n }\n\n // Update hulls + cross-layer links on each tick\n simulation.on('tick', () => {\n // Regular links\n link.each(function(d) {\n const dx=d.target.x-d.source.x, dy=d.target.y-d.source.y;\n const dist=Math.sqrt(dx*dx+dy*dy)||1;\n const rT=nodeRadius(d.target)*nodeScale, rS=nodeRadius(d.source)*nodeScale;\n d3.select(this)\n .attr('x1',d.source.x+(dx/dist)*rS).attr('y1',d.source.y+(dy/dist)*rS)\n .attr('x2',d.target.x-(dx/dist)*rT).attr('y2',d.target.y-(dy/dist)*rT);\n });\n node.attr('transform', d=>\\`translate(\\${d.x},\\${d.y})\\`);\n // Cross-layer links — resolve node positions by ID\n if (crossLinkData.length > 0) {\n const nodeById = {};\n DATA.nodes.forEach(n => { nodeById[n.id] = n; });\n crossLink.each(function(d) {\n const sN = nodeById[d.source], tN = nodeById[d.target];\n if (!sN || !tN) return;\n const dx = tN.x - sN.x, dy = tN.y - sN.y;\n const dist = Math.sqrt(dx*dx + dy*dy) || 1;\n const rS = nodeRadius(sN) * nodeScale, rT = nodeRadius(tN) * nodeScale;\n d3.select(this)\n .attr('x1', sN.x + (dx/dist)*rS).attr('y1', sN.y + (dy/dist)*rS)\n .attr('x2', tN.x - (dx/dist)*rT).attr('y2', tN.y - (dy/dist)*rT);\n });\n crossLabel.each(function(d) {\n const sN = nodeById[d.source], tN = nodeById[d.target];\n if (!sN || !tN) return;\n d3.select(this).attr('x', (sN.x + tN.x) / 2).attr('y', (sN.y + tN.y) / 2 - 6);\n });\n }\n updateHulls();\n });\n\n // ─── Layer legend ──────────────────────────\n const layerLegend = document.getElementById('layer-legend');\n LAYERS.forEach(layer => {\n const item = document.createElement('div');\n item.className = 'legend-item';\n item.innerHTML = '<div class=\"legend-dot\" style=\"background:' + esc(layer.color) + '\"></div> ' + esc(layer.name);\n layerLegend.appendChild(item);\n });\n // Cross-layer edge legend\n if (CROSS_EDGES && CROSS_EDGES.length > 0) {\n const crossItem = document.createElement('div');\n crossItem.className = 'legend-item';\n crossItem.innerHTML = '<span style=\"color:#f0883e;font-size:11px\">- - →</span> Cross-layer link';\n layerLegend.appendChild(crossItem);\n }\n // Add separator\n const sep = document.createElement('hr');\n sep.style.cssText = 'border:none;border-top:1px solid var(--border);margin:6px 0;';\n layerLegend.appendChild(sep);\n\n // ─── Layer tabs (multi-select toggles in tab bar) ───────────────\n const layerTabsEl = document.getElementById('layer-tabs');\n const allTab = document.createElement('div');\n allTab.className = 'layer-tab active';\n allTab.textContent = 'All';\n allTab.onclick = () => {\n activeLayers.clear();\n syncLayerTabUI();\n applyLayerFilter();\n if (hierBuilt && hierSyncFromTab) { hierSyncFromTab(); hierRelayout(); }\n };\n layerTabsEl.appendChild(allTab);\n\n LAYERS.forEach(layer => {\n const tab = document.createElement('div');\n tab.className = 'layer-tab';\n tab.dataset.layer = layer.name;\n tab.innerHTML = '<div class=\"lt-dot\" style=\"background:' + esc(layer.color) + '\"></div>' + esc(layer.name);\n tab.onclick = (e) => {\n if (e.shiftKey) {\n // Shift+click: solo this layer\n activeLayers.clear();\n activeLayers.add(layer.name);\n } else {\n // Toggle\n if (activeLayers.has(layer.name)) activeLayers.delete(layer.name);\n else activeLayers.add(layer.name);\n }\n syncLayerTabUI();\n applyLayerFilter();\n if (hierBuilt && hierSyncFromTab) { hierSyncFromTab(); hierRelayout(); }\n };\n layerTabsEl.appendChild(tab);\n });\n\n function syncLayerTabUI() {\n allTab.classList.toggle('active', activeLayers.size === 0);\n layerTabsEl.querySelectorAll('.layer-tab[data-layer]').forEach(t => {\n t.classList.toggle('active', activeLayers.has(t.dataset.layer));\n });\n // Also sync the filter bar layer pills\n layerRowEl.querySelectorAll('.layer-pill[data-layer]').forEach(p => {\n p.classList.toggle('active', activeLayers.has(p.dataset.layer));\n });\n }\n\n applyLayerFilter = function() {\n const isSingleLayer = activeLayers.size === 1;\n const hasLayerFilter = activeLayers.size > 0;\n node.attr('display', d => {\n if (!activeDirs.has(d.dir)) return 'none';\n if (hasLayerFilter && !activeLayers.has(d.layer)) return 'none';\n return null;\n });\n link.attr('display', l => {\n const s = l.source.id ?? l.source, t = l.target.id ?? l.target;\n const sN = DATA.nodes.find(n => n.id === s), tN = DATA.nodes.find(n => n.id === t);\n if (!sN || !tN) return 'none';\n if (!activeDirs.has(sN.dir) || !activeDirs.has(tN.dir)) return 'none';\n if (hasLayerFilter && (!activeLayers.has(sN.layer) || !activeLayers.has(tN.layer))) return 'none';\n return null;\n });\n // Refresh node colors: single-layer = dir-based, multi-layer = layer-based\n node.select('circle')\n .attr('fill', nodeColor)\n .attr('stroke', d => d.deps >= 5 ? 'var(--yellow)' : nodeColor(d));\n // Cross-layer links: respect user toggle + layer filter\n if (typeof crossLink !== 'undefined') {\n if (!crossLinksUserEnabled || isSingleLayer) {\n crossLink.attr('display', 'none');\n crossLabel.attr('display', 'none');\n } else if (hasLayerFilter) {\n crossLink.attr('display', d => (activeLayers.has(d.sourceLayer) && activeLayers.has(d.targetLayer)) ? null : 'none');\n crossLabel.attr('display', d => (activeLayers.has(d.sourceLayer) && activeLayers.has(d.targetLayer)) ? null : 'none');\n } else {\n crossLink.attr('display', null);\n crossLabel.attr('display', null);\n }\n }\n // Update stats\n const visibleNodes = DATA.nodes.filter(d => {\n if (!activeDirs.has(d.dir)) return false;\n if (hasLayerFilter && !activeLayers.has(d.layer)) return false;\n return true;\n });\n const visibleIds = new Set(visibleNodes.map(n => n.id));\n const visibleEdges = DATA.links.filter(l => {\n const s = l.source.id ?? l.source, t = l.target.id ?? l.target;\n return visibleIds.has(s) && visibleIds.has(t);\n });\n document.getElementById('s-files').textContent = visibleNodes.length;\n document.getElementById('s-edges').textContent = visibleEdges.length;\n const visCirc = DATA.circularFiles.filter(f => visibleIds.has(f));\n document.getElementById('s-circular').textContent = visCirc.length;\n updateHulls();\n // Delegate physics update and zoom to fit\n updateLayerPhysics();\n simulation.alpha(0.6).restart();\n setTimeout(() => zoomFit(), 600);\n }\n\n // Separated physics update: handles charge/layer forces based on filter state.\n // Called by applyLayerFilter (with zoomFit), setGravity, setLayerGravity (without zoomFit).\n updateLayerPhysics = function() {\n const isSingleLayer = activeLayers.size === 1;\n const lStrength = layerGravity / 100;\n if (isSingleLayer) {\n simulation.force('charge', d3.forceManyBody().strength(-gravityStrength * 3).distanceMax(800));\n simulation.force('layerX', d3.forceX(0).strength(0.03));\n simulation.force('layerY', d3.forceY(0).strength(0.03));\n } else {\n const centers = getLayerCenters();\n simulation.force('charge', d3.forceManyBody().strength(-gravityStrength).distanceMax(500));\n simulation.force('layerX', d3.forceX(d => centers[d.layer]?.x || 0).strength(d => d.layer ? lStrength : 0.03));\n simulation.force('layerY', d3.forceY(d => centers[d.layer]?.y || 0).strength(d => d.layer ? lStrength : 0.03));\n }\n }\n\n // ─── Layer filter pills (new grouped bar) ────────────────────\n const layerRowEl = document.getElementById('filter-layer-row');\n const dirPanelEl = document.getElementById('filter-dir-panel');\n\n // Dir toggle button\n const dirToggle = document.createElement('div');\n dirToggle.id = 'filter-dir-toggle';\n dirToggle.textContent = '▸ Dirs';\n dirToggle.onclick = () => {\n dirToggle.classList.toggle('open');\n dirPanelEl.classList.toggle('open');\n dirToggle.textContent = dirPanelEl.classList.contains('open') ? '▾ Dirs' : '▸ Dirs';\n };\n layerRowEl.appendChild(dirToggle);\n\n // Cross-layer link toggle (in settings sidebar)\n let crossLinksUserEnabled = true;\n if (crossLinkData.length > 0) {\n document.getElementById('cross-layer-setting').style.display = '';\n window.toggleCrossLinks = () => {\n crossLinksUserEnabled = !crossLinksUserEnabled;\n const btn = document.getElementById('cross-link-toggle');\n btn.textContent = crossLinksUserEnabled ? 'ON' : 'OFF';\n btn.classList.toggle('active', crossLinksUserEnabled);\n applyLayerFilter();\n };\n }\n\n LAYERS.forEach(layer => {\n const layerNodes = DATA.nodes.filter(n => n.layer === layer.name);\n const pill = document.createElement('div');\n pill.className = 'layer-pill';\n pill.dataset.layer = layer.name;\n pill.innerHTML = '<div class=\"lp-dot\" style=\"background:' + esc(layer.color) + '\"></div>' + esc(layer.name) + ' <span class=\"lp-count\">' + layerNodes.length + '</span>';\n pill.onclick = () => {\n if (activeLayers.has(layer.name)) activeLayers.delete(layer.name);\n else activeLayers.add(layer.name);\n syncLayerTabUI();\n applyLayerFilter();\n };\n pill.onmouseenter = () => {\n if (pinnedNode) return;\n node.select('circle').transition().duration(120).attr('opacity', d => d.layer === layer.name ? 1 : 0.1);\n node.select('text').transition().duration(120).attr('opacity', d => d.layer === layer.name ? 1 : 0.05);\n };\n pill.onmouseleave = () => {\n if (pinnedNode) return;\n node.select('circle').transition().duration(150).attr('opacity', 1);\n node.select('text').transition().duration(150).attr('opacity', d => d.dependents >= 1 || d.deps >= 3 ? 1 : 0.5);\n };\n layerRowEl.appendChild(pill);\n\n // Build dir group in panel for this layer\n const layerDirs = [...new Set(layerNodes.map(n => n.dir))].sort();\n if (layerDirs.length > 0) {\n const group = document.createElement('div');\n group.className = 'dir-group';\n const label = document.createElement('div');\n label.className = 'dir-group-label';\n label.innerHTML = '<div class=\"dg-dot\" style=\"background:' + esc(layer.color) + '\"></div>' + esc(layer.name);\n group.appendChild(label);\n const pillsWrap = document.createElement('div');\n pillsWrap.className = 'dir-group-pills';\n layerDirs.forEach(dir => {\n const dp = document.createElement('div');\n dp.className = 'filter-pill active';\n const shortDir = dir.includes('/') ? dir.substring(dir.indexOf('/') + 1) : dir;\n dp.innerHTML = '<div class=\"pill-dot\" style=\"background:' + dirColor(dir) + '\"></div>' + esc(shortDir || '.') + ' <span class=\"pill-count\">' + (dirCounts[dir] || 0) + '</span>';\n dp.onclick = () => {\n if (activeDirs.has(dir)) { activeDirs.delete(dir); dp.classList.remove('active'); }\n else { activeDirs.add(dir); dp.classList.add('active'); }\n applyLayerFilter();\n };\n pillsWrap.appendChild(dp);\n });\n group.appendChild(pillsWrap);\n dirPanelEl.appendChild(group);\n }\n });\n\n // Override applyFilter to respect layers\n window._origApplyFilter = applyFilter;\n}\n\nsetTimeout(()=>zoomFit(), 1500);\n\n// Restore saved settings — phase 2: apply to graph elements now that they exist\nif (_savedSettings) {\n if (_savedSettings.fontSize) setFontSize(_savedSettings.fontSize);\n}\n\n// ─── Highlight helper ─────────────────────────\nlet pinnedNode = null;\n\nfunction highlightNode(d) {\n const conn = new Set([d.id]);\n DATA.links.forEach(l => { const s=l.source.id??l.source,t=l.target.id??l.target; if(s===d.id)conn.add(t); if(t===d.id)conn.add(s); });\n node.select('circle').transition().duration(150).attr('opacity',n=>conn.has(n.id)?1:0.1);\n node.select('text').transition().duration(150).attr('opacity',n=>conn.has(n.id)?1:0.05);\n link.transition().duration(150)\n .attr('opacity',l=>{const s=l.source.id??l.source,t=l.target.id??l.target;return s===d.id||t===d.id?0.9:0.03;})\n .attr('stroke',l=>{const s=l.source.id??l.source,t=l.target.id??l.target;if(s===d.id)return'#58a6ff';if(t===d.id)return'#3fb950';return l.type==='type-only'?'#1f3d5c':'#30363d';})\n .attr('stroke-width',l=>{const s=l.source.id??l.source,t=l.target.id??l.target;return s===d.id||t===d.id?2:1;})\n .attr('marker-end',l=>{const s=l.source.id??l.source,t=l.target.id??l.target;if(s===d.id)return'url(#arrow-1)';if(t===d.id)return'url(#arrow-2)';return'url(#arrow-0)';});\n}\n\nfunction resetGraphHighlight() {\n pinnedNode = null;\n node.select('circle').transition().duration(200).attr('opacity',1);\n node.select('text').transition().duration(200).attr('opacity',d=>d.dependents>=1||d.deps>=3?1:0.5);\n link.transition().duration(200)\n .attr('opacity',baseLinkOpacity)\n .attr('stroke',d=>d.type==='type-only'?'#1f3d5c':'#30363d')\n .attr('stroke-width',1).attr('marker-end','url(#arrow-0)');\n}\n\n// ─── Hover ───────────────────────────────────\nnode.on('mouseover', (e,d) => {\n showTooltip(e,d);\n if (!pinnedNode) highlightNode(d);\n})\n.on('mousemove', e=>positionTooltip(e))\n.on('mouseout', () => { scheduleHideTooltip(); if (!pinnedNode) { /* highlight resets via scheduleHideTooltip */ } });\n\n// ─── Click: pin highlight + detail panel ─────\nnode.on('click', (e,d) => {\n e.stopPropagation();\n pinnedNode = d;\n highlightNode(d);\n showDetail(d);\n});\nsvg.on('click', () => {\n resetGraphHighlight();\n tooltip.style.display = 'none';\n tooltipLocked = false;\n closeDetail();\n});\n\nfunction showDetail(d) {\n const p=document.getElementById('detail');\n document.getElementById('d-name').textContent=d.id;\n document.getElementById('d-meta').innerHTML=i('detail.dir')+': '+esc(d.dir)+'<br>'+i('detail.dependencies')+': '+d.deps+' \\\\u00b7 '+i('detail.dependents')+': '+d.dependents;\n const deptL=document.getElementById('d-dependents'), depsL=document.getElementById('d-deps');\n deptL.innerHTML=(d.dependentsList||[]).map(x=>'<li data-focus=\"'+esc(x)+'\">\\\\u2190 '+esc(x)+'</li>').join('')||'<li style=\"color:var(--text-muted)\">'+i('detail.none')+'</li>';\n depsL.innerHTML=(d.dependencies||[]).map(x=>'<li data-focus=\"'+esc(x)+'\">\\\\u2192 '+esc(x)+'</li>').join('')||'<li style=\"color:var(--text-muted)\">'+i('detail.none')+'</li>';\n p.classList.add('open');\n}\n// Event delegation for detail panel list items (avoids inline onclick)\ndocument.getElementById('d-dependents').addEventListener('click', function(e) { var li=e.target.closest('li[data-focus]'); if(li) focusNode(li.dataset.focus); });\ndocument.getElementById('d-deps').addEventListener('click', function(e) { var li=e.target.closest('li[data-focus]'); if(li) focusNode(li.dataset.focus); });\nwindow.closeDetail=()=>document.getElementById('detail').classList.remove('open');\nwindow.focusNode=(id)=>{\n const n=DATA.nodes.find(x=>x.id===id); if(!n)return; showDetail(n);\n svg.transition().duration(500).call(zoom.transform,d3.zoomIdentity.translate(W/2-n.x*1.5,H/2-n.y*1.5).scale(1.5));\n};\n\n// Drag\nfunction dragStart(e,d){if(!e.active)simulation.alphaTarget(0.3).restart();d.fx=d.x;d.fy=d.y;}\nfunction dragging(e,d){d.fx=e.x;d.fy=e.y;}\nfunction dragEnd(e,d){if(!e.active)simulation.alphaTarget(0);}\n\n// ─── Search ──────────────────────────────────\nconst searchInput=document.getElementById('search');\ndocument.addEventListener('keydown',e=>{\n if(e.key==='/'&&document.activeElement!==searchInput){e.preventDefault();searchInput.focus();}\n if(e.key==='Escape'){searchInput.value='';searchInput.blur();resetGraphHighlight();}\n});\nsearchInput.addEventListener('input',e=>{\n const q=e.target.value.toLowerCase();\n if(!q){resetGraphHighlight();return;}\n node.select('circle').attr('opacity',d=>d.id.toLowerCase().includes(q)?1:0.06);\n node.select('text').attr('opacity',d=>d.id.toLowerCase().includes(q)?1:0.04);\n link.attr('opacity',0.03);\n});\n\n// ─── Filters (click=toggle, hover=highlight nodes) ──\nif (!LAYERS) {\n // Non-layer mode: flat pills in filter-layer-row\n const filterRowEl=document.getElementById('filter-layer-row');\n DATA.dirs.forEach(dir=>{\n const pill=document.createElement('div');\n pill.className='filter-pill active';\n pill.innerHTML='<div class=\"pill-dot\" style=\"background:'+dirColor(dir)+'\"></div>'+esc(dir||'.')+' <span class=\"pill-count\">'+dirCounts[dir]+'</span>';\n pill.onclick=()=>{\n if(activeDirs.has(dir)){activeDirs.delete(dir);pill.classList.remove('active');}\n else{activeDirs.add(dir);pill.classList.add('active');}\n applyFilter();\n };\n pill.onmouseenter=()=>{\n if(pinnedNode)return;\n node.select('circle').transition().duration(120).attr('opacity',d=>d.dir===dir?1:0.1);\n node.select('text').transition().duration(120).attr('opacity',d=>d.dir===dir?1:0.05);\n };\n pill.onmouseleave=()=>{\n if(pinnedNode)return;\n node.select('circle').transition().duration(150).attr('opacity',1);\n node.select('text').transition().duration(150).attr('opacity',d=>d.dependents>=1||d.deps>=3?1:0.5);\n };\n filterRowEl.appendChild(pill);\n });\n}\nfunction applyFilter(){\n if (LAYERS) {\n // Delegate to layer-aware filter\n if (typeof applyLayerFilter === 'function') { applyLayerFilter(); return; }\n }\n node.attr('display',d=>activeDirs.has(d.dir)?null:'none');\n link.attr('display',l=>{\n const s=l.source.id??l.source,t=l.target.id??l.target;\n const sD=DATA.nodes.find(n=>n.id===s)?.dir,tD=DATA.nodes.find(n=>n.id===t)?.dir;\n return activeDirs.has(sD)&&activeDirs.has(tD)?null:'none';\n });\n}\n\n// ─── Impact simulation mode ──────────────────\nlet impactMode=false;\nconst impactBadge=document.getElementById('impact-badge');\nwindow.toggleImpactMode=()=>{\n impactMode=!impactMode;\n document.getElementById('impact-btn').classList.toggle('active',impactMode);\n if(!impactMode){impactBadge.style.display='none';resetGraphHighlight();}\n};\nfunction getTransitiveDependents(startId){\n const result=new Set();const queue=[startId];\n const revMap={};\n DATA.links.forEach(l=>{const s=l.source.id??l.source,t=l.target.id??l.target;if(!revMap[t])revMap[t]=[];revMap[t].push(s);});\n while(queue.length){const id=queue.shift();if(result.has(id))continue;result.add(id);(revMap[id]||[]).forEach(x=>queue.push(x));}\n return result;\n}\n// Override click in impact mode\nconst origClick=node.on('click');\nnode.on('click',(e,d)=>{\n if(!impactMode){e.stopPropagation();pinnedNode=d;highlightNode(d);showDetail(d);return;}\n e.stopPropagation();\n const affected=getTransitiveDependents(d.id);\n node.select('circle').transition().duration(200).attr('opacity',n=>affected.has(n.id)?1:0.06)\n .attr('stroke',n=>affected.has(n.id)&&n.id!==d.id?'var(--red)':n.deps>=5?'var(--yellow)':nodeColor(n))\n .attr('stroke-width',n=>affected.has(n.id)?3:1.5);\n node.select('text').transition().duration(200).attr('opacity',n=>affected.has(n.id)?1:0.04);\n link.transition().duration(200).attr('opacity',l=>{\n const s=l.source.id??l.source,t=l.target.id??l.target;\n return affected.has(s)&&affected.has(t)?0.8:0.03;\n }).attr('stroke',l=>{\n const s=l.source.id??l.source,t=l.target.id??l.target;\n return affected.has(s)&&affected.has(t)?'var(--red)':l.type==='type-only'?'#1f3d5c':'#30363d';\n });\n impactBadge.textContent=d.id.split('/').pop()+' → '+(affected.size-1)+' '+i('impact.transitive');\n impactBadge.style.display='block';\n});\n\nwindow.addEventListener('resize',()=>{\n const w=window.innerWidth,h=window.innerHeight-44;\n svg.attr('width',w).attr('height',h);\n});\n\n${buildHierarchyJs()}\n${buildDiffJs(diffData)}\n// ═══════════════════════════════════════════════\n// INIT\n// ═══════════════════════════════════════════════\napplyI18n();\n</script>\n</body>\n</html>`;\n}\n","import { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Read version from package.json at build/runtime — single source of truth */\nfunction loadVersion(): string {\n // Walk up from this file to find package.json\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, \"package.json\"), \"utf-8\"));\n return pkg.version;\n } catch {\n dir = dirname(dir);\n }\n }\n return \"0.0.0\";\n}\n\nexport const VERSION = loadVersion();\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,aAAa;AACtB,SAAS,aAAAA,YAAW,SAAAC,cAAa;AACjC,SAAS,QAAAC,aAAY;;;ACHrB,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;;;ACDrB,SAAS,SAAS,gBAAsB;AACxC,SAAS,MAAM,UAAU,eAAe;;;ACCjC,SAAS,aAAa,OAA+C;AAC1E,QAAM,MAAM,oBAAI,IAAsB;AACtC,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,IAAI,IAAI,KAAK,MAAM,EAAG,KAAI,IAAI,KAAK,QAAQ,CAAC,CAAC;AAClD,QAAI,IAAI,KAAK,MAAM,EAAG,KAAK,KAAK,MAAM;AAAA,EACxC;AAEA,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,SAA+B,CAAC;AACtC,QAAM,YAAY,oBAAI,IAAY;AAElC,WAAS,IAAI,MAAc,MAAsB;AAC/C,QAAI,QAAQ,IAAI,IAAI,GAAG;AACrB,YAAM,aAAa,KAAK,QAAQ,IAAI;AACpC,UAAI,eAAe,IAAI;AACrB,cAAM,QAAQ,KAAK,MAAM,UAAU;AACnC,cAAM,MAAM,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,QAAQ;AAC3C,YAAI,CAAC,UAAU,IAAI,GAAG,GAAG;AACvB,oBAAU,IAAI,GAAG;AACjB,iBAAO,KAAK,EAAE,MAAM,CAAC;AAAA,QACvB;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI,QAAQ,IAAI,IAAI,EAAG;AAEvB,YAAQ,IAAI,IAAI;AAChB,YAAQ,IAAI,IAAI;AAChB,SAAK,KAAK,IAAI;AAEd,eAAW,YAAY,IAAI,IAAI,IAAI,KAAK,CAAC,GAAG;AAC1C,UAAI,UAAU,IAAI;AAAA,IACpB;AAEA,SAAK,IAAI;AACT,YAAQ,OAAO,IAAI;AAAA,EACrB;AAEA,aAAW,QAAQ,IAAI,KAAK,GAAG;AAC7B,QAAI,CAAC,QAAQ,IAAI,IAAI,GAAG;AACtB,UAAI,MAAM,CAAC,CAAC;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AACT;;;AC1CO,SAAS,cAAc,SAAiB,OAA6B;AAC1E,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO,YAAY,OAAO;AAAA,IAC5B,KAAK;AACH,aAAO,UAAU,OAAO;AAAA,IAC1B,KAAK;AACH,aAAO,YAAY,OAAO;AAAA,IAC5B,KAAK;AACH,aAAO,UAAU,OAAO;AAAA,IAC1B,KAAK;AACH,aAAO,SAAS,OAAO;AAAA,IACzB,SAAS;AACP,YAAM,cAAqB;AAC3B,YAAM,IAAI,MAAM,0BAA0B,WAAW,EAAE;AAAA,IACzD;AAAA,EACF;AACF;AAQA,SAAS,YAAY,SAAyB;AAC5C,MAAI,SAAS;AACb,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AAEzB,QAAI,QAAQ,CAAC,MAAM,OAAO,IAAI,IAAI,QAAQ,QAAQ;AAChD,UAAI,SAAS;AACb,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C;AACA;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAI5C,iBAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,oBAAU;AAAA,QACZ;AACA,YAAI,IAAI;AAER,eAAO,IAAI,QAAQ,QAAQ;AACzB,cAAI,QAAQ,CAAC,MAAM,KAAK;AAEtB,gBAAI,cAAc;AAClB,gBAAI,IAAI,IAAI;AACZ,mBAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,OAAO,cAAc,QAAQ;AACvE;AACA;AAAA,YACF;AACA,gBAAI,gBAAgB,QAAQ;AAE1B,uBAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,0BAAU;AAAA,cACZ;AACA,kBAAI;AACJ;AAAA,YACF;AAAA,UACF;AACA,oBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,QACF;AACA;AAAA,MACF;AAAA,IAEF;AAGA,QAAI,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AAEhD,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAChD,kBAAU;AACV;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AACrD,gBAAU;AACV;AACA,gBAAU;AACV;AACA,aAAO,IAAI,QAAQ,QAAQ;AACzB,YAAI,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AAChD,oBAAU;AACV;AACA,oBAAU;AACV;AACA;AAAA,QACF;AAEA,kBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU,QAAQ,CAAC;AACnB;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,CAAC;AACnB;AACA,oBAAU,QAAQ,CAAC;AACnB;AAAA,QACF,WAAW,QAAQ,CAAC,MAAM,MAAM;AAC9B,oBAAU;AACV;AAAA,QACF,OAAO;AACL,oBAAU,QAAQ,CAAC;AACnB;AAAA,QACF;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AACtB,kBAAU,QAAQ,CAAC;AACnB;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU,QAAQ,CAAC;AACnB;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,CAAC;AACnB;AACA,oBAAU,QAAQ,CAAC;AACnB;AAAA,QACF,OAAO;AACL,oBAAU,QAAQ,CAAC;AACnB;AAAA,QACF;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AACtB,kBAAU,QAAQ,CAAC;AACnB;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU;AACV;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,kBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AACtB,kBAAU;AACV;AAAA,MACF;AAAA,IACF,OAEK;AACH,gBAAU,QAAQ,CAAC;AACnB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,UAAU,SAAyB;AAC1C,MAAI,SAAS;AACb,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AACzB,QAAI,QAAQ,CAAC,MAAM,KAAK;AACtB,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAChD,kBAAU;AACV;AAAA,MACF;AAAA,IACF,WAAW,QAAQ,CAAC,MAAM,KAAK;AAC7B,gBAAU,QAAQ,CAAC;AACnB;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IACvD,WAAW,QAAQ,CAAC,MAAM,KAAK;AAC7B,gBAAU,QAAQ,CAAC;AACnB;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IACvD,OAAO;AACL,gBAAU,QAAQ,CAAC;AACnB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,YAAY,SAAyB;AAC5C,MAAI,SAAS;AACb,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AAEzB,QAAI,YAAY;AAChB,QAAI,QAAQ;AACZ,QAAI,IAAI,QAAQ,QAAQ;AACtB,YAAM,KAAK,QAAQ,CAAC;AACpB,YAAM,KAAK,IAAI,IAAI,QAAQ,SAAS,QAAQ,IAAI,CAAC,IAAI;AACrD,YAAM,KAAK,IAAI,IAAI,QAAQ,SAAS,QAAQ,IAAI,CAAC,IAAI;AAErD,WACG,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,SAC7E,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,SAC7E,OAAO,OAAO,OAAO,MACtB;AAEA,cAAM,QAAQ,KAAK,IAAI,YAAY;AACnC,YAAI,SAAS,QAAQ,SAAS,QAAQ,SAAS,QAAQ,SAAS,MAAM;AACpE,sBAAY;AACZ,kBAAQ,KAAK,SAAS,GAAG;AAAA,QAC3B;AAAA,MACF;AAEA,UACE,cAAc,MACb,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,SAC7E,OAAO,OAAO,OAAO,MACtB;AACA,oBAAY;AACZ,gBAAQ,OAAO,OAAO,OAAO;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,aAAa,IAAI;AAGvB,QACE,aAAa,IAAI,QAAQ,WACxB,QAAQ,UAAU,MAAM,OAAO,QAAQ,UAAU,MAAM,QACxD,QAAQ,aAAa,CAAC,MAAM,QAAQ,UAAU,KAC9C,QAAQ,aAAa,CAAC,MAAM,QAAQ,UAAU,GAC9C;AACA,YAAM,QAAQ,QAAQ,UAAU;AAEhC,eAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,kBAAU;AAAA,MACZ;AAEA,gBAAU;AACV,UAAI,aAAa;AACjB,aAAO,IAAI,QAAQ,QAAQ;AACzB,YAAI,QAAQ,CAAC,MAAM,SAAS,QAAQ,IAAI,CAAC,MAAM,SAAS,QAAQ,IAAI,CAAC,MAAM,OAAO;AAChF,oBAAU;AACV,eAAK;AACL;AAAA,QACF;AACA,YAAI,CAAC,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AAC3D,oBAAU;AACV;AACA,oBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,QACF,OAAO;AACL,oBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAES,cAAc,KAAK,QAAQ,CAAC,MAAM,KAAK;AAC9C,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAChD,kBAAU;AACV;AAAA,MACF;AAAA,IACF,WAGE,aAAa,QAAQ,WACpB,QAAQ,UAAU,MAAM,OAAO,QAAQ,UAAU,MAAM,SACvD,YAAY,KAAK,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,MAAM,MACvD;AACA,YAAM,QAAQ,QAAQ,UAAU;AAEhC,eAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,kBAAU,QAAQ,CAAC;AAAA,MACrB;AACA,gBAAU,QAAQ,UAAU;AAC5B,UAAI,aAAa;AACjB,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,OAAO;AACjD,YAAI,CAAC,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AAC3D,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,WAAW,QAAQ,CAAC,MAAM,MAAM;AAC9B,oBAAU;AACV;AACA;AAAA,QACF,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,OAAO;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IAC/E,OACK;AACH,gBAAU,QAAQ,CAAC;AACnB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,UAAU,SAAyB;AAC1C,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAU;AAEd,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,WAAW,gBAAgB,KAAK,IAAI,GAAG;AAC1C,gBAAU;AACV,aAAO,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;AACnC;AAAA,IACF;AACA,QAAI,SAAS;AACX,UAAI,cAAc,KAAK,IAAI,GAAG;AAC5B,kBAAU;AAAA,MACZ;AACA,aAAO,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;AACnC;AAAA,IACF;AAGA,QAAI,YAAY;AAChB,QAAI,IAAI;AACR,WAAO,IAAI,KAAK,QAAQ;AACtB,UAAI,KAAK,CAAC,MAAM,KAAK;AAEnB,qBAAa,IAAI,OAAO,KAAK,SAAS,CAAC;AACvC;AAAA,MACF,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B,qBAAa,KAAK,CAAC;AAAG;AACtB,oBAAY,2BAA2B,MAAM,GAAG,SAAS;AACzD,YAAK,gCAAgC,MAAM,CAAC;AAAA,MAC9C,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B,qBAAa,KAAK,CAAC;AAAG;AACtB,eAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,KAAK;AACzC,cAAI,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAC3C,yBAAa,KAAK,GAAG;AACrB,yBAAa,KAAK,GAAG;AAAA,UACvB,OAAO;AACL,yBAAa,KAAK,GAAG;AAAA,UACvB;AAAA,QACF;AACA,YAAI,IAAI,KAAK,QAAQ;AAAE,uBAAa,KAAK,CAAC;AAAG;AAAA,QAAK;AAAA,MACpD,OAAO;AACL,qBAAa,KAAK,CAAC;AAAG;AAAA,MACxB;AAAA,IACF;AACA,WAAO,KAAK,SAAS;AAAA,EACvB;AAEA,SAAO,OAAO,KAAK,IAAI;AACzB;AAMA,SAAS,2BAA2B,MAAc,QAAgB,WAA2B;AAC3F,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,KAAK;AACzC,QAAI,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAC3C,mBAAa,KAAK,GAAG;AACrB,mBAAa,KAAK,GAAG;AAAA,IACvB,WAAW,KAAK,CAAC,MAAM,OAAO,IAAI,IAAI,KAAK,UAAU,KAAK,IAAI,CAAC,MAAM,KAAK;AAExE,mBAAa,KAAK,GAAG;AACrB,mBAAa,KAAK,GAAG;AACrB,UAAI,QAAQ;AACZ,aAAO,IAAI,KAAK,UAAU,QAAQ,GAAG;AACnC,YAAI,KAAK,CAAC,MAAM,KAAK;AACnB;AACA,uBAAa,KAAK,GAAG;AAAA,QACvB,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B;AACA,uBAAa,KAAK,GAAG;AAAA,QACvB,WAAW,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAClD,uBAAa,KAAK,GAAG;AACrB,uBAAa,KAAK,GAAG;AAAA,QACvB,OAAO;AACL,uBAAa,KAAK,GAAG;AAAA,QACvB;AAAA,MACF;AAAA,IACF,OAAO;AACL,mBAAa,KAAK,GAAG;AAAA,IACvB;AAAA,EACF;AACA,MAAI,IAAI,KAAK,QAAQ;AAAE,iBAAa,KAAK,CAAC;AAAA,EAAmB;AAC7D,SAAO;AACT;AAKA,SAAS,gCAAgC,MAAc,QAAwB;AAC7E,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,KAAK;AACzC,QAAI,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAC3C,WAAK;AAAA,IACP,WAAW,KAAK,CAAC,MAAM,OAAO,IAAI,IAAI,KAAK,UAAU,KAAK,IAAI,CAAC,MAAM,KAAK;AACxE,WAAK;AACL,UAAI,QAAQ;AACZ,aAAO,IAAI,KAAK,UAAU,QAAQ,GAAG;AACnC,YAAI,KAAK,CAAC,MAAM,KAAK;AACnB;AACA;AAAA,QACF,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B;AACA;AAAA,QACF,WAAW,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAClD,eAAK;AAAA,QACP,OAAO;AACL;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,MAAI,IAAI,KAAK,QAAQ;AAAE;AAAA,EAA0B;AACjD,SAAO;AACT;AAMA,SAAS,SAAS,SAAyB;AACzC,MAAI,SAAS;AACb,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AAEzB,QAAI,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AAC1E,YAAM,eAAe;AACrB,UAAI,IAAI,IAAI;AACZ,UAAI,WAAW;AAGf,UAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC5C,mBAAW;AACX;AAAA,MACF;AAEA,YAAM,aAAa;AACnB,aAAO,IAAI,QAAQ,UAAU,eAAe,KAAK,QAAQ,CAAC,CAAC,GAAG;AAC5D;AAAA,MACF;AACA,YAAM,aAAa,QAAQ,MAAM,YAAY,CAAC;AAC9C,UAAI,WAAW,SAAS,GAAG;AAEzB,YAAI,YAAY,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AACxD;AAAA,QACF;AAGA,YAAI,eAAe;AACnB,YAAI,UAAU;AAEd,YAAI,UAAU,QAAQ,UAAU,QAAQ,OAAO,MAAM,MAAM;AACzD,yBAAe;AAAA,QACjB;AACA,YAAI,cAAc;AAEhB,mBAAS,IAAI,cAAc,KAAK,SAAS,KAAK;AAC5C,gBAAI,QAAQ,CAAC,MAAM,MAAM;AACvB,wBAAU;AAAA,YACZ,OAAO;AACL,wBAAU,QAAQ,CAAC;AAAA,YACrB;AAAA,UACF;AACA,cAAI,UAAU;AAGd,cAAI,QAAQ;AACZ,iBAAO,IAAI,QAAQ,UAAU,CAAC,OAAO;AAEnC,kBAAM,YAAY;AAElB,gBAAI,aAAa;AACjB,mBAAO,aAAa,QAAQ,UAAU,QAAQ,UAAU,MAAM,MAAM;AAClE;AAAA,YACF;AACA,kBAAM,cAAc,QAAQ,MAAM,WAAW,UAAU;AAEvD,kBAAM,cAAc,YAAY,UAAU;AAC1C,gBACE,gBAAgB,cAChB,gBAAgB,aAAa,OAC7B,gBAAgB,aAAa,OAC7B,gBAAgB,aAAa,QAC7B,gBAAgB,aAAa,OAC7B,YAAY,WAAW,aAAa,GAAG,KACvC,gBAAgB,YAChB;AAEA,uBAAS,IAAI,WAAW,IAAI,YAAY,KAAK;AAC3C,0BAAU,QAAQ,CAAC;AAAA,cACrB;AACA,kBAAI;AACJ,kBAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAC7C,0BAAU;AACV;AAAA,cACF;AACA,sBAAQ;AAAA,YACV,OAAO;AAEL,uBAAS,IAAI,WAAW,IAAI,YAAY,KAAK;AAC3C,0BAAU;AAAA,cACZ;AACA,kBAAI;AACJ,kBAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAC7C,0BAAU;AACV;AAAA,cACF;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,MACF;AAEA,gBAAU,QAAQ,CAAC;AACnB;AACA;AAAA,IACF;AAGA,QAAK,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,OAAQ,QAAQ,CAAC,MAAM,KAAK;AACxE,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAChD,kBAAU;AACV;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AACrD,gBAAU;AAAK;AACf,gBAAU;AAAK;AACf,aAAO,IAAI,QAAQ,QAAQ;AACzB,YAAI,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AAChD,oBAAU;AAAK;AACf,oBAAU;AAAK;AACf;AAAA,QACF;AACA,kBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU,QAAQ,CAAC;AAAG;AACtB,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IACvD,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU,QAAQ,CAAC;AAAG;AACtB,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IACvD,OACK;AACH,gBAAU,QAAQ,CAAC;AAAG;AAAA,IACxB;AAAA,EACF;AACA,SAAO;AACT;;;AFvlBO,IAAM,cAAN,MAA4C;AAAA,EACjD,YAAoB,QAAwB;AAAxB;AAAA,EAAyB;AAAA,EAE7C,MAAM,QACJ,SACA,UAAqD,CAAC,GAC5B;AAC1B,UAAM,aAAa,QAAQ,OAAO;AAClC,UAAM,kBAAkB;AAAA,MACtB,GAAI,KAAK,OAAO,kBAAkB,CAAC;AAAA,MACnC,GAAI,QAAQ,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,EAAE,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC;AAG1B,UAAM,eAAe,MAAM,KAAK;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,YAAY;AAAA,IACtB;AAEA,UAAM,iBAAiB,IAAI,IAAI,YAAY;AAG3C,UAAM,QAAkC,CAAC;AACzC,UAAM,QAA0B,CAAC;AACjC,UAAM,UAAU,oBAAI,IAAY;AAGhC,eAAW,YAAY,cAAc;AACnC,YAAM,UAAU,SAAS,YAAY,QAAQ;AAC7C,YAAM,OAAO,IAAI;AAAA,QACf,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,cAAc,CAAC;AAAA,QACf,YAAY,CAAC;AAAA,MACf;AAAA,IACF;AAGA,eAAW,YAAY,cAAc;AACnC,YAAM,YAAY,SAAS,YAAY,QAAQ;AAC/C,UAAI;AACJ,UAAI;AACF,kBAAU,MAAM,SAAS,UAAU,OAAO;AAAA,MAC5C,QAAQ;AACN,YAAI,MAAM,SAAS,EAAG,OAAM,SAAS,EAAE,SAAS;AAChD;AAAA,MACF;AAEA,YAAM,WAAW,cAAc,SAAS,KAAK,OAAO,YAAY;AAChE,YAAM,UAAU,KAAK,eAAe,UAAU,UAAU,YAAY,cAAc;AAClF,iBAAW,cAAc,SAAS;AAChC,cAAM,WAAW,KAAK,OAAO;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,YAAI,CAAC,SAAU;AAEf,cAAM,YAAY,SAAS,YAAY,QAAQ;AAC/C,YAAI,CAAC,MAAM,SAAS,EAAG;AACvB,YAAI,cAAc,UAAW;AAE7B,cAAM,UAAU,GAAG,SAAS,KAAK,SAAS;AAC1C,YAAI,QAAQ,IAAI,OAAO,EAAG;AAC1B,gBAAQ,IAAI,OAAO;AAEnB,cAAM,KAAK;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAED,cAAM,SAAS,EAAE,aAAa,KAAK,SAAS;AAC5C,cAAM,SAAS,EAAE,WAAW,KAAK,SAAS;AAAA,MAC5C;AAAA,IACF;AAGA,UAAM,uBAAuB,aAAa,KAAK;AAE/C,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,KAAK,KAAK,EAAE;AAAA,MAC/B,YAAY,MAAM;AAAA,IACpB;AAAA,EACF;AAAA,EAEQ,eACN,SACA,UACA,SACA,cACU;AAEV,QAAI,KAAK,OAAO,gBAAgB;AAC9B,aAAO,KAAK,OAAO,eAAe,SAAS,UAAU,SAAS,YAAY;AAAA,IAC5E;AAEA,UAAM,UAAoB,CAAC;AAC3B,eAAW,WAAW,KAAK,OAAO,gBAAgB;AAEhD,YAAM,QAAQ,QAAQ,MAAM,MAAM,SAAS,GAAG,IAAI,QAAQ,MAAM,QAAQ,QAAQ,MAAM,QAAQ;AAC9F,YAAM,QAAQ,IAAI,OAAO,QAAQ,MAAM,QAAQ,KAAK;AACpD,UAAI;AACJ,cAAQ,QAAQ,MAAM,KAAK,OAAO,OAAO,MAAM;AAE7C,YAAI,MAAM,CAAC,GAAG;AACZ,kBAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,aACZ,KACA,YACA,iBACA,UACA,eAAuB,GACJ;AACnB,QAAI,WAAW,KAAK,gBAAgB,SAAU,QAAO,CAAC;AAEtD,UAAM,UAAoB,CAAC;AAC3B,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACtD,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,KAAK,KAAK,MAAM,IAAI;AACrC,YAAM,UAAU,SAAS,YAAY,QAAQ;AAG7C,UACE,gBAAgB;AAAA,QACd,CAAC,MAAM,EAAE,KAAK,MAAM,IAAI,KAAK,EAAE,KAAK,OAAO,KAAK,EAAE,KAAK,QAAQ;AAAA,MACjE,GACA;AACA;AAAA,MACF;AAEA,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,MAAM,KAAK,WAAW,GAAG,EAAG;AAChC,cAAM,MAAM,MAAM,KAAK;AAAA,UACrB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,eAAe;AAAA,QACjB;AACA,gBAAQ,KAAK,GAAG,GAAG;AAAA,MACrB,WAAW,MAAM,OAAO,GAAG;AACzB,cAAM,SAAS,MAAM,KAAK,YAAY,GAAG;AACzC,YAAI,SAAS,GAAG;AACd,gBAAM,MAAM,MAAM,KAAK,MAAM,MAAM;AACnC,cAAI,KAAK,OAAO,WAAW,SAAS,GAAG,GAAG;AACxC,oBAAQ,KAAK,QAAQ;AAAA,UACvB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;AGzLA,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,QAAAC,aAAY;AAIrB,IAAM,UAAyD;AAAA,EAC7D,EAAE,MAAM,cAAc,UAAU,OAAO;AAAA,EACvC,EAAE,MAAM,UAAU,UAAU,KAAK;AAAA,EACjC,EAAE,MAAM,kBAAkB,UAAU,SAAS;AAAA,EAC7C,EAAE,MAAM,YAAY,UAAU,SAAS;AAAA,EACvC,EAAE,MAAM,oBAAoB,UAAU,SAAS;AAAA,EAC/C,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EACtC,EAAE,MAAM,WAAW,UAAU,OAAO;AAAA,EACpC,EAAE,MAAM,gBAAgB,UAAU,OAAO;AAAA,EACzC,EAAE,MAAM,oBAAoB,UAAU,SAAS;AAAA,EAC/C,EAAE,MAAM,aAAa,UAAU,QAAQ;AAAA,EACvC,EAAE,MAAM,YAAY,UAAU,QAAQ;AAAA,EACtC,EAAE,MAAM,iBAAiB,UAAU,QAAQ;AAAA,EAC3C,EAAE,MAAM,WAAW,UAAU,OAAO;AAAA,EACpC,EAAE,MAAM,iBAAiB,UAAU,MAAM;AAAA,EACzC,EAAE,MAAM,gBAAgB,UAAU,OAAO;AAAA,EACzC,EAAE,MAAM,kBAAkB,UAAU,QAAQ;AAAA,EAC5C,EAAE,MAAM,YAAY,UAAU,QAAQ;AAAA,EACtC,EAAE,MAAM,gBAAgB,UAAU,aAAa;AAAA,EAC/C,EAAE,MAAM,iBAAiB,UAAU,aAAa;AAClD;AAGA,IAAM,cAA2C;AAAA,EAC/C,CAAC,QAAQ,SAAS;AAAA,EAClB,CAAC,WAAW,SAAS;AACvB;AAGA,IAAM,UAAsC;AAAA,EAC1C,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AACT;AAOA,eAAsB,eAAe,SAAsC;AAEzE,aAAW,UAAU,SAAS;AAC5B,QAAI;AACF,YAAM,IAAI,MAAMD,MAAKC,MAAK,SAAS,OAAO,IAAI,CAAC;AAC/C,UAAI,EAAE,OAAO,KAAK,EAAE,YAAY,GAAG;AACjC,eAAO,OAAO;AAAA,MAChB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,MAAI;AACF,UAAM,aAAa,MAAMF,SAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AACjE,eAAW,SAAS,YAAY;AAC9B,UAAI,CAAC,MAAM,OAAO,EAAG;AACrB,iBAAW,CAAC,KAAK,IAAI,KAAK,aAAa;AACrC,YAAI,MAAM,KAAK,SAAS,GAAG,EAAG,QAAO;AAAA,MACvC;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,SAAS,oBAAI,IAAwB;AAC3C,MAAI;AACF,UAAM,eAAe,SAAS,QAAQ,GAAG,CAAC;AAAA,EAC5C,QAAQ;AAAA,EAER;AAEA,MAAI,OAAO,OAAO,GAAG;AACnB,QAAI,UAAsB;AAC1B,QAAI,WAAW;AACf,eAAW,CAAC,MAAM,KAAK,KAAK,QAAQ;AAClC,UAAI,QAAQ,UAAU;AACpB,mBAAW;AACX,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,eAAe,eACb,KACA,QACA,UACA,cACe;AACf,MAAI,gBAAgB,SAAU;AAE9B,QAAM,UAAU,MAAMA,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,SAAS,eAAgB;AAEjE,QAAI,MAAM,YAAY,KAAK,eAAe,WAAW,GAAG;AACtD,YAAM;AAAA,QACJE,MAAK,KAAK,MAAM,IAAI;AAAA,QACpB;AAAA,QACA;AAAA,QACA,eAAe;AAAA,MACjB;AAAA,IACF,WAAW,MAAM,OAAO,GAAG;AACzB,YAAM,SAAS,MAAM,KAAK,YAAY,GAAG;AACzC,UAAI,SAAS,GAAG;AACd,cAAM,MAAM,MAAM,KAAK,MAAM,MAAM;AACnC,cAAM,OAAO,QAAQ,GAAG;AACxB,YAAI,MAAM;AACR,iBAAO,IAAI,OAAO,OAAO,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AClJA,SAAS,oBAAoB;AAC7B,SAAS,QAAAC,OAAM,SAAS,WAAAC,gBAAe;;;ACEhC,IAAM,eAAe;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;;;ADXA,IAAM,SAAyB;AAAA,EAC7B,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,8CAA8C;AAAA;AAAA,EAEzD;AAAA;AAAA;AAAA,EAGA,eAAe,SAA2B;AACxC,UAAM,UAAoB,CAAC;AAG3B,UAAM,YAAY;AAClB,QAAI;AACJ,YAAQ,QAAQ,UAAU,KAAK,OAAO,OAAO,MAAM;AACjD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAGA,UAAM,cAAc;AACpB,YAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,YAAM,UAAU,MAAM,CAAC,EAAE,MAAM,GAAG;AAClC,iBAAW,OAAO,SAAS;AACzB,cAAM,UAAU,IAAI,KAAK;AACzB,YAAI,QAAS,SAAQ,KAAK,OAAO;AAAA,MACnC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,QAAI,WAAW,WAAW,GAAG,GAAG;AAC9B,YAAM,OAAO,WAAW,MAAM,MAAM,IAAI,CAAC,EAAE,UAAU;AACrD,UAAI,OAAO,QAAQ,UAAU;AAC7B,eAAS,IAAI,GAAG,IAAI,MAAM,IAAK,QAAO,QAAQ,IAAI;AAClD,YAAM,OAAO,WAAW,MAAM,IAAI,EAAE,QAAQ,OAAO,GAAG;AACtD,aAAO,iBAAiBC,MAAK,MAAM,IAAI,GAAG,YAAY;AAAA,IACxD;AAEA,UAAM,QAAQ,WAAW,QAAQ,OAAO,GAAG;AAC3C,WAAO,iBAAiBA,MAAK,SAAS,KAAK,GAAG,YAAY;AAAA,EAC5D;AAAA,EACA,gBAAgB,CAAC,eAAe,WAAW,QAAQ,eAAe,QAAQ,OAAO;AACnF;AAEA,SAAS,iBAAiB,MAAc,cAA0C;AAEhF,MAAI,aAAa,IAAI,OAAO,KAAK,EAAG,QAAO,OAAO;AAElD,MAAI,aAAa,IAAIA,MAAK,MAAM,aAAa,CAAC,EAAG,QAAOA,MAAK,MAAM,aAAa;AAChF,SAAO;AACT;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAA2B;AACxC,UAAM,UAAoB,CAAC;AAG3B,UAAM,WAAW;AACjB,QAAI;AACJ,YAAQ,QAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAChD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAGA,UAAM,WAAW;AACjB,YAAQ,QAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAChD,YAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,0BAAoB,MAAM,IAAI,OAAO;AAAA,IACvC;AAGA,UAAM,gBAAgB;AACtB,YAAQ,QAAQ,cAAc,KAAK,OAAO,OAAO,MAAM;AACrD,YAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,0BAAoB,MAAM,IAAI,SAAS,OAAO;AAAA,IAChD;AAGA,UAAM,eAAe;AACrB,YAAQ,QAAQ,aAAa,KAAK,OAAO,OAAO,MAAM;AACpD,YAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,0BAAoB,MAAM,IAAI,SAAS,MAAM;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAC3D,UAAM,SAASA,MAAK,SAAS,KAAK;AAElC,QAAI,CAAC,WAAW,SAAS,IAAI,GAAG;AAE9B,YAAM,YAAY,QAAQ,UAAU;AACpC,YAAM,SAASA,MAAK,WAAW,aAAa,KAAK;AACjD,UAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,YAAM,QAAQA,MAAK,WAAW,YAAY,QAAQ;AAClD,UAAI,aAAa,IAAI,KAAK,EAAG,QAAO;AACpC,aAAO;AAAA,IACT;AAGA,QAAI,WAAW,WAAW,SAAS,GAAG;AACpC,YAAM,YAAY,QAAQ,QAAQ,UAAU,CAAC;AAC7C,YAAMC,YAAW,WAAW,MAAM,UAAU,MAAM,EAAE,MAAM,IAAI;AAC9D,eAAS,IAAIA,UAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,cAAM,OAAOA,UAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C,cAAM,SAASD,MAAK,WAAW,OAAO,KAAK;AAC3C,YAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,cAAM,QAAQA,MAAK,WAAW,MAAM,QAAQ;AAC5C,YAAI,aAAa,IAAI,KAAK,EAAG,QAAO;AAAA,MACtC;AACA,aAAO;AAAA,IACT;AAGA,QAAI,WAAW,WAAW,QAAQ,GAAG;AACnC,YAAM,UAAU,QAAQ,UAAU;AAClC,YAAMC,YAAW,WAAW,MAAM,SAAS,MAAM,EAAE,MAAM,IAAI;AAC7D,eAAS,IAAIA,UAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,cAAM,OAAOA,UAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C,cAAM,SAASD,MAAK,SAAS,OAAO,KAAK;AACzC,YAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,cAAM,QAAQA,MAAK,SAAS,MAAM,QAAQ;AAC1C,YAAI,aAAa,IAAI,KAAK,EAAG,QAAO;AAAA,MACtC;AACA,aAAO;AAAA,IACT;AAGA,UAAM,WAAW,WAAW,MAAM,IAAI;AAEtC,aAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,YAAM,OAAO,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C,YAAM,SAASA,MAAK,QAAQ,OAAO,KAAK;AACxC,UAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,YAAM,QAAQA,MAAK,QAAQ,MAAM,QAAQ;AACzC,UAAI,aAAa,IAAI,KAAK,EAAG,QAAO;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,QAAQ;AAC3B;AAQA,SAAS,oBAAoB,MAAc,QAAgB,SAAmB,YAA2B;AACvG,QAAM,UAAU,KAAK,KAAK;AAG1B,QAAM,aAAa,QAAQ,QAAQ,GAAG;AACtC,MAAI,eAAe,IAAI;AAErB,QAAI,OAAO,SAAS,GAAG,MAAM,KAAK,OAAO,KAAK;AAC9C,QAAI,YAAY;AACd,aAAO,GAAG,UAAU,KAAK,IAAI;AAAA,IAC/B;AAGA,QAAI,QAAQ,CAAC,KAAK,SAAS,GAAG,GAAG;AAC/B,cAAQ,KAAK,IAAI;AAAA,IACnB;AACA;AAAA,EACF;AAGA,MAAI,aAAa,QAAQ,MAAM,GAAG,UAAU,EAAE,KAAK;AACnD,MAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,iBAAa,WAAW,MAAM,GAAG,EAAE;AAAA,EACrC;AACA,QAAM,aAAa,SAAS,GAAG,MAAM,KAAK,UAAU,KAAK;AAGzD,MAAI,QAAQ;AACZ,MAAI,WAAW;AACf,WAAS,IAAI,YAAY,IAAI,QAAQ,QAAQ,KAAK;AAChD,QAAI,QAAQ,CAAC,MAAM,IAAK;AAAA,aACf,QAAQ,CAAC,MAAM,KAAK;AAC3B;AACA,UAAI,UAAU,GAAG;AAAE,mBAAW;AAAG;AAAA,MAAO;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,aAAa,GAAI;AAGrB,QAAM,QAAQ,QAAQ,MAAM,aAAa,GAAG,QAAQ,EAAE,KAAK;AAC3D,QAAM,QAAQ,qBAAqB,KAAK;AAExC,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,YAAY,QAAQ;AACtB,YAAM,WAAW,aAAa,GAAG,UAAU,KAAK,UAAU,KAAK;AAC/D,cAAQ,KAAK,QAAQ;AAAA,IACvB,WAAW,SAAS;AAClB,0BAAoB,SAAS,YAAY,SAAS,UAAU;AAAA,IAC9D;AAAA,EACF;AACF;AAGA,SAAS,qBAAqB,GAAqB;AACjD,QAAM,QAAkB,CAAC;AACzB,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,QAAI,EAAE,CAAC,MAAM,IAAK;AAAA,aACT,EAAE,CAAC,MAAM,IAAK;AAAA,aACd,EAAE,CAAC,MAAM,OAAO,UAAU,GAAG;AACpC,YAAM,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC;AAC5B,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AACA,QAAM,KAAK,EAAE,MAAM,KAAK,CAAC;AACzB,SAAO;AACT;AAGA,IAAM,KAAqB;AAAA,EACzB,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAA2B;AACxC,UAAM,UAAoB,CAAC;AAG3B,UAAM,cAAc;AACpB,QAAI;AACJ,YAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAGA,UAAM,aAAa;AACnB,YAAQ,QAAQ,WAAW,KAAK,OAAO,OAAO,MAAM;AAClD,YAAM,QAAQ,MAAM,CAAC;AACrB,YAAM,aAAa;AACnB,UAAI;AACJ,cAAQ,QAAQ,WAAW,KAAK,KAAK,OAAO,MAAM;AAChD,gBAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAG5D,UAAM,YAAY,eAAe,OAAO;AACxC,QAAI,CAAC,aAAa,CAAC,WAAW,WAAW,SAAS,EAAG,QAAO;AAE5D,UAAM,UAAU,WAAW,MAAM,UAAU,SAAS,CAAC;AAErD,UAAM,SAASA,MAAK,SAAS,OAAO;AACpC,eAAW,KAAK,cAAc;AAC5B,UAAI,EAAE,WAAW,SAAS,GAAG,KAAK,EAAE,SAAS,KAAK,EAAG,QAAO;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,QAAQ;AAC3B;AAMA,IAAM,aAAa,oBAAI,IAA2B;AAClD,SAAS,eAAe,SAAgC;AACtD,MAAI,WAAW,IAAI,OAAO,EAAG,QAAO,WAAW,IAAI,OAAO;AAC1D,MAAI;AACF,UAAM,UAAU,aAAaA,MAAK,SAAS,QAAQ,GAAG,OAAO;AAC7D,UAAM,QAAQ,QAAQ,MAAM,kBAAkB;AAC9C,UAAM,SAAS,QAAQ,MAAM,CAAC,EAAE,KAAK,IAAI;AACzC,eAAW,IAAI,SAAS,MAAM;AAC9B,WAAO;AAAA,EACT,QAAQ;AACN,eAAW,IAAI,SAAS,IAAI;AAC5B,WAAO;AAAA,EACT;AACF;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,OAAO;AAAA,EACpB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA;AAAA,IAGd,EAAE,OAAO,uCAAuC;AAAA,EAClD;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAE5D,QAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,aAAO;AAAA,IACT;AAKA,UAAM,WAAW,WAAW,MAAM,GAAG;AACrC,aAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,YAAM,WAAW,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI;AAClD,iBAAW,WAAW,CAAC,IAAI,kBAAkB,QAAQ,oBAAoB,GAAG;AAC1E,cAAM,OAAOA,MAAK,SAAS,SAAS,QAAQ;AAC5C,YAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,MACrC;AAAA,IACF;AAGA,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,WAAW,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;AAC/C,iBAAW,WAAW,CAAC,IAAI,kBAAkB,QAAQ,oBAAoB,GAAG;AAC1E,cAAM,OAAOA,MAAK,SAAS,SAAS,QAAQ;AAC5C,YAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,MACrC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,SAAS,UAAU,aAAa,SAAS;AAC5D;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,MAAM,QAAQ,OAAO,QAAQ,MAAM,MAAM;AAAA,EACtD,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,0BAA0B;AAAA,EACrC;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,UAAM,aAAaE,SAAQ,QAAQ,UAAU,GAAG,UAAU;AAC1D,QAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AAEzC,UAAM,WAAWF,MAAK,SAAS,UAAU;AACzC,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AAEvC,eAAW,UAAU,CAAC,WAAW,KAAK,GAAG;AACvC,YAAM,OAAOA,MAAK,SAAS,QAAQ,UAAU;AAC7C,UAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,IACrC;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,SAAS,eAAe,SAAS,SAAS;AAC7D;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,0CAA0C;AAAA;AAAA,IAEnD,EAAE,OAAO,iCAAiC;AAAA,EAC5C;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAC3D,UAAM,UAAU,WAAW,SAAS,KAAK,IAAI,aAAa,aAAa;AAEvE,UAAM,aAAaE,SAAQ,QAAQ,UAAU,GAAG,OAAO;AACvD,QAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AAEzC,UAAM,WAAWF,MAAK,SAAS,OAAO;AACtC,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,UAAM,UAAUA,MAAK,SAAS,OAAO,OAAO;AAC5C,QAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AACtC,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,UAAU,WAAW;AACxC;AAGA,IAAM,MAAsB;AAAA,EAC1B,IAAI;AAAA,EACJ,YAAY,CAAC,MAAM;AAAA,EACnB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,uDAAuD;AAAA;AAAA,IAEhE,EAAE,OAAO,sEAAsE;AAAA;AAAA,IAE/E,EAAE,OAAO,8CAA8C;AAAA,EACzD;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,QAAI,iBAAiB;AACrB,QAAI,eAAe,WAAW,GAAG,GAAG;AAClC,uBAAiB,eAAe,MAAM,CAAC;AAAA,IACzC;AAEA,QAAI,eAAe,SAAS,GAAG,KAAK,eAAe,SAAS,MAAM,GAAG;AACnE,YAAM,UAAU,eAAe,SAAS,MAAM,IAAI,iBAAiB,iBAAiB;AACpF,YAAM,aAAaE,SAAQ,QAAQ,UAAU,GAAG,OAAO;AACvD,UAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AACzC,YAAMC,YAAWH,MAAK,SAAS,OAAO;AACtC,UAAI,aAAa,IAAIG,SAAQ,EAAG,QAAOA;AACvC,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,WAAW,QAAQ,OAAO,GAAG,IAAI;AAClD,UAAM,WAAWH,MAAK,SAAS,QAAQ;AACvC,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,UAAM,UAAUA,MAAK,SAAS,OAAO,QAAQ;AAC7C,QAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AACtC,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,QAAQ;AAC3B;AAIA,IAAM,mBAAmB,oBAAI,IAAI,CAAC,WAAW,QAAQ,eAAe,eAAe,CAAC;AAEpF,IAAM,QAAwB;AAAA,EAC5B,IAAI;AAAA,EACJ,YAAY,CAAC,QAAQ;AAAA,EACrB,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAAiB,UAAkB,UAAkB,cAAqC;AACvG,UAAM,UAAoB,CAAC;AAG3B,UAAM,cAAc;AACpB,QAAI;AACJ,YAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAIA,UAAM,UAAU,oBAAI,IAAoB;AACxC,eAAW,KAAK,cAAc;AAC5B,UAAI,MAAM,YAAY,CAAC,EAAE,SAAS,QAAQ,EAAG;AAC7C,YAAM,WAAW,EAAE,MAAM,GAAG,EAAE,IAAI,EAAG,QAAQ,YAAY,EAAE;AAC3D,UAAI,CAAC,YAAY,iBAAiB,IAAI,QAAQ,EAAG;AACjD,cAAQ,IAAI,UAAU,CAAC;AAAA,IACzB;AAEA,QAAI,QAAQ,OAAO,GAAG;AACpB,YAAM,UAAU,CAAC,GAAG,QAAQ,KAAK,CAAC,EAAE;AAAA,QAAI,CAAC,MACvC,EAAE,QAAQ,uBAAuB,MAAM;AAAA,MACzC;AACA,YAAM,WAAW,IAAI,OAAO,OAAO,QAAQ,KAAK,GAAG,CAAC,QAAQ,GAAG;AAC/D,YAAM,UAAU,oBAAI,IAAY;AAChC,cAAQ,QAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAChD,cAAM,WAAW,MAAM,CAAC;AACxB,cAAM,aAAa,QAAQ,IAAI,QAAQ;AACvC,YAAI,cAAc,CAAC,QAAQ,IAAI,UAAU,GAAG;AAC1C,kBAAQ,IAAI,UAAU;AACtB,kBAAQ,KAAK,UAAU;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAE5D,QAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AAGzC,UAAM,SAASA,MAAK,SAAS,WAAW,UAAU;AAClD,eAAW,KAAK,cAAc;AAC5B,UAAI,EAAE,WAAW,SAAS,GAAG,KAAK,EAAE,SAAS,QAAQ,EAAG,QAAO;AAAA,IACjE;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,YAAY,aAAa;AAC5C;AAGA,IAAM,SAAyB;AAAA,EAC7B,IAAI;AAAA,EACJ,YAAY,CAAC,OAAO,MAAM;AAAA,EAC1B,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,wBAAwB;AAAA,EACnC;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAE5D,QAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,aAAO;AAAA,IACT;AAGA,QAAI,YAAY;AAChB,QAAI,UAAU,SAAS,GAAG,GAAG;AAC3B,kBAAY,UAAU,MAAM,GAAG,EAAE;AAAA,IACnC;AAGA,UAAM,WAAW,UAAU,MAAM,GAAG;AACpC,UAAM,WAAW,SAAS,KAAK,GAAG;AAClC,eAAW,OAAO,CAAC,OAAO,MAAM,GAAG;AACjC,iBAAW,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,GAAG;AACD,cAAM,OAAOA,MAAK,SAAS,SAAS,WAAW,GAAG;AAClD,YAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,MACrC;AAAA,IACF;AAEA,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,aAAa,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG;AAC7C,iBAAW,OAAO,CAAC,OAAO,MAAM,GAAG;AACjC,mBAAW,WAAW;AAAA,UACpB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,GAAG;AACD,gBAAM,OAAOA,MAAK,SAAS,SAAS,aAAa,GAAG;AACpD,cAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,SAAS,aAAa,SAAS;AAClD;AAIA,IAAM,qBAAqB,oBAAI,IAAI,CAAC,gBAAgB,cAAc,CAAC;AAEnE,IAAM,SAAyB;AAAA,EAC7B,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAAS,UAAU,UAAU,cAAc;AACxD,UAAM,UAAoB,CAAC;AAG3B,UAAM,aAAa;AACnB,QAAI;AACJ,YAAQ,QAAQ,WAAW,KAAK,OAAO,OAAO,MAAM;AAClD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAIA,UAAM,WAAW,oBAAI,IAAoB;AACzC,eAAW,KAAK,cAAc;AAC5B,UAAI,MAAM,SAAU;AACpB,UAAI,CAAC,EAAE,SAAS,KAAK,EAAG;AACxB,YAAM,WAAW,EAAE,MAAM,GAAG,EAAE,IAAI;AAClC,YAAM,YAAY,SAAS,QAAQ,gBAAgB,EAAE,EAAE,QAAQ,UAAU,EAAE;AAC3E,UAAI,CAAC,aAAa,mBAAmB,IAAI,SAAS,EAAG;AACrD,eAAS,IAAI,WAAW,CAAC;AAAA,IAC3B;AAEA,QAAI,SAAS,OAAO,GAAG;AAErB,YAAM,UAAU,CAAC,GAAG,SAAS,KAAK,CAAC,EAAE;AAAA,QAAI,CAAC,MACxC,EAAE,QAAQ,uBAAuB,MAAM;AAAA,MACzC;AACA,YAAM,WAAW,IAAI,OAAO,OAAO,QAAQ,KAAK,GAAG,CAAC,QAAQ,GAAG;AAC/D,YAAM,UAAU,oBAAI,IAAY;AAChC,cAAQ,QAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAChD,cAAM,YAAY,MAAM,CAAC;AACzB,cAAM,aAAa,SAAS,IAAI,SAAS;AACzC,YAAI,cAAc,CAAC,QAAQ,IAAI,UAAU,GAAG;AAC1C,kBAAQ,IAAI,UAAU;AACtB,kBAAQ,KAAK,UAAU;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAE5D,QAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AAGzC,UAAM,WAAW,WAAW,MAAM,GAAG;AAGrC,aAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,YAAM,WAAW,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI;AAClD,iBAAW,WAAW,CAAC,IAAI,QAAQ,MAAM,GAAG;AAC1C,cAAM,OAAOA,MAAK,SAAS,SAAS,QAAQ;AAC5C,YAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,MACrC;AAAA,IACF;AAIA,aAAS,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS;AACpD,YAAM,UAAU,SAAS,MAAM,KAAK,EAAE,KAAK,GAAG;AAC9C,iBAAW,WAAW,CAAC,IAAI,QAAQ,MAAM,GAAG;AAC1C,cAAM,SAASA,MAAK,SAAS,SAAS,OAAO,IAAI;AACjD,mBAAW,KAAK,cAAc;AAC5B,cAAI,EAAE,WAAW,MAAM,KAAK,EAAE,SAAS,KAAK,EAAG,QAAO;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,OAAO,OAAO,SAAS,YAAY,aAAa;AACnE;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,OAAO;AAAA,EACpB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,+BAA+B;AAAA;AAAA,IAExC,EAAE,OAAO,+BAA+B;AAAA,EAC1C;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,QAAI,WAAW,WAAW,OAAO,EAAG,QAAO;AAG3C,QAAI,WAAW,WAAW,UAAU,GAAG;AACrC,YAAM,aAAa,gBAAgB,OAAO;AAC1C,UAAI,CAAC,WAAY,QAAO;AACxB,YAAM,SAAS,WAAW,UAAU;AACpC,UAAI,CAAC,WAAW,WAAW,MAAM,EAAG,QAAO;AAC3C,YAAM,UAAU,WAAW,MAAM,OAAO,MAAM;AAE9C,YAAM,UAAUA,MAAK,SAAS,OAAO,OAAO;AAC5C,UAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AAEtC,YAAM,WAAWA,MAAK,SAAS,OAAO;AACtC,UAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,aAAO;AAAA,IACT;AAGA,UAAM,WAAWE,SAAQ,QAAQ,UAAU,GAAG,UAAU;AACxD,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,gBAAgB,SAAS,aAAa;AACzD;AAEA,IAAM,mBAAmB,oBAAI,IAA2B;AACxD,SAAS,gBAAgB,SAAgC;AACvD,MAAI,iBAAiB,IAAI,OAAO,EAAG,QAAO,iBAAiB,IAAI,OAAO;AACtE,MAAI;AACF,UAAM,UAAU,aAAaF,MAAK,SAAS,cAAc,GAAG,OAAO;AACnE,UAAM,QAAQ,QAAQ,MAAM,iBAAiB;AAC7C,UAAM,OAAO,QAAQ,MAAM,CAAC,IAAI;AAChC,qBAAiB,IAAI,SAAS,IAAI;AAClC,WAAO;AAAA,EACT,QAAQ;AACN,qBAAiB,IAAI,SAAS,IAAI;AAClC,WAAO;AAAA,EACT;AACF;AAGA,IAAM,QAAwB;AAAA,EAC5B,IAAI;AAAA,EACJ,YAAY,CAAC,UAAU,KAAK;AAAA,EAC5B,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAA2B;AACxC,UAAM,UAAoB,CAAC;AAI3B,UAAM,cAAc;AACpB,QAAI;AACJ,YAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,YAAM,OAAO,MAAM,CAAC;AAEpB,YAAM,aAAa,KAAK,MAAM,yBAAyB;AACvD,UAAI,YAAY;AACd,cAAM,SAAS,WAAW,CAAC;AAC3B,cAAM,QAAQ,WAAW,CAAC,EAAE,MAAM,GAAG;AACrC,mBAAW,QAAQ,OAAO;AACxB,gBAAM,UAAU,KAAK,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC;AAC1C,cAAI,YAAY,IAAK;AACrB,kBAAQ,KAAK,GAAG,MAAM,IAAI,OAAO,EAAE;AAAA,QACrC;AAAA,MACF,WAAW,KAAK,SAAS,IAAI,GAAG;AAE9B;AAAA,MACF,OAAO;AACL,gBAAQ,KAAK,IAAI;AAAA,MACnB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAC5D,UAAM,WAAW,WAAW,MAAM,GAAG;AAErC,aAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,YAAM,WAAW,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC9C,iBAAW,OAAO,CAAC,UAAU,KAAK,GAAG;AACnC,mBAAW,WAAW,CAAC,IAAI,mBAAmB,QAAQ,MAAM,GAAG;AAC7D,gBAAM,OAAOA,MAAK,SAAS,SAAS,WAAW,GAAG;AAClD,cAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,aAAa,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG;AAC7C,iBAAW,OAAO,CAAC,UAAU,KAAK,GAAG;AACnC,mBAAW,WAAW,CAAC,IAAI,mBAAmB,QAAQ,MAAM,GAAG;AAC7D,gBAAM,OAAOA,MAAK,SAAS,SAAS,aAAa,GAAG;AACpD,cAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,UAAU,UAAU,aAAa,UAAU;AAC9D;AAGA,IAAM,aAA6B;AAAA,EACjC,IAAI;AAAA,EACJ,YAAY,CAAC,OAAO,QAAQ,OAAO,QAAQ,QAAQ,MAAM;AAAA,EACzD,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,mEAAmE;AAAA;AAAA,IAE5E,EAAE,OAAO,yCAAyC;AAAA;AAAA,IAElD,EAAE,OAAO,mFAAmF;AAAA;AAAA,IAE5F,EAAE,OAAO,0CAA0C;AAAA,EACrD;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,QAAI,WAAW,WAAW,OAAO,EAAG,QAAO;AAC3C,QAAI,CAAC,WAAW,WAAW,GAAG,EAAG,QAAO;AAGxC,UAAM,WAAWE,SAAQ,QAAQ,UAAU,GAAG,UAAU;AAGxD,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AAGvC,QAAI,SAAS,SAAS,KAAK,GAAG;AAC5B,YAAM,SAAS,SAAS,MAAM,GAAG,EAAE,IAAI;AACvC,UAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,YAAM,UAAU,SAAS,MAAM,GAAG,EAAE,IAAI;AACxC,UAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AAAA,IACxC;AACA,QAAI,SAAS,SAAS,MAAM,GAAG;AAC7B,YAAM,UAAU,SAAS,MAAM,GAAG,EAAE,IAAI;AACxC,UAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AAAA,IACxC;AAGA,eAAW,OAAO,CAAC,OAAO,QAAQ,OAAO,QAAQ,QAAQ,MAAM,GAAG;AAChE,UAAI,aAAa,IAAI,WAAW,GAAG,EAAG,QAAO,WAAW;AAAA,IAC1D;AAGA,eAAW,OAAO,CAAC,aAAa,cAAc,aAAa,YAAY,GAAG;AACxE,UAAI,aAAa,IAAI,WAAW,GAAG,EAAG,QAAO,WAAW;AAAA,IAC1D;AAEA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,gBAAgB,cAAc,QAAQ,SAAS,UAAU;AAC5E;AAGA,IAAM,mBAA8D;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,kBAAkB,IAAuC;AACvE,SAAO,iBAAiB,EAAE,KAAK;AACjC;;;AL/xBA,eAAsB,eACpB,SACA,UAA0B,CAAC,GACD;AAC1B,QAAM,aAAaE,SAAQ,OAAO;AAGlC,MAAI;AACF,UAAM,IAAI,MAAMC,MAAK,UAAU;AAC/B,QAAI,CAAC,EAAE,YAAY,GAAG;AACpB,YAAM,IAAI,cAAc,oBAAoB,UAAU,EAAE;AAAA,IAC1D;AAAA,EACF,SAAS,OAAO;AACd,QAAI,iBAAiB,cAAe,OAAM;AAC1C,UAAM,IAAI,cAAc,wBAAwB,UAAU,IAAI,EAAE,OAAO,MAAM,CAAC;AAAA,EAChF;AAEA,QAAM,WAAW,QAAQ,YAAa,MAAM,eAAe,UAAU;AAErE,MAAI;AACF,UAAM,SAAS,kBAAkB,QAAQ;AACzC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,cAAc,oCAAoC,QAAQ,EAAE;AAAA,IACxE;AAEA,WAAO,MAAM,IAAI,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO;AAAA,EAClE,SAAS,OAAO;AACd,QAAI,iBAAiB,cAAe,OAAM;AAC1C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAM,IAAI,cAAc,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,EACnD;AACF;AAGO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACd;AACF;;;AO3DA,IAAI,gBAAwB,aAAa;AAGlC,SAAS,YAAoB;AAClC,SAAO;AACT;AAGO,SAAS,UAAU,QAAsB;AAC9C,kBAAgB;AAClB;AAGA,SAAS,eAAuB;AAC9B,QAAM,MAAM,QAAQ,IAAI,UAAU,QAAQ,IAAI,QAAQ;AACtD,MAAI,IAAI,WAAW,IAAI,EAAG,QAAO;AACjC,SAAO;AACT;AAGO,SAAS,EAAE,KAAa,MAAgD;AAC7E,QAAM,WAAW,kBAAkB,OAAO,KAAK;AAC/C,MAAI,MAAO,SAAoC,GAAG,KAAM,GAA8B,GAAG,KAAK;AAE9F,MAAI,MAAM;AACR,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,GAAG;AACzC,YAAM,IAAI,WAAW,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;AAIA,IAAM,KAAK;AAAA;AAAA,EAET,mBAAmB;AAAA,EACnB,qBAAqB;AAAA;AAAA,EAGrB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,2BAA2B;AAAA;AAAA,EAG3B,uBAAuB;AAAA;AAAA,EAGvB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA;AAAA,EAGpB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA,EAGlB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA;AAAA,EAGhB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,qBAAqB;AAAA;AAAA,EAGrB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,yBAAyB;AAAA;AAAA,EAGzB,gBAAgB;AAAA;AAAA,EAGhB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA;AAAA,EAGjB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA;AAAA,EAGhB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,wBAAwB;AAC1B;AAEA,IAAM,KAAK;AAAA;AAAA,EAET,mBAAmB;AAAA,EACnB,qBAAqB;AAAA;AAAA,EAGrB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,2BAA2B;AAAA;AAAA,EAG3B,uBAAuB;AAAA;AAAA,EAGvB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA;AAAA,EAGpB,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA,EAGlB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA;AAAA,EAGhB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,qBAAqB;AAAA;AAAA,EAGrB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,yBAAyB;AAAA;AAAA,EAGzB,gBAAgB;AAAA;AAAA,EAGhB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA;AAAA,EAGjB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA;AAAA,EAGhB,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,wBAAwB;AAC1B;;;AC1OO,SAAS,qBACd,OACA,UAA6B,CAAC,GACtB;AACR,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,QAAkB,CAAC;AACzB,QAAM,QAAQ,OAAO,OAAO,MAAM,KAAK;AAGvC,QAAM,KAAK,EAAE,eAAe,CAAC;AAC7B,QAAM,KAAK,EAAE,kBAAkB,CAAC;AAChC,QAAM,KAAK,EAAE,sBAAsB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC/D,QAAM,KAAK,EAAE,sBAAsB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC/D,QAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC,CAAC;AAGnF,QAAM,WAAW,MACd,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC,EACrC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM,EACxD,MAAM,GAAG,IAAI;AAEhB,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,SAAS,OAAO,CAAC,CAAC;AACjE,eAAW,KAAK,UAAU;AACxB,YAAM,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,WAAW,OAAO,CAAC,CAAC;AAAA,IACpF;AAAA,EACF;AAGA,MAAI,MAAM,qBAAqB,SAAS,GAAG;AACzC,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC,CAAC;AACnF,eAAW,KAAK,MAAM,sBAAsB;AAC1C,YAAM,KAAK,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,KAAK,UAAK,EAAE,CAAC,CAAC;AAAA,IACtE;AAAA,EACF;AAGA,QAAM,eAAe,MAClB,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,CAAC,EACvC,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,SAAS,EAAE,aAAa,MAAM,EAC5D,MAAM,GAAG,IAAI;AAEhB,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,aAAa,OAAO,CAAC,CAAC;AACrE,eAAW,KAAK,cAAc;AAC5B,YAAM,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,aAAa,OAAO,CAAC,CAAC;AAAA,IACtF;AAAA,EACF;AAGA,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,MAAM,EAAE,WAAW,WAAW,KAAK,EAAE,aAAa,WAAW;AAAA,EAChE;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,KAAK,EAAE,uBAAuB,EAAE,OAAO,QAAQ,OAAO,CAAC,CAAC;AAC9D,eAAW,KAAK,SAAS;AACvB,YAAM,KAAK,KAAK,EAAE,IAAI,EAAE;AAAA,IAC1B;AAAA,EACF;AAGA,QAAM,YAAY,oBAAI,IAAoB;AAC1C,aAAW,KAAK,OAAO;AACrB,UAAM,MAAM,EAAE,KAAK,SAAS,GAAG,IAAI,EAAE,KAAK,UAAU,GAAG,EAAE,KAAK,YAAY,GAAG,CAAC,IAAI;AAClF,cAAU,IAAI,MAAM,UAAU,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,EAClD;AAEA,MAAI,UAAU,OAAO,GAAG;AACtB,UAAM,KAAK,EAAE,oBAAoB,CAAC;AAClC,UAAM,SAAS,CAAC,GAAG,UAAU,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAClE,eAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,YAAM,KAAK,EAAE,qBAAqB,EAAE,KAAK,MAAM,CAAC,CAAC;AAAA,IACnD;AAAA,EACF;AAGA,MAAI,MAAM,qBAAqB,WAAW,KAAK,QAAQ,WAAW,GAAG;AACnE,UAAM,KAAK,EAAE,kBAAkB,CAAC;AAAA,EAClC;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC5FA,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,gBAAAC,qBAAoB;AAY7B,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQA,eAAsB,kBACpB,aACA,WAC0B;AAC1B,QAAM,SAA0C,CAAC;AACjD,QAAM,gBAAiC,CAAC;AAExC,WAAS,MAAM,GAAG,MAAM,UAAU,QAAQ,OAAO;AAC/C,UAAM,MAAM,UAAU,GAAG;AACzB,UAAM,YAAYC,SAAQ,aAAa,IAAI,SAAS;AAEpD,UAAM,QAAQ,MAAM,eAAe,WAAW;AAAA,MAC5C,SAAS,IAAI;AAAA,MACb,UAAU,IAAI;AAAA,IAChB,CAAC;AAGD,UAAM,WAAW,IAAI,YAAa,MAAM,eAAe,SAAS,KAAM;AAEtE,WAAO,IAAI,IAAI,IAAI;AAEnB,kBAAc,KAAK;AAAA,MACjB,MAAM,IAAI;AAAA,MACV,iBAAiB,MAAM;AAAA,MACvB;AAAA,MACA,OAAO,IAAI,SAAS,aAAa,MAAM,aAAa,MAAM;AAAA,MAC1D,aAAa,IAAI;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,IACnB,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,iBAAiB,aAAa,MAAM;AAEnD,SAAO,EAAE,QAAQ,eAAe,OAAO;AACzC;AAiBO,SAAS,4BACd,QACA,WACwB;AACxB,QAAM,kBAAkB;AACxB,QAAM,sBAAsB;AAG5B,QAAM,mBAAmB,oBAAI,IAAiC;AAC9D,aAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACvD,UAAM,cAAc,oBAAI,IAAoB;AAC5C,eAAW,YAAY,OAAO,KAAK,MAAM,KAAK,GAAG;AAC/C,YAAM,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI;AACzC,YAAM,YAAY,SAAS,QAAQ,YAAY,EAAE;AACjD,UAAI,UAAU,SAAS,mBAAmB,kBAAkB,IAAI,UAAU,YAAY,CAAC,EAAG;AAC1F,kBAAY,IAAI,WAAW,QAAQ;AAAA,IACrC;AACA,qBAAiB,IAAI,WAAW,WAAW;AAAA,EAC7C;AAGA,QAAM,iBAAiB,oBAAI,IAAoB;AAC/C,aAAW,CAAC,EAAE,GAAG,KAAK,kBAAkB;AACtC,eAAW,QAAQ,IAAI,KAAK,GAAG;AAC7B,qBAAe,IAAI,OAAO,eAAe,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,IAC9D;AAAA,EACF;AAGA,QAAM,WAAW,oBAAI,IAA2D;AAEhF,WAAS,OAAO,SAAiB,MAA4B,OAAe;AAC1E,QAAI,QAAQ,oBAAqB;AACjC,UAAM,WAAW,SAAS,IAAI,OAAO;AACrC,QAAI,CAAC,YAAY,QAAQ,SAAS,OAAO;AACvC,eAAS,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAAA,IACvC;AAAA,EACF;AAGA,WAAS,cAAc,SAAiB,MAAuB;AAC7D,UAAM,cAAc;AAAA,MAClB,IAAI,OAAO,8DAA8D,YAAY,IAAI,CAAC,KAAK;AAAA,MAC/F,IAAI,OAAO,6BAA6B,YAAY,IAAI,CAAC,KAAK;AAAA,MAC9D,IAAI,OAAO,MAAM,YAAY,IAAI,CAAC,6CAA6C;AAAA,IACjF;AACA,WAAO,YAAY,KAAK,CAAC,OAAO,GAAG,KAAK,OAAO,CAAC;AAAA,EAClD;AAGA,WAAS,kBAAkB,SAAiB,MAAuB;AACjE,UAAM,QAAQ,IAAI,OAAO,MAAM,YAAY,IAAI,CAAC,OAAO,GAAG;AAC1D,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAI,gBAAgB;AACpB,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,MAAM,KAAK,IAAI,EAAG;AACvB,YAAM,YAAY;AAElB,YAAM,gBAAgB,4EAA4E,KAAK,IAAI;AAC3G,UAAI,CAAC,eAAe;AAClB,wBAAgB;AAChB;AAAA,MACF;AAAA,IACF;AACA,WAAO,CAAC;AAAA,EACV;AAEA,aAAW,CAAC,aAAa,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACzD,UAAM,WAAW,iBAAiB,IAAI,WAAW,KAAK,oBAAI,IAAI;AAE9D,eAAW,YAAY,OAAO,KAAK,MAAM,KAAK,GAAG;AAC/C,YAAM,UAAUC,MAAK,MAAM,SAAS,QAAQ;AAC5C,UAAI;AACJ,UAAI;AACF,kBAAUC,cAAa,SAAS,OAAO;AAAA,MACzC,QAAQ;AAAE;AAAA,MAAU;AAGpB,iBAAW,CAAC,aAAa,SAAS,KAAK,kBAAkB;AACvD,YAAI,gBAAgB,YAAa;AACjC,mBAAW,CAAC,YAAY,UAAU,KAAK,WAAW;AAEhD,cAAI,SAAS,IAAI,UAAU,EAAG;AAE9B,eAAK,eAAe,IAAI,UAAU,KAAK,KAAK,EAAG;AAC/C,cAAI,CAAC,QAAQ,SAAS,UAAU,EAAG;AACnC,gBAAM,QAAQ,IAAI,OAAO,MAAM,YAAY,UAAU,CAAC,KAAK;AAC3D,cAAI,CAAC,MAAM,KAAK,OAAO,EAAG;AAE1B,cAAI,cAAc,SAAS,UAAU,EAAG;AAExC,cAAI,kBAAkB,SAAS,UAAU,EAAG;AAE5C,gBAAM,UAAU,GAAG,WAAW,SAAI,WAAW;AAE7C,gBAAM,eAAe,cAAc,KAAK,UAAU;AAClD,gBAAM,YAAY,WAAW,UAAU,eAAe,IAAI;AAC1D,iBAAO,SAAS;AAAA,YACd,WAAW;AAAA,YACX,UAAU;AAAA,YACV,SAAS;AAAA,YACT,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,OAAO;AAAA,UACT,GAAG,SAAS;AAAA,QACd;AAAA,MACF;AAGA,iBAAW,OAAO,WAAW;AAC3B,YAAI,IAAI,SAAS,YAAa;AAC9B,cAAM,UAAU,GAAG,WAAW,SAAI,IAAI,IAAI;AAC1C,cAAM,YAAY,IAAI;AACtB,cAAM,WAAW,CAAC,UAAU,WAAW,OAAO,WAAW,YAAY,WAAW,WAAW,SAAS,WAAW,WAAW;AAC1H,cAAM,UAAU,IAAI,OAAO,MAAM,YAAY,SAAS,CAAC,MAAM,SAAS,KAAK,GAAG,CAAC,MAAM;AACrF,YAAI,QAAQ,KAAK,OAAO,GAAG;AACzB,gBAAM,cAAc,OAAO,IAAI,IAAI;AACnC,cAAI,CAAC,YAAa;AAClB,gBAAM,YAAY,eAAe,WAAW;AAC5C,cAAI,WAAW;AACb,mBAAO,SAAS;AAAA,cACd,WAAW;AAAA,cACX,UAAU;AAAA,cACV,SAAS,IAAI;AAAA,cACb,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,OAAO,GAAG,SAAS;AAAA,YACrB,GAAG,EAAE;AAAA,UACP;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,OAAO,WAAW;AAC3B,YAAI,IAAI,SAAS,YAAa;AAC9B,cAAM,UAAU,GAAG,WAAW,SAAI,IAAI,IAAI;AAC1C,cAAM,UAAU,IAAI,UAAU,MAAM,GAAG,EAAE,IAAI;AAC7C,cAAM,cAAc,QAAQ,UAAU;AAEtC,cAAM,WAA4C,CAAC;AAEnD,YAAI,CAAC,aAAa;AAGhB,mBAAS,KAAK,EAAE,IAAI,IAAI,OAAO,uCAAuC,YAAY,OAAO,CAAC,OAAO,GAAG,GAAG,OAAO,GAAG,CAAC;AAElH,mBAAS,KAAK,EAAE,IAAI,IAAI,OAAO,UAAU,YAAY,OAAO,CAAC,UAAU,GAAG,GAAG,OAAO,GAAG,CAAC;AAAA,QAC1F,OAAO;AAGL,mBAAS,KAAK,EAAE,IAAI,IAAI,OAAO,qCAAqC,YAAY,OAAO,CAAC,KAAK,GAAG,GAAG,OAAO,GAAG,CAAC;AAE9G,mBAAS,KAAK,EAAE,IAAI,IAAI,OAAO,kCAAkC,YAAY,OAAO,CAAC,UAAU,GAAG,GAAG,OAAO,GAAG,CAAC;AAAA,QAClH;AAEA,mBAAW,EAAE,IAAI,MAAM,KAAK,UAAU;AACpC,cAAI,GAAG,KAAK,OAAO,GAAG;AACpB,kBAAM,cAAc,OAAO,IAAI,IAAI;AACnC,gBAAI,CAAC,YAAa;AAClB,kBAAM,YAAY,eAAe,WAAW;AAC5C,gBAAI,WAAW;AACb,qBAAO,SAAS;AAAA,gBACd,WAAW;AAAA,gBACX,UAAU;AAAA,gBACV,SAAS,IAAI;AAAA,gBACb,QAAQ;AAAA,gBACR,MAAM;AAAA,gBACN,OAAO,UAAK,IAAI,IAAI;AAAA,cACtB,GAAG,KAAK;AAAA,YACV;AACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,CAAC,GAAG,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACjD;AAEA,SAAS,YAAY,GAAmB;AACtC,SAAO,EAAE,QAAQ,uBAAuB,MAAM;AAChD;AAGA,SAAS,eAAe,OAAuC;AAC7D,QAAM,QAAQ,OAAO,OAAO,MAAM,KAAK;AACvC,MAAI,MAAM,WAAW,EAAG,QAAO;AAG/B,QAAM,SAAS,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM;AAC7E,MAAI,OAAO,CAAC,EAAE,WAAW,SAAS,EAAG,QAAO,OAAO,CAAC,EAAE;AAGtD,QAAM,aAAa,CAAC,QAAQ,SAAS,OAAO,UAAU,OAAO,KAAK;AAClE,aAAW,QAAQ,YAAY;AAC7B,UAAM,QAAQ,MAAM,KAAK,CAAC,MAAM;AAC9B,YAAM,WAAW,EAAE,KAAK,MAAM,GAAG,EAAE,IAAI,EAAG,QAAQ,YAAY,EAAE,EAAE,YAAY;AAC9E,aAAO,aAAa;AAAA,IACtB,CAAC;AACD,QAAI,MAAO,QAAO,MAAM;AAAA,EAC1B;AAEA,SAAO,MAAM,CAAC,EAAE;AAClB;AAGA,IAAM,oBAAoB,oBAAI,IAAI;AAAA;AAAA,EAEhC;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAU;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAO;AAAA,EAC1D;AAAA,EAAS;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EAAW;AAAA;AAAA,EAE1D;AAAA,EAAS;AAAA,EAAW;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAe;AAAA,EAC/D;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAW;AAAA;AAAA,EAE3D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAc;AAAA,EAAY;AAAA,EACxD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAU;AAAA,EAC/D;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAC5D;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAW;AAAA;AAAA,EAExD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAC5D;AAAA,EAAU;AAAA,EAAY;AAAA,EAAa;AAAA,EAAU;AAAA,EAAU;AAAA,EACvD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAS;AAAA;AAAA,EAE5D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAc;AAAA,EACzD;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EAAe;AAAA,EAAU;AAAA,EAC3D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAW;AAAA,EAAW;AAAA;AAAA,EAEvD;AAAA,EAAS;AAAA,EAAY;AAAA,EAAU;AAAA,EAAS;AAAA,EAAW;AAAA,EACnD;AAAA,EAAY;AAAA,EAAc;AAAA,EAAW;AAAA,EAAW;AAAA;AAAA,EAEhD;AAAA,EAAW;AAAA,EAAY;AAAA,EAAY;AAAA,EAAc;AAAA,EAAW;AAAA,EAC5D;AAAA,EAAS;AAAA,EAAa;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA;AAAA,EAErD;AAAA,EAAU;AAAA,EAAa;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EACpD;AAAA,EAAc;AAAA,EAAW;AAAA,EAAU;AAAA,EAAc;AAAA;AAAA,EAEjD;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAW;AAC/C,CAAC;AAMD,SAAS,iBACP,aACA,QACiB;AACjB,QAAM,cAAwC,CAAC;AAC/C,QAAM,cAAgC,CAAC;AACvC,QAAM,iBAAwC,CAAC;AAE/C,aAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACvD,eAAW,CAAC,UAAU,IAAI,KAAK,OAAO,QAAQ,MAAM,KAAK,GAAG;AAC1D,YAAM,eAAe,GAAG,SAAS,IAAI,QAAQ;AAC7C,kBAAY,YAAY,IAAI;AAAA,QAC1B,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK,aAAa,IAAI,CAAC,MAAM,GAAG,SAAS,IAAI,CAAC,EAAE;AAAA,QAC9D,YAAY,KAAK,WAAW,IAAI,CAAC,MAAM,GAAG,SAAS,IAAI,CAAC,EAAE;AAAA,MAC5D;AAAA,IACF;AAEA,eAAW,QAAQ,MAAM,OAAO;AAC9B,kBAAY,KAAK;AAAA,QACf,QAAQ,GAAG,SAAS,IAAI,KAAK,MAAM;AAAA,QACnC,QAAQ,GAAG,SAAS,IAAI,KAAK,MAAM;AAAA,QACnC,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH;AAEA,eAAW,QAAQ,MAAM,sBAAsB;AAC7C,qBAAe,KAAK;AAAA,QAClB,OAAO,KAAK,MAAM,IAAI,CAAC,MAAM,GAAG,SAAS,IAAI,CAAC,EAAE;AAAA,MAClD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAASF,SAAQ,WAAW;AAAA,IAC5B,OAAO;AAAA,IACP,OAAO;AAAA,IACP,sBAAsB;AAAA,IACtB,YAAY,OAAO,KAAK,WAAW,EAAE;AAAA,IACrC,YAAY,YAAY;AAAA,EAC1B;AACF;;;AClXA,SAAS,YAAAG,WAAU,WAAW,aAAa;AAC3C,SAAS,QAAAC,aAAY;AACrB,SAAS,SAAS;AAIlB,IAAM,kBAAkB;AACxB,IAAM,cAAc;AAEpB,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL;AAAA,IACC;AAAA,IACA;AAAA,EACF;AAAA,EACF,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,UAAU,EAAE,KAAK,YAAY,EAAE,SAAS;AAAA,EACxC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACtC,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAED,IAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,WAAW,EAAE,OAAO;AAAA,EACpB,UAAU,EAAE,OAAO;AAAA,EACnB,SAAS,EAAE,OAAO;AAAA,EAClB,QAAQ,EAAE,OAAO;AAAA,EACjB,MAAM,EAAE,KAAK,CAAC,YAAY,SAAS,aAAa,QAAQ,CAAC;AAAA,EACzD,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAEM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,SAAS,EAAE,QAAQ,KAAK;AAAA,EACxB,QAAQ,EACL,MAAM,qBAAqB,EAC3B,IAAI,CAAC,EACL;AAAA,IACC,CAAC,WAAW;AACV,YAAM,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AACtC,aAAO,IAAI,IAAI,KAAK,EAAE,SAAS,MAAM;AAAA,IACvC;AAAA,IACA,EAAE,SAAS,6BAA6B;AAAA,EAC1C;AAAA,EACF,aAAa,EAAE,MAAM,0BAA0B,EAAE,SAAS;AAC5D,CAAC;AAMD,eAAsB,gBACpB,aAC6B;AAC7B,QAAM,WAAWC,MAAK,aAAa,iBAAiB,WAAW;AAE/D,MAAI;AACJ,MAAI;AACF,UAAM,MAAMC,UAAS,UAAU,OAAO;AAAA,EACxC,SAAS,OAAgB;AACvB,QAAI,YAAY,KAAK,KAAK,MAAM,SAAS,UAAU;AACjD,aAAO;AAAA,IACT;AACA,UAAM,IAAI,MAAM,kBAAkB,QAAQ,EAAE;AAAA,EAC9C;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI,MAAM,mBAAmB,QAAQ,EAAE;AAAA,EAC/C;AAEA,QAAM,SAAS,kBAAkB,UAAU,MAAM;AACjD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAChD,MAAM,GAAG,CAAC,EACV,KAAK,IAAI;AACZ,UAAM,IAAI,MAAM;AAAA,EAAmC,MAAM,EAAE;AAAA,EAC7D;AAEA,SAAO,OAAO;AAChB;AAKA,eAAsB,gBACpB,aACA,QACe;AACf,QAAM,UAAUD,MAAK,aAAa,eAAe;AACjD,QAAM,WAAWA,MAAK,SAAS,WAAW;AAC1C,QAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,UAAU,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAC3E;AAEA,SAAS,YAAY,OAAgD;AACnE,SAAO,iBAAiB,SAAS,UAAU;AAC7C;;;AC9EA,eAAsB,aAAa,MAKR;AACzB,QAAM,cAAc,MAAM,gBAAgB,KAAK,WAAW;AAC1D,MAAI,aAAa;AACf,UAAM,QAAQ,MAAM,kBAAkB,KAAK,aAAa,YAAY,MAAM;AAC1E,UAAM,kBAAkB,4BAA4B,MAAM,QAAQ,YAAY,MAAM;AACpF,UAAM,oBAAoB,YAAY,eAAe,CAAC;AACtD,UAAM,aAAa,IAAI,IAAI,kBAAkB;AAAA,MAC3C,CAAC,MAAM,GAAG,EAAE,SAAS,IAAI,EAAE,QAAQ,SAAI,EAAE,OAAO,IAAI,EAAE,MAAM;AAAA,IAC9D,CAAC;AACD,UAAM,SAAS;AAAA,MACb,GAAG;AAAA,MACH,GAAG,gBAAgB;AAAA,QACjB,CAAC,MAAM,CAAC,WAAW,IAAI,GAAG,EAAE,SAAS,IAAI,EAAE,QAAQ,SAAI,EAAE,OAAO,IAAI,EAAE,MAAM,EAAE;AAAA,MAChF;AAAA,IACF;AACA,WAAO;AAAA,MACL,OAAO,MAAM;AAAA,MACb,YAAY;AAAA,MACZ,eAAe,MAAM;AAAA,MACrB,iBAAiB;AAAA,IACnB;AAAA,EACF;AAEA,QAAM,QAAQ,MAAM,eAAe,KAAK,WAAW;AAAA,IACjD,SAAS,KAAK;AAAA,IACd,UAAU,KAAK;AAAA,EACjB,CAAC;AACD,SAAO,EAAE,MAAM;AACjB;;;ACxDA,SAAS,SAAAE,QAAO,aAAAC,YAAW,YAAAC,WAAU,cAAc;AACnD,SAAS,QAAAC,aAAY;AACrB,SAAS,KAAAC,UAAS;;;ACGX,IAAM,iBAAiB;;;ADG9B,IAAMC,mBAAkB;AACxB,IAAM,gBAAgB;AAGtB,IAAM,iBAAiBC,GAAE,OAAO;AAAA,EAC9B,MAAMA,GAAE,OAAO;AAAA,EACf,QAAQA,GAAE,QAAQ;AAAA,EAClB,cAAcA,GAAE,MAAMA,GAAE,OAAO,CAAC;AAAA,EAChC,YAAYA,GAAE,MAAMA,GAAE,OAAO,CAAC;AAChC,CAAC;AAED,IAAM,wBAAwBA,GAAE,OAAO;AAAA,EACrC,SAASA,GAAE,OAAO;AAAA,EAClB,OAAOA,GAAE,OAAOA,GAAE,OAAO,GAAG,cAAc;AAAA,EAC1C,OAAOA,GAAE,MAAMA,GAAE,OAAO;AAAA,IACtB,QAAQA,GAAE,OAAO;AAAA,IACjB,QAAQA,GAAE,OAAO;AAAA,IACjB,MAAMA,GAAE,KAAK,CAAC,UAAU,WAAW,WAAW,CAAC;AAAA,EACjD,CAAC,CAAC;AAAA,EACF,sBAAsBA,GAAE,MAAMA,GAAE,OAAO,EAAE,OAAOA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,EACtE,YAAYA,GAAE,OAAO;AAAA,EACrB,YAAYA,GAAE,OAAO;AACvB,CAAC;AAED,IAAM,iBAAiBA,GAAE,OAAO;AAAA,EAC9B,SAASA,GAAE,KAAK,CAAC,gBAAgB,KAAK,CAAC;AAAA,EACvC,WAAWA,GAAE,OAAO;AAAA,EACpB,SAASA,GAAE,OAAO;AAAA,EAClB,OAAO;AACT,CAAC;AAQD,eAAsB,aACpB,aACA,OACA,YACuB;AACvB,QAAM,UAAUC,MAAK,aAAaF,gBAAe;AACjD,QAAM,WAAWE,MAAK,SAAS,aAAa;AAE5C,QAAM,WAAyB;AAAA,IAC7B,SAAS;AAAA,IACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,SAAS,MAAM;AAAA,IACf;AAAA,IACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,EACrC;AAEA,QAAMC,OAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AACxC,QAAMC,WAAU,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,GAAG,OAAO;AAEpE,SAAO;AACT;AAQA,eAAsB,aACpB,aAC8B;AAC9B,QAAM,WAAWF,MAAK,aAAaF,kBAAiB,aAAa;AAGjE,MAAI;AACJ,MAAI;AACF,UAAM,MAAMK,UAAS,UAAU,OAAO;AAAA,EACxC,SAAS,OAAgB;AACvB,QAAIC,aAAY,KAAK,KAAK,MAAM,SAAS,UAAU;AACjD,aAAO;AAAA,IACT;AACA,UAAM,IAAI;AAAA,MACR,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAAA,MAC1C,EAAE,OAAO,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAAA,IAC7C;AAAA,EACF;AAGA,QAAM,SAAS,eAAe,UAAU,MAAM;AAC9C,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAChD,MAAM,GAAG,CAAC,EACV,KAAK,IAAI;AACZ,UAAM,IAAI;AAAA,MACR,EAAE,yBAAyB,EAAE,OAAO,CAAC;AAAA,IACvC;AAAA,EACF;AAEA,SAAO,OAAO;AAChB;AAeO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAASC,aAAY,OAAgD;AACnE,SAAO,iBAAiB,SAAS,UAAU;AAC7C;;;AE9HO,SAAS,YACd,UACA,UACU;AACV,QAAM,WAAW,IAAI,IAAI,OAAO,KAAK,SAAS,KAAK,CAAC;AACpD,QAAM,WAAW,IAAI,IAAI,OAAO,KAAK,SAAS,KAAK,CAAC;AAGpD,QAAM,QAAQ,CAAC,GAAG,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAG1D,QAAM,UAAU,CAAC,GAAG,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAG5D,QAAM,WAAqB,CAAC;AAC5B,aAAW,QAAQ,UAAU;AAC3B,QAAI,CAAC,SAAS,IAAI,IAAI,EAAG;AAEzB,UAAM,UAAU,SAAS,MAAM,IAAI,EAAE,aAAa,MAAM,EAAE,KAAK;AAC/D,UAAM,UAAU,SAAS,MAAM,IAAI,EAAE,aAAa,MAAM,EAAE,KAAK;AAE/D,QAAI,CAAC,YAAY,SAAS,OAAO,GAAG;AAClC,eAAS,KAAK,IAAI;AAAA,IACpB;AAAA,EACF;AAGA,QAAM,aAAa,IAAI,IAAI,OAAO;AAClC,QAAM,eAAe,oBAAI,IAAI,CAAC,GAAG,SAAS,GAAG,QAAQ,CAAC;AACtD,QAAM,qBAAqD,CAAC;AAC5D,QAAM,eAAe,oBAAI,IAAY;AAErC,aAAW,eAAe,cAAc;AAGtC,UAAM,QAAQ,WAAW,IAAI,WAAW,IAAI,WAAW;AACvD,UAAM,OAAO,MAAM,MAAM,WAAW;AACpC,QAAI,CAAC,KAAM;AAEX,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,MAAM,GAAG,SAAS,SAAI,WAAW;AACvC,UAAI,aAAa,IAAI,GAAG,EAAG;AAC3B,mBAAa,IAAI,GAAG;AAEpB,YAAM,SAAS,WAAW,IAAI,WAAW,IACrC,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC,IAC7C,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAElD,yBAAmB,KAAK;AAAA,QACtB,MAAM;AAAA,QACN;AAAA,QACA,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAIA,aAAW,aAAa,OAAO;AAC7B,UAAM,OAAO,SAAS,MAAM,SAAS;AACrC,QAAI,CAAC,KAAM;AAEX,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,MAAM,GAAG,SAAS,SAAI,SAAS;AACrC,UAAI,aAAa,IAAI,GAAG,EAAG;AAC3B,mBAAa,IAAI,GAAG;AAEpB,yBAAmB,KAAK;AAAA,QACtB,MAAM;AAAA,QACN,QAAQ,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAAA,QACjD,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,SAAS,UAAU,mBAAmB;AACxD;AAGO,SAAS,iBAAiB,MAAwB;AACvD,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,EAAE,YAAY,CAAC;AAE1B,MAAI,KAAK,MAAM,WAAW,KAAK,KAAK,QAAQ,WAAW,KAAK,KAAK,SAAS,WAAW,GAAG;AACtF,UAAM,KAAK,EAAE,gBAAgB,CAAC;AAC9B,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAEA,MAAI,KAAK,MAAM,SAAS,GAAG;AACzB,UAAM,KAAK,EAAE,cAAc,EAAE,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC;AACxD,eAAW,KAAK,KAAK,OAAO;AAC1B,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,UAAM,KAAK,EAAE,gBAAgB,EAAE,OAAO,KAAK,QAAQ,OAAO,CAAC,CAAC;AAC5D,eAAW,KAAK,KAAK,SAAS;AAC5B,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,UAAM,KAAK,EAAE,iBAAiB,EAAE,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC;AAC9D,eAAW,KAAK,KAAK,UAAU;AAC7B,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,mBAAmB,SAAS,GAAG;AACtC,UAAM,KAAK,EAAE,iBAAiB,EAAE,OAAO,KAAK,mBAAmB,OAAO,CAAC,CAAC;AACxE,eAAW,KAAK,KAAK,oBAAoB;AACvC,YAAM,KAAK,OAAO,EAAE,IAAI,EAAE;AAC1B,YAAM,KAAK,OAAO,EAAE,MAAM,EAAE;AAAA,IAC9B;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,YAAY,GAAa,GAAsB;AACtD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;;;AC/IA,SAAS,oBAAoB;;;ACItB,SAAS,cAAsB;AACpC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0JT;;;AC3JO,SAAS,kBAA0B;AACxC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0KT;;;ACrKO,SAAS,mBAA2B;AACzC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyTT;;;AC5TO,SAAS,YAAY,UAA0B;AACpD,SAAO;AAAA;AAAA;AAAA;AAAA,eAIM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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AAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,E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","names":["writeFile","mkdir","join","resolve","stat","readdir","stat","join","join","resolve","join","segments","resolve","fromRoot","resolve","stat","resolve","join","readFileSync","resolve","join","readFileSync","readFile","join","join","readFile","mkdir","writeFile","readFile","join","z","ARCHTRACKER_DIR","z","join","mkdir","writeFile","readFile","isNodeError","isNodeError","readFileSync","join","dirname","join","mkdir","writeFile"]}
1
+ {"version":3,"sources":["../../src/cli/index.ts","../../src/analyzer/analyze.ts","../../src/analyzer/engines/regex-engine.ts","../../src/analyzer/engines/cycle.ts","../../src/analyzer/engines/strip-comments.ts","../../src/analyzer/engines/detect.ts","../../src/analyzer/engines/languages.ts","../../src/analyzer/engines/types.ts","../../src/i18n/index.ts","../../src/analyzer/report.ts","../../src/analyzer/multi-layer.ts","../../src/storage/layers.ts","../../src/storage/graph-cache.ts","../../src/analyzer/resolve.ts","../../src/storage/snapshot.ts","../../src/types/schema.ts","../../src/storage/diff.ts","../../src/web/server.ts","../../src/web/template.ts","../../src/web/styles.ts","../../src/web/viewer-html.ts","../../src/utils/html-escape.ts","../../src/utils/version.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { watch } from \"node:fs\";\nimport { writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { AnalyzerError, formatAnalysisReport, resolveGraph } from \"../analyzer/index.js\";\nimport {\n saveSnapshot,\n loadSnapshot,\n listSnapshots,\n loadSnapshotByTimestamp,\n computeDiff,\n formatDiffReport,\n StorageError,\n} from \"../storage/index.js\";\nimport { loadLayerConfig, saveLayerConfig } from \"../storage/layers.js\";\nimport { startViewer } from \"../web/server.js\";\nimport { t, setLocale } from \"../i18n/index.js\";\nimport type { Locale } from \"../i18n/index.js\";\nimport { VERSION } from \"../utils/version.js\";\nimport { LANGUAGE_IDS } from \"../analyzer/engines/types.js\";\nimport type { LanguageId } from \"../analyzer/engines/types.js\";\n\nconst VALID_LANGUAGES = LANGUAGE_IDS as readonly string[];\n\n/** CLI wrapper: resolveGraph always checks layers.json in projectRoot */\nasync function resolveGraphCli(opts: {\n target: string;\n root: string;\n exclude?: string[];\n language?: LanguageId;\n noCache?: boolean;\n}) {\n return resolveGraph({\n targetDir: opts.target,\n projectRoot: opts.root,\n exclude: opts.exclude,\n language: opts.language,\n noCache: opts.noCache,\n });\n}\n\nconst program = new Command();\n\nprogram\n .name(\"archtracker\")\n .description(\n \"Architecture & Dependency Tracker — Prevent missed architecture changes in AI-driven development\",\n )\n .version(VERSION)\n .option(\"--lang <locale>\", \"Language (en/ja, auto-detected from LANG env)\")\n .hook(\"preAction\", (thisCommand) => {\n const lang = thisCommand.opts().lang;\n if (lang === \"en\" || lang === \"ja\") {\n setLocale(lang as Locale);\n }\n });\n\n// ─── archtracker init ───────────────────────────────────────────\n\nprogram\n .command(\"init\")\n .description(\"Generate initial snapshot and save to .archtracker/\")\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\n \"-e, --exclude <patterns...>\",\n \"Exclude patterns (regex)\",\n )\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n console.log(t(\"cli.analyzing\"));\n const { graph, multiLayer } = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n exclude: opts.exclude,\n language,\n });\n\n const snapshot = await saveSnapshot(opts.root, graph, multiLayer, {\n targetDir: opts.target, language, exclude: opts.exclude,\n });\n\n console.log(t(\"cli.snapshotSaved\"));\n console.log(t(\"cli.timestamp\", { ts: snapshot.timestamp }));\n console.log(t(\"cli.fileCount\", { count: graph.totalFiles }));\n console.log(t(\"cli.edgeCount\", { count: graph.totalEdges }));\n\n if (graph.circularDependencies.length > 0) {\n console.log(\n t(\"cli.circularCount\", { count: graph.circularDependencies.length }),\n );\n }\n\n // Show top 5 key components\n const top = Object.values(graph.files)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, 5);\n\n if (top.length > 0 && top[0].dependents.length > 0) {\n console.log(t(\"cli.keyComponents\"));\n for (const f of top) {\n if (f.dependents.length === 0) break;\n console.log(` ${t(\"cli.dependedBy\", { path: f.path, count: f.dependents.length })}`);\n }\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker analyze ─────────────────────────────────────\n\nprogram\n .command(\"analyze\")\n .description(\n \"Comprehensive architecture analysis for existing projects\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\n \"-e, --exclude <patterns...>\",\n \"Exclude patterns (regex)\",\n )\n .option(\"-n, --top <number>\", \"Number of top components to show\", \"10\")\n .option(\"--save\", \"Also save a snapshot after analysis\")\n .option(\"--no-cache\", \"Force fresh analysis (ignore graph cache)\")\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n console.log(t(\"cli.analyzing\"));\n const { graph, multiLayer } = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n exclude: opts.exclude,\n language,\n noCache: opts.noCache,\n });\n\n const report = formatAnalysisReport(graph, { topN: parseInt(opts.top, 10) });\n console.log(report);\n\n if (opts.save) {\n await saveSnapshot(opts.root, graph, multiLayer, {\n targetDir: opts.target, language, exclude: opts.exclude,\n });\n console.log(t(\"analyze.snapshotSaved\"));\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker check ─────────────────────────────────────────\n\nprogram\n .command(\"check\")\n .description(\n \"Compare snapshot with current code and report change impacts\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\"--ci\", \"CI mode: exit code 1 if affected files exist\")\n .option(\"--from <timestamp>\", \"Compare from a historical snapshot (use 'archtracker history' to see timestamps)\")\n .option(\"--no-cache\", \"Force fresh analysis (ignore graph cache)\")\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n\n let existingSnapshot;\n if (opts.from) {\n existingSnapshot = await loadSnapshotByTimestamp(opts.root, opts.from);\n if (!existingSnapshot) {\n console.log(t(\"history.snapshotNotFound\", { ts: opts.from }));\n process.exit(1);\n }\n } else {\n existingSnapshot = await loadSnapshot(opts.root);\n if (!existingSnapshot) {\n console.log(t(\"cli.noSnapshot\"));\n process.exit(1);\n }\n }\n\n console.log(t(\"cli.analyzing\"));\n const { graph: currentGraph } = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n language,\n noCache: opts.noCache,\n });\n const diff = computeDiff(existingSnapshot.graph, currentGraph);\n const report = formatDiffReport(diff);\n\n console.log(report);\n\n // CI mode: exit with error if there are affected dependents\n if (opts.ci && diff.affectedDependents.length > 0) {\n console.log(t(\"cli.ciFailed\", { count: diff.affectedDependents.length }));\n process.exit(1);\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker context ────────────────────────────────────────\n\nprogram\n .command(\"context\")\n .description(\n \"Display current architecture context (for AI session initialization)\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\"--json\", \"Output in JSON format\")\n .option(\"--no-cache\", \"Force fresh analysis (ignore graph cache)\")\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n let snapshot = await loadSnapshot(opts.root);\n\n if (!snapshot) {\n console.log(t(\"cli.autoGenerating\"));\n const result = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n language,\n noCache: opts.noCache,\n });\n snapshot = await saveSnapshot(opts.root, result.graph, result.multiLayer, {\n targetDir: opts.target, language,\n });\n }\n\n const graph = snapshot.graph;\n\n if (opts.json) {\n const context = {\n validPaths: Object.keys(graph.files).sort(),\n snapshotTimestamp: snapshot.timestamp,\n totalFiles: graph.totalFiles,\n totalEdges: graph.totalEdges,\n circularDependencies: graph.circularDependencies.length,\n keyComponents: Object.values(graph.files)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, 20)\n .map((f) => ({\n path: f.path,\n dependentCount: f.dependents.length,\n dependencyCount: f.dependencies.length,\n })),\n };\n console.log(JSON.stringify(context, null, 2));\n return;\n }\n\n console.log(t(\"cli.project\", { path: graph.rootDir }));\n console.log(t(\"cli.fileCount\", { count: graph.totalFiles }));\n console.log(t(\"cli.edgeCount\", { count: graph.totalEdges }));\n console.log(t(\"cli.circularCount\", { count: graph.circularDependencies.length }));\n console.log(t(\"cli.snapshot\", { ts: snapshot.timestamp }));\n\n console.log(t(\"cli.validPaths\"));\n for (const f of Object.keys(graph.files).sort()) {\n console.log(` ${f}`);\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker serve ──────────────────────────────────────────\n\nprogram\n .command(\"serve\")\n .description(\n \"Start interactive architecture graph viewer in browser\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\"-p, --port <number>\", \"Port number\", \"3000\")\n .option(\n \"-e, --exclude <patterns...>\",\n \"Exclude patterns (regex)\",\n )\n .option(\"-w, --watch\", \"Watch for file changes and auto-reload\")\n .option(\"--no-cache\", \"Force fresh analysis (ignore graph cache)\")\n .option(\"-l, --language <lang>\", `Target language (${LANGUAGE_IDS.join(\", \")})`)\n .action(async (opts) => {\n try {\n const language = validateLanguage(opts.language);\n console.log(t(\"web.starting\"));\n console.log(t(\"cli.analyzing\"));\n\n // Use snapshot if available, otherwise analyze fresh\n let diff = null;\n const result = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n exclude: opts.exclude,\n language,\n noCache: opts.noCache,\n });\n const snapshot = await loadSnapshot(opts.root);\n if (snapshot) {\n diff = computeDiff(snapshot.graph, result.graph);\n }\n\n const port = parseInt(opts.port, 10);\n const viewer = startViewer(result.graph, {\n port,\n diff,\n layerMetadata: result.layerMetadata,\n crossLayerEdges: result.crossLayerEdges,\n });\n\n console.log(t(\"web.listening\", { port }));\n console.log(t(\"web.stop\"));\n\n if (opts.watch) {\n console.log(t(\"web.watching\", { dir: opts.target }));\n let debounce: ReturnType<typeof setTimeout> | null = null;\n watch(opts.target, { recursive: true }, () => {\n if (debounce) clearTimeout(debounce);\n debounce = setTimeout(async () => {\n try {\n console.log(t(\"web.reloading\"));\n const newResult = await resolveGraphCli({\n target: opts.target,\n root: opts.root,\n exclude: opts.exclude,\n language,\n noCache: true, // Watch mode always needs fresh analysis\n });\n viewer.close();\n startViewer(newResult.graph, {\n port,\n layerMetadata: newResult.layerMetadata,\n crossLayerEdges: newResult.crossLayerEdges,\n });\n console.log(t(\"web.reloaded\"));\n } catch { /* ignore transient errors during file saves */ }\n }, 500);\n });\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker ci-setup ────────────────────────────────────────\n\nprogram\n .command(\"ci-setup\")\n .description(\n \"Generate GitHub Actions workflow for architecture checks on PRs\",\n )\n .option(\"-t, --target <dir>\", \"Target directory\", \"src\")\n .action(async (opts) => {\n const workflow = `name: Architecture Check\n\non:\n pull_request:\n branches: [main, master]\n\njobs:\n arch-check:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: '20'\n - run: npm ci\n - run: npx archtracker check --target ${opts.target} --ci\n`;\n try {\n const dir = join(\".github\", \"workflows\");\n await mkdir(dir, { recursive: true });\n const path = join(dir, \"arch-check.yml\");\n await writeFile(path, workflow, \"utf-8\");\n console.log(t(\"ci.generated\", { path }));\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker history ─────────────────────────────────────────\n\nprogram\n .command(\"history\")\n .description(\"List all saved architecture snapshots\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .option(\"-n, --limit <number>\", \"Max entries to show\", \"20\")\n .action(async (opts) => {\n try {\n const snapshots = await listSnapshots(opts.root);\n\n if (snapshots.length === 0) {\n console.log(t(\"history.empty\"));\n return;\n }\n\n const limit = parseInt(opts.limit, 10);\n const shown = snapshots.slice(0, limit);\n\n console.log(t(\"history.title\"));\n console.log(t(\"history.count\", { count: snapshots.length }));\n console.log(\"\");\n for (const s of shown) {\n const layers = s.hasMultiLayer ? \" [multi-layer]\" : \"\";\n console.log(t(\"history.entry\", {\n ts: s.timestamp,\n files: s.totalFiles,\n edges: s.totalEdges,\n circular: s.circularDeps,\n layers,\n }));\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── archtracker layers ──────────────────────────────────────────\n\nconst layersCmd = program\n .command(\"layers\")\n .description(\"Manage multi-layer architecture configuration\");\n\nlayersCmd\n .command(\"init\")\n .description(\"Create a template .archtracker/layers.json\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .action(async (opts) => {\n try {\n const existing = await loadLayerConfig(opts.root);\n if (existing) {\n console.log(t(\"layers.alreadyExists\"));\n return;\n }\n\n const config = {\n version: \"1.0\" as const,\n layers: [\n { name: \"Frontend\", targetDir: \"frontend\", description: \"UI layer\" },\n { name: \"Backend\", targetDir: \"backend\", description: \"API layer\" },\n ],\n };\n\n await saveLayerConfig(opts.root, config);\n console.log(t(\"layers.created\"));\n } catch (error) {\n handleError(error);\n }\n });\n\nlayersCmd\n .command(\"list\")\n .description(\"List configured layers\")\n .option(\"-r, --root <dir>\", \"Project root\", \".\")\n .action(async (opts) => {\n try {\n const config = await loadLayerConfig(opts.root);\n if (!config) {\n console.log(t(\"layers.notFound\"));\n return;\n }\n\n console.log(t(\"layers.header\", { count: config.layers.length }));\n for (const layer of config.layers) {\n const lang = layer.language ? ` [${layer.language}]` : \"\";\n const desc = layer.description ? ` — ${layer.description}` : \"\";\n console.log(` ${layer.name}: ${layer.targetDir}${lang}${desc}`);\n }\n } catch (error) {\n handleError(error);\n }\n });\n\n// ─── Error handling ─────────────────────────────────────────────\n\nfunction validateLanguage(lang?: string): LanguageId | undefined {\n if (!lang) return undefined;\n if (VALID_LANGUAGES.includes(lang)) return lang as LanguageId;\n console.error(`Invalid language: ${lang}`);\n console.error(`Valid languages: ${LANGUAGE_IDS.join(\", \")}`);\n process.exit(1);\n}\n\nfunction handleError(error: unknown): never {\n if (error instanceof AnalyzerError) {\n console.error(t(\"error.cli.analyzer\", { message: error.message }));\n } else if (error instanceof StorageError) {\n console.error(t(\"error.cli.storage\", { message: error.message }));\n } else if (error instanceof Error) {\n console.error(t(\"error.cli.generic\", { message: error.message }));\n } else {\n console.error(t(\"error.cli.unexpected\", { message: String(error) }));\n }\n process.exit(1);\n}\n\nprogram.parse();\n","import { resolve } from \"node:path\";\nimport { stat } from \"node:fs/promises\";\nimport type { DependencyGraph } from \"../types/schema.js\";\nimport { RegexEngine } from \"./engines/regex-engine.js\";\nimport { detectLanguage } from \"./engines/detect.js\";\nimport { getLanguageConfig } from \"./engines/languages.js\";\nimport type { LanguageId } from \"./engines/types.js\";\n\n/** Options for the dependency analyzer */\nexport interface AnalyzeOptions {\n /** Regex patterns to exclude from analysis (e.g. [\"node_modules\", \"\\\\.test\\\\.ts$\"]) */\n exclude?: string[];\n /** Maximum recursion depth (0 = unlimited) */\n maxDepth?: number;\n /** Target language (auto-detected if omitted) */\n language?: LanguageId;\n}\n\n/**\n * Analyze project dependencies using regex-based import extraction.\n * Supports all 13 languages. Language is auto-detected if not specified.\n */\nexport async function analyzeProject(\n rootDir: string,\n options: AnalyzeOptions = {},\n): Promise<DependencyGraph> {\n const absRootDir = resolve(rootDir);\n\n // Validate directory exists\n try {\n const s = await stat(absRootDir);\n if (!s.isDirectory()) {\n throw new AnalyzerError(`Not a directory: ${absRootDir}`);\n }\n } catch (error) {\n if (error instanceof AnalyzerError) throw error;\n throw new AnalyzerError(`Directory not found: ${absRootDir}`, { cause: error });\n }\n\n const language = options.language ?? (await detectLanguage(absRootDir));\n\n try {\n const config = getLanguageConfig(language);\n if (!config) {\n throw new AnalyzerError(`No analyzer config for language: ${language}`);\n }\n\n return await new RegexEngine(config).analyze(absRootDir, options);\n } catch (error) {\n if (error instanceof AnalyzerError) throw error;\n const message = error instanceof Error ? error.message : String(error);\n throw new AnalyzerError(message, { cause: error });\n }\n}\n\n/** Custom error class for analyzer failures */\nexport class AnalyzerError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"AnalyzerError\";\n }\n}\n","import { readdir, readFile, stat } from \"node:fs/promises\";\nimport { join, relative, resolve } from \"node:path\";\nimport type {\n DependencyGraph,\n DependencyEdge,\n FileNode,\n} from \"../../types/schema.js\";\nimport type { AnalyzerEngine, LanguageConfig } from \"./types.js\";\nimport { detectCycles } from \"./cycle.js\";\nimport { stripComments } from \"./strip-comments.js\";\n\nexport class RegexEngine implements AnalyzerEngine {\n constructor(private config: LanguageConfig) {}\n\n async analyze(\n rootDir: string,\n options: { exclude?: string[]; maxDepth?: number } = {},\n ): Promise<DependencyGraph> {\n const absRootDir = resolve(rootDir);\n const excludePatterns = [\n ...(this.config.defaultExclude ?? []),\n ...(options.exclude ?? []),\n \"\\\\.archtracker\",\n ].map((p) => new RegExp(p));\n\n // 1. Collect all matching files\n const projectFiles = await this.collectFiles(\n absRootDir,\n absRootDir,\n excludePatterns,\n options.maxDepth ?? 0,\n );\n\n const projectFileSet = new Set(projectFiles);\n\n // 2. Extract imports and resolve edges\n const files: Record<string, FileNode> = {};\n const edges: DependencyEdge[] = [];\n const edgeSet = new Set<string>(); // \"source\\0target\" for dedup\n\n // Initialize all file nodes\n for (const filePath of projectFiles) {\n const relPath = relative(absRootDir, filePath);\n files[relPath] = {\n path: relPath,\n exists: true,\n dependencies: [],\n dependents: [],\n };\n }\n\n // Process each file for imports\n for (const filePath of projectFiles) {\n const relSource = relative(absRootDir, filePath);\n let content: string;\n try {\n content = await readFile(filePath, \"utf-8\");\n } catch {\n if (files[relSource]) files[relSource].exists = false;\n continue;\n }\n\n const stripped = stripComments(content, this.config.commentStyle);\n const imports = this.extractImports(stripped, filePath, absRootDir, projectFileSet);\n for (const importPath of imports) {\n const resolved = this.config.resolveImport(\n importPath,\n filePath,\n absRootDir,\n projectFileSet,\n );\n if (!resolved) continue;\n\n const relTarget = relative(absRootDir, resolved);\n if (!files[relTarget]) continue; // skip external\n if (relSource === relTarget) continue; // skip self-import\n\n const edgeKey = `${relSource}\\0${relTarget}`;\n if (edgeSet.has(edgeKey)) continue; // deduplicate\n edgeSet.add(edgeKey);\n\n edges.push({\n source: relSource,\n target: relTarget,\n type: \"static\",\n });\n\n files[relSource].dependencies.push(relTarget);\n files[relTarget].dependents.push(relSource);\n }\n }\n\n // 3. Detect cycles\n const circularDependencies = detectCycles(edges);\n\n return {\n rootDir: absRootDir,\n files,\n edges,\n circularDependencies,\n totalFiles: Object.keys(files).length,\n totalEdges: edges.length,\n };\n }\n\n private extractImports(\n content: string,\n filePath: string,\n rootDir: string,\n projectFiles: Set<string>,\n ): string[] {\n // Use custom extractor if defined (e.g., Rust grouped use, C# class refs)\n if (this.config.extractImports) {\n return this.config.extractImports(content, filePath, rootDir, projectFiles);\n }\n\n const imports: string[] = [];\n for (const pattern of this.config.importPatterns) {\n // Reset regex state, ensuring 'g' flag is present to prevent infinite loops\n const flags = pattern.regex.flags.includes('g') ? pattern.regex.flags : pattern.regex.flags + 'g';\n const regex = new RegExp(pattern.regex.source, flags);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(content)) !== null) {\n // Use the first capturing group as the import path\n if (match[1]) {\n imports.push(match[1]);\n }\n }\n }\n return imports;\n }\n\n private async collectFiles(\n dir: string,\n absRootDir: string,\n excludePatterns: RegExp[],\n maxDepth: number,\n currentDepth: number = 0,\n ): Promise<string[]> {\n if (maxDepth > 0 && currentDepth >= maxDepth) return [];\n\n const results: string[] = [];\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n return results;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n const relPath = relative(absRootDir, fullPath);\n\n // Check excludes against the entry name and relative path\n if (\n excludePatterns.some(\n (p) => p.test(entry.name) || p.test(relPath) || p.test(fullPath),\n )\n ) {\n continue;\n }\n\n if (entry.isDirectory()) {\n if (entry.name.startsWith(\".\")) continue;\n const sub = await this.collectFiles(\n fullPath,\n absRootDir,\n excludePatterns,\n maxDepth,\n currentDepth + 1,\n );\n results.push(...sub);\n } else if (entry.isFile()) {\n const dotIdx = entry.name.lastIndexOf(\".\");\n if (dotIdx > 0) {\n const ext = entry.name.slice(dotIdx);\n if (this.config.extensions.includes(ext)) {\n results.push(fullPath);\n }\n }\n }\n }\n\n return results;\n }\n}\n","import type { DependencyEdge, CircularDependency } from \"../../types/schema.js\";\n\nexport function detectCycles(edges: DependencyEdge[]): CircularDependency[] {\n const adj = new Map<string, string[]>();\n for (const edge of edges) {\n if (!adj.has(edge.source)) adj.set(edge.source, []);\n adj.get(edge.source)!.push(edge.target);\n }\n\n const visited = new Set<string>();\n const inStack = new Set<string>();\n const cycles: CircularDependency[] = [];\n const cycleKeys = new Set<string>();\n\n function dfs(node: string, path: string[]): void {\n if (inStack.has(node)) {\n const cycleStart = path.indexOf(node);\n if (cycleStart !== -1) {\n const cycle = path.slice(cycleStart);\n const key = [...cycle].sort().join(\"\\u2192\");\n if (!cycleKeys.has(key)) {\n cycleKeys.add(key);\n cycles.push({ cycle });\n }\n }\n return;\n }\n if (visited.has(node)) return;\n\n visited.add(node);\n inStack.add(node);\n path.push(node);\n\n for (const neighbor of adj.get(node) ?? []) {\n dfs(neighbor, path);\n }\n\n path.pop();\n inStack.delete(node);\n }\n\n for (const node of adj.keys()) {\n if (!visited.has(node)) {\n dfs(node, []);\n }\n }\n\n return cycles;\n}\n","import type { CommentStyle } from \"./types.js\";\n\n/**\n * Strip comments and string literals from source code.\n * Replaces them with whitespace to preserve line numbers and positions.\n */\nexport function stripComments(content: string, style: CommentStyle): string {\n switch (style) {\n case \"c-style\":\n return stripCStyle(content);\n case \"hash\":\n return stripHash(content);\n case \"python\":\n return stripPython(content);\n case \"ruby\":\n return stripRuby(content);\n case \"php\":\n return stripPhp(content);\n default: {\n const _exhaustive: never = style;\n throw new Error(`Unknown comment style: ${_exhaustive}`);\n }\n }\n}\n\n/**\n * C-style comments: // line comments and /* block comments *\\/\n * Used by: Rust, Go, Java, C/C++, Swift, Kotlin\n * Also strips string literals (\"...\" and '...' for char literals)\n * Handles Rust raw strings: r\"...\", r#\"...\"#, r##\"...\"## etc.\n */\nfunction stripCStyle(content: string): string {\n let result = \"\";\n let i = 0;\n while (i < content.length) {\n // Rust raw strings: r\"...\", r#\"...\"#, r##\"...\"##, etc.\n if (content[i] === \"r\" && i + 1 < content.length) {\n let hashes = 0;\n let j = i + 1;\n while (j < content.length && content[j] === \"#\") {\n hashes++;\n j++;\n }\n if (j < content.length && content[j] === '\"') {\n // This is a raw string: r\"...\", r#\"...\"#, etc.\n // Replace entire raw string content with spaces\n // Replace r, hashes, and opening quote with spaces\n for (let k = i; k <= j; k++) {\n result += \" \";\n }\n i = j + 1; // move past opening quote\n // Scan for closing pattern\n while (i < content.length) {\n if (content[i] === '\"') {\n // Check if followed by correct number of hashes\n let matchHashes = 0;\n let m = i + 1;\n while (m < content.length && content[m] === \"#\" && matchHashes < hashes) {\n matchHashes++;\n m++;\n }\n if (matchHashes === hashes) {\n // Found the closing delimiter\n for (let k = i; k < m; k++) {\n result += \" \";\n }\n i = m;\n break;\n }\n }\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n continue;\n }\n // Not a raw string, fall through to normal character handling\n }\n\n // Single-line comment\n if (content[i] === \"/\" && content[i + 1] === \"/\") {\n // Skip to end of line, preserve newline\n while (i < content.length && content[i] !== \"\\n\") {\n result += \" \";\n i++;\n }\n }\n // Block comment\n else if (content[i] === \"/\" && content[i + 1] === \"*\") {\n result += \" \";\n i++; // /\n result += \" \";\n i++; // *\n while (i < content.length) {\n if (content[i] === \"*\" && content[i + 1] === \"/\") {\n result += \" \";\n i++;\n result += \" \";\n i++;\n break;\n }\n // Preserve newlines for line-number accuracy\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n }\n // Double-quoted string\n else if (content[i] === '\"') {\n result += content[i]; // keep the quote\n i++;\n while (i < content.length && content[i] !== '\"') {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i]; // backslash\n i++;\n result += content[i]; // escaped char\n i++;\n } else if (content[i] === \"\\n\") {\n result += \"\\n\";\n i++;\n } else {\n result += content[i];\n i++;\n }\n }\n if (i < content.length) {\n result += content[i]; // closing quote\n i++;\n }\n }\n // Single-quoted char literal (e.g. '/', '\\'', '\\\\', '\\n')\n else if (content[i] === \"'\") {\n result += content[i]; // opening quote\n i++;\n while (i < content.length && content[i] !== \"'\") {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i]; // backslash\n i++;\n result += content[i]; // escaped char\n i++;\n } else {\n result += content[i];\n i++;\n }\n }\n if (i < content.length) {\n result += content[i]; // closing quote\n i++;\n }\n }\n // Raw/backtick string (Go)\n else if (content[i] === \"`\") {\n result += \" \"; // replace backtick string content\n i++;\n while (i < content.length && content[i] !== \"`\") {\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n if (i < content.length) {\n result += \" \";\n i++;\n }\n }\n // Normal character\n else {\n result += content[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Hash-style comments: # line comments\n * Used by: (standalone, though mostly via python/ruby)\n */\nfunction stripHash(content: string): string {\n let result = \"\";\n let i = 0;\n while (i < content.length) {\n if (content[i] === \"#\") {\n while (i < content.length && content[i] !== \"\\n\") {\n result += \" \";\n i++;\n }\n } else if (content[i] === '\"') {\n result += content[i];\n i++;\n while (i < content.length && content[i] !== '\"') {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else {\n result += content[i++];\n }\n }\n if (i < content.length) { result += content[i]; i++; }\n } else if (content[i] === \"'\") {\n result += content[i];\n i++;\n while (i < content.length && content[i] !== \"'\") {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else {\n result += content[i++];\n }\n }\n if (i < content.length) { result += content[i]; i++; }\n } else {\n result += content[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Python comments: # line comments + triple-quoted strings (\"\"\" and ''')\n * Handles string prefixes: r, b, f, rb, fr, br (case-insensitive)\n * For raw strings (r prefix), backslash is NOT treated as escape.\n */\nfunction stripPython(content: string): string {\n let result = \"\";\n let i = 0;\n while (i < content.length) {\n // Check for string prefix (r, b, f, rb, fr, br) before quotes\n let prefixLen = 0;\n let isRaw = false;\n if (i < content.length) {\n const c0 = content[i];\n const c1 = i + 1 < content.length ? content[i + 1] : \"\";\n const c2 = i + 2 < content.length ? content[i + 2] : \"\";\n // Two-char prefixes: rb, br, fr, rf\n if (\n (c0 === \"r\" || c0 === \"R\" || c0 === \"b\" || c0 === \"B\" || c0 === \"f\" || c0 === \"F\") &&\n (c1 === \"r\" || c1 === \"R\" || c1 === \"b\" || c1 === \"B\" || c1 === \"f\" || c1 === \"F\") &&\n (c2 === '\"' || c2 === \"'\")\n ) {\n // Validate it's a real two-char prefix (rb, br, fr, rf)\n const pair = (c0 + c1).toLowerCase();\n if (pair === \"rb\" || pair === \"br\" || pair === \"fr\" || pair === \"rf\") {\n prefixLen = 2;\n isRaw = pair.includes(\"r\");\n }\n }\n // Single-char prefix: r, b, f\n if (\n prefixLen === 0 &&\n (c0 === \"r\" || c0 === \"R\" || c0 === \"b\" || c0 === \"B\" || c0 === \"f\" || c0 === \"F\") &&\n (c1 === '\"' || c1 === \"'\")\n ) {\n prefixLen = 1;\n isRaw = c0 === \"r\" || c0 === \"R\";\n }\n }\n\n const quoteStart = i + prefixLen;\n\n // Triple-quoted strings (with or without prefix)\n if (\n quoteStart + 2 < content.length &&\n (content[quoteStart] === '\"' || content[quoteStart] === \"'\") &&\n content[quoteStart + 1] === content[quoteStart] &&\n content[quoteStart + 2] === content[quoteStart]\n ) {\n const quote = content[quoteStart];\n // Output prefix chars\n for (let k = 0; k < prefixLen; k++) {\n result += \" \";\n }\n // Replace triple-quoted content with spaces\n result += \" \";\n i = quoteStart + 3;\n while (i < content.length) {\n if (content[i] === quote && content[i + 1] === quote && content[i + 2] === quote) {\n result += \" \";\n i += 3;\n break;\n }\n if (!isRaw && content[i] === \"\\\\\" && i + 1 < content.length) {\n result += \" \";\n i++;\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n } else {\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n }\n }\n // Hash comment\n else if (prefixLen === 0 && content[i] === \"#\") {\n while (i < content.length && content[i] !== \"\\n\") {\n result += \" \";\n i++;\n }\n }\n // Single/double quoted strings (non-triple), with or without prefix\n else if (\n quoteStart < content.length &&\n (content[quoteStart] === '\"' || content[quoteStart] === \"'\") &&\n (prefixLen > 0 || content[i] === '\"' || content[i] === \"'\")\n ) {\n const quote = content[quoteStart];\n // Output prefix\n for (let k = i; k < quoteStart; k++) {\n result += content[k];\n }\n result += content[quoteStart]; // opening quote\n i = quoteStart + 1;\n while (i < content.length && content[i] !== quote) {\n if (!isRaw && content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else if (content[i] === \"\\n\") {\n result += \"\\n\";\n i++;\n break; // unterminated string\n } else {\n result += content[i++];\n }\n }\n if (i < content.length && content[i] === quote) { result += content[i]; i++; }\n }\n else {\n result += content[i];\n i++;\n }\n }\n return result;\n}\n\n/**\n * Ruby comments: # line comments + =begin/=end block comments\n * Handles string interpolation #{} inside double-quoted strings.\n */\nfunction stripRuby(content: string): string {\n const lines = content.split(\"\\n\");\n const result: string[] = [];\n let inBlock = false;\n\n for (const line of lines) {\n if (!inBlock && /^=begin(\\s|$)/.test(line)) {\n inBlock = true;\n result.push(\" \".repeat(line.length));\n continue;\n }\n if (inBlock) {\n if (/^=end(\\s|$)/.test(line)) {\n inBlock = false;\n }\n result.push(\" \".repeat(line.length));\n continue;\n }\n\n // Process line for # comments (respecting strings)\n let processed = \"\";\n let i = 0;\n while (i < line.length) {\n if (line[i] === \"#\") {\n // Outside of any string, this is a comment\n processed += \" \".repeat(line.length - i);\n break;\n } else if (line[i] === '\"') {\n processed += line[i]; i++;\n processed = scanRubyDoubleQuotedString(line, i, processed);\n i = (scanRubyDoubleQuotedStringIndex(line, i));\n } else if (line[i] === \"'\") {\n processed += line[i]; i++;\n while (i < line.length && line[i] !== \"'\") {\n if (line[i] === \"\\\\\" && i + 1 < line.length) {\n processed += line[i++];\n processed += line[i++];\n } else {\n processed += line[i++];\n }\n }\n if (i < line.length) { processed += line[i]; i++; }\n } else {\n processed += line[i]; i++;\n }\n }\n result.push(processed);\n }\n\n return result.join(\"\\n\");\n}\n\n/**\n * Scans a Ruby double-quoted string body starting at position i (after the opening \"),\n * handling #{} interpolation. Returns the resulting processed string content appended to `processed`.\n */\nfunction scanRubyDoubleQuotedString(line: string, startI: number, processed: string): string {\n let i = startI;\n while (i < line.length && line[i] !== '\"') {\n if (line[i] === \"\\\\\" && i + 1 < line.length) {\n processed += line[i++];\n processed += line[i++];\n } else if (line[i] === \"#\" && i + 1 < line.length && line[i + 1] === \"{\") {\n // String interpolation: #{...}\n processed += line[i++]; // #\n processed += line[i++]; // {\n let depth = 1;\n while (i < line.length && depth > 0) {\n if (line[i] === \"{\") {\n depth++;\n processed += line[i++];\n } else if (line[i] === \"}\") {\n depth--;\n processed += line[i++];\n } else if (line[i] === \"\\\\\" && i + 1 < line.length) {\n processed += line[i++];\n processed += line[i++];\n } else {\n processed += line[i++];\n }\n }\n } else {\n processed += line[i++];\n }\n }\n if (i < line.length) { processed += line[i]; /* closing \" */ }\n return processed;\n}\n\n/**\n * Returns the index after scanning a Ruby double-quoted string body starting at startI.\n */\nfunction scanRubyDoubleQuotedStringIndex(line: string, startI: number): number {\n let i = startI;\n while (i < line.length && line[i] !== '\"') {\n if (line[i] === \"\\\\\" && i + 1 < line.length) {\n i += 2;\n } else if (line[i] === \"#\" && i + 1 < line.length && line[i + 1] === \"{\") {\n i += 2; // skip #{\n let depth = 1;\n while (i < line.length && depth > 0) {\n if (line[i] === \"{\") {\n depth++;\n i++;\n } else if (line[i] === \"}\") {\n depth--;\n i++;\n } else if (line[i] === \"\\\\\" && i + 1 < line.length) {\n i += 2;\n } else {\n i++;\n }\n }\n } else {\n i++;\n }\n }\n if (i < line.length) { i++; /* skip closing \" */ }\n return i;\n}\n\n/**\n * PHP comments: // line, /* block *\\/, and # line\n * Also handles heredoc (<<<IDENTIFIER) and nowdoc (<<<'IDENTIFIER').\n */\nfunction stripPhp(content: string): string {\n let result = \"\";\n let i = 0;\n while (i < content.length) {\n // Heredoc / Nowdoc: <<<IDENTIFIER or <<<'IDENTIFIER'\n if (content[i] === \"<\" && content[i + 1] === \"<\" && content[i + 2] === \"<\") {\n const heredocStart = i;\n let j = i + 3;\n let isNowdoc = false;\n // Skip optional whitespace after <<<\n // Check for nowdoc (single-quoted identifier)\n if (j < content.length && content[j] === \"'\") {\n isNowdoc = true;\n j++; // skip opening '\n }\n // Read identifier\n const identStart = j;\n while (j < content.length && /[A-Za-z0-9_]/.test(content[j])) {\n j++;\n }\n const identifier = content.slice(identStart, j);\n if (identifier.length > 0) {\n // For nowdoc, skip the closing '\n if (isNowdoc && j < content.length && content[j] === \"'\") {\n j++;\n }\n // Check for optional semicolon and newline (or just newline) to validate this is heredoc syntax\n // We need at least a newline after the identifier line\n let validHeredoc = false;\n let lineEnd = j;\n // There might be a semicolon and/or other chars before newline, but typically just newline\n if (lineEnd < content.length && content[lineEnd] === \"\\n\") {\n validHeredoc = true;\n }\n if (validHeredoc) {\n // Output the heredoc opening line as-is (<<<IDENTIFIER or <<<'IDENTIFIER')\n for (let k = heredocStart; k <= lineEnd; k++) {\n if (content[k] === \"\\n\") {\n result += \"\\n\";\n } else {\n result += content[k];\n }\n }\n i = lineEnd + 1; // move past the newline\n // Now scan for the closing identifier on its own line\n // The closing identifier may optionally be followed by ; and must be at end of line\n let found = false;\n while (i < content.length && !found) {\n // Check if current line starts with the identifier\n const lineStart = i;\n // Read the current line\n let lineEndIdx = i;\n while (lineEndIdx < content.length && content[lineEndIdx] !== \"\\n\") {\n lineEndIdx++;\n }\n const currentLine = content.slice(lineStart, lineEndIdx);\n // PHP 7.3+ allows indented heredoc closing, but we check trimmed\n const trimmedLine = currentLine.trimStart();\n if (\n trimmedLine === identifier ||\n trimmedLine === identifier + \";\" ||\n trimmedLine === identifier + \",\" ||\n trimmedLine === identifier + \");\" ||\n trimmedLine === identifier + \")\" ||\n trimmedLine.startsWith(identifier + \";\") ||\n trimmedLine === identifier\n ) {\n // This is the closing line - output it as-is\n for (let k = lineStart; k < lineEndIdx; k++) {\n result += content[k];\n }\n i = lineEndIdx;\n if (i < content.length && content[i] === \"\\n\") {\n result += \"\\n\";\n i++;\n }\n found = true;\n } else {\n // Body line: replace content with spaces, preserve newlines\n for (let k = lineStart; k < lineEndIdx; k++) {\n result += \" \";\n }\n i = lineEndIdx;\n if (i < content.length && content[i] === \"\\n\") {\n result += \"\\n\";\n i++;\n }\n }\n }\n continue;\n }\n }\n // If not a valid heredoc, just output the < and continue\n result += content[i];\n i++;\n continue;\n }\n\n // Single-line comments (// or #)\n if ((content[i] === \"/\" && content[i + 1] === \"/\") || content[i] === \"#\") {\n while (i < content.length && content[i] !== \"\\n\") {\n result += \" \";\n i++;\n }\n }\n // Block comments\n else if (content[i] === \"/\" && content[i + 1] === \"*\") {\n result += \" \"; i++;\n result += \" \"; i++;\n while (i < content.length) {\n if (content[i] === \"*\" && content[i + 1] === \"/\") {\n result += \" \"; i++;\n result += \" \"; i++;\n break;\n }\n result += content[i] === \"\\n\" ? \"\\n\" : \" \";\n i++;\n }\n }\n // Double-quoted string\n else if (content[i] === '\"') {\n result += content[i]; i++;\n while (i < content.length && content[i] !== '\"') {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else {\n result += content[i++];\n }\n }\n if (i < content.length) { result += content[i]; i++; }\n }\n // Single-quoted string\n else if (content[i] === \"'\") {\n result += content[i]; i++;\n while (i < content.length && content[i] !== \"'\") {\n if (content[i] === \"\\\\\" && i + 1 < content.length) {\n result += content[i++];\n result += content[i++];\n } else {\n result += content[i++];\n }\n }\n if (i < content.length) { result += content[i]; i++; }\n }\n else {\n result += content[i]; i++;\n }\n }\n return result;\n}\n","import { readdir, stat } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { LanguageId } from \"./types.js\";\n\n/** Marker files that identify a project's primary language */\nconst MARKERS: Array<{ file: string; language: LanguageId }> = [\n { file: \"Cargo.toml\", language: \"rust\" },\n { file: \"go.mod\", language: \"go\" },\n { file: \"pyproject.toml\", language: \"python\" },\n { file: \"setup.py\", language: \"python\" },\n { file: \"requirements.txt\", language: \"python\" },\n { file: \"Pipfile\", language: \"python\" },\n { file: \"pom.xml\", language: \"java\" },\n { file: \"build.gradle\", language: \"java\" },\n { file: \"build.gradle.kts\", language: \"kotlin\" },\n { file: \"build.sbt\", language: \"scala\" },\n { file: \"build.sc\", language: \"scala\" },\n { file: \"Package.swift\", language: \"swift\" },\n { file: \"Gemfile\", language: \"ruby\" },\n { file: \"composer.json\", language: \"php\" },\n { file: \"pubspec.yaml\", language: \"dart\" },\n { file: \"CMakeLists.txt\", language: \"c-cpp\" },\n { file: \"Makefile\", language: \"c-cpp\" },\n { file: \"package.json\", language: \"javascript\" },\n { file: \"tsconfig.json\", language: \"javascript\" },\n];\n\n/** Extension-based markers for languages without fixed-name marker files */\nconst EXT_MARKERS: Array<[string, LanguageId]> = [\n [\".sln\", \"c-sharp\"],\n [\".csproj\", \"c-sharp\"],\n];\n\n/** Extension-to-language mapping for fallback detection */\nconst EXT_MAP: Record<string, LanguageId> = {\n \".ts\": \"javascript\",\n \".tsx\": \"javascript\",\n \".js\": \"javascript\",\n \".jsx\": \"javascript\",\n \".mjs\": \"javascript\",\n \".cjs\": \"javascript\",\n \".py\": \"python\",\n \".rs\": \"rust\",\n \".go\": \"go\",\n \".java\": \"java\",\n \".c\": \"c-cpp\",\n \".cpp\": \"c-cpp\",\n \".cc\": \"c-cpp\",\n \".cxx\": \"c-cpp\",\n \".h\": \"c-cpp\",\n \".hpp\": \"c-cpp\",\n \".rb\": \"ruby\",\n \".php\": \"php\",\n \".swift\": \"swift\",\n \".kt\": \"kotlin\",\n \".kts\": \"kotlin\",\n \".cs\": \"c-sharp\",\n \".dart\": \"dart\",\n \".scala\": \"scala\",\n \".sc\": \"scala\",\n};\n\n/**\n * Auto-detect the primary language of a project directory.\n * 1. Check for marker files (Cargo.toml, go.mod, etc.)\n * 2. Fallback: count file extensions in the top-level directory\n */\nexport async function detectLanguage(rootDir: string): Promise<LanguageId> {\n // Phase 1: marker files\n for (const marker of MARKERS) {\n try {\n const s = await stat(join(rootDir, marker.file));\n if (s.isFile() || s.isDirectory()) {\n return marker.language;\n }\n } catch {\n // file doesn't exist, continue\n }\n }\n\n // Phase 1.5: extension-based markers (e.g. *.sln, *.csproj for C#)\n try {\n const topEntries = await readdir(rootDir, { withFileTypes: true });\n for (const entry of topEntries) {\n if (!entry.isFile()) continue;\n for (const [ext, lang] of EXT_MARKERS) {\n if (entry.name.endsWith(ext)) return lang;\n }\n }\n } catch {\n // ignore\n }\n\n // Phase 2: extension frequency (shallow scan)\n const counts = new Map<LanguageId, number>();\n try {\n await scanExtensions(rootDir, counts, 2, 0);\n } catch {\n // if scan fails, default to JS\n }\n\n if (counts.size > 0) {\n let maxLang: LanguageId = \"javascript\";\n let maxCount = 0;\n for (const [lang, count] of counts) {\n if (count > maxCount) {\n maxCount = count;\n maxLang = lang;\n }\n }\n return maxLang;\n }\n\n return \"javascript\";\n}\n\nasync function scanExtensions(\n dir: string,\n counts: Map<LanguageId, number>,\n maxDepth: number,\n currentDepth: number,\n): Promise<void> {\n if (currentDepth >= maxDepth) return;\n\n const entries = await readdir(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n\n if (entry.isDirectory() && currentDepth < maxDepth - 1) {\n await scanExtensions(\n join(dir, entry.name),\n counts,\n maxDepth,\n currentDepth + 1,\n );\n } else if (entry.isFile()) {\n const dotIdx = entry.name.lastIndexOf(\".\");\n if (dotIdx > 0) {\n const ext = entry.name.slice(dotIdx);\n const lang = EXT_MAP[ext];\n if (lang) {\n counts.set(lang, (counts.get(lang) ?? 0) + 1);\n }\n }\n }\n }\n}\n","import { readFileSync } from \"node:fs\";\nimport { join, dirname, resolve } from \"node:path\";\nimport { LANGUAGE_IDS } from \"./types.js\";\nimport type { LanguageConfig, LanguageId } from \"./types.js\";\n\n// ─── Python ──────────────────────────────────────────\nconst python: LanguageConfig = {\n id: \"python\",\n extensions: [\".py\"],\n commentStyle: \"python\",\n importPatterns: [\n // from package.module import something (including indented, e.g. inside try/except)\n { regex: /^\\s*from\\s+(\\.[\\w.]*|\\w[\\w.]*)\\s+import\\b/gm },\n // import package.module (handled by extractImports for multi-module case)\n ],\n // Bug #1 fix: custom extractImports to handle `import a, b, c`\n // Bug #12 fix: allow leading whitespace to catch try/except indented imports\n extractImports(content: string): string[] {\n const imports: string[] = [];\n\n // from package.module import something (including indented)\n const fromRegex = /^\\s*from\\s+(\\.[\\w.]*|\\w[\\w.]*)\\s+import\\b/gm;\n let match: RegExpExecArray | null;\n while ((match = fromRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // import a, b, c — captures all comma-separated modules (including indented)\n const importRegex = /^\\s*import\\s+([\\w.]+(?:\\s*,\\s*[\\w.]+)*)/gm;\n while ((match = importRegex.exec(content)) !== null) {\n const modules = match[1].split(\",\");\n for (const mod of modules) {\n const trimmed = mod.trim();\n if (trimmed) imports.push(trimmed);\n }\n }\n\n return imports;\n },\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Relative imports (starts with .)\n if (importPath.startsWith(\".\")) {\n const dots = importPath.match(/^\\.+/)?.[0].length ?? 1;\n let base = dirname(sourceFile);\n for (let i = 1; i < dots; i++) base = dirname(base);\n const rest = importPath.slice(dots).replace(/\\./g, \"/\");\n return tryPythonResolve(join(base, rest), projectFiles);\n }\n // Absolute imports\n const parts = importPath.replace(/\\./g, \"/\");\n return tryPythonResolve(join(rootDir, parts), projectFiles);\n },\n defaultExclude: [\"__pycache__\", \"\\\\.venv\", \"venv\", \"\\\\.egg-info\", \"dist\", \"build\"],\n};\n\nfunction tryPythonResolve(base: string, projectFiles: Set<string>): string | null {\n // Try as module file\n if (projectFiles.has(base + \".py\")) return base + \".py\";\n // Try as package __init__.py\n if (projectFiles.has(join(base, \"__init__.py\"))) return join(base, \"__init__.py\");\n return null;\n}\n\n// ─── Rust ────────────────────────────────────────────\nconst rust: LanguageConfig = {\n id: \"rust\",\n extensions: [\".rs\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports\n extractImports(content: string): string[] {\n const imports: string[] = [];\n\n // mod declarations: mod child;\n const modRegex = /\\bmod\\s+(\\w+)\\s*;/gm;\n let match: RegExpExecArray | null;\n while ((match = modRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // use crate::... (simple and grouped)\n const useRegex = /\\buse\\s+crate::([\\s\\S]*?);/gm;\n while ((match = useRegex.exec(content)) !== null) {\n const body = match[1].trim();\n extractRustUsePaths(body, \"\", imports);\n }\n\n // Bug #2 fix: use super::... imports\n const useSuperRegex = /\\buse\\s+super::([\\s\\S]*?);/gm;\n while ((match = useSuperRegex.exec(content)) !== null) {\n const body = match[1].trim();\n extractRustUsePaths(body, \"\", imports, \"super\");\n }\n\n // Bug #2 fix: use self::... imports\n const useSelfRegex = /\\buse\\s+self::([\\s\\S]*?);/gm;\n while ((match = useSelfRegex.exec(content)) !== null) {\n const body = match[1].trim();\n extractRustUsePaths(body, \"\", imports, \"self\");\n }\n\n return imports;\n },\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n const srcDir = join(rootDir, \"src\");\n\n if (!importPath.includes(\"::\")) {\n // mod declaration: look for child module\n const parentDir = dirname(sourceFile);\n const asFile = join(parentDir, importPath + \".rs\");\n if (projectFiles.has(asFile)) return asFile;\n const asDir = join(parentDir, importPath, \"mod.rs\");\n if (projectFiles.has(asDir)) return asDir;\n return null;\n }\n\n // Bug #3 fix: use super::x::y — resolve from parent directory of source file\n if (importPath.startsWith(\"super::\")) {\n const parentDir = dirname(dirname(sourceFile));\n const segments = importPath.slice(\"super::\".length).split(\"::\");\n for (let i = segments.length; i > 0; i--) {\n const path = segments.slice(0, i).join(\"/\");\n const asFile = join(parentDir, path + \".rs\");\n if (projectFiles.has(asFile)) return asFile;\n const asDir = join(parentDir, path, \"mod.rs\");\n if (projectFiles.has(asDir)) return asDir;\n }\n return null;\n }\n\n // Bug #4 fix: use self::x::y — resolve from same directory as source file\n if (importPath.startsWith(\"self::\")) {\n const selfDir = dirname(sourceFile);\n const segments = importPath.slice(\"self::\".length).split(\"::\");\n for (let i = segments.length; i > 0; i--) {\n const path = segments.slice(0, i).join(\"/\");\n const asFile = join(selfDir, path + \".rs\");\n if (projectFiles.has(asFile)) return asFile;\n const asDir = join(selfDir, path, \"mod.rs\");\n if (projectFiles.has(asDir)) return asDir;\n }\n return null;\n }\n\n // use crate::x::y — resolve from src/\n const segments = importPath.split(\"::\");\n // Try progressively shorter prefixes as directory/file\n for (let i = segments.length; i > 0; i--) {\n const path = segments.slice(0, i).join(\"/\");\n const asFile = join(srcDir, path + \".rs\");\n if (projectFiles.has(asFile)) return asFile;\n const asDir = join(srcDir, path, \"mod.rs\");\n if (projectFiles.has(asDir)) return asDir;\n }\n return null;\n },\n defaultExclude: [\"target\"],\n};\n\n/**\n * Recursively extract Rust use paths, handling grouped imports.\n * e.g. \"foo::{bar, baz::qux}\" → [\"foo::bar\", \"foo::baz::qux\"]\n * e.g. \"foo::bar\" → [\"foo::bar\"]\n * The optional rootPrefix parameter prepends \"super\" or \"self\" to all paths.\n */\nfunction extractRustUsePaths(body: string, prefix: string, results: string[], rootPrefix?: string): void {\n const trimmed = body.trim();\n\n // Check for grouped import: path::{...}\n const braceStart = trimmed.indexOf(\"{\");\n if (braceStart === -1) {\n // Simple path like \"foo::bar::Baz\" or just \"foo\"\n let path = prefix ? `${prefix}::${trimmed}` : trimmed;\n if (rootPrefix) {\n path = `${rootPrefix}::${path}`;\n }\n // Remove trailing items after last :: that aren't module names\n // Keep the full path — resolver handles progressively shorter prefixes\n if (path && !path.includes(\"{\")) {\n results.push(path);\n }\n return;\n }\n\n // Extract the prefix before the brace\n let pathPrefix = trimmed.slice(0, braceStart).trim();\n if (pathPrefix.endsWith(\"::\")) {\n pathPrefix = pathPrefix.slice(0, -2);\n }\n const fullPrefix = prefix ? `${prefix}::${pathPrefix}` : pathPrefix;\n\n // Find matching closing brace\n let depth = 0;\n let braceEnd = -1;\n for (let i = braceStart; i < trimmed.length; i++) {\n if (trimmed[i] === \"{\") depth++;\n else if (trimmed[i] === \"}\") {\n depth--;\n if (depth === 0) { braceEnd = i; break; }\n }\n }\n if (braceEnd === -1) return;\n\n // Split the brace content by commas (respecting nested braces)\n const inner = trimmed.slice(braceStart + 1, braceEnd).trim();\n const items = splitByTopLevelComma(inner);\n\n for (const item of items) {\n const cleaned = item.trim();\n if (cleaned === \"self\") {\n const selfPath = rootPrefix ? `${rootPrefix}::${fullPrefix}` : fullPrefix;\n results.push(selfPath);\n } else if (cleaned) {\n extractRustUsePaths(cleaned, fullPrefix, results, rootPrefix);\n }\n }\n}\n\n/** Split string by commas, respecting nested braces */\nfunction splitByTopLevelComma(s: string): string[] {\n const parts: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < s.length; i++) {\n if (s[i] === \"{\") depth++;\n else if (s[i] === \"}\") depth--;\n else if (s[i] === \",\" && depth === 0) {\n parts.push(s.slice(start, i));\n start = i + 1;\n }\n }\n parts.push(s.slice(start));\n return parts;\n}\n\n// ─── Go ──────────────────────────────────────────────\nconst go: LanguageConfig = {\n id: \"go\",\n extensions: [\".go\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports\n extractImports(content: string): string[] {\n const imports: string[] = [];\n\n // Single import: import \"path\" or import alias \"path\"\n const singleRegex = /\\bimport\\s+(?:\\w+\\s+)?\"([^\"]+)\"/gm;\n let match: RegExpExecArray | null;\n while ((match = singleRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // Import block: import ( ... )\n const blockRegex = /\\bimport\\s*\\(([^)]*)\\)/gms;\n while ((match = blockRegex.exec(content)) !== null) {\n const block = match[1];\n const entryRegex = /(?:\\w+\\s+)?\"([^\"]+)\"/g;\n let entry: RegExpExecArray | null;\n while ((entry = entryRegex.exec(block)) !== null) {\n imports.push(entry[1]);\n }\n }\n\n return imports;\n },\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Only resolve project-internal imports\n // Read go.mod module path to strip prefix\n const modPrefix = goModulePrefix(rootDir);\n if (!modPrefix || !importPath.startsWith(modPrefix)) return null;\n\n const relPath = importPath.slice(modPrefix.length + 1); // +1 for /\n // Go packages are directories; find any .go file in the directory\n const pkgDir = join(rootDir, relPath);\n for (const f of projectFiles) {\n if (f.startsWith(pkgDir + \"/\") && f.endsWith(\".go\")) return f;\n }\n return null;\n },\n defaultExclude: [\"vendor\"],\n};\n\n// Bug #11: goModCache is a per-process cache of go.mod module prefixes, keyed by rootDir.\n// It is intentionally never cleared because the module path for a given rootDir does not\n// change during the lifetime of a single process. If a long-running process needs to pick\n// up go.mod changes, restart the process.\nconst goModCache = new Map<string, string | null>();\nfunction goModulePrefix(rootDir: string): string | null {\n if (goModCache.has(rootDir)) return goModCache.get(rootDir)!;\n try {\n const content = readFileSync(join(rootDir, \"go.mod\"), \"utf-8\");\n const match = content.match(/^module\\s+(.+)$/m);\n const prefix = match ? match[1].trim() : null;\n goModCache.set(rootDir, prefix);\n return prefix;\n } catch {\n goModCache.set(rootDir, null);\n return null;\n }\n}\n\n// ─── Java ────────────────────────────────────────────\nconst java: LanguageConfig = {\n id: \"java\",\n extensions: [\".java\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // Bug #5 fix: import com.example.ClassName; and import com.example.*; (wildcard)\n // Bug #6: static imports also captured here\n { regex: /^import\\s+(?:static\\s+)?([\\w.*]+);/gm },\n ],\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Bug #5 fix: wildcard imports — can't resolve to a single file, return null\n if (importPath.endsWith(\".*\")) {\n return null;\n }\n\n // Bug #6 fix: static imports — try progressively shorter paths\n // e.g. com.example.Class.method → try com/example/Class/method.java,\n // then com/example/Class.java, then com/example.java, etc.\n const segments = importPath.split(\".\");\n for (let i = segments.length; i > 0; i--) {\n const filePath = segments.slice(0, i).join(\"/\") + \".java\";\n for (const srcRoot of [\"\", \"src/main/java/\", \"src/\", \"app/src/main/java/\"]) {\n const full = join(rootDir, srcRoot, filePath);\n if (projectFiles.has(full)) return full;\n }\n }\n // Suffix fallback: try right-side segments for non-Maven layouts\n // e.g. com.example.analytics.processors.TraceProcessor → processors/TraceProcessor.java\n for (let i = 1; i < segments.length; i++) {\n const filePath = segments.slice(i).join(\"/\") + \".java\";\n for (const srcRoot of [\"\", \"src/main/java/\", \"src/\", \"app/src/main/java/\"]) {\n const full = join(rootDir, srcRoot, filePath);\n if (projectFiles.has(full)) return full;\n }\n }\n return null;\n },\n defaultExclude: [\"build\", \"target\", \"\\\\.gradle\", \"\\\\.idea\"],\n};\n\n// ─── C/C++ ───────────────────────────────────────────\nconst cCpp: LanguageConfig = {\n id: \"c-cpp\",\n extensions: [\".c\", \".cpp\", \".cc\", \".cxx\", \".h\", \".hpp\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // #include \"file.h\" (skip <system> includes)\n { regex: /^#include\\s+\"([^\"]+)\"/gm },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Resolve relative to source file first\n const fromSource = resolve(dirname(sourceFile), importPath);\n if (projectFiles.has(fromSource)) return fromSource;\n // Then relative to root\n const fromRoot = join(rootDir, importPath);\n if (projectFiles.has(fromRoot)) return fromRoot;\n // Try common include dirs\n for (const incDir of [\"include\", \"src\"]) {\n const full = join(rootDir, incDir, importPath);\n if (projectFiles.has(full)) return full;\n }\n return null;\n },\n defaultExclude: [\"build\", \"cmake-build\", \"\\\\.o$\", \"\\\\.obj$\"],\n};\n\n// ─── Ruby ────────────────────────────────────────────\nconst ruby: LanguageConfig = {\n id: \"ruby\",\n extensions: [\".rb\"],\n commentStyle: \"ruby\",\n importPatterns: [\n // require_relative 'path'\n { regex: /\\brequire_relative\\s+['\"]([^'\"]+)['\"]/gm },\n // require 'path' (for project-internal requires)\n { regex: /\\brequire\\s+['\"]([^'\"]+)['\"]/gm },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n const withExt = importPath.endsWith(\".rb\") ? importPath : importPath + \".rb\";\n // require_relative: relative to source\n const fromSource = resolve(dirname(sourceFile), withExt);\n if (projectFiles.has(fromSource)) return fromSource;\n // require: relative to root or lib/\n const fromRoot = join(rootDir, withExt);\n if (projectFiles.has(fromRoot)) return fromRoot;\n const fromLib = join(rootDir, \"lib\", withExt);\n if (projectFiles.has(fromLib)) return fromLib;\n return null;\n },\n defaultExclude: [\"vendor\", \"\\\\.bundle\"],\n};\n\n// ─── PHP ─────────────────────────────────────────────\nconst php: LanguageConfig = {\n id: \"php\",\n extensions: [\".php\"],\n commentStyle: \"php\",\n importPatterns: [\n // require/include/require_once/include_once 'path'\n { regex: /\\b(?:require|include)(?:_once)?\\s+['\"]([^'\"]+)['\"]/gm },\n // require_once __DIR__ . '/path' (common PHP pattern)\n { regex: /\\b(?:require|include)(?:_once)?\\s+__DIR__\\s*\\.\\s*['\"]([^'\"]+)['\"]/gm },\n // Bug #9 fix: use Namespace\\Class — skip `function` and `const` keywords\n { regex: /^use\\s+(?:function\\s+|const\\s+)?([\\w\\\\]+)/gm },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Handle __DIR__ relative paths: strip leading /\n let normalizedPath = importPath;\n if (normalizedPath.startsWith(\"/\")) {\n normalizedPath = normalizedPath.slice(1);\n }\n // Direct file path\n if (normalizedPath.includes(\"/\") || normalizedPath.endsWith(\".php\")) {\n const withExt = normalizedPath.endsWith(\".php\") ? normalizedPath : normalizedPath + \".php\";\n const fromSource = resolve(dirname(sourceFile), withExt);\n if (projectFiles.has(fromSource)) return fromSource;\n const fromRoot = join(rootDir, withExt);\n if (projectFiles.has(fromRoot)) return fromRoot;\n return null;\n }\n // PSR-4: Namespace\\Class → Namespace/Class.php\n const filePath = importPath.replace(/\\\\/g, \"/\") + \".php\";\n const fromRoot = join(rootDir, filePath);\n if (projectFiles.has(fromRoot)) return fromRoot;\n const fromSrc = join(rootDir, \"src\", filePath);\n if (projectFiles.has(fromSrc)) return fromSrc;\n return null;\n },\n defaultExclude: [\"vendor\"],\n};\n\n// ─── Swift ───────────────────────────────────────────\n/** Files that don't define referenceable Swift types */\nconst SWIFT_SKIP_FILES = new Set([\"Package\", \"main\", \"AppDelegate\", \"SceneDelegate\"]);\n\nconst swift: LanguageConfig = {\n id: \"swift\",\n extensions: [\".swift\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports\n extractImports(content: string, filePath: string, _rootDir: string, projectFiles: Set<string>): string[] {\n const imports: string[] = [];\n\n // 1. Module imports: import ModuleName / @testable import ModuleName\n const moduleRegex = /^(?:@testable\\s+)?import\\s+(?:class\\s+|struct\\s+|enum\\s+|protocol\\s+|func\\s+|var\\s+|let\\s+|typealias\\s+)?(\\w+)/gm;\n let match: RegExpExecArray | null;\n while ((match = moduleRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // 2. Intra-module type reference scanning (like C# engine)\n // Swift files in the same module can reference each other without imports\n const typeMap = new Map<string, string>();\n for (const f of projectFiles) {\n if (f === filePath || !f.endsWith(\".swift\")) continue;\n const basename = f.split(\"/\").pop()!.replace(/\\.swift$/, \"\");\n if (!basename || SWIFT_SKIP_FILES.has(basename)) continue;\n typeMap.set(basename, f);\n }\n\n if (typeMap.size > 0) {\n const escaped = [...typeMap.keys()].map((n) =>\n n.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"),\n );\n const combined = new RegExp(`\\\\b(${escaped.join(\"|\")})\\\\b`, \"g\");\n const matched = new Set<string>();\n while ((match = combined.exec(content)) !== null) {\n const typeName = match[1];\n const targetPath = typeMap.get(typeName);\n if (targetPath && !matched.has(targetPath)) {\n matched.add(targetPath);\n imports.push(targetPath); // full path — resolveImport passes through\n }\n }\n }\n\n return imports;\n },\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Direct file path (from type reference scanning)\n if (projectFiles.has(importPath)) return importPath;\n\n // Cross-module: import Module → find Sources/Module/*.swift\n const spmDir = join(rootDir, \"Sources\", importPath);\n for (const f of projectFiles) {\n if (f.startsWith(spmDir + \"/\") && f.endsWith(\".swift\")) return f;\n }\n return null;\n },\n defaultExclude: [\"\\\\.build\", \"DerivedData\"],\n};\n\n// ─── Kotlin ──────────────────────────────────────────\nconst kotlin: LanguageConfig = {\n id: \"kotlin\",\n extensions: [\".kt\", \".kts\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // Bug #7/#8 fix: import com.example.ClassName and import com.example.*\n { regex: /^import\\s+([\\w.*]+)/gm },\n ],\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Bug #8 fix: wildcard imports — can't resolve to a single file, return null\n if (importPath.endsWith(\".*\")) {\n return null;\n }\n\n // Bug #7 fix: strip trailing dot if present (from previous regex issues)\n let cleanPath = importPath;\n if (cleanPath.endsWith(\".\")) {\n cleanPath = cleanPath.slice(0, -1);\n }\n\n // Convert com.example.Class to com/example/Class.kt\n const segments = cleanPath.split(\".\");\n const filePath = segments.join(\"/\");\n for (const ext of [\".kt\", \".kts\"]) {\n for (const srcRoot of [\n \"\",\n \"src/main/kotlin/\",\n \"src/main/java/\",\n \"src/\",\n \"app/src/main/kotlin/\",\n \"app/src/main/java/\",\n ]) {\n const full = join(rootDir, srcRoot, filePath + ext);\n if (projectFiles.has(full)) return full;\n }\n }\n // Suffix fallback: try right-side segments for non-standard layouts\n for (let i = 1; i < segments.length; i++) {\n const suffixPath = segments.slice(i).join(\"/\");\n for (const ext of [\".kt\", \".kts\"]) {\n for (const srcRoot of [\n \"\",\n \"src/main/kotlin/\",\n \"src/main/java/\",\n \"src/\",\n \"app/src/main/kotlin/\",\n \"app/src/main/java/\",\n ]) {\n const full = join(rootDir, srcRoot, suffixPath + ext);\n if (projectFiles.has(full)) return full;\n }\n }\n }\n return null;\n },\n defaultExclude: [\"build\", \"\\\\.gradle\", \"\\\\.idea\"],\n};\n\n// ─── C# ──────────────────────────────────────────────\n/** Files that don't define referenceable classes */\nconst CS_SKIP_CLASSNAMES = new Set([\"AssemblyInfo\", \"GlobalUsings\"]);\n\nconst cSharp: LanguageConfig = {\n id: \"c-sharp\",\n extensions: [\".cs\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports\n extractImports(content, filePath, _rootDir, projectFiles) {\n const imports: string[] = [];\n\n // 1. using statements (indented, global, static)\n const usingRegex = /^\\s*(?:global\\s+)?using\\s+(?:static\\s+)?([\\w.]+)\\s*;/gm;\n let match: RegExpExecArray | null;\n while ((match = usingRegex.exec(content)) !== null) {\n imports.push(match[1]);\n }\n\n // 2. Class-name reference scanning (same-namespace dependencies)\n // C# convention: class name = file name → scan for references\n const classMap = new Map<string, string>();\n for (const f of projectFiles) {\n if (f === filePath) continue;\n if (!f.endsWith(\".cs\")) continue;\n const basename = f.split(\"/\").pop()!;\n const className = basename.replace(/\\.xaml\\.cs$/i, \"\").replace(/\\.cs$/i, \"\");\n if (!className || CS_SKIP_CLASSNAMES.has(className)) continue;\n classMap.set(className, f);\n }\n\n if (classMap.size > 0) {\n // Build combined regex for efficient single-pass scanning\n const escaped = [...classMap.keys()].map((n) =>\n n.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"),\n );\n const combined = new RegExp(`\\\\b(${escaped.join(\"|\")})\\\\b`, \"g\");\n const matched = new Set<string>();\n while ((match = combined.exec(content)) !== null) {\n const className = match[1];\n const targetPath = classMap.get(className);\n if (targetPath && !matched.has(targetPath)) {\n matched.add(targetPath);\n imports.push(targetPath); // full path — resolveImport passes through\n }\n }\n }\n\n return imports;\n },\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n // Direct file path (from class name scanning)\n if (projectFiles.has(importPath)) return importPath;\n\n // Namespace-based resolution (using statements)\n const segments = importPath.split(\".\");\n\n // 1. Try as direct file path (progressively shorter, like Java static imports)\n for (let i = segments.length; i > 0; i--) {\n const filePath = segments.slice(0, i).join(\"/\") + \".cs\";\n for (const srcRoot of [\"\", \"src/\", \"lib/\"]) {\n const full = join(rootDir, srcRoot, filePath);\n if (projectFiles.has(full)) return full;\n }\n }\n\n // 2. C# using = namespace import; try as directory, find first .cs file\n // Strip leading segments progressively (handles root namespace prefix)\n for (let start = 0; start < segments.length; start++) {\n const dirPath = segments.slice(start).join(\"/\");\n for (const srcRoot of [\"\", \"src/\", \"lib/\"]) {\n const prefix = join(rootDir, srcRoot, dirPath) + \"/\";\n for (const f of projectFiles) {\n if (f.startsWith(prefix) && f.endsWith(\".cs\")) return f;\n }\n }\n }\n\n return null;\n },\n defaultExclude: [\"bin\", \"obj\", \"\\\\.vs\", \"packages\", \"TestResults\"],\n};\n\n// ─── Dart ────────────────────────────────────────────\nconst dart: LanguageConfig = {\n id: \"dart\",\n extensions: [\".dart\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // import 'package:pkg/file.dart'; or import 'relative/path.dart';\n { regex: /^import\\s+['\"]([^'\"]+)['\"]/gm },\n // export 'file.dart'; (re-exports create dependencies too)\n { regex: /^export\\s+['\"]([^'\"]+)['\"]/gm },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Skip dart: stdlib imports\n if (importPath.startsWith(\"dart:\")) return null;\n\n // package: imports — resolve own package to lib/\n if (importPath.startsWith(\"package:\")) {\n const ownPackage = dartPackageName(rootDir);\n if (!ownPackage) return null;\n const prefix = `package:${ownPackage}/`;\n if (!importPath.startsWith(prefix)) return null; // external package\n const relPath = importPath.slice(prefix.length);\n // Try lib/ first (standard pub.dev packages)\n const libPath = join(rootDir, \"lib\", relPath);\n if (projectFiles.has(libPath)) return libPath;\n // Fallback to root level (Flutter apps without lib/)\n const rootPath = join(rootDir, relPath);\n if (projectFiles.has(rootPath)) return rootPath;\n return null;\n }\n\n // Relative imports\n const resolved = resolve(dirname(sourceFile), importPath);\n if (projectFiles.has(resolved)) return resolved;\n return null;\n },\n defaultExclude: [\"\\\\.dart_tool\", \"build\", \"\\\\.packages\"],\n};\n\nconst dartPackageCache = new Map<string, string | null>();\nfunction dartPackageName(rootDir: string): string | null {\n if (dartPackageCache.has(rootDir)) return dartPackageCache.get(rootDir)!;\n try {\n const content = readFileSync(join(rootDir, \"pubspec.yaml\"), \"utf-8\");\n const match = content.match(/^name:\\s*(\\S+)/m);\n const name = match ? match[1] : null;\n dartPackageCache.set(rootDir, name);\n return name;\n } catch {\n dartPackageCache.set(rootDir, null);\n return null;\n }\n}\n\n// ─── Scala ───────────────────────────────────────────\nconst scala: LanguageConfig = {\n id: \"scala\",\n extensions: [\".scala\", \".sc\"],\n commentStyle: \"c-style\",\n importPatterns: [], // handled by extractImports for grouped syntax\n extractImports(content: string): string[] {\n const imports: string[] = [];\n // import pkg.Class\n // import pkg.{A, B, C}\n // import pkg._ (wildcard)\n const importRegex = /\\bimport\\s+([\\w.]+(?:\\.\\{[^}]+\\}|\\.\\w+|\\._))/gm;\n let match: RegExpExecArray | null;\n while ((match = importRegex.exec(content)) !== null) {\n const full = match[1];\n // Check for grouped imports: import pkg.{A, B}\n const braceMatch = full.match(/^([\\w.]+)\\.\\{([^}]+)\\}$/);\n if (braceMatch) {\n const prefix = braceMatch[1];\n const items = braceMatch[2].split(\",\");\n for (const item of items) {\n const trimmed = item.trim().split(/\\s+/)[0]; // handle \"A => B\" rename\n if (trimmed === \"_\") continue; // wildcard in group\n imports.push(`${prefix}.${trimmed}`);\n }\n } else if (full.endsWith(\"._\")) {\n // Wildcard import — skip\n continue;\n } else {\n imports.push(full);\n }\n }\n return imports;\n },\n resolveImport(importPath, _sourceFile, rootDir, projectFiles) {\n const segments = importPath.split(\".\");\n // Try progressively shorter left-prefix paths\n for (let i = segments.length; i > 0; i--) {\n const filePath = segments.slice(0, i).join(\"/\");\n for (const ext of [\".scala\", \".sc\"]) {\n for (const srcRoot of [\"\", \"src/main/scala/\", \"src/\", \"app/\"]) {\n const full = join(rootDir, srcRoot, filePath + ext);\n if (projectFiles.has(full)) return full;\n }\n }\n }\n // Suffix fallback: try right-side segments for non-SBT layouts\n for (let i = 1; i < segments.length; i++) {\n const suffixPath = segments.slice(i).join(\"/\");\n for (const ext of [\".scala\", \".sc\"]) {\n for (const srcRoot of [\"\", \"src/main/scala/\", \"src/\", \"app/\"]) {\n const full = join(rootDir, srcRoot, suffixPath + ext);\n if (projectFiles.has(full)) return full;\n }\n }\n }\n return null;\n },\n defaultExclude: [\"target\", \"\\\\.bsp\", \"\\\\.metals\", \"\\\\.bloop\"],\n};\n\n// ─── JavaScript / TypeScript ──────────────────────────\nconst javascript: LanguageConfig = {\n id: \"javascript\",\n extensions: [\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\"],\n commentStyle: \"c-style\",\n importPatterns: [\n // ES6: import [type] [stuff from] \"path\"\n { regex: /import\\s+(?:type\\s+)?(?:[\\w*{}\\s,]+\\s+from\\s+)?[\"']([^\"']+)[\"']/g },\n // Dynamic: import(\"path\")\n { regex: /\\bimport\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g },\n // Re-export: export [type] { stuff } from \"path\" / export * from \"path\"\n { regex: /export\\s+(?:type\\s+)?(?:\\{[^}]*\\}|\\*(?:\\s+as\\s+\\w+)?)\\s+from\\s+[\"']([^\"']+)[\"']/g },\n // CommonJS: require(\"path\")\n { regex: /\\brequire\\s*\\(\\s*[\"']([^\"']+)[\"']\\s*\\)/g },\n ],\n resolveImport(importPath, sourceFile, rootDir, projectFiles) {\n // Skip external modules: node:, @scope/pkg, bare specifiers without ./ or ../\n if (importPath.startsWith(\"node:\")) return null;\n if (!importPath.startsWith(\".\")) return null;\n\n // Resolve relative to source file\n const resolved = resolve(dirname(sourceFile), importPath);\n\n // 1. Exact match (e.g., \"./foo.js\" where foo.js actually exists)\n if (projectFiles.has(resolved)) return resolved;\n\n // 2. ESM convention: .js → .ts / .tsx (TypeScript emits .js in import paths)\n if (resolved.endsWith(\".js\")) {\n const tsPath = resolved.slice(0, -3) + \".ts\";\n if (projectFiles.has(tsPath)) return tsPath;\n const tsxPath = resolved.slice(0, -3) + \".tsx\";\n if (projectFiles.has(tsxPath)) return tsxPath;\n }\n if (resolved.endsWith(\".jsx\")) {\n const tsxPath = resolved.slice(0, -4) + \".tsx\";\n if (projectFiles.has(tsxPath)) return tsxPath;\n }\n\n // 3. Try adding extensions\n for (const ext of [\".ts\", \".tsx\", \".js\", \".jsx\", \".mjs\", \".cjs\"]) {\n if (projectFiles.has(resolved + ext)) return resolved + ext;\n }\n\n // 4. Try as directory with index file\n for (const idx of [\"/index.ts\", \"/index.tsx\", \"/index.js\", \"/index.jsx\"]) {\n if (projectFiles.has(resolved + idx)) return resolved + idx;\n }\n\n return null;\n },\n defaultExclude: [\"node_modules\", \"\\\\.d\\\\.ts$\", \"dist\", \"build\", \"coverage\"],\n};\n\n// ─── Registry ────────────────────────────────────────\nconst LANGUAGE_CONFIGS: Record<LanguageId, LanguageConfig | null> = {\n javascript,\n python,\n rust,\n go,\n java,\n \"c-cpp\": cCpp,\n \"c-sharp\": cSharp,\n ruby,\n php,\n swift,\n kotlin,\n dart,\n scala,\n};\n\nexport function getLanguageConfig(id: LanguageId): LanguageConfig | null {\n return LANGUAGE_CONFIGS[id] ?? null;\n}\n\nexport function getAllLanguageIds(): LanguageId[] {\n return [...LANGUAGE_IDS];\n}\n","import type { DependencyGraph } from \"../../types/schema.js\";\n\n/** Single source of truth for supported language IDs */\nexport const LANGUAGE_IDS = [\n \"javascript\",\n \"python\",\n \"rust\",\n \"go\",\n \"java\",\n \"c-cpp\",\n \"c-sharp\",\n \"ruby\",\n \"php\",\n \"swift\",\n \"kotlin\",\n \"dart\",\n \"scala\",\n] as const;\n\nexport type LanguageId = (typeof LANGUAGE_IDS)[number];\n\nexport interface AnalyzerEngine {\n analyze(\n rootDir: string,\n options: { exclude?: string[]; maxDepth?: number },\n ): Promise<DependencyGraph>;\n}\n\nexport interface ImportPattern {\n regex: RegExp;\n}\n\nexport type ImportResolver = (\n importPath: string,\n sourceFile: string,\n rootDir: string,\n projectFiles: Set<string>,\n) => string | null;\n\nexport type CommentStyle =\n | \"c-style\" // // and /* */\n | \"hash\" // #\n | \"python\" // # and \"\"\" / '''\n | \"ruby\" // # and =begin/=end\n | \"php\"; // //, /* */, and #\n\nexport interface LanguageConfig {\n id: LanguageId;\n extensions: string[];\n importPatterns: ImportPattern[];\n resolveImport: ImportResolver;\n commentStyle: CommentStyle;\n defaultExclude?: string[];\n /** Custom import extractor for languages with complex syntax (e.g. Rust grouped use, C# class references) */\n extractImports?: (\n content: string,\n filePath: string,\n rootDir: string,\n projectFiles: Set<string>,\n ) => string[];\n}\n","export type Locale = \"en\" | \"ja\";\n\nlet currentLocale: Locale = detectLocale();\n\n/** Get the current locale */\nexport function getLocale(): Locale {\n return currentLocale;\n}\n\n/** Set the locale explicitly */\nexport function setLocale(locale: Locale): void {\n currentLocale = locale;\n}\n\n/** Auto-detect locale from LANG/LC_ALL environment variable */\nfunction detectLocale(): Locale {\n const env = process.env.LC_ALL || process.env.LANG || \"\";\n if (env.startsWith(\"ja\")) return \"ja\";\n return \"en\";\n}\n\n/** Get a translated message by key */\nexport function t(key: string, vars?: Record<string, string | number>): string {\n const messages = currentLocale === \"ja\" ? ja : en;\n let msg = (messages as Record<string, string>)[key] ?? (en as Record<string, string>)[key] ?? key;\n\n if (vars) {\n for (const [k, v] of Object.entries(vars)) {\n msg = msg.replaceAll(`{${k}}`, String(v));\n }\n }\n return msg;\n}\n\n// ─── Message catalogs ───────────────────────────────────────────\n\nconst en = {\n // Analyzer\n \"analyzer.failed\": \"dependency-cruiser failed: {message}\",\n \"analyzer.exitCode\": \"Analysis finished with error code {code}\",\n\n // Storage\n \"storage.parseFailed\": \"Failed to parse snapshot.json. File may be corrupted: {path}\",\n \"storage.readFailed\": \"Failed to read snapshot.json: {path}\",\n \"storage.invalidSchema\": \"snapshot.json schema is invalid. Please regenerate with `archtracker init`:\\n{issues}\",\n \"storage.versionMismatch\": \"snapshot.json version ({version}) is incompatible with current schema ({expected}). Regenerate with `archtracker init`.\",\n\n // Path guard\n \"pathGuard.traversal\": \"Path points outside project root: \\\"{input}\\\" → \\\"{resolved}\\\" (allowed: \\\"{boundary}\\\")\",\n\n // Diff report\n \"diff.title\": \"# Architecture Change Report\\n\",\n \"diff.noChanges\": \"No changes — snapshot matches current code.\\n\",\n \"diff.added\": \"## Added Files ({count})\",\n \"diff.removed\": \"## Removed Files ({count})\",\n \"diff.modified\": \"## Modified Dependencies ({count})\",\n \"diff.affected\": \"## Files Requiring Review ({count})\",\n \"diff.reasonRemoved\": \"Dependency \\\"{file}\\\" was removed\",\n \"diff.reasonModified\": \"Dependency \\\"{file}\\\" had its dependencies changed\",\n \"diff.reasonAdded\": \"New dependency \\\"{file}\\\" was added\",\n \"diff.testSummary\": \" ... and {count} test/fixture file(s)\",\n \"diff.testAffectedSummary\": \" ... and {count} test/fixture-related review(s)\",\n\n // Search\n \"search.pathMatch\": \"Path matches \\\"{pattern}\\\"\",\n \"search.affected\": \"May be affected by changes to \\\"{file}\\\" (via: {via})\",\n \"search.critical\": \"{count} files depend on this component\",\n \"search.orphan\": \"Orphan file (no dependencies, no dependents)\",\n \"search.noResults\": \"No results: \\\"{query}\\\" (mode: {mode})\",\n \"search.results\": \"Results: {count} (mode: {mode})\",\n\n // CLI\n \"cli.analyzing\": \"Analyzing...\",\n \"cli.snapshotSaved\": \"Snapshot saved\",\n \"cli.timestamp\": \" Timestamp: {ts}\",\n \"cli.fileCount\": \" Files: {count}\",\n \"cli.edgeCount\": \" Edges: {count}\",\n \"cli.circularCount\": \" Circular deps: {count}\",\n \"cli.keyComponents\": \"\\nKey components:\",\n \"cli.dependedBy\": \"{path} ({count} dependents)\",\n \"cli.noSnapshot\": \"No snapshot found. Run `archtracker init` first.\",\n \"cli.ciFailed\": \"\\nCI check failed: {count} file(s) require review\",\n \"cli.autoGenerating\": \"No snapshot found, auto-generating...\",\n \"cli.project\": \"Project: {path}\",\n \"cli.validPaths\": \"\\nValid file paths:\",\n \"cli.snapshot\": \"Snapshot: {ts}\",\n\n // MCP\n \"mcp.analyzeComplete\": \"Analysis complete: {files} files, {edges} edges\",\n \"mcp.circularFound\": \"Circular deps: {count} found\",\n \"mcp.circularNone\": \"Circular deps: none\",\n \"mcp.snapshotSaved\": \"Snapshot saved\",\n \"mcp.autoInit\": \"No snapshot existed. Initial snapshot auto-generated.\",\n \"mcp.nextCheckEnabled\": \"Diff checking will be active from the next run.\",\n \"mcp.queryRequired\": \"\\\"{mode}\\\" mode requires the query parameter\",\n\n // Analyze report\n \"analyze.title\": \"# Architecture Analysis Report\\n\",\n \"analyze.overview\": \"## Overview\",\n \"analyze.totalFiles\": \" Total files: {count}\",\n \"analyze.totalEdges\": \" Total edges: {count}\",\n \"analyze.totalCircular\": \" Circular dependencies: {count}\",\n \"analyze.criticalTitle\": \"\\n## Critical Components (Top {count})\",\n \"analyze.criticalItem\": \" {path} ({count} dependents)\",\n \"analyze.circularTitle\": \"\\n## Circular Dependencies ({count})\",\n \"analyze.circularItem\": \" {files}\",\n \"analyze.orphanTitle\": \"\\n## Orphan Files ({count})\",\n \"analyze.couplingTitle\": \"\\n## High Coupling (Top {count} by import count)\",\n \"analyze.couplingItem\": \" {path} ({count} imports)\",\n \"analyze.layerTitle\": \"\\n## Directory Breakdown\",\n \"analyze.layerItem\": \" {dir}/ — {count} files\",\n \"analyze.noIssues\": \"\\nNo architectural issues detected.\",\n \"analyze.snapshotSaved\": \"\\nSnapshot saved alongside analysis.\",\n\n // CI\n \"ci.generated\": \"GitHub Actions workflow generated: {path}\",\n\n // Layers\n \"layers.alreadyExists\": \"layers.json already exists. Edit it manually to modify.\",\n \"layers.created\": \"Created .archtracker/layers.json — edit it to configure your layers.\",\n \"layers.notFound\": \"No .archtracker/layers.json found. Run 'archtracker layers init' to create one.\",\n \"layers.header\": \"Configured layers ({count}):\",\n\n // Web viewer\n \"web.starting\": \"Starting architecture viewer...\",\n \"web.listening\": \"Architecture graph available at: http://localhost:{port}\",\n \"web.stop\": \"Press Ctrl+C to stop\",\n \"web.watching\": \"Watching {dir}/ for changes...\",\n \"web.reloading\": \"File change detected, reloading...\",\n \"web.reloaded\": \"Graph reloaded\",\n\n // History\n \"history.title\": \"# Snapshot History\\n\",\n \"history.empty\": \"No snapshots found. Run `archtracker init` to create one.\",\n \"history.entry\": \" {ts} | {files} files, {edges} edges, {circular} circular{layers}\",\n \"history.count\": \"{count} snapshot(s) recorded\",\n \"history.snapshotNotFound\": \"Snapshot not found for timestamp: {ts}\",\n\n // Errors\n \"error.analyzer\": \"[Analysis Error] {message}\",\n \"error.storage\": \"[Storage Error] {message}\",\n \"error.pathTraversal\": \"[Security Error] {message}\",\n \"error.generic\": \"[Error] {message}\",\n \"error.unexpected\": \"[Error] Unexpected error: {message}\",\n \"error.cli.analyzer\": \"Analysis error: {message}\",\n \"error.cli.storage\": \"Storage error: {message}\",\n \"error.cli.generic\": \"Error: {message}\",\n \"error.cli.unexpected\": \"Unexpected error: {message}\",\n} as const;\n\nconst ja = {\n // Analyzer\n \"analyzer.failed\": \"dependency-cruiser の実行に失敗しました: {message}\",\n \"analyzer.exitCode\": \"解析がエラーコード {code} で終了しました\",\n\n // Storage\n \"storage.parseFailed\": \"snapshot.json のパースに失敗しました。ファイルが破損している可能性があります: {path}\",\n \"storage.readFailed\": \"snapshot.json の読み取りに失敗しました: {path}\",\n \"storage.invalidSchema\": \"snapshot.json のスキーマが不正です。archtracker init で再生成してください:\\n{issues}\",\n \"storage.versionMismatch\": \"snapshot.json のバージョン ({version}) が現在のスキーマ ({expected}) と互換性がありません。archtracker init で再生成してください。\",\n\n // Path guard\n \"pathGuard.traversal\": \"パスがプロジェクトルートの外部を指しています: \\\"{input}\\\" → \\\"{resolved}\\\" (許可範囲: \\\"{boundary}\\\")\",\n\n // Diff report\n \"diff.title\": \"# アーキテクチャ変更レポート\\n\",\n \"diff.noChanges\": \"変更なし — スナップショットと現在のコードは一致しています。\\n\",\n \"diff.added\": \"## 追加されたファイル ({count}件)\",\n \"diff.removed\": \"## 削除されたファイル ({count}件)\",\n \"diff.modified\": \"## 依存関係が変更されたファイル ({count}件)\",\n \"diff.affected\": \"## 確認が必要なファイル ({count}件)\",\n \"diff.reasonRemoved\": \"依存先 \\\"{file}\\\" が削除されました\",\n \"diff.reasonModified\": \"依存先 \\\"{file}\\\" の依存関係が変更されました\",\n \"diff.reasonAdded\": \"新しい依存先 \\\"{file}\\\" が追加されました\",\n \"diff.testSummary\": \" ... 他 {count}件のテスト/フィクスチャファイル\",\n \"diff.testAffectedSummary\": \" ... 他 {count}件のテスト/フィクスチャ関連の確認項目\",\n\n // Search\n \"search.pathMatch\": \"パスが \\\"{pattern}\\\" にマッチ\",\n \"search.affected\": \"\\\"{file}\\\" の変更により影響を受ける可能性(経由: {via})\",\n \"search.critical\": \"{count}件のファイルが依存する重要コンポーネント\",\n \"search.orphan\": \"孤立ファイル(依存なし・被依存なし)\",\n \"search.noResults\": \"検索結果なし: \\\"{query}\\\" (モード: {mode})\",\n \"search.results\": \"検索結果: {count}件 (モード: {mode})\",\n\n // CLI\n \"cli.analyzing\": \"解析中...\",\n \"cli.snapshotSaved\": \"スナップショットを保存しました\",\n \"cli.timestamp\": \" タイムスタンプ: {ts}\",\n \"cli.fileCount\": \" ファイル数: {count}\",\n \"cli.edgeCount\": \" エッジ数: {count}\",\n \"cli.circularCount\": \" 循環参照: {count}件\",\n \"cli.keyComponents\": \"\\n主要コンポーネント:\",\n \"cli.dependedBy\": \"{path} ({count}件が依存)\",\n \"cli.noSnapshot\": \"スナップショットが見つかりません。`archtracker init` を先に実行してください。\",\n \"cli.ciFailed\": \"\\nCI チェック失敗: {count}件の要確認ファイルがあります\",\n \"cli.autoGenerating\": \"スナップショットが無いため自動生成します...\",\n \"cli.project\": \"プロジェクト: {path}\",\n \"cli.validPaths\": \"\\n有効なファイルパス:\",\n \"cli.snapshot\": \"スナップショット: {ts}\",\n\n // MCP\n \"mcp.analyzeComplete\": \"解析完了: {files}ファイル, {edges}エッジ\",\n \"mcp.circularFound\": \"循環参照: {count}件検出\",\n \"mcp.circularNone\": \"循環参照: なし\",\n \"mcp.snapshotSaved\": \"スナップショットを保存しました\",\n \"mcp.autoInit\": \"スナップショットが存在しなかったため、初期スナップショットを自動生成しました。\",\n \"mcp.nextCheckEnabled\": \"次回の実行時から差分チェックが有効になります。\",\n \"mcp.queryRequired\": \"\\\"{mode}\\\" モードでは query パラメータが必須です\",\n\n // Analyze report\n \"analyze.title\": \"# アーキテクチャ分析レポート\\n\",\n \"analyze.overview\": \"## 概要\",\n \"analyze.totalFiles\": \" 総ファイル数: {count}\",\n \"analyze.totalEdges\": \" 総エッジ数: {count}\",\n \"analyze.totalCircular\": \" 循環参照: {count}件\",\n \"analyze.criticalTitle\": \"\\n## 重要コンポーネント (上位{count}件)\",\n \"analyze.criticalItem\": \" {path} ({count}件が依存)\",\n \"analyze.circularTitle\": \"\\n## 循環参照 ({count}件)\",\n \"analyze.circularItem\": \" {files}\",\n \"analyze.orphanTitle\": \"\\n## 孤立ファイル ({count}件)\",\n \"analyze.couplingTitle\": \"\\n## 高結合ファイル (import数 上位{count}件)\",\n \"analyze.couplingItem\": \" {path} ({count}件をimport)\",\n \"analyze.layerTitle\": \"\\n## ディレクトリ構成\",\n \"analyze.layerItem\": \" {dir}/ — {count}ファイル\",\n \"analyze.noIssues\": \"\\nアーキテクチャ上の問題は検出されませんでした。\",\n \"analyze.snapshotSaved\": \"\\n分析と同時にスナップショットを保存しました。\",\n\n // CI\n \"ci.generated\": \"GitHub Actions ワークフローを生成しました: {path}\",\n\n // Layers\n \"layers.alreadyExists\": \"layers.json は既に存在します。直接編集してください。\",\n \"layers.created\": \".archtracker/layers.json を作成しました。レイヤーを設定してください。\",\n \"layers.notFound\": \".archtracker/layers.json が見つかりません。'archtracker layers init' で作成してください。\",\n \"layers.header\": \"設定済みレイヤー ({count}件):\",\n\n // Web viewer\n \"web.starting\": \"アーキテクチャビューアーを起動中...\",\n \"web.listening\": \"アーキテクチャグラフ: http://localhost:{port}\",\n \"web.stop\": \"Ctrl+C で停止\",\n \"web.watching\": \"{dir}/ を監視中...\",\n \"web.reloading\": \"ファイル変更を検出、リロード中...\",\n \"web.reloaded\": \"グラフを更新しました\",\n\n // History\n \"history.title\": \"# スナップショット履歴\\n\",\n \"history.empty\": \"スナップショットが見つかりません。`archtracker init` で作成してください。\",\n \"history.entry\": \" {ts} | {files}ファイル, {edges}エッジ, 循環{circular}件{layers}\",\n \"history.count\": \"{count}件のスナップショットを記録\",\n \"history.snapshotNotFound\": \"指定のタイムスタンプのスナップショットが見つかりません: {ts}\",\n\n // Errors\n \"error.analyzer\": \"[解析エラー] {message}\",\n \"error.storage\": \"[ストレージエラー] {message}\",\n \"error.pathTraversal\": \"[セキュリティエラー] {message}\",\n \"error.generic\": \"[エラー] {message}\",\n \"error.unexpected\": \"[エラー] 予期しないエラーが発生しました: {message}\",\n \"error.cli.analyzer\": \"解析エラー: {message}\",\n \"error.cli.storage\": \"ストレージエラー: {message}\",\n \"error.cli.generic\": \"エラー: {message}\",\n \"error.cli.unexpected\": \"予期しないエラー: {message}\",\n} as const;\n","import type { DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\n/**\n * Generate a comprehensive architecture analysis report.\n *\n * Designed for onboarding into existing projects — provides\n * a full overview of dependencies, critical components, circular\n * references, orphan files, coupling hotspots, and directory breakdown.\n */\nexport function formatAnalysisReport(\n graph: DependencyGraph,\n options: { topN?: number } = {},\n): string {\n const topN = options.topN ?? 10;\n const lines: string[] = [];\n const files = Object.values(graph.files);\n\n // ─── Overview ────────────────────────────────────────────\n lines.push(t(\"analyze.title\"));\n lines.push(t(\"analyze.overview\"));\n lines.push(t(\"analyze.totalFiles\", { count: graph.totalFiles }));\n lines.push(t(\"analyze.totalEdges\", { count: graph.totalEdges }));\n lines.push(t(\"analyze.totalCircular\", { count: graph.circularDependencies.length }));\n\n // ─── Critical Components ─────────────────────────────────\n const critical = files\n .filter((f) => f.dependents.length > 0)\n .sort((a, b) => b.dependents.length - a.dependents.length)\n .slice(0, topN);\n\n if (critical.length > 0) {\n lines.push(t(\"analyze.criticalTitle\", { count: critical.length }));\n for (const f of critical) {\n lines.push(t(\"analyze.criticalItem\", { path: f.path, count: f.dependents.length }));\n }\n }\n\n // ─── Circular Dependencies ──────────────────────────────\n if (graph.circularDependencies.length > 0) {\n lines.push(t(\"analyze.circularTitle\", { count: graph.circularDependencies.length }));\n for (const c of graph.circularDependencies) {\n lines.push(t(\"analyze.circularItem\", { files: c.cycle.join(\" → \") }));\n }\n }\n\n // ─── High Coupling (most imports) ───────────────────────\n const highCoupling = files\n .filter((f) => f.dependencies.length > 0)\n .sort((a, b) => b.dependencies.length - a.dependencies.length)\n .slice(0, topN);\n\n if (highCoupling.length > 0) {\n lines.push(t(\"analyze.couplingTitle\", { count: highCoupling.length }));\n for (const f of highCoupling) {\n lines.push(t(\"analyze.couplingItem\", { path: f.path, count: f.dependencies.length }));\n }\n }\n\n // ─── Orphan Files ───────────────────────────────────────\n const orphans = files.filter(\n (f) => f.dependents.length === 0 && f.dependencies.length === 0,\n );\n\n if (orphans.length > 0) {\n lines.push(t(\"analyze.orphanTitle\", { count: orphans.length }));\n for (const f of orphans) {\n lines.push(` ${f.path}`);\n }\n }\n\n // ─── Directory Breakdown ────────────────────────────────\n const dirCounts = new Map<string, number>();\n for (const f of files) {\n const dir = f.path.includes(\"/\") ? f.path.substring(0, f.path.lastIndexOf(\"/\")) : \".\";\n dirCounts.set(dir, (dirCounts.get(dir) ?? 0) + 1);\n }\n\n if (dirCounts.size > 1) {\n lines.push(t(\"analyze.layerTitle\"));\n const sorted = [...dirCounts.entries()].sort((a, b) => b[1] - a[1]);\n for (const [dir, count] of sorted) {\n lines.push(t(\"analyze.layerItem\", { dir, count }));\n }\n }\n\n // ─── Summary ────────────────────────────────────────────\n if (graph.circularDependencies.length === 0 && orphans.length === 0) {\n lines.push(t(\"analyze.noIssues\"));\n }\n\n return lines.join(\"\\n\");\n}\n","import { resolve, join } from \"node:path\";\nimport { readFileSync } from \"node:fs\";\nimport type {\n DependencyGraph,\n DependencyEdge,\n FileNode,\n LayerMetadata,\n MultiLayerGraph,\n} from \"../types/schema.js\";\nimport type { LayerDefinition, CrossLayerConnection } from \"../types/layers.js\";\nimport { analyzeProject } from \"./analyze.js\";\nimport { detectLanguage } from \"./engines/detect.js\";\n\nconst LAYER_COLORS = [\n \"#58a6ff\",\n \"#3fb950\",\n \"#d2a8ff\",\n \"#f0883e\",\n \"#79c0ff\",\n \"#56d4dd\",\n \"#db61a2\",\n \"#f778ba\",\n \"#ffa657\",\n \"#7ee787\",\n];\n\n/**\n * Analyze multiple layers and produce a MultiLayerGraph.\n *\n * Each layer is analyzed independently via analyzeProject().\n * Results are merged into a unified graph with layer-prefixed node paths.\n */\nexport async function analyzeMultiLayer(\n projectRoot: string,\n layerDefs: LayerDefinition[],\n globalExclude?: string[],\n): Promise<MultiLayerGraph> {\n const layers: Record<string, DependencyGraph> = {};\n const layerMetadata: LayerMetadata[] = [];\n\n for (let idx = 0; idx < layerDefs.length; idx++) {\n const def = layerDefs[idx];\n const targetDir = resolve(projectRoot, def.targetDir);\n\n const graph = await analyzeProject(targetDir, {\n exclude: def.exclude ?? globalExclude,\n language: def.language,\n });\n\n // Detect actual language used (may have been auto-detected)\n const language = def.language ?? (await detectLanguage(targetDir)) ?? \"javascript\";\n\n layers[def.name] = graph;\n\n layerMetadata.push({\n name: def.name,\n originalRootDir: graph.rootDir,\n language,\n color: def.color ?? LAYER_COLORS[idx % LAYER_COLORS.length],\n description: def.description,\n fileCount: graph.totalFiles,\n edgeCount: graph.totalEdges,\n });\n }\n\n const merged = mergeLayerGraphs(projectRoot, layers);\n\n return { layers, layerMetadata, merged };\n}\n\n/**\n * Auto-detect cross-layer connections by scanning file contents\n * for references to identifiers defined in other layers.\n *\n * Strategies:\n * 1. Unique type-name matching — PascalCase/snake_case names unique to exactly\n * one target layer, with context validation (not self-definitions, not\n * local imports, not ambiguous across layers).\n * 2. Typed reference patterns — \"StorageClient\", \"AuthService\", etc.\n * 3. Cross-layer import/URL patterns — import paths or URL patterns referencing\n * another layer's directory, with strict context for short names.\n *\n * Results are deduplicated to max 1 connection per (sourceLayer → targetLayer) pair,\n * keeping the highest-scoring match. A minimum score threshold is enforced.\n */\nexport function detectCrossLayerConnections(\n layers: Record<string, DependencyGraph>,\n layerDefs: LayerDefinition[],\n): CrossLayerConnection[] {\n const MIN_NAME_LENGTH = 6;\n const MIN_SCORE_THRESHOLD = 10;\n\n // Build identifier map per layer: { layerName → { identifierName → filePath } }\n const layerIdentifiers = new Map<string, Map<string, string>>();\n for (const [layerName, graph] of Object.entries(layers)) {\n const identifiers = new Map<string, string>();\n for (const filePath of Object.keys(graph.files)) {\n const basename = filePath.split(\"/\").pop()!;\n const nameNoExt = basename.replace(/\\.[^.]+$/, \"\");\n if (nameNoExt.length < MIN_NAME_LENGTH || GENERIC_BASENAMES.has(nameNoExt.toLowerCase())) continue;\n identifiers.set(nameNoExt, filePath);\n }\n layerIdentifiers.set(layerName, identifiers);\n }\n\n // Pre-compute: names that exist in 2+ layers → ambiguous, skip\n const nameLayerCount = new Map<string, number>();\n for (const [, ids] of layerIdentifiers) {\n for (const name of ids.keys()) {\n nameLayerCount.set(name, (nameLayerCount.get(name) ?? 0) + 1);\n }\n }\n\n // Track best connection per (source→target) layer pair, with score\n const pairBest = new Map<string, { conn: CrossLayerConnection; score: number }>();\n\n function tryAdd(pairKey: string, conn: CrossLayerConnection, score: number) {\n if (score < MIN_SCORE_THRESHOLD) return; // discard low-quality\n const existing = pairBest.get(pairKey);\n if (!existing || score > existing.score) {\n pairBest.set(pairKey, { conn, score });\n }\n }\n\n // Regex for detecting self-definitions (class Foo, struct Foo, etc.)\n function isSelfDefined(content: string, name: string): boolean {\n const defPatterns = [\n new RegExp(`\\\\b(?:class|struct|enum|interface|protocol|type|object)\\\\s+${escapeRegex(name)}\\\\b`),\n new RegExp(`\\\\b(?:def|func|fun|fn)\\\\s+${escapeRegex(name)}\\\\b`),\n new RegExp(`\\\\b${escapeRegex(name)}\\\\s*=\\\\s*(?:class|struct|type|interface)\\\\b`),\n ];\n return defPatterns.some((re) => re.test(content));\n }\n\n // Check if a name match appears only in local import context (not cross-layer)\n function isLocalImportOnly(content: string, name: string): boolean {\n const regex = new RegExp(`\\\\b${escapeRegex(name)}\\\\b`, \"g\");\n const lines = content.split(\"\\n\");\n let crossLayerRef = false;\n for (const line of lines) {\n if (!regex.test(line)) continue;\n regex.lastIndex = 0;\n // Local import patterns: from . / from .. / require('./ / import ./ / #include \"\n const isLocalImport = /^\\s*(?:from\\s+[.'\"]|import\\s+[.'\"]|require\\s*\\(\\s*['\"][.\\/]|#include\\s*\")/.test(line);\n if (!isLocalImport) {\n crossLayerRef = true;\n break;\n }\n }\n return !crossLayerRef;\n }\n\n for (const [sourceLayer, graph] of Object.entries(layers)) {\n const ownNames = layerIdentifiers.get(sourceLayer) ?? new Map();\n\n for (const filePath of Object.keys(graph.files)) {\n const absPath = join(graph.rootDir, filePath);\n let content: string;\n try {\n content = readFileSync(absPath, \"utf-8\");\n } catch { continue; }\n\n // Strategy 1: File-name matching across layers\n for (const [targetLayer, targetIds] of layerIdentifiers) {\n if (targetLayer === sourceLayer) continue;\n for (const [targetName, targetFile] of targetIds) {\n // Skip if name exists in source layer (own concept)\n if (ownNames.has(targetName)) continue;\n // Skip if name exists in 2+ layers (ambiguous — e.g. ProfileScreen in both Mobile and Android)\n if ((nameLayerCount.get(targetName) ?? 0) > 1) continue;\n if (!content.includes(targetName)) continue;\n const regex = new RegExp(`\\\\b${escapeRegex(targetName)}\\\\b`);\n if (!regex.test(content)) continue;\n // Skip if source file defines this name itself (e.g. class AuthService in auth_service.dart)\n if (isSelfDefined(content, targetName)) continue;\n // Skip if only appears in local import context (from ./foo import bar)\n if (isLocalImportOnly(content, targetName)) continue;\n\n const pairKey = `${sourceLayer}→${targetLayer}`;\n // Score: longer + PascalCase names are more specific\n const isPascalCase = /^[A-Z][a-z]/.test(targetName);\n const baseScore = targetName.length + (isPascalCase ? 5 : 0);\n tryAdd(pairKey, {\n fromLayer: sourceLayer,\n fromFile: filePath,\n toLayer: targetLayer,\n toFile: targetFile,\n type: \"auto\",\n label: targetName,\n }, baseScore);\n }\n }\n\n // Strategy 2: Typed reference patterns (StorageClient, AuthService, etc.)\n for (const def of layerDefs) {\n if (def.name === sourceLayer) continue;\n const pairKey = `${sourceLayer}→${def.name}`;\n const layerName = def.name;\n const suffixes = [\"Client\", \"Service\", \"API\", \"Handler\", \"Provider\", \"Manager\", \"Gateway\", \"Proxy\", \"Adapter\", \"Connector\"];\n const typedRe = new RegExp(`\\\\b${escapeRegex(layerName)}(?:${suffixes.join(\"|\")})\\\\b`);\n if (typedRe.test(content)) {\n const targetGraph = layers[def.name];\n if (!targetGraph) continue;\n const entryFile = findEntryPoint(targetGraph);\n if (entryFile) {\n tryAdd(pairKey, {\n fromLayer: sourceLayer,\n fromFile: filePath,\n toLayer: def.name,\n toFile: entryFile,\n type: \"auto\",\n label: `${layerName}*`,\n }, 25);\n }\n }\n }\n\n // Strategy 3: Cross-layer import/URL patterns\n for (const def of layerDefs) {\n if (def.name === sourceLayer) continue;\n const pairKey = `${sourceLayer}→${def.name}`;\n const dirName = def.targetDir.split(\"/\").pop()!;\n const isShortName = dirName.length <= 4;\n\n const patterns: { re: RegExp; score: number }[] = [];\n\n if (!isShortName) {\n // Longer dir names: moderate context needed\n // Import context: from <dir> import / require('<dir>/...')\n patterns.push({ re: new RegExp(`(?:from|require|import)\\\\s+['\"].*\\\\b${escapeRegex(dirName)}\\\\b`, \"i\"), score: 15 });\n // URL path: /<dir>/\n patterns.push({ re: new RegExp(`['\"\\`/]${escapeRegex(dirName)}/[\\\\w]`, \"i\"), score: 12 });\n } else {\n // Short dir names (api, auth, cms, ios): require strict context\n // Must appear in import/require with path separator\n patterns.push({ re: new RegExp(`(?:from|require|import)\\\\s+['\"].*/${escapeRegex(dirName)}/`, \"i\"), score: 13 });\n // URL path: must have leading / and trailing /\n patterns.push({ re: new RegExp(`['\"\\`]\\\\s*(?:https?://[^'\"]*)?/${escapeRegex(dirName)}/[\\\\w]`, \"i\"), score: 11 });\n }\n\n for (const { re, score } of patterns) {\n if (re.test(content)) {\n const targetGraph = layers[def.name];\n if (!targetGraph) continue;\n const entryFile = findEntryPoint(targetGraph);\n if (entryFile) {\n tryAdd(pairKey, {\n fromLayer: sourceLayer,\n fromFile: filePath,\n toLayer: def.name,\n toFile: entryFile,\n type: \"auto\",\n label: `→ ${def.name}`,\n }, score);\n }\n break;\n }\n }\n }\n }\n }\n\n return [...pairBest.values()].map((v) => v.conn);\n}\n\nfunction escapeRegex(s: string): string {\n return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\n/** Find the most likely entry point file in a layer's graph */\nfunction findEntryPoint(graph: DependencyGraph): string | null {\n const files = Object.values(graph.files);\n if (files.length === 0) return null;\n\n // Prefer files with most dependents (highest fan-in)\n const sorted = files.sort((a, b) => b.dependents.length - a.dependents.length);\n if (sorted[0].dependents.length > 0) return sorted[0].path;\n\n // Fallback: common entry point names\n const entryNames = [\"main\", \"index\", \"app\", \"server\", \"lib\", \"mod\"];\n for (const name of entryNames) {\n const entry = files.find((f) => {\n const basename = f.path.split(\"/\").pop()!.replace(/\\.[^.]+$/, \"\").toLowerCase();\n return basename === name;\n });\n if (entry) return entry.path;\n }\n\n return files[0].path;\n}\n\n/** Generic basenames that would cause too many false positives */\nconst GENERIC_BASENAMES = new Set([\n // Build / project structure\n \"index\", \"main\", \"app\", \"config\", \"setup\", \"init\", \"mod\", \"package\",\n \"build\", \"makefile\", \"dockerfile\", \"rakefile\", \"gemfile\", \"podfile\",\n // Common modules\n \"utils\", \"helpers\", \"types\", \"models\", \"views\", \"controllers\", \"services\",\n \"lib\", \"src\", \"test\", \"spec\", \"tests\", \"bench\", \"example\", \"examples\",\n // Infrastructure / patterns\n \"server\", \"client\", \"routes\", \"middleware\", \"database\", \"engine\",\n \"error\", \"errors\", \"logger\", \"logging\", \"constants\", \"common\", \"base\",\n \"core\", \"data\", \"manager\", \"handler\", \"factory\", \"context\", \"state\",\n \"store\", \"cache\", \"queue\", \"task\", \"worker\", \"adapter\", \"bridge\",\n // UI / presentation\n \"event\", \"events\", \"model\", \"view\", \"home\", \"user\", \"page\", \"layout\",\n \"router\", \"provider\", \"component\", \"widget\", \"screen\", \"template\",\n \"header\", \"footer\", \"sidebar\", \"navbar\", \"dialog\", \"modal\", \"panel\",\n // Data / IO\n \"reader\", \"writer\", \"parser\", \"formatter\", \"serializer\", \"converter\",\n \"loader\", \"exporter\", \"importer\", \"transformer\", \"mapper\", \"reducer\",\n \"filter\", \"sorter\", \"validator\", \"checker\", \"scanner\", \"analyzer\",\n // Auth / Security (generic enough to exist in many layers)\n \"login\", \"register\", \"verify\", \"token\", \"session\", \"credential\",\n \"password\", \"permission\", \"profile\", \"account\", \"settings\",\n // Network / API\n \"request\", \"response\", \"endpoint\", \"controller\", \"service\", \"gateway\",\n \"proxy\", \"connector\", \"socket\", \"channel\", \"stream\", \"pipeline\",\n // Storage / DB\n \"schema\", \"migration\", \"seed\", \"fixture\", \"record\", \"entity\",\n \"repository\", \"storage\", \"driver\", \"connection\", \"pool\",\n // Testing\n \"mock\", \"stub\", \"fake\", \"helper\", \"fixture\", \"factory\",\n]);\n\n/**\n * Merge multiple layer graphs into a single DependencyGraph.\n * Node paths are prefixed: \"LayerName/original/path.ext\"\n */\nfunction mergeLayerGraphs(\n projectRoot: string,\n layers: Record<string, DependencyGraph>,\n): DependencyGraph {\n const mergedFiles: Record<string, FileNode> = {};\n const mergedEdges: DependencyEdge[] = [];\n const mergedCircular: { cycle: string[] }[] = [];\n\n for (const [layerName, graph] of Object.entries(layers)) {\n for (const [origPath, node] of Object.entries(graph.files)) {\n const prefixedPath = `${layerName}/${origPath}`;\n mergedFiles[prefixedPath] = {\n path: prefixedPath,\n exists: node.exists,\n dependencies: node.dependencies.map((d) => `${layerName}/${d}`),\n dependents: node.dependents.map((d) => `${layerName}/${d}`),\n };\n }\n\n for (const edge of graph.edges) {\n mergedEdges.push({\n source: `${layerName}/${edge.source}`,\n target: `${layerName}/${edge.target}`,\n type: edge.type,\n });\n }\n\n for (const circ of graph.circularDependencies) {\n mergedCircular.push({\n cycle: circ.cycle.map((f) => `${layerName}/${f}`),\n });\n }\n }\n\n return {\n rootDir: resolve(projectRoot),\n files: mergedFiles,\n edges: mergedEdges,\n circularDependencies: mergedCircular,\n totalFiles: Object.keys(mergedFiles).length,\n totalEdges: mergedEdges.length,\n };\n}\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport { LANGUAGE_IDS } from \"../analyzer/engines/types.js\";\nimport type { LayerConfig } from \"../types/layers.js\";\n\nconst ARCHTRACKER_DIR = \".archtracker\";\nconst LAYERS_FILE = \"layers.json\";\n\nconst LayerDefinitionSchema = z.object({\n name: z\n .string()\n .min(1)\n .regex(\n /^[a-zA-Z0-9_-]+$/,\n \"Layer name must be alphanumeric (hyphens/underscores allowed)\",\n ),\n targetDir: z.string().min(1),\n language: z.enum(LANGUAGE_IDS).optional(),\n exclude: z.array(z.string()).optional(),\n color: z.string().optional(),\n description: z.string().optional(),\n});\n\nconst CrossLayerConnectionSchema = z.object({\n fromLayer: z.string(),\n fromFile: z.string(),\n toLayer: z.string(),\n toFile: z.string(),\n type: z.enum([\"api-call\", \"event\", \"data-flow\", \"manual\"]),\n label: z.string().optional(),\n});\n\nexport const LayerConfigSchema = z.object({\n version: z.literal(\"1.0\"),\n layers: z\n .array(LayerDefinitionSchema)\n .min(1)\n .refine(\n (layers) => {\n const names = layers.map((l) => l.name);\n return new Set(names).size === names.length;\n },\n { message: \"Layer names must be unique\" },\n ),\n connections: z.array(CrossLayerConnectionSchema).optional(),\n});\n\n/**\n * Load layers.json from .archtracker/ if it exists.\n * Returns null if no config file found.\n */\nexport async function loadLayerConfig(\n projectRoot: string,\n): Promise<LayerConfig | null> {\n const filePath = join(projectRoot, ARCHTRACKER_DIR, LAYERS_FILE);\n\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (error: unknown) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return null;\n }\n throw new Error(`Failed to read ${filePath}`);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error(`Invalid JSON in ${filePath}`);\n }\n\n const result = LayerConfigSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .slice(0, 5)\n .join(\"\\n\");\n throw new Error(`layers.json validation failed:\\n${issues}`);\n }\n\n return result.data as LayerConfig;\n}\n\n/**\n * Save a LayerConfig to .archtracker/layers.json.\n */\nexport async function saveLayerConfig(\n projectRoot: string,\n config: LayerConfig,\n): Promise<void> {\n const dirPath = join(projectRoot, ARCHTRACKER_DIR);\n const filePath = join(dirPath, LAYERS_FILE);\n await mkdir(dirPath, { recursive: true });\n await writeFile(filePath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n","/**\n * Persistent graph cache — stores analysis results in .archtracker/graph.json\n * to avoid full re-analysis on every MCP tool call or CLI command.\n *\n * Cache invalidation: two-phase validation\n * Phase 1 (fast): compare file mtimes — if unchanged, cache is valid\n * Phase 2 (reliable): for files with changed mtimes, compare SHA-256 content hashes\n * This handles `touch` (mtime change without content change) gracefully.\n */\nimport { readFile, writeFile, mkdir, stat } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { readdir } from \"node:fs/promises\";\nimport { createHash } from \"node:crypto\";\nimport type { GraphCache, DependencyGraph, MultiLayerGraph, LayerMetadata } from \"../types/schema.js\";\n\nconst CACHE_FILE = \"graph.json\";\nconst ARCHTRACKER_DIR = \".archtracker\";\n\n/** File fingerprint: mtime for fast-path, hash for reliable comparison */\nexport interface FileFingerprint {\n mtime: number;\n hash: string;\n}\n\n/** Compute SHA-256 hash of file contents */\nasync function hashFile(filePath: string): Promise<string> {\n const content = await readFile(filePath);\n return createHash(\"sha256\").update(content).digest(\"hex\");\n}\n\n/** Collect all source file fingerprints under a directory (recursive) */\nasync function collectFileFingerprints(\n dir: string,\n exclude: string[] = [],\n): Promise<Record<string, FileFingerprint>> {\n const fingerprints: Record<string, FileFingerprint> = {};\n const excludeRegexes = exclude.map((p) => new RegExp(p));\n\n async function walk(currentDir: string): Promise<void> {\n let entries;\n try {\n entries = await readdir(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n const relativePath = fullPath.slice(dir.length + 1);\n\n // Skip hidden dirs, node_modules, and excluded patterns\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n if (excludeRegexes.some((r) => r.test(relativePath))) continue;\n\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.isFile()) {\n try {\n const s = await stat(fullPath);\n const hash = await hashFile(fullPath);\n fingerprints[relativePath] = { mtime: s.mtimeMs, hash };\n } catch {\n // Skip files we can't stat/read\n }\n }\n }\n }\n\n await walk(dir);\n return fingerprints;\n}\n\n/** Save graph cache to .archtracker/graph.json */\nexport async function saveGraphCache(\n projectRoot: string,\n graph: DependencyGraph,\n options: {\n targetDir: string;\n projectRoot: string;\n language?: string;\n exclude?: string[];\n },\n extra?: {\n multiLayer?: MultiLayerGraph;\n layerMetadata?: LayerMetadata[];\n layerDirs?: string[];\n },\n): Promise<void> {\n const absRoot = resolve(projectRoot);\n const dir = join(absRoot, ARCHTRACKER_DIR);\n await mkdir(dir, { recursive: true });\n\n // Multi-layer: collect fingerprints from all layer directories\n // Single-dir: collect from targetDir only\n let fingerprints: Record<string, FileFingerprint>;\n if (extra?.layerDirs?.length) {\n fingerprints = {};\n for (const layerDir of extra.layerDirs) {\n const layerPath = resolve(absRoot, layerDir);\n const layerFp = await collectFileFingerprints(layerPath, options.exclude);\n for (const [key, value] of Object.entries(layerFp)) {\n fingerprints[`${layerDir}/${key}`] = value;\n }\n }\n } else {\n fingerprints = await collectFingerprintsForGraph(absRoot, options);\n }\n\n // Hash layers.json for change detection\n let layersJsonHash: string | undefined;\n try {\n layersJsonHash = await hashFile(join(dir, \"layers.json\"));\n } catch { /* layers.json doesn't exist */ }\n\n // Store both old-format fileMtimes (backward compat) and new fileFingerprints\n const mtimes: Record<string, number> = {};\n for (const [k, v] of Object.entries(fingerprints)) {\n mtimes[k] = v.mtime;\n }\n\n const cache: GraphCache = {\n version: \"1.0\",\n timestamp: new Date().toISOString(),\n options: {\n targetDir: options.targetDir,\n projectRoot: options.projectRoot,\n language: options.language,\n exclude: options.exclude,\n },\n fileMtimes: mtimes,\n fileHashes: Object.fromEntries(\n Object.entries(fingerprints).map(([k, v]) => [k, v.hash]),\n ),\n graph,\n multiLayer: extra?.multiLayer,\n layerMetadata: extra?.layerMetadata,\n layerDirs: extra?.layerDirs,\n layersJsonHash,\n };\n\n await writeFile(join(dir, CACHE_FILE), JSON.stringify(cache), \"utf-8\");\n}\n\n/** Load graph cache from .archtracker/graph.json */\nexport async function loadGraphCache(\n projectRoot: string,\n): Promise<GraphCache | null> {\n const absRoot = resolve(projectRoot);\n const filePath = join(absRoot, ARCHTRACKER_DIR, CACHE_FILE);\n try {\n const raw = await readFile(filePath, \"utf-8\");\n const data = JSON.parse(raw);\n if (data?.version !== \"1.0\" || !data?.graph || !data?.fileMtimes) {\n return null;\n }\n return data as GraphCache;\n } catch {\n return null;\n }\n}\n\n/**\n * Check if graph cache is still valid.\n *\n * Two-phase validation:\n * 1. Fast path: if all file mtimes match, cache is valid (no disk reads needed)\n * 2. Hash path: for files with changed mtimes, compute SHA-256 and compare\n * This correctly handles `touch` (mtime changed but content didn't)\n */\nexport async function isGraphCacheValid(\n cache: GraphCache,\n projectRoot: string,\n options: {\n targetDir: string;\n projectRoot: string;\n language?: string;\n exclude?: string[];\n },\n): Promise<boolean> {\n // Options must match\n if (\n cache.options.targetDir !== options.targetDir ||\n cache.options.projectRoot !== options.projectRoot ||\n (cache.options.language ?? \"\") !== (options.language ?? \"\") ||\n JSON.stringify(cache.options.exclude ?? []) !== JSON.stringify(options.exclude ?? [])\n ) {\n return false;\n }\n\n const absRoot = resolve(projectRoot);\n const targetPath = resolve(absRoot, options.targetDir);\n const isMultiLayer = (cache.layerDirs?.length ?? 0) > 0;\n\n // Check layers.json consistency (detect added/removed/modified layers config)\n let currentLayersHash: string | undefined;\n try {\n currentLayersHash = await hashFile(join(absRoot, ARCHTRACKER_DIR, \"layers.json\"));\n } catch { /* layers.json doesn't exist */ }\n if ((cache.layersJsonHash ?? \"\") !== (currentLayersHash ?? \"\")) return false;\n\n // Collect current mtimes (multi-layer: all layer dirs, single: targetDir only)\n let currentMtimes: Record<string, number>;\n if (isMultiLayer) {\n currentMtimes = {};\n for (const layerDir of cache.layerDirs!) {\n const layerPath = resolve(absRoot, layerDir);\n const dirMtimes = await collectFileMtimes(layerPath, options.exclude);\n for (const [key, value] of Object.entries(dirMtimes)) {\n currentMtimes[`${layerDir}/${key}`] = value;\n }\n }\n } else {\n currentMtimes = await collectFileMtimes(targetPath, options.exclude);\n }\n\n // Check for new or deleted files\n const cachedKeys = new Set(Object.keys(cache.fileMtimes));\n const currentKeys = new Set(Object.keys(currentMtimes));\n if (cachedKeys.size !== currentKeys.size) return false;\n for (const key of cachedKeys) {\n if (!currentKeys.has(key)) return false;\n }\n\n // Phase 1: check mtimes\n const mtimeChanged: string[] = [];\n for (const key of cachedKeys) {\n if (Math.abs(cache.fileMtimes[key] - currentMtimes[key]) > 1) {\n mtimeChanged.push(key);\n }\n }\n\n // All mtimes match — cache is valid (fast path)\n if (mtimeChanged.length === 0) return true;\n\n // Phase 2: for files with changed mtimes, check content hashes\n // If cache has no hashes (old format), fall back to mtime-only → invalid\n if (!cache.fileHashes) return false;\n\n for (const key of mtimeChanged) {\n const cachedHash = cache.fileHashes[key];\n if (!cachedHash) return false; // No hash for this file → can't verify\n\n try {\n // Multi-layer keys include layer dir prefix (e.g., \"frontend/App.ts\")\n // Single-dir keys are relative to targetDir (e.g., \"App.ts\")\n const fullPath = isMultiLayer\n ? join(absRoot, key)\n : join(targetPath, key);\n const currentHash = await hashFile(fullPath);\n if (currentHash !== cachedHash) return false; // Content actually changed\n } catch {\n return false; // Can't read file → assume changed\n }\n }\n\n // All changed-mtime files have same content hash — cache is valid\n return true;\n}\n\n/** Collect mtimes only (fast, no content reading) */\nasync function collectFileMtimes(\n dir: string,\n exclude?: string[],\n): Promise<Record<string, number>> {\n const mtimes: Record<string, number> = {};\n const excludeRegexes = (exclude ?? []).map((p) => new RegExp(p));\n\n async function walk(currentDir: string): Promise<void> {\n let entries;\n try {\n entries = await readdir(currentDir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const fullPath = join(currentDir, entry.name);\n const relativePath = fullPath.slice(dir.length + 1);\n\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n if (excludeRegexes.some((r) => r.test(relativePath))) continue;\n\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.isFile()) {\n try {\n const s = await stat(fullPath);\n mtimes[relativePath] = s.mtimeMs;\n } catch {\n // Skip\n }\n }\n }\n }\n\n await walk(dir);\n return mtimes;\n}\n\n/** Collect fingerprints for files relevant to a graph analysis */\nasync function collectFingerprintsForGraph(\n absRoot: string,\n options: {\n targetDir: string;\n exclude?: string[];\n },\n): Promise<Record<string, FileFingerprint>> {\n const targetPath = resolve(absRoot, options.targetDir);\n return collectFileFingerprints(targetPath, options.exclude);\n}\n","/**\n * Shared graph resolution logic for CLI and MCP.\n * Centralizes multi-layer auto-detection to avoid duplicated code.\n * Integrates persistent graph cache for fast repeated access.\n */\nimport { analyzeProject } from \"./analyze.js\";\nimport { analyzeMultiLayer, detectCrossLayerConnections } from \"./multi-layer.js\";\nimport { loadLayerConfig } from \"../storage/layers.js\";\nimport { loadGraphCache, saveGraphCache, isGraphCacheValid } from \"../storage/graph-cache.js\";\nimport type { DependencyGraph, MultiLayerGraph, LayerMetadata } from \"../types/schema.js\";\nimport type { CrossLayerConnection } from \"../types/layers.js\";\nimport type { LanguageId } from \"./engines/types.js\";\n\nexport interface ResolvedGraph {\n graph: DependencyGraph;\n multiLayer?: MultiLayerGraph;\n layerMetadata?: LayerMetadata[];\n crossLayerEdges?: CrossLayerConnection[];\n /** True if result was served from cache */\n fromCache?: boolean;\n}\n\n/**\n * Resolve a dependency graph. Always checks for layers.json in projectRoot.\n * If layers.json exists, performs multi-layer analysis regardless of how\n * targetDir was specified. Otherwise falls back to single-dir analysis.\n *\n * Uses persistent graph cache (.archtracker/graph.json) to avoid\n * re-analysis when source files haven't changed.\n * Pass `noCache: true` to force a fresh analysis.\n */\nexport async function resolveGraph(opts: {\n targetDir: string;\n projectRoot: string;\n exclude?: string[];\n language?: LanguageId;\n noCache?: boolean;\n}): Promise<ResolvedGraph> {\n // ─── Cache check ───\n if (!opts.noCache) {\n const cache = await loadGraphCache(opts.projectRoot);\n if (cache) {\n const valid = await isGraphCacheValid(cache, opts.projectRoot, {\n targetDir: opts.targetDir,\n projectRoot: opts.projectRoot,\n language: opts.language,\n exclude: opts.exclude,\n });\n if (valid) {\n // Cache hit — reconstruct cross-layer edges from layers.json\n const result: ResolvedGraph = {\n graph: cache.graph,\n multiLayer: cache.multiLayer,\n layerMetadata: cache.layerMetadata,\n fromCache: true,\n };\n if (cache.multiLayer) {\n const layerConfig = await loadLayerConfig(opts.projectRoot);\n if (layerConfig) {\n const autoConnections = detectCrossLayerConnections(\n cache.multiLayer.layers, layerConfig.layers,\n );\n const manualConnections = layerConfig.connections ?? [];\n const manualKeys = new Set(manualConnections.map(\n (c) => `${c.fromLayer}/${c.fromFile}→${c.toLayer}/${c.toFile}`,\n ));\n result.crossLayerEdges = [\n ...manualConnections,\n ...autoConnections.filter(\n (c) => !manualKeys.has(`${c.fromLayer}/${c.fromFile}→${c.toLayer}/${c.toFile}`),\n ),\n ];\n }\n }\n return result;\n }\n }\n }\n\n // ─── Fresh analysis ───\n const layerConfig = await loadLayerConfig(opts.projectRoot);\n let result: ResolvedGraph;\n\n if (layerConfig) {\n const multi = await analyzeMultiLayer(opts.projectRoot, layerConfig.layers, opts.exclude);\n const autoConnections = detectCrossLayerConnections(multi.layers, layerConfig.layers);\n const manualConnections = layerConfig.connections ?? [];\n const manualKeys = new Set(manualConnections.map(\n (c) => `${c.fromLayer}/${c.fromFile}→${c.toLayer}/${c.toFile}`,\n ));\n const merged = [\n ...manualConnections,\n ...autoConnections.filter(\n (c) => !manualKeys.has(`${c.fromLayer}/${c.fromFile}→${c.toLayer}/${c.toFile}`),\n ),\n ];\n result = {\n graph: multi.merged,\n multiLayer: multi,\n layerMetadata: multi.layerMetadata,\n crossLayerEdges: merged,\n };\n } else {\n const graph = await analyzeProject(opts.targetDir, {\n exclude: opts.exclude,\n language: opts.language,\n });\n result = { graph };\n }\n\n // ─── Save cache ───\n try {\n await saveGraphCache(\n opts.projectRoot,\n result.graph,\n {\n targetDir: opts.targetDir,\n projectRoot: opts.projectRoot,\n language: opts.language,\n exclude: opts.exclude,\n },\n {\n multiLayer: result.multiLayer,\n layerMetadata: result.layerMetadata,\n layerDirs: layerConfig?.layers.map((l) => l.targetDir),\n },\n );\n } catch {\n // Cache save failure is non-fatal\n }\n\n return result;\n}\n","import { mkdir, writeFile, readFile, readdir, access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { z } from \"zod\";\nimport type { ArchSnapshot, MultiLayerGraph, SnapshotSummary } from \"../types/schema.js\";\nimport { SCHEMA_VERSION } from \"../types/schema.js\";\nimport type { DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\nconst ARCHTRACKER_DIR = \".archtracker\";\nconst SNAPSHOT_FILE = \"snapshot.json\";\nconst HISTORY_DIR = \"history\";\n\n/** Zod schema for runtime validation of loaded snapshots */\nconst FileNodeSchema = z.object({\n path: z.string(),\n exists: z.boolean(),\n dependencies: z.array(z.string()),\n dependents: z.array(z.string()),\n});\n\nconst DependencyGraphSchema = z.object({\n rootDir: z.string(),\n files: z.record(z.string(), FileNodeSchema),\n edges: z.array(z.object({\n source: z.string(),\n target: z.string(),\n type: z.enum([\"static\", \"dynamic\", \"type-only\"]),\n })),\n circularDependencies: z.array(z.object({ cycle: z.array(z.string()) })),\n totalFiles: z.number(),\n totalEdges: z.number(),\n});\n\nconst SnapshotSchema = z.object({\n version: z.enum([SCHEMA_VERSION, \"1.0\"]),\n timestamp: z.string(),\n rootDir: z.string(),\n graph: DependencyGraphSchema,\n});\n\n/**\n * Save a dependency graph as a versioned snapshot.\n *\n * Creates .archtracker/ directory if it doesn't exist.\n * Writes snapshot.json with schema version and timestamp.\n */\nexport async function saveSnapshot(\n projectRoot: string,\n graph: DependencyGraph,\n multiLayer?: MultiLayerGraph,\n analysisOptions?: {\n targetDir?: string;\n language?: string;\n exclude?: string[];\n },\n): Promise<ArchSnapshot> {\n const dirPath = join(projectRoot, ARCHTRACKER_DIR);\n const filePath = join(dirPath, SNAPSHOT_FILE);\n\n const snapshot: ArchSnapshot = {\n version: SCHEMA_VERSION,\n timestamp: new Date().toISOString(),\n rootDir: graph.rootDir,\n graph,\n ...(multiLayer ? { multiLayer } : {}),\n ...(analysisOptions ? { analysisOptions } : {}),\n };\n\n await mkdir(dirPath, { recursive: true });\n const json = JSON.stringify(snapshot, null, 2);\n await writeFile(filePath, json, \"utf-8\");\n\n // Save to history: .archtracker/history/<safe-timestamp>.json\n try {\n const historyPath = join(dirPath, HISTORY_DIR);\n await mkdir(historyPath, { recursive: true });\n const safeTs = snapshot.timestamp.replace(/[:.]/g, \"-\");\n await writeFile(join(historyPath, `${safeTs}.json`), json, \"utf-8\");\n } catch {\n // History save failure is non-fatal\n }\n\n return snapshot;\n}\n\n/**\n * Load the most recent snapshot from .archtracker/snapshot.json.\n *\n * Returns null if no snapshot exists.\n * Validates schema structure with Zod for data integrity.\n */\nexport async function loadSnapshot(\n projectRoot: string,\n): Promise<ArchSnapshot | null> {\n const filePath = join(projectRoot, ARCHTRACKER_DIR, SNAPSHOT_FILE);\n\n // Read file directly — no TOCTOU race with access() check\n let raw: string;\n try {\n raw = await readFile(filePath, \"utf-8\");\n } catch (error: unknown) {\n if (isNodeError(error) && error.code === \"ENOENT\") {\n return null;\n }\n throw new StorageError(\n t(\"storage.readFailed\", { path: filePath }),\n { cause: error },\n );\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new StorageError(\n t(\"storage.parseFailed\", { path: filePath }),\n );\n }\n\n // Validate structure with Zod\n const result = SnapshotSchema.safeParse(parsed);\n if (!result.success) {\n const issues = result.error.issues\n .map((i) => ` ${i.path.join(\".\")}: ${i.message}`)\n .slice(0, 5)\n .join(\"\\n\");\n throw new StorageError(\n t(\"storage.invalidSchema\", { issues }),\n );\n }\n\n return result.data as ArchSnapshot;\n}\n\n/**\n * List all historical snapshots, newest first.\n * Returns summaries (no full graph data) for lightweight listing.\n */\nexport async function listSnapshots(\n projectRoot: string,\n): Promise<SnapshotSummary[]> {\n const historyPath = join(projectRoot, ARCHTRACKER_DIR, HISTORY_DIR);\n let entries: string[];\n try {\n entries = await readdir(historyPath);\n } catch {\n return [];\n }\n\n const jsonFiles = entries.filter((e) => e.endsWith(\".json\")).sort().reverse();\n const summaries: SnapshotSummary[] = [];\n\n for (const file of jsonFiles) {\n try {\n const raw = await readFile(join(historyPath, file), \"utf-8\");\n const parsed = JSON.parse(raw) as Record<string, unknown>;\n const result = SnapshotSchema.safeParse(parsed);\n if (result.success) {\n const snap = result.data as ArchSnapshot;\n summaries.push({\n timestamp: snap.timestamp,\n totalFiles: snap.graph.totalFiles,\n totalEdges: snap.graph.totalEdges,\n circularDeps: snap.graph.circularDependencies.length,\n hasMultiLayer: \"multiLayer\" in parsed && parsed.multiLayer != null,\n });\n }\n } catch {\n // Skip corrupted history entries\n }\n }\n\n return summaries;\n}\n\n/**\n * Load a specific historical snapshot by timestamp.\n * Matches the closest timestamp if exact match is not found.\n */\nexport async function loadSnapshotByTimestamp(\n projectRoot: string,\n timestamp: string,\n): Promise<ArchSnapshot | null> {\n const historyPath = join(projectRoot, ARCHTRACKER_DIR, HISTORY_DIR);\n let entries: string[];\n try {\n entries = await readdir(historyPath);\n } catch {\n return null;\n }\n\n const jsonFiles = entries.filter((e) => e.endsWith(\".json\")).sort();\n\n // Find exact match first (timestamp may be in filename-safe format)\n const safeTs = timestamp.replace(/[:.]/g, \"-\");\n const exactMatch = jsonFiles.find((f) => f === `${safeTs}.json`);\n if (exactMatch) {\n return loadHistoryFile(join(historyPath, exactMatch));\n }\n\n // Find closest match by prefix\n const prefixMatch = jsonFiles.find((f) => f.startsWith(safeTs.slice(0, 10)));\n if (prefixMatch) {\n return loadHistoryFile(join(historyPath, prefixMatch));\n }\n\n return null;\n}\n\nasync function loadHistoryFile(filePath: string): Promise<ArchSnapshot | null> {\n try {\n const raw = await readFile(filePath, \"utf-8\");\n const parsed = JSON.parse(raw);\n const result = SnapshotSchema.safeParse(parsed);\n if (result.success) return result.data as ArchSnapshot;\n return null;\n } catch {\n return null;\n }\n}\n\n/** Check if .archtracker directory exists */\nexport async function hasArchtrackerDir(\n projectRoot: string,\n): Promise<boolean> {\n try {\n await access(join(projectRoot, ARCHTRACKER_DIR));\n return true;\n } catch {\n return false;\n }\n}\n\n/** Custom error class for storage failures */\nexport class StorageError extends Error {\n constructor(message: string, options?: ErrorOptions) {\n super(message, options);\n this.name = \"StorageError\";\n }\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && \"code\" in error;\n}\n","/**\n * ArchTracker core schema definitions.\n * Snapshot format is versioned for backward compatibility.\n */\n\nexport const SCHEMA_VERSION = \"1.1\" as const;\n\n/** A single dependency edge: source imports target */\nexport interface DependencyEdge {\n source: string;\n target: string;\n type: \"static\" | \"dynamic\" | \"type-only\";\n}\n\n/** A file node in the architecture graph */\nexport interface FileNode {\n path: string;\n exists: boolean;\n dependencies: string[];\n dependents: string[];\n}\n\n/** A detected circular dependency */\nexport interface CircularDependency {\n cycle: string[];\n}\n\n/** The full dependency graph */\nexport interface DependencyGraph {\n rootDir: string;\n files: Record<string, FileNode>;\n edges: DependencyEdge[];\n circularDependencies: CircularDependency[];\n totalFiles: number;\n totalEdges: number;\n}\n\n/** Metadata about a single layer within a multi-layer graph */\nexport interface LayerMetadata {\n name: string;\n originalRootDir: string;\n language: string;\n color: string;\n description?: string;\n fileCount: number;\n edgeCount: number;\n}\n\n/** A multi-layer dependency graph combining multiple single-layer analyses */\nexport interface MultiLayerGraph {\n /** Individual layer graphs (un-prefixed paths, original rootDirs) */\n layers: Record<string, DependencyGraph>;\n /** Layer metadata for rendering */\n layerMetadata: LayerMetadata[];\n /** Merged graph with layer-prefixed paths (\"LayerName/path.ts\") */\n merged: DependencyGraph;\n}\n\n/** Persisted snapshot with schema version */\nexport interface ArchSnapshot {\n version: typeof SCHEMA_VERSION | \"1.0\";\n timestamp: string;\n rootDir: string;\n graph: DependencyGraph;\n /** Present only when layers.json was used */\n multiLayer?: MultiLayerGraph;\n /** Analysis options used to generate this snapshot (for diff consistency checks) */\n analysisOptions?: {\n targetDir?: string;\n language?: string;\n exclude?: string[];\n };\n}\n\n/** Diff result between two snapshots */\nexport interface ArchDiff {\n added: string[];\n removed: string[];\n modified: string[];\n affectedDependents: Array<{\n file: string;\n reason: string;\n dependsOn: string;\n }>;\n}\n\n/** Cached graph with file modification times for incremental validation */\nexport interface GraphCache {\n version: \"1.0\";\n timestamp: string;\n /** Options used to generate this cache (for invalidation) */\n options: {\n targetDir: string;\n projectRoot: string;\n language?: string;\n exclude?: string[];\n };\n /** File path → mtime in ms at analysis time */\n fileMtimes: Record<string, number>;\n /** File path → SHA-256 content hash (for two-phase validation) */\n fileHashes?: Record<string, string>;\n /** The cached dependency graph */\n graph: DependencyGraph;\n /** Multi-layer data if applicable */\n multiLayer?: MultiLayerGraph;\n /** Layer metadata */\n layerMetadata?: LayerMetadata[];\n /** Layer directories (for multi-layer cache validation) */\n layerDirs?: string[];\n /** SHA-256 hash of layers.json (detects config changes) */\n layersJsonHash?: string;\n}\n\n/** Summary info for a historical snapshot (without full graph data) */\nexport interface SnapshotSummary {\n timestamp: string;\n totalFiles: number;\n totalEdges: number;\n circularDeps: number;\n hasMultiLayer: boolean;\n}\n\n/** Context summary for AI session initialization */\nexport interface ArchContext {\n validPaths: string[];\n summary: string;\n snapshotExists: boolean;\n snapshotTimestamp?: string;\n keyComponents: Array<{\n path: string;\n dependentCount: number;\n dependencyCount: number;\n }>;\n}\n","import type { ArchDiff, DependencyGraph } from \"../types/schema.js\";\nimport { t } from \"../i18n/index.js\";\n\n/**\n * Compute the diff between an old and new dependency graph.\n *\n * Identifies:\n * - Added files (new in the codebase)\n * - Removed files (deleted from the codebase)\n * - Modified files (dependencies changed)\n * - Affected dependents (files that depend on changed/removed files and may need updates)\n */\nexport function computeDiff(\n oldGraph: DependencyGraph,\n newGraph: DependencyGraph,\n): ArchDiff {\n const oldFiles = new Set(Object.keys(oldGraph.files));\n const newFiles = new Set(Object.keys(newGraph.files));\n\n // Files that exist in new but not in old\n const added = [...newFiles].filter((f) => !oldFiles.has(f));\n\n // Files that existed in old but not in new\n const removed = [...oldFiles].filter((f) => !newFiles.has(f));\n\n // Files that exist in both but have different dependency sets\n const modified: string[] = [];\n for (const file of newFiles) {\n if (!oldFiles.has(file)) continue;\n\n const oldDeps = oldGraph.files[file].dependencies.slice().sort();\n const newDeps = newGraph.files[file].dependencies.slice().sort();\n\n if (!arraysEqual(oldDeps, newDeps)) {\n modified.push(file);\n }\n }\n\n // Find all files that depend on changed/removed files\n const removedSet = new Set(removed);\n const changedFiles = new Set([...removed, ...modified]);\n const affectedDependents: ArchDiff[\"affectedDependents\"] = [];\n const seenAffected = new Set<string>();\n\n for (const changedFile of changedFiles) {\n // Look up dependents in the NEW graph (for modified files)\n // and in the OLD graph (for removed files)\n const graph = removedSet.has(changedFile) ? oldGraph : newGraph;\n const node = graph.files[changedFile];\n if (!node) continue;\n\n for (const dependent of node.dependents) {\n const key = `${dependent}→${changedFile}`;\n if (seenAffected.has(key)) continue;\n seenAffected.add(key);\n\n const reason = removedSet.has(changedFile)\n ? t(\"diff.reasonRemoved\", { file: changedFile })\n : t(\"diff.reasonModified\", { file: changedFile });\n\n affectedDependents.push({\n file: dependent,\n reason,\n dependsOn: changedFile,\n });\n }\n }\n\n // Also check for files that depend on added files\n // (new dependencies that might need verification)\n for (const addedFile of added) {\n const node = newGraph.files[addedFile];\n if (!node) continue;\n\n for (const dependent of node.dependents) {\n const key = `${dependent}→${addedFile}`;\n if (seenAffected.has(key)) continue;\n seenAffected.add(key);\n\n affectedDependents.push({\n file: dependent,\n reason: t(\"diff.reasonAdded\", { file: addedFile }),\n dependsOn: addedFile,\n });\n }\n }\n\n return { added, removed, modified, affectedDependents };\n}\n\n/** Check if a file path looks like a test or fixture file */\nfunction isTestOrFixture(path: string): boolean {\n return /(__fixtures__|__tests__|__mocks__|\\.test\\.|\\.spec\\.|\\.e2e\\.)/.test(path);\n}\n\n/** Partition an array into [matching, rest] based on a predicate */\nfunction partition<T>(arr: T[], pred: (item: T) => boolean): [T[], T[]] {\n const yes: T[] = [];\n const no: T[] = [];\n for (const item of arr) {\n (pred(item) ? yes : no).push(item);\n }\n return [yes, no];\n}\n\n/** Generate a human-readable report from an ArchDiff */\nexport function formatDiffReport(diff: ArchDiff): string {\n const lines: string[] = [];\n\n lines.push(t(\"diff.title\"));\n\n if (diff.added.length === 0 && diff.removed.length === 0 && diff.modified.length === 0) {\n lines.push(t(\"diff.noChanges\"));\n return lines.join(\"\\n\");\n }\n\n if (diff.added.length > 0) {\n const [testFiles, srcFiles] = partition(diff.added, isTestOrFixture);\n lines.push(t(\"diff.added\", { count: diff.added.length }));\n for (const f of srcFiles) {\n lines.push(` + ${f}`);\n }\n if (testFiles.length > 0) {\n lines.push(t(\"diff.testSummary\", { count: testFiles.length }));\n }\n lines.push(\"\");\n }\n\n if (diff.removed.length > 0) {\n const [testFiles, srcFiles] = partition(diff.removed, isTestOrFixture);\n lines.push(t(\"diff.removed\", { count: diff.removed.length }));\n for (const f of srcFiles) {\n lines.push(` - ${f}`);\n }\n if (testFiles.length > 0) {\n lines.push(t(\"diff.testSummary\", { count: testFiles.length }));\n }\n lines.push(\"\");\n }\n\n if (diff.modified.length > 0) {\n const [testFiles, srcFiles] = partition(diff.modified, isTestOrFixture);\n lines.push(t(\"diff.modified\", { count: diff.modified.length }));\n for (const f of srcFiles) {\n lines.push(` ~ ${f}`);\n }\n if (testFiles.length > 0) {\n lines.push(t(\"diff.testSummary\", { count: testFiles.length }));\n }\n lines.push(\"\");\n }\n\n if (diff.affectedDependents.length > 0) {\n const [testEntries, srcEntries] = partition(\n diff.affectedDependents,\n (a) => isTestOrFixture(a.file) || isTestOrFixture(a.dependsOn),\n );\n lines.push(t(\"diff.affected\", { count: diff.affectedDependents.length }));\n for (const a of srcEntries) {\n lines.push(` ! ${a.file}`);\n lines.push(` ${a.reason}`);\n }\n if (testEntries.length > 0) {\n lines.push(t(\"diff.testAffectedSummary\", { count: testEntries.length }));\n }\n lines.push(\"\");\n }\n\n return lines.join(\"\\n\");\n}\n\nfunction arraysEqual(a: string[], b: string[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n","import { createServer } from \"node:http\";\nimport type { DependencyGraph, ArchDiff, LayerMetadata } from \"../types/schema.js\";\nimport type { CrossLayerConnection } from \"../types/layers.js\";\nimport { buildGraphPage } from \"./template.js\";\nimport { getLocale } from \"../i18n/index.js\";\nimport type { Locale } from \"../i18n/index.js\";\n\n/**\n * Start a local web server to visualize the dependency graph.\n *\n * Serves:\n * - GET / → Interactive graph visualization (single HTML page)\n * - GET /api/graph → Raw graph data as JSON\n */\nexport function startViewer(\n graph: DependencyGraph,\n options: {\n port?: number;\n locale?: Locale;\n diff?: ArchDiff | null;\n layerMetadata?: LayerMetadata[];\n crossLayerEdges?: CrossLayerConnection[];\n } = {},\n): { port: number; close: () => void } {\n const port = options.port ?? 3000;\n const locale = options.locale ?? getLocale();\n\n const html = buildGraphPage(graph, {\n locale,\n diff: options.diff,\n layerMetadata: options.layerMetadata,\n crossLayerEdges: options.crossLayerEdges,\n });\n const graphJson = JSON.stringify(graph);\n\n const server = createServer((req, res) => {\n if (req.url === \"/api/graph\") {\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(graphJson);\n return;\n }\n\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" });\n res.end(html);\n });\n\n server.listen(port);\n\n return {\n port,\n close: () => server.close(),\n };\n}\n","import { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { DependencyGraph, LayerMetadata } from \"../types/schema.js\";\nimport type { ArchDiff } from \"../types/schema.js\";\nimport type { Locale } from \"../i18n/index.js\";\nimport type { CrossLayerConnection } from \"../types/layers.js\";\nimport { buildStyles } from \"./styles.js\";\nimport { buildViewerHtml } from \"./viewer-html.js\";\nimport { escapeHtml } from \"../utils/html-escape.js\";\n\nexport interface ViewerOptions {\n locale?: Locale;\n diff?: ArchDiff | null;\n layerMetadata?: LayerMetadata[];\n crossLayerEdges?: CrossLayerConnection[];\n}\n\n/** Lazy-loaded compiled viewer JS bundle */\nlet _viewerJs: string | null = null;\n\nfunction getViewerJs(): string {\n if (!_viewerJs) {\n const thisDir = dirname(fileURLToPath(import.meta.url));\n // Try multiple locations: production (bundled in dist/) and dev/test (running from src/)\n const candidates = [\n join(thisDir, \"..\", \"web\", \"viewer.js\"), // dist/mcp/ or dist/cli/ → dist/web/\n join(thisDir, \"..\", \"..\", \"dist\", \"web\", \"viewer.js\"), // src/web/ → dist/web/\n ];\n for (const p of candidates) {\n try { _viewerJs = readFileSync(p, \"utf-8\"); break; }\n catch { /* try next */ }\n }\n if (!_viewerJs) throw new Error(\"viewer.js not found. Run 'npm run build:client' first.\");\n }\n return _viewerJs;\n}\n\n/**\n * Build the complete HTML page for the interactive graph viewer.\n * Two views: Force-directed Graph + Hierarchical Diagram (draw.io style).\n */\nexport function buildGraphPage(graph: DependencyGraph, options: ViewerOptions = {}): string {\n const locale = options.locale ?? \"en\";\n const diff = options.diff ?? null;\n const layers = options.layerMetadata ?? null;\n const crossEdges = options.crossLayerEdges ?? null;\n const files = Object.values(graph.files);\n const nodes = files.map((f) => ({\n id: f.path,\n deps: f.dependencies.length,\n dependents: f.dependents.length,\n dependencies: f.dependencies,\n dependentsList: f.dependents,\n isOrphan: f.dependencies.length === 0 && f.dependents.length === 0,\n dir: f.path.includes(\"/\") ? f.path.substring(0, f.path.lastIndexOf(\"/\")) : \".\",\n layer: layers && f.path.includes(\"/\") ? f.path.substring(0, f.path.indexOf(\"/\")) : null,\n }));\n\n const links = graph.edges.map((e) => ({\n source: e.source,\n target: e.target,\n type: e.type,\n }));\n\n const circularFiles = new Set<string>();\n for (const c of graph.circularDependencies) {\n for (const f of c.cycle) circularFiles.add(f);\n }\n\n const dirs = [...new Set(nodes.map((n) => n.dir))].sort();\n const projectName = graph.rootDir.split(\"/\").filter(Boolean).pop() || \"Project\";\n const diffData = diff ? JSON.stringify(diff) : \"null\";\n const layersData = layers ? JSON.stringify(layers) : \"null\";\n const crossEdgesData = crossEdges ? JSON.stringify(crossEdges) : \"null\";\n const graphData = JSON.stringify({ nodes, links, circularFiles: [...circularFiles], dirs, projectName });\n\n const viewerJs = getViewerJs();\n\n return /* html */ `<!DOCTYPE html>\n<html lang=\"${locale}\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${escapeHtml(projectName)} — Architecture Viewer</title>\n${buildStyles()}\n</head>\n<body>\n${buildViewerHtml()}\n<script src=\"https://d3js.org/d3.v7.min.js\"></script>\n<script>\nwindow.__ARCH = {\n data: ${graphData},\n layers: ${layersData},\n crossEdges: ${crossEdgesData},\n diff: ${diffData},\n locale: '${locale}'\n};\n</script>\n<script>${viewerJs}</script>\n</body>\n</html>`;\n}\n","/**\n * CSS styles for the architecture viewer.\n * Extracted from template.ts for maintainability.\n */\nexport function buildStyles(): string {\n return `<style>\n:root {\n --bg: #0d1117; --bg-card: #161b22; --bg-hover: #1c2129;\n --border: #30363d; --border-active: #58a6ff;\n --text: #c9d1d9; --text-dim: #8b949e; --text-muted: #484f58;\n --accent: #58a6ff; --green: #3fb950; --red: #f97583; --yellow: #f0e68c;\n --radius: 8px; --font-size: 13px;\n}\n[data-theme=\"light\"] {\n --bg: #ffffff; --bg-card: #f6f8fa; --bg-hover: #eef1f5;\n --border: #d0d7de; --border-active: #0969da;\n --text: #1f2328; --text-dim: #656d76; --text-muted: #8b949e;\n --accent: #0969da; --green: #1a7f37; --red: #cf222e; --yellow: #9a6700;\n}\n* { margin: 0; padding: 0; box-sizing: border-box; }\nbody { background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; font-size: 13px; overflow: hidden; transition: background 0.3s, color 0.3s; }\n\n/* ─── Tab bar ──────────────────────────────── */\n#tab-bar { position: fixed; top: 0; left: 0; right: 0; height: 44px; background: var(--bg-card); border-bottom: 1px solid var(--border); display: flex; align-items: center; z-index: 30; padding: 0 16px; gap: 2px; transition: background 0.3s; }\n#tab-bar .logo { font-weight: 700; font-size: 14px; color: var(--accent); margin-right: 16px; letter-spacing: -0.3px; outline: none; border-bottom: 1px dashed transparent; cursor: text; min-width: 40px; }\n#tab-bar .logo:hover { border-bottom-color: var(--text-muted); }\n#tab-bar .logo:focus { border-bottom-color: var(--accent); }\n.tab { padding: 8px 16px; font-size: 13px; color: var(--text-dim); cursor: pointer; border-radius: 6px 6px 0 0; border: 1px solid transparent; border-bottom: none; transition: all 0.15s; user-select: none; position: relative; top: 1px; }\n.tab:hover { color: var(--text); background: var(--bg-hover); }\n.tab.active { color: var(--text); background: var(--bg); border-color: var(--border); }\n.tab-right { margin-left: auto; display: flex; align-items: center; gap: 12px; }\n.tab-stats { font-size: 12px; color: var(--text-muted); display: flex; gap: 14px; }\n.tab-stats span b { color: var(--text-dim); }\n.settings-btn { background: none; border: 1px solid var(--border); border-radius: 6px; padding: 4px 8px; cursor: pointer; color: var(--text-dim); font-size: 16px; transition: all 0.15s; }\n.settings-btn:hover { border-color: var(--accent); color: var(--text); }\n\n/* ─── Views ────────────────────────────────── */\n.view { position: fixed; top: 44px; left: 0; right: 0; bottom: 0; display: none; }\n.view.active { display: block; }\n.view svg { width: 100%; height: 100%; }\n\n/* ─── HUD ─────────────────────────────────── */\n#hud { position: absolute; top: 12px; left: 12px; z-index: 10; display: flex; flex-direction: column; gap: 8px; }\n.hud-panel { background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px; font-size: 12px; backdrop-filter: blur(8px); transition: background 0.3s; }\n#search-box { display: flex; align-items: center; gap: 8px; }\n#search-box input { background: transparent; border: none; outline: none; color: var(--text); font-size: 13px; width: 180px; }\n#search-box input::placeholder { color: var(--text-muted); }\nkbd { background: #21262d; border: 1px solid var(--border); border-radius: 3px; padding: 1px 5px; font-size: 10px; color: var(--text-muted); font-family: inherit; }\n.legend-item { display: flex; align-items: center; gap: 6px; margin: 3px 0; color: var(--text-dim); }\n.legend-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }\n\n/* ─── Tooltip (interactive — mouse can enter) ─ */\n#tooltip { position: fixed; display: none; background: var(--bg-card); border: 1px solid var(--accent); border-radius: var(--radius); padding: 14px 16px; font-size: 13px; z-index: 40; max-width: 420px; pointer-events: auto; box-shadow: 0 8px 24px rgba(0,0,0,0.5); transition: background 0.3s; }\n#tooltip .tt-name { color: var(--accent); font-size: 14px; font-weight: 600; margin-bottom: 8px; word-break: break-all; }\n#tooltip .tt-badge { display: inline-block; background: var(--bg-hover); border-radius: 10px; padding: 1px 8px; font-size: 11px; margin: 0 2px; }\n#tooltip .tt-section { margin-top: 8px; font-size: 12px; color: var(--text-dim); max-height: 140px; overflow-y: auto; }\n#tooltip .tt-section div { padding: 2px 0; }\n#tooltip .tt-out { color: var(--accent); }\n#tooltip .tt-in { color: var(--green); }\n\n/* ─── Filter bar ──────────────────────────── */\n#filter-bar { position: absolute; bottom: 12px; left: 12px; right: 120px; z-index: 10; display: flex; flex-direction: column; gap: 6px; pointer-events: none; }\n#filter-bar > * { pointer-events: auto; }\n#filter-layer-row { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }\n#filter-dir-toggle { background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; padding: 3px 10px; font-size: 11px; cursor: pointer; user-select: none; color: var(--text-dim); transition: all 0.15s; flex-shrink: 0; }\n#filter-dir-toggle:hover { border-color: var(--text-dim); color: var(--text); }\n#filter-dir-toggle.open { border-color: var(--accent); color: var(--text); }\n#filter-dir-panel { display: none; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 12px; max-height: 220px; overflow-y: auto; backdrop-filter: blur(8px); }\n#filter-dir-panel.open { display: block; }\n.dir-group { margin-bottom: 8px; }\n.dir-group:last-child { margin-bottom: 0; }\n.dir-group-label { font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; display: flex; align-items: center; gap: 5px; cursor: pointer; user-select: none; }\n.dir-group-label .dg-dot { width: 6px; height: 6px; border-radius: 50%; }\n.dir-group-pills { display: flex; flex-wrap: wrap; gap: 3px; }\n.filter-pill { background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; padding: 2px 8px; font-size: 10px; cursor: pointer; user-select: none; transition: all 0.15s; display: flex; align-items: center; gap: 4px; }\n.filter-pill:hover { border-color: var(--text-dim); }\n.filter-pill.active { border-color: var(--accent); }\n.filter-pill .pill-dot { width: 5px; height: 5px; border-radius: 50%; }\n.filter-pill .pill-count { color: var(--text-muted); font-size: 9px; }\n\n/* ─── Zoom controls ───────────────────────── */\n#zoom-ctrl { position: absolute; bottom: 52px; right: 12px; z-index: 10; display: flex; flex-direction: column; gap: 2px; }\n#zoom-ctrl button { width: 32px; height: 32px; background: var(--bg-card); border: 1px solid var(--border); color: var(--text-dim); border-radius: 6px; cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center; transition: all 0.1s; }\n#zoom-ctrl button:hover { background: var(--bg-hover); color: var(--text); }\n\n/* ─── Detail panel ────────────────────────── */\n#detail { position: absolute; top: 12px; right: 12px; width: 280px; z-index: 10; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; font-size: 13px; display: none; max-height: calc(100vh - 100px); overflow-y: auto; transition: background 0.3s; }\n#detail.open { display: block; }\n#detail .detail-name { color: var(--accent); font-weight: 600; font-size: 14px; word-break: break-all; margin-bottom: 8px; }\n#detail .detail-meta { color: var(--text-dim); margin-bottom: 12px; }\n#detail .detail-section { margin-top: 10px; }\n#detail .detail-section h4 { font-size: 11px; text-transform: uppercase; color: var(--text-muted); letter-spacing: 0.5px; margin-bottom: 4px; }\n#detail .detail-list { list-style: none; }\n#detail .detail-list li { padding: 3px 0; font-size: 12px; color: var(--text-dim); cursor: pointer; }\n#detail .detail-list li:hover { color: var(--accent); }\n#detail .close-btn { position: absolute; top: 8px; right: 10px; background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 16px; }\n\n/* ─── Settings panel ──────────────────────── */\n#settings-panel { position: fixed; top: 44px; right: 0; width: 280px; height: calc(100vh - 44px); background: var(--bg-card); border-left: 1px solid var(--border); z-index: 25; padding: 20px; transform: translateX(100%); transition: transform 0.25s ease, background 0.3s; overflow-y: auto; }\n#settings-panel.open { transform: translateX(0); }\n#settings-panel h3 { font-size: 14px; color: var(--text); margin-bottom: 16px; }\n.setting-group { margin-bottom: 18px; }\n.setting-group label { display: block; font-size: 12px; color: var(--text-dim); margin-bottom: 6px; }\n.setting-group select, .setting-group input[type=range] { width: 100%; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; color: var(--text); padding: 6px 8px; font-size: 13px; }\n.setting-group input[type=range] { padding: 4px 0; border: none; accent-color: var(--accent); }\n.setting-value { font-size: 11px; color: var(--text-muted); text-align: right; }\n.theme-toggle { display: flex; gap: 6px; }\n.theme-btn { flex: 1; padding: 8px; background: var(--bg); border: 1px solid var(--border); border-radius: 6px; cursor: pointer; text-align: center; font-size: 12px; color: var(--text-dim); transition: all 0.15s; }\n.theme-btn:hover { border-color: var(--text-dim); }\n.theme-btn.active { border-color: var(--accent); color: var(--accent); }\n\n/* ─── Hierarchy detail panel ──────────────── */\n#hier-detail { position: absolute; top: 12px; right: 12px; width: 280px; z-index: 10; background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; font-size: 13px; display: none; max-height: calc(100vh - 100px); overflow-y: auto; transition: background 0.3s; }\n#hier-detail.open { display: block; }\n#hier-detail .detail-name { color: var(--accent); font-weight: 600; font-size: 14px; word-break: break-all; margin-bottom: 8px; }\n#hier-detail .detail-meta { color: var(--text-dim); margin-bottom: 12px; }\n#hier-detail .detail-section { margin-top: 10px; }\n#hier-detail .detail-section h4 { font-size: 11px; text-transform: uppercase; color: var(--text-muted); letter-spacing: 0.5px; margin-bottom: 4px; }\n#hier-detail .detail-list { list-style: none; }\n#hier-detail .detail-list li { padding: 3px 0; font-size: 12px; color: var(--text-dim); cursor: pointer; }\n#hier-detail .detail-list li:hover { color: var(--accent); }\n#hier-detail .close-btn { position: absolute; top: 8px; right: 10px; background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 16px; }\n\n/* ─── Hierarchy ───────────────────────────── */\n.hier-node { cursor: pointer; }\n.hier-node rect { rx: 6; ry: 6; stroke-width: 1.5; transition: stroke 0.15s; }\n.hier-node:hover rect { stroke: var(--accent) !important; stroke-width: 2; }\n.hier-node text { fill: var(--text); pointer-events: none; }\n.hier-link { fill: none; stroke: var(--border); stroke-width: 1; }\n.hier-layer-label { fill: var(--text-muted); font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }\n\n/* ─── Impact mode ─────────────────────────── */\n#impact-btn.active { background: var(--accent) !important; color: #fff !important; border-color: var(--accent) !important; }\n#impact-badge { position: absolute; bottom: 52px; left: 12px; z-index: 10; display: none; background: var(--accent); color: #fff; font-size: 12px; font-weight: 600; padding: 6px 12px; border-radius: var(--radius); }\n\n/* ─── Diff focus button ──────────────────────── */\n#diff-focus-btn.active { background: var(--accent) !important; color: #fff !important; border-color: var(--accent) !important; }\n\n/* ─── Help bar ─────────────────────────────── */\n#help-bar { position: absolute; bottom: 12px; right: 12px; z-index: 10; font-size: 11px; color: var(--text-muted); background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); padding: 6px 10px; transition: background 0.3s; }\n\n/* ─── Layer hulls ─────────────────────────── */\n.layer-hull { fill-opacity: 0.06; stroke-width: 1.5; stroke-dasharray: 6,4; pointer-events: none; }\n.layer-hull-label { font-size: 13px; font-weight: 700; letter-spacing: 0.5px; pointer-events: none; opacity: 0.7; }\n\n/* ─── Layer tabs ──────────────────────────── */\n#layer-tabs { display: flex; gap: 2px; margin-left: 12px; padding-left: 12px; border-left: 1px solid var(--border); }\n.layer-tab { padding: 4px 10px; font-size: 11px; color: var(--text-dim); cursor: pointer; border-radius: 4px; border: 1px solid transparent; transition: all 0.15s; user-select: none; display: flex; align-items: center; gap: 5px; }\n.layer-tab:hover { color: var(--text); background: var(--bg-hover); }\n.layer-tab.active { border-color: var(--accent); color: var(--text); }\n.layer-tab .lt-dot { width: 6px; height: 6px; border-radius: 50%; }\n\n/* ─── Layer filter pills ─────────────────── */\n.layer-pill { background: var(--bg-card); border: 1px solid var(--border); border-radius: 14px; padding: 2px 9px; font-size: 11px; font-weight: 600; cursor: pointer; user-select: none; transition: all 0.15s; display: flex; align-items: center; gap: 5px; }\n.layer-pill:hover { border-color: var(--text-dim); }\n.layer-pill.active { border-color: var(--accent); }\n.layer-pill .lp-dot { width: 6px; height: 6px; border-radius: 50%; }\n.layer-pill .lp-count { color: var(--text-muted); font-size: 9px; font-weight: 400; }\n</style>`;\n}\n","/**\n * HTML body structure for the architecture viewer.\n * Extracted from template.ts for maintainability.\n */\nexport function buildViewerHtml(): string {\n return `\n<!-- Tab bar -->\n<div id=\"tab-bar\">\n <span class=\"logo\" id=\"project-title\" contenteditable=\"true\" spellcheck=\"false\" title=\"Click to edit project name\"></span>\n <div class=\"tab active\" data-view=\"graph-view\" data-i18n=\"tab.graph\">Graph</div>\n <div class=\"tab\" data-view=\"hier-view\" data-i18n=\"tab.hierarchy\">Hierarchy</div>\n <div class=\"tab\" data-view=\"diff-view\" id=\"diff-tab\" style=\"display:none\" data-i18n=\"tab.diff\">Diff</div>\n <div id=\"layer-tabs\"></div>\n <div class=\"tab-right\">\n <div class=\"tab-stats\">\n <span><span data-i18n=\"stats.files\">Files</span> <b id=\"s-files\">0</b></span>\n <span><span data-i18n=\"stats.edges\">Edges</span> <b id=\"s-edges\">0</b></span>\n <span><span data-i18n=\"stats.circular\">Circular</span> <b id=\"s-circular\">0</b></span>\n </div>\n <button class=\"settings-btn\" onclick=\"toggleSettings()\" title=\"Settings\">\\u2699</button>\n </div>\n</div>\n\n<!-- Settings panel -->\n<div id=\"settings-panel\">\n <h3 data-i18n=\"settings.title\">Settings</h3>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.theme\">Theme</label>\n <div class=\"theme-toggle\">\n <div class=\"theme-btn active\" data-theme-val=\"dark\" onclick=\"setTheme('dark')\">\\uD83C\\uDF19 Dark</div>\n <div class=\"theme-btn\" data-theme-val=\"light\" onclick=\"setTheme('light')\">\\u2600\\uFE0F Light</div>\n </div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.fontSize\">Font Size</label>\n <input type=\"range\" id=\"font-size-slider\" min=\"10\" max=\"18\" value=\"13\" oninput=\"setFontSize(this.value)\">\n <div class=\"setting-value\"><span id=\"font-size-val\">13</span>px</div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.nodeSize\">Node Size</label>\n <input type=\"range\" id=\"node-size-slider\" min=\"50\" max=\"200\" value=\"100\" oninput=\"setNodeScale(this.value)\">\n <div class=\"setting-value\"><span id=\"node-size-val\">100</span>%</div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.linkOpacity\">Link Opacity</label>\n <input type=\"range\" id=\"link-opacity-slider\" min=\"10\" max=\"100\" value=\"40\" oninput=\"setLinkOpacity(this.value)\">\n <div class=\"setting-value\"><span id=\"link-opacity-val\">40</span>%</div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.gravity\">Gravity</label>\n <input type=\"range\" id=\"gravity-slider\" min=\"10\" max=\"500\" value=\"150\" oninput=\"setGravity(this.value)\">\n <div class=\"setting-value\"><span id=\"gravity-val\">150</span></div>\n </div>\n <div id=\"layer-gravity-setting\" class=\"setting-group\" style=\"display:none\">\n <label>Layer Cohesion</label>\n <input type=\"range\" id=\"layer-gravity-slider\" min=\"1\" max=\"40\" value=\"12\" oninput=\"setLayerGravity(this.value)\">\n <div class=\"setting-value\"><span id=\"layer-gravity-val\">12</span></div>\n </div>\n <div class=\"setting-group\">\n <label data-i18n=\"settings.language\">Language</label>\n <div class=\"theme-toggle\">\n <div class=\"theme-btn lang-btn\" data-lang=\"en\" onclick=\"setLang('en')\">English</div>\n <div class=\"theme-btn lang-btn\" data-lang=\"ja\" onclick=\"setLang('ja')\">\\u65E5\\u672C\\u8A9E</div>\n </div>\n </div>\n <div id=\"cross-layer-setting\" class=\"setting-group\" style=\"display:none\">\n <label>Cross-layer Links</label>\n <div class=\"theme-toggle\">\n <div class=\"theme-btn active\" id=\"cross-link-toggle\" onclick=\"toggleCrossLinks()\">ON</div>\n </div>\n </div>\n <div class=\"setting-group\" style=\"margin-top:12px;padding-top:12px;border-top:1px solid var(--border)\">\n <label data-i18n=\"settings.export\">Export</label>\n <div class=\"theme-toggle\">\n <div class=\"theme-btn\" onclick=\"exportSVG()\">SVG</div>\n <div class=\"theme-btn\" onclick=\"exportPNG()\">PNG</div>\n </div>\n </div>\n</div>\n\n<!-- Graph View -->\n<div id=\"graph-view\" class=\"view active\">\n <svg id=\"graph-svg\"></svg>\n <div id=\"hud\">\n <div class=\"hud-panel\" id=\"search-box\">\n <svg width=\"13\" height=\"13\" viewBox=\"0 0 16 16\" fill=\"currentColor\" style=\"color:var(--text-muted)\"><path d=\"M11.5 7a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0Zm-.82 4.74a6 6 0 1 1 1.06-1.06l3.04 3.04a.75.75 0 1 1-1.06 1.06l-3.04-3.04Z\"/></svg>\n <input id=\"search\" type=\"text\" data-i18n-placeholder=\"search.placeholder\" placeholder=\"Search files...\" autocomplete=\"off\">\n <kbd>/</kbd>\n </div>\n <div class=\"hud-panel\" id=\"legend-panel\">\n <div id=\"layer-legend\"></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--red)\"></div> <span data-i18n=\"legend.circular\">Circular dep</span></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--text-muted)\"></div> <span data-i18n=\"legend.orphan\">Orphan</span></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"border:2px solid var(--yellow);width:6px;height:6px\"></div> <span data-i18n=\"legend.highCoupling\">High coupling</span></div>\n <div class=\"legend-item\" style=\"margin-top:4px;font-size:11px;gap:3px\"><span style=\"color:var(--accent)\">\\u2014\\u2192</span> <span data-i18n=\"legend.imports\">imports</span> <span style=\"margin-left:6px;color:var(--green)\">\\u2190\\u2014</span> <span data-i18n=\"legend.importedBy\">imported by</span></div>\n </div>\n </div>\n <div id=\"detail\">\n <button class=\"close-btn\" onclick=\"closeDetail()\">\\u2715</button>\n <div class=\"detail-name\" id=\"d-name\"></div>\n <div class=\"detail-meta\" id=\"d-meta\"></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.importedBy\">Imported by</h4><ul class=\"detail-list\" id=\"d-dependents\"></ul></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.imports\">Imports</h4><ul class=\"detail-list\" id=\"d-deps\"></ul></div>\n </div>\n <div id=\"filter-bar\">\n <div id=\"filter-dir-panel\"></div>\n <div id=\"filter-layer-row\"></div>\n </div>\n <div id=\"zoom-ctrl\">\n <button onclick=\"zoomIn()\" title=\"Zoom in\">+</button>\n <button onclick=\"zoomOut()\" title=\"Zoom out\">\\u2212</button>\n <button onclick=\"zoomFit()\" title=\"Fit\">\\u229E</button>\n <button id=\"impact-btn\" onclick=\"toggleImpactMode()\" title=\"Impact simulation\" style=\"font-size:12px;margin-top:4px\" data-i18n=\"impact.btn\">Impact</button>\n </div>\n <div id=\"impact-badge\"></div>\n <div id=\"help-bar\" data-i18n=\"help.graph\">Scroll: zoom \\u00B7 Drag: pan \\u00B7 Click: select \\u00B7 / search</div>\n</div>\n\n<!-- Hierarchy View -->\n<div id=\"hier-view\" class=\"view\">\n <svg id=\"hier-svg\"></svg>\n <div id=\"hier-hud\" style=\"position:absolute;top:12px;left:12px;z-index:10;display:flex;flex-direction:column;gap:8px;\">\n <div class=\"hud-panel\" id=\"hier-legend\">\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--red)\"></div> <span data-i18n=\"legend.circular\">Circular dep</span></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--text-muted)\"></div> <span data-i18n=\"legend.orphan\">Orphan</span></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"border:2px solid var(--yellow);width:6px;height:6px\"></div> <span data-i18n=\"legend.highCoupling\">High coupling</span></div>\n </div>\n </div>\n <div id=\"hier-detail\">\n <button class=\"close-btn\" onclick=\"closeHierDetail()\">\\u2715</button>\n <div class=\"detail-name\" id=\"hd-name\"></div>\n <div class=\"detail-meta\" id=\"hd-meta\"></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.importedBy\">Imported by</h4><ul class=\"detail-list\" id=\"hd-dependents\"></ul></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.imports\">Imports</h4><ul class=\"detail-list\" id=\"hd-deps\"></ul></div>\n </div>\n <div id=\"hier-filter-bar\" style=\"position:absolute;bottom:12px;left:12px;right:120px;z-index:10;display:none;\">\n <div id=\"hier-filter-row\" style=\"display:flex;flex-wrap:wrap;gap:4px;\"></div>\n </div>\n <div id=\"help-bar\" style=\"position:absolute\" data-i18n=\"help.hierarchy\">Scroll to navigate \\u00B7 Click to highlight</div>\n</div>\n\n<!-- Diff View -->\n<div id=\"diff-view\" class=\"view\">\n <svg id=\"diff-svg\"></svg>\n <div id=\"diff-hud\" style=\"position:absolute;top:12px;left:12px;z-index:10;display:flex;flex-direction:column;gap:8px;\">\n <div class=\"hud-panel\">\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--green)\"></div> <span data-i18n=\"diff.addedLabel\">Added</span> <b id=\"diff-added-count\" style=\"margin-left:auto\">0</b></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--red)\"></div> <span data-i18n=\"diff.removedLabel\">Removed</span> <b id=\"diff-removed-count\" style=\"margin-left:auto\">0</b></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--yellow)\"></div> <span data-i18n=\"diff.modifiedLabel\">Modified</span> <b id=\"diff-modified-count\" style=\"margin-left:auto\">0</b></div>\n <div class=\"legend-item\"><div class=\"legend-dot\" style=\"background:var(--accent)\"></div> <span data-i18n=\"diff.affectedLabel\">Affected</span> <b id=\"diff-affected-count\" style=\"margin-left:auto\">0</b></div>\n <div style=\"margin-top:6px;border-top:1px solid var(--border);padding-top:6px;\">\n <button id=\"diff-focus-btn\" onclick=\"toggleDiffFocus()\" style=\"background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:4px 10px;cursor:pointer;color:var(--text-dim);font-size:11px;width:100%;transition:all 0.15s;\" data-i18n=\"diff.focusChanges\">Focus changes</button>\n </div>\n </div>\n </div>\n <div id=\"diff-detail\" style=\"position:absolute;top:12px;right:12px;width:280px;z-index:10;background:var(--bg-card);border:1px solid var(--border);border-radius:var(--radius);padding:16px;font-size:13px;display:none;max-height:calc(100vh - 100px);overflow-y:auto;transition:background 0.3s;\">\n <button class=\"close-btn\" onclick=\"closeDiffDetail()\" style=\"position:absolute;top:8px;right:10px;background:none;border:none;color:var(--text-muted);cursor:pointer;font-size:16px;\">\\u2715</button>\n <div class=\"detail-name\" id=\"dd-name\"></div>\n <div id=\"dd-status\" style=\"margin:6px 0;font-size:12px;font-weight:600;\"></div>\n <div class=\"detail-meta\" id=\"dd-meta\"></div>\n <div class=\"detail-section\"><h4 data-i18n=\"diff.affectedByChange\">Affected by this change</h4><ul class=\"detail-list\" id=\"dd-affected\"></ul></div>\n <div class=\"detail-section\"><h4 data-i18n=\"detail.imports\">Imports</h4><ul class=\"detail-list\" id=\"dd-deps\"></ul></div>\n </div>\n <div id=\"help-bar\" style=\"position:absolute\" data-i18n=\"help.diff\">Green=added \\u00B7 Red=removed \\u00B7 Yellow=modified \\u00B7 Blue=affected \\u00B7 Click: impact chain</div>\n</div>\n\n<!-- Tooltip (shared, interactive) -->\n<div id=\"tooltip\">\n <div class=\"tt-name\" id=\"tt-name\"></div>\n <div>\n <span class=\"tt-badge tt-out\" id=\"tt-dep-count\"></span> <span data-i18n=\"tooltip.imports\">imports</span>\n <span class=\"tt-badge tt-in\" id=\"tt-dpt-count\" style=\"margin-left:6px\"></span> <span data-i18n=\"tooltip.importedBy\">imported by</span>\n </div>\n <div class=\"tt-section\" id=\"tt-details\"></div>\n</div>`;\n}\n","/**\n * Escape a string for safe insertion into HTML.\n * Handles the 5 characters that can break HTML context:\n * & < > \" '\n *\n * Used both server-side (Node.js) and mirrored in browser-side\n * inline JS (template.ts `esc()`) with identical logic.\n */\nexport function escapeHtml(s: string): string {\n return s\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&#39;\");\n}\n\n/**\n * Returns the `esc()` function body as an inline JS string.\n * Ensures template.ts and unit tests use the exact same logic.\n */\nexport const ESC_FUNCTION_JS =\n `function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/\"/g,'&quot;').replace(/'/g,'&#39;'); }`;\n","import { readFileSync } from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n/** Read version from package.json at build/runtime — single source of truth */\nfunction loadVersion(): string {\n // Walk up from this file to find package.json\n let dir = dirname(fileURLToPath(import.meta.url));\n for (let i = 0; i < 5; i++) {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, \"package.json\"), \"utf-8\"));\n return pkg.version;\n } catch {\n dir = dirname(dir);\n }\n }\n return \"0.0.0\";\n}\n\nexport const VERSION = loadVersion();\n"],"mappings":";;;AAAA,SAAS,eAAe;AACxB,SAAS,aAAa;AACtB,SAAS,aAAAA,YAAW,SAAAC,cAAa;AACjC,SAAS,QAAAC,cAAY;;;ACHrB,SAAS,WAAAC,gBAAe;AACxB,SAAS,QAAAC,aAAY;;;ACDrB,SAAS,SAAS,gBAAsB;AACxC,SAAS,MAAM,UAAU,eAAe;;;ACCjC,SAAS,aAAa,OAA+C;AAC1E,QAAM,MAAM,oBAAI,IAAsB;AACtC,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,IAAI,IAAI,KAAK,MAAM,EAAG,KAAI,IAAI,KAAK,QAAQ,CAAC,CAAC;AAClD,QAAI,IAAI,KAAK,MAAM,EAAG,KAAK,KAAK,MAAM;AAAA,EACxC;AAEA,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,SAA+B,CAAC;AACtC,QAAM,YAAY,oBAAI,IAAY;AAElC,WAAS,IAAI,MAAc,MAAsB;AAC/C,QAAI,QAAQ,IAAI,IAAI,GAAG;AACrB,YAAM,aAAa,KAAK,QAAQ,IAAI;AACpC,UAAI,eAAe,IAAI;AACrB,cAAM,QAAQ,KAAK,MAAM,UAAU;AACnC,cAAM,MAAM,CAAC,GAAG,KAAK,EAAE,KAAK,EAAE,KAAK,QAAQ;AAC3C,YAAI,CAAC,UAAU,IAAI,GAAG,GAAG;AACvB,oBAAU,IAAI,GAAG;AACjB,iBAAO,KAAK,EAAE,MAAM,CAAC;AAAA,QACvB;AAAA,MACF;AACA;AAAA,IACF;AACA,QAAI,QAAQ,IAAI,IAAI,EAAG;AAEvB,YAAQ,IAAI,IAAI;AAChB,YAAQ,IAAI,IAAI;AAChB,SAAK,KAAK,IAAI;AAEd,eAAW,YAAY,IAAI,IAAI,IAAI,KAAK,CAAC,GAAG;AAC1C,UAAI,UAAU,IAAI;AAAA,IACpB;AAEA,SAAK,IAAI;AACT,YAAQ,OAAO,IAAI;AAAA,EACrB;AAEA,aAAW,QAAQ,IAAI,KAAK,GAAG;AAC7B,QAAI,CAAC,QAAQ,IAAI,IAAI,GAAG;AACtB,UAAI,MAAM,CAAC,CAAC;AAAA,IACd;AAAA,EACF;AAEA,SAAO;AACT;;;AC1CO,SAAS,cAAc,SAAiB,OAA6B;AAC1E,UAAQ,OAAO;AAAA,IACb,KAAK;AACH,aAAO,YAAY,OAAO;AAAA,IAC5B,KAAK;AACH,aAAO,UAAU,OAAO;AAAA,IAC1B,KAAK;AACH,aAAO,YAAY,OAAO;AAAA,IAC5B,KAAK;AACH,aAAO,UAAU,OAAO;AAAA,IAC1B,KAAK;AACH,aAAO,SAAS,OAAO;AAAA,IACzB,SAAS;AACP,YAAM,cAAqB;AAC3B,YAAM,IAAI,MAAM,0BAA0B,WAAW,EAAE;AAAA,IACzD;AAAA,EACF;AACF;AAQA,SAAS,YAAY,SAAyB;AAC5C,MAAI,SAAS;AACb,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AAEzB,QAAI,QAAQ,CAAC,MAAM,OAAO,IAAI,IAAI,QAAQ,QAAQ;AAChD,UAAI,SAAS;AACb,UAAI,IAAI,IAAI;AACZ,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C;AACA;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAI5C,iBAAS,IAAI,GAAG,KAAK,GAAG,KAAK;AAC3B,oBAAU;AAAA,QACZ;AACA,YAAI,IAAI;AAER,eAAO,IAAI,QAAQ,QAAQ;AACzB,cAAI,QAAQ,CAAC,MAAM,KAAK;AAEtB,gBAAI,cAAc;AAClB,gBAAI,IAAI,IAAI;AACZ,mBAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,OAAO,cAAc,QAAQ;AACvE;AACA;AAAA,YACF;AACA,gBAAI,gBAAgB,QAAQ;AAE1B,uBAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,0BAAU;AAAA,cACZ;AACA,kBAAI;AACJ;AAAA,YACF;AAAA,UACF;AACA,oBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,QACF;AACA;AAAA,MACF;AAAA,IAEF;AAGA,QAAI,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AAEhD,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAChD,kBAAU;AACV;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AACrD,gBAAU;AACV;AACA,gBAAU;AACV;AACA,aAAO,IAAI,QAAQ,QAAQ;AACzB,YAAI,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AAChD,oBAAU;AACV;AACA,oBAAU;AACV;AACA;AAAA,QACF;AAEA,kBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU,QAAQ,CAAC;AACnB;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,CAAC;AACnB;AACA,oBAAU,QAAQ,CAAC;AACnB;AAAA,QACF,WAAW,QAAQ,CAAC,MAAM,MAAM;AAC9B,oBAAU;AACV;AAAA,QACF,OAAO;AACL,oBAAU,QAAQ,CAAC;AACnB;AAAA,QACF;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AACtB,kBAAU,QAAQ,CAAC;AACnB;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU,QAAQ,CAAC;AACnB;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,CAAC;AACnB;AACA,oBAAU,QAAQ,CAAC;AACnB;AAAA,QACF,OAAO;AACL,oBAAU,QAAQ,CAAC;AACnB;AAAA,QACF;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AACtB,kBAAU,QAAQ,CAAC;AACnB;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU;AACV;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,kBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AACtB,kBAAU;AACV;AAAA,MACF;AAAA,IACF,OAEK;AACH,gBAAU,QAAQ,CAAC;AACnB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,UAAU,SAAyB;AAC1C,MAAI,SAAS;AACb,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AACzB,QAAI,QAAQ,CAAC,MAAM,KAAK;AACtB,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAChD,kBAAU;AACV;AAAA,MACF;AAAA,IACF,WAAW,QAAQ,CAAC,MAAM,KAAK;AAC7B,gBAAU,QAAQ,CAAC;AACnB;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IACvD,WAAW,QAAQ,CAAC,MAAM,KAAK;AAC7B,gBAAU,QAAQ,CAAC;AACnB;AACA,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IACvD,OAAO;AACL,gBAAU,QAAQ,CAAC;AACnB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOA,SAAS,YAAY,SAAyB;AAC5C,MAAI,SAAS;AACb,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AAEzB,QAAI,YAAY;AAChB,QAAI,QAAQ;AACZ,QAAI,IAAI,QAAQ,QAAQ;AACtB,YAAM,KAAK,QAAQ,CAAC;AACpB,YAAM,KAAK,IAAI,IAAI,QAAQ,SAAS,QAAQ,IAAI,CAAC,IAAI;AACrD,YAAM,KAAK,IAAI,IAAI,QAAQ,SAAS,QAAQ,IAAI,CAAC,IAAI;AAErD,WACG,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,SAC7E,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,SAC7E,OAAO,OAAO,OAAO,MACtB;AAEA,cAAM,QAAQ,KAAK,IAAI,YAAY;AACnC,YAAI,SAAS,QAAQ,SAAS,QAAQ,SAAS,QAAQ,SAAS,MAAM;AACpE,sBAAY;AACZ,kBAAQ,KAAK,SAAS,GAAG;AAAA,QAC3B;AAAA,MACF;AAEA,UACE,cAAc,MACb,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,OAAO,SAC7E,OAAO,OAAO,OAAO,MACtB;AACA,oBAAY;AACZ,gBAAQ,OAAO,OAAO,OAAO;AAAA,MAC/B;AAAA,IACF;AAEA,UAAM,aAAa,IAAI;AAGvB,QACE,aAAa,IAAI,QAAQ,WACxB,QAAQ,UAAU,MAAM,OAAO,QAAQ,UAAU,MAAM,QACxD,QAAQ,aAAa,CAAC,MAAM,QAAQ,UAAU,KAC9C,QAAQ,aAAa,CAAC,MAAM,QAAQ,UAAU,GAC9C;AACA,YAAM,QAAQ,QAAQ,UAAU;AAEhC,eAAS,IAAI,GAAG,IAAI,WAAW,KAAK;AAClC,kBAAU;AAAA,MACZ;AAEA,gBAAU;AACV,UAAI,aAAa;AACjB,aAAO,IAAI,QAAQ,QAAQ;AACzB,YAAI,QAAQ,CAAC,MAAM,SAAS,QAAQ,IAAI,CAAC,MAAM,SAAS,QAAQ,IAAI,CAAC,MAAM,OAAO;AAChF,oBAAU;AACV,eAAK;AACL;AAAA,QACF;AACA,YAAI,CAAC,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AAC3D,oBAAU;AACV;AACA,oBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,QACF,OAAO;AACL,oBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,QACF;AAAA,MACF;AAAA,IACF,WAES,cAAc,KAAK,QAAQ,CAAC,MAAM,KAAK;AAC9C,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAChD,kBAAU;AACV;AAAA,MACF;AAAA,IACF,WAGE,aAAa,QAAQ,WACpB,QAAQ,UAAU,MAAM,OAAO,QAAQ,UAAU,MAAM,SACvD,YAAY,KAAK,QAAQ,CAAC,MAAM,OAAO,QAAQ,CAAC,MAAM,MACvD;AACA,YAAM,QAAQ,QAAQ,UAAU;AAEhC,eAAS,IAAI,GAAG,IAAI,YAAY,KAAK;AACnC,kBAAU,QAAQ,CAAC;AAAA,MACrB;AACA,gBAAU,QAAQ,UAAU;AAC5B,UAAI,aAAa;AACjB,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,OAAO;AACjD,YAAI,CAAC,SAAS,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AAC3D,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,WAAW,QAAQ,CAAC,MAAM,MAAM;AAC9B,oBAAU;AACV;AACA;AAAA,QACF,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,OAAO;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IAC/E,OACK;AACH,gBAAU,QAAQ,CAAC;AACnB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAMA,SAAS,UAAU,SAAyB;AAC1C,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,SAAmB,CAAC;AAC1B,MAAI,UAAU;AAEd,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,WAAW,gBAAgB,KAAK,IAAI,GAAG;AAC1C,gBAAU;AACV,aAAO,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;AACnC;AAAA,IACF;AACA,QAAI,SAAS;AACX,UAAI,cAAc,KAAK,IAAI,GAAG;AAC5B,kBAAU;AAAA,MACZ;AACA,aAAO,KAAK,IAAI,OAAO,KAAK,MAAM,CAAC;AACnC;AAAA,IACF;AAGA,QAAI,YAAY;AAChB,QAAI,IAAI;AACR,WAAO,IAAI,KAAK,QAAQ;AACtB,UAAI,KAAK,CAAC,MAAM,KAAK;AAEnB,qBAAa,IAAI,OAAO,KAAK,SAAS,CAAC;AACvC;AAAA,MACF,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B,qBAAa,KAAK,CAAC;AAAG;AACtB,oBAAY,2BAA2B,MAAM,GAAG,SAAS;AACzD,YAAK,gCAAgC,MAAM,CAAC;AAAA,MAC9C,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B,qBAAa,KAAK,CAAC;AAAG;AACtB,eAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,KAAK;AACzC,cAAI,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAC3C,yBAAa,KAAK,GAAG;AACrB,yBAAa,KAAK,GAAG;AAAA,UACvB,OAAO;AACL,yBAAa,KAAK,GAAG;AAAA,UACvB;AAAA,QACF;AACA,YAAI,IAAI,KAAK,QAAQ;AAAE,uBAAa,KAAK,CAAC;AAAG;AAAA,QAAK;AAAA,MACpD,OAAO;AACL,qBAAa,KAAK,CAAC;AAAG;AAAA,MACxB;AAAA,IACF;AACA,WAAO,KAAK,SAAS;AAAA,EACvB;AAEA,SAAO,OAAO,KAAK,IAAI;AACzB;AAMA,SAAS,2BAA2B,MAAc,QAAgB,WAA2B;AAC3F,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,KAAK;AACzC,QAAI,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAC3C,mBAAa,KAAK,GAAG;AACrB,mBAAa,KAAK,GAAG;AAAA,IACvB,WAAW,KAAK,CAAC,MAAM,OAAO,IAAI,IAAI,KAAK,UAAU,KAAK,IAAI,CAAC,MAAM,KAAK;AAExE,mBAAa,KAAK,GAAG;AACrB,mBAAa,KAAK,GAAG;AACrB,UAAI,QAAQ;AACZ,aAAO,IAAI,KAAK,UAAU,QAAQ,GAAG;AACnC,YAAI,KAAK,CAAC,MAAM,KAAK;AACnB;AACA,uBAAa,KAAK,GAAG;AAAA,QACvB,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B;AACA,uBAAa,KAAK,GAAG;AAAA,QACvB,WAAW,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAClD,uBAAa,KAAK,GAAG;AACrB,uBAAa,KAAK,GAAG;AAAA,QACvB,OAAO;AACL,uBAAa,KAAK,GAAG;AAAA,QACvB;AAAA,MACF;AAAA,IACF,OAAO;AACL,mBAAa,KAAK,GAAG;AAAA,IACvB;AAAA,EACF;AACA,MAAI,IAAI,KAAK,QAAQ;AAAE,iBAAa,KAAK,CAAC;AAAA,EAAmB;AAC7D,SAAO;AACT;AAKA,SAAS,gCAAgC,MAAc,QAAwB;AAC7E,MAAI,IAAI;AACR,SAAO,IAAI,KAAK,UAAU,KAAK,CAAC,MAAM,KAAK;AACzC,QAAI,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAC3C,WAAK;AAAA,IACP,WAAW,KAAK,CAAC,MAAM,OAAO,IAAI,IAAI,KAAK,UAAU,KAAK,IAAI,CAAC,MAAM,KAAK;AACxE,WAAK;AACL,UAAI,QAAQ;AACZ,aAAO,IAAI,KAAK,UAAU,QAAQ,GAAG;AACnC,YAAI,KAAK,CAAC,MAAM,KAAK;AACnB;AACA;AAAA,QACF,WAAW,KAAK,CAAC,MAAM,KAAK;AAC1B;AACA;AAAA,QACF,WAAW,KAAK,CAAC,MAAM,QAAQ,IAAI,IAAI,KAAK,QAAQ;AAClD,eAAK;AAAA,QACP,OAAO;AACL;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL;AAAA,IACF;AAAA,EACF;AACA,MAAI,IAAI,KAAK,QAAQ;AAAE;AAAA,EAA0B;AACjD,SAAO;AACT;AAMA,SAAS,SAAS,SAAyB;AACzC,MAAI,SAAS;AACb,MAAI,IAAI;AACR,SAAO,IAAI,QAAQ,QAAQ;AAEzB,QAAI,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AAC1E,YAAM,eAAe;AACrB,UAAI,IAAI,IAAI;AACZ,UAAI,WAAW;AAGf,UAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC5C,mBAAW;AACX;AAAA,MACF;AAEA,YAAM,aAAa;AACnB,aAAO,IAAI,QAAQ,UAAU,eAAe,KAAK,QAAQ,CAAC,CAAC,GAAG;AAC5D;AAAA,MACF;AACA,YAAM,aAAa,QAAQ,MAAM,YAAY,CAAC;AAC9C,UAAI,WAAW,SAAS,GAAG;AAEzB,YAAI,YAAY,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AACxD;AAAA,QACF;AAGA,YAAI,eAAe;AACnB,YAAI,UAAU;AAEd,YAAI,UAAU,QAAQ,UAAU,QAAQ,OAAO,MAAM,MAAM;AACzD,yBAAe;AAAA,QACjB;AACA,YAAI,cAAc;AAEhB,mBAAS,IAAI,cAAc,KAAK,SAAS,KAAK;AAC5C,gBAAI,QAAQ,CAAC,MAAM,MAAM;AACvB,wBAAU;AAAA,YACZ,OAAO;AACL,wBAAU,QAAQ,CAAC;AAAA,YACrB;AAAA,UACF;AACA,cAAI,UAAU;AAGd,cAAI,QAAQ;AACZ,iBAAO,IAAI,QAAQ,UAAU,CAAC,OAAO;AAEnC,kBAAM,YAAY;AAElB,gBAAI,aAAa;AACjB,mBAAO,aAAa,QAAQ,UAAU,QAAQ,UAAU,MAAM,MAAM;AAClE;AAAA,YACF;AACA,kBAAM,cAAc,QAAQ,MAAM,WAAW,UAAU;AAEvD,kBAAM,cAAc,YAAY,UAAU;AAC1C,gBACE,gBAAgB,cAChB,gBAAgB,aAAa,OAC7B,gBAAgB,aAAa,OAC7B,gBAAgB,aAAa,QAC7B,gBAAgB,aAAa,OAC7B,YAAY,WAAW,aAAa,GAAG,KACvC,gBAAgB,YAChB;AAEA,uBAAS,IAAI,WAAW,IAAI,YAAY,KAAK;AAC3C,0BAAU,QAAQ,CAAC;AAAA,cACrB;AACA,kBAAI;AACJ,kBAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAC7C,0BAAU;AACV;AAAA,cACF;AACA,sBAAQ;AAAA,YACV,OAAO;AAEL,uBAAS,IAAI,WAAW,IAAI,YAAY,KAAK;AAC3C,0BAAU;AAAA,cACZ;AACA,kBAAI;AACJ,kBAAI,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAC7C,0BAAU;AACV;AAAA,cACF;AAAA,YACF;AAAA,UACF;AACA;AAAA,QACF;AAAA,MACF;AAEA,gBAAU,QAAQ,CAAC;AACnB;AACA;AAAA,IACF;AAGA,QAAK,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,OAAQ,QAAQ,CAAC,MAAM,KAAK;AACxE,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,MAAM;AAChD,kBAAU;AACV;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AACrD,gBAAU;AAAK;AACf,gBAAU;AAAK;AACf,aAAO,IAAI,QAAQ,QAAQ;AACzB,YAAI,QAAQ,CAAC,MAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,KAAK;AAChD,oBAAU;AAAK;AACf,oBAAU;AAAK;AACf;AAAA,QACF;AACA,kBAAU,QAAQ,CAAC,MAAM,OAAO,OAAO;AACvC;AAAA,MACF;AAAA,IACF,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU,QAAQ,CAAC;AAAG;AACtB,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IACvD,WAES,QAAQ,CAAC,MAAM,KAAK;AAC3B,gBAAU,QAAQ,CAAC;AAAG;AACtB,aAAO,IAAI,QAAQ,UAAU,QAAQ,CAAC,MAAM,KAAK;AAC/C,YAAI,QAAQ,CAAC,MAAM,QAAQ,IAAI,IAAI,QAAQ,QAAQ;AACjD,oBAAU,QAAQ,GAAG;AACrB,oBAAU,QAAQ,GAAG;AAAA,QACvB,OAAO;AACL,oBAAU,QAAQ,GAAG;AAAA,QACvB;AAAA,MACF;AACA,UAAI,IAAI,QAAQ,QAAQ;AAAE,kBAAU,QAAQ,CAAC;AAAG;AAAA,MAAK;AAAA,IACvD,OACK;AACH,gBAAU,QAAQ,CAAC;AAAG;AAAA,IACxB;AAAA,EACF;AACA,SAAO;AACT;;;AFvlBO,IAAM,cAAN,MAA4C;AAAA,EACjD,YAAoB,QAAwB;AAAxB;AAAA,EAAyB;AAAA,EAE7C,MAAM,QACJ,SACA,UAAqD,CAAC,GAC5B;AAC1B,UAAM,aAAa,QAAQ,OAAO;AAClC,UAAM,kBAAkB;AAAA,MACtB,GAAI,KAAK,OAAO,kBAAkB,CAAC;AAAA,MACnC,GAAI,QAAQ,WAAW,CAAC;AAAA,MACxB;AAAA,IACF,EAAE,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC;AAG1B,UAAM,eAAe,MAAM,KAAK;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ,YAAY;AAAA,IACtB;AAEA,UAAM,iBAAiB,IAAI,IAAI,YAAY;AAG3C,UAAM,QAAkC,CAAC;AACzC,UAAM,QAA0B,CAAC;AACjC,UAAM,UAAU,oBAAI,IAAY;AAGhC,eAAW,YAAY,cAAc;AACnC,YAAM,UAAU,SAAS,YAAY,QAAQ;AAC7C,YAAM,OAAO,IAAI;AAAA,QACf,MAAM;AAAA,QACN,QAAQ;AAAA,QACR,cAAc,CAAC;AAAA,QACf,YAAY,CAAC;AAAA,MACf;AAAA,IACF;AAGA,eAAW,YAAY,cAAc;AACnC,YAAM,YAAY,SAAS,YAAY,QAAQ;AAC/C,UAAI;AACJ,UAAI;AACF,kBAAU,MAAM,SAAS,UAAU,OAAO;AAAA,MAC5C,QAAQ;AACN,YAAI,MAAM,SAAS,EAAG,OAAM,SAAS,EAAE,SAAS;AAChD;AAAA,MACF;AAEA,YAAM,WAAW,cAAc,SAAS,KAAK,OAAO,YAAY;AAChE,YAAM,UAAU,KAAK,eAAe,UAAU,UAAU,YAAY,cAAc;AAClF,iBAAW,cAAc,SAAS;AAChC,cAAM,WAAW,KAAK,OAAO;AAAA,UAC3B;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AACA,YAAI,CAAC,SAAU;AAEf,cAAM,YAAY,SAAS,YAAY,QAAQ;AAC/C,YAAI,CAAC,MAAM,SAAS,EAAG;AACvB,YAAI,cAAc,UAAW;AAE7B,cAAM,UAAU,GAAG,SAAS,KAAK,SAAS;AAC1C,YAAI,QAAQ,IAAI,OAAO,EAAG;AAC1B,gBAAQ,IAAI,OAAO;AAEnB,cAAM,KAAK;AAAA,UACT,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,MAAM;AAAA,QACR,CAAC;AAED,cAAM,SAAS,EAAE,aAAa,KAAK,SAAS;AAC5C,cAAM,SAAS,EAAE,WAAW,KAAK,SAAS;AAAA,MAC5C;AAAA,IACF;AAGA,UAAM,uBAAuB,aAAa,KAAK;AAE/C,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,OAAO,KAAK,KAAK,EAAE;AAAA,MAC/B,YAAY,MAAM;AAAA,IACpB;AAAA,EACF;AAAA,EAEQ,eACN,SACA,UACA,SACA,cACU;AAEV,QAAI,KAAK,OAAO,gBAAgB;AAC9B,aAAO,KAAK,OAAO,eAAe,SAAS,UAAU,SAAS,YAAY;AAAA,IAC5E;AAEA,UAAM,UAAoB,CAAC;AAC3B,eAAW,WAAW,KAAK,OAAO,gBAAgB;AAEhD,YAAM,QAAQ,QAAQ,MAAM,MAAM,SAAS,GAAG,IAAI,QAAQ,MAAM,QAAQ,QAAQ,MAAM,QAAQ;AAC9F,YAAM,QAAQ,IAAI,OAAO,QAAQ,MAAM,QAAQ,KAAK;AACpD,UAAI;AACJ,cAAQ,QAAQ,MAAM,KAAK,OAAO,OAAO,MAAM;AAE7C,YAAI,MAAM,CAAC,GAAG;AACZ,kBAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,QACvB;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,aACZ,KACA,YACA,iBACA,UACA,eAAuB,GACJ;AACnB,QAAI,WAAW,KAAK,gBAAgB,SAAU,QAAO,CAAC;AAEtD,UAAM,UAAoB,CAAC;AAC3B,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACtD,QAAQ;AACN,aAAO;AAAA,IACT;AAEA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAW,KAAK,KAAK,MAAM,IAAI;AACrC,YAAM,UAAU,SAAS,YAAY,QAAQ;AAG7C,UACE,gBAAgB;AAAA,QACd,CAAC,MAAM,EAAE,KAAK,MAAM,IAAI,KAAK,EAAE,KAAK,OAAO,KAAK,EAAE,KAAK,QAAQ;AAAA,MACjE,GACA;AACA;AAAA,MACF;AAEA,UAAI,MAAM,YAAY,GAAG;AACvB,YAAI,MAAM,KAAK,WAAW,GAAG,EAAG;AAChC,cAAM,MAAM,MAAM,KAAK;AAAA,UACrB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,eAAe;AAAA,QACjB;AACA,gBAAQ,KAAK,GAAG,GAAG;AAAA,MACrB,WAAW,MAAM,OAAO,GAAG;AACzB,cAAM,SAAS,MAAM,KAAK,YAAY,GAAG;AACzC,YAAI,SAAS,GAAG;AACd,gBAAM,MAAM,MAAM,KAAK,MAAM,MAAM;AACnC,cAAI,KAAK,OAAO,WAAW,SAAS,GAAG,GAAG;AACxC,oBAAQ,KAAK,QAAQ;AAAA,UACvB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;AGzLA,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,QAAAC,aAAY;AAIrB,IAAM,UAAyD;AAAA,EAC7D,EAAE,MAAM,cAAc,UAAU,OAAO;AAAA,EACvC,EAAE,MAAM,UAAU,UAAU,KAAK;AAAA,EACjC,EAAE,MAAM,kBAAkB,UAAU,SAAS;AAAA,EAC7C,EAAE,MAAM,YAAY,UAAU,SAAS;AAAA,EACvC,EAAE,MAAM,oBAAoB,UAAU,SAAS;AAAA,EAC/C,EAAE,MAAM,WAAW,UAAU,SAAS;AAAA,EACtC,EAAE,MAAM,WAAW,UAAU,OAAO;AAAA,EACpC,EAAE,MAAM,gBAAgB,UAAU,OAAO;AAAA,EACzC,EAAE,MAAM,oBAAoB,UAAU,SAAS;AAAA,EAC/C,EAAE,MAAM,aAAa,UAAU,QAAQ;AAAA,EACvC,EAAE,MAAM,YAAY,UAAU,QAAQ;AAAA,EACtC,EAAE,MAAM,iBAAiB,UAAU,QAAQ;AAAA,EAC3C,EAAE,MAAM,WAAW,UAAU,OAAO;AAAA,EACpC,EAAE,MAAM,iBAAiB,UAAU,MAAM;AAAA,EACzC,EAAE,MAAM,gBAAgB,UAAU,OAAO;AAAA,EACzC,EAAE,MAAM,kBAAkB,UAAU,QAAQ;AAAA,EAC5C,EAAE,MAAM,YAAY,UAAU,QAAQ;AAAA,EACtC,EAAE,MAAM,gBAAgB,UAAU,aAAa;AAAA,EAC/C,EAAE,MAAM,iBAAiB,UAAU,aAAa;AAClD;AAGA,IAAM,cAA2C;AAAA,EAC/C,CAAC,QAAQ,SAAS;AAAA,EAClB,CAAC,WAAW,SAAS;AACvB;AAGA,IAAM,UAAsC;AAAA,EAC1C,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AAAA,EACP,OAAO;AAAA,EACP,SAAS;AAAA,EACT,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,SAAS;AAAA,EACT,UAAU;AAAA,EACV,OAAO;AACT;AAOA,eAAsB,eAAe,SAAsC;AAEzE,aAAW,UAAU,SAAS;AAC5B,QAAI;AACF,YAAM,IAAI,MAAMD,MAAKC,MAAK,SAAS,OAAO,IAAI,CAAC;AAC/C,UAAI,EAAE,OAAO,KAAK,EAAE,YAAY,GAAG;AACjC,eAAO,OAAO;AAAA,MAChB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,MAAI;AACF,UAAM,aAAa,MAAMF,SAAQ,SAAS,EAAE,eAAe,KAAK,CAAC;AACjE,eAAW,SAAS,YAAY;AAC9B,UAAI,CAAC,MAAM,OAAO,EAAG;AACrB,iBAAW,CAAC,KAAK,IAAI,KAAK,aAAa;AACrC,YAAI,MAAM,KAAK,SAAS,GAAG,EAAG,QAAO;AAAA,MACvC;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAGA,QAAM,SAAS,oBAAI,IAAwB;AAC3C,MAAI;AACF,UAAM,eAAe,SAAS,QAAQ,GAAG,CAAC;AAAA,EAC5C,QAAQ;AAAA,EAER;AAEA,MAAI,OAAO,OAAO,GAAG;AACnB,QAAI,UAAsB;AAC1B,QAAI,WAAW;AACf,eAAW,CAAC,MAAM,KAAK,KAAK,QAAQ;AAClC,UAAI,QAAQ,UAAU;AACpB,mBAAW;AACX,kBAAU;AAAA,MACZ;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEA,eAAe,eACb,KACA,QACA,UACA,cACe;AACf,MAAI,gBAAgB,SAAU;AAE9B,QAAM,UAAU,MAAMA,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,aAAW,SAAS,SAAS;AAC3B,QAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,SAAS,eAAgB;AAEjE,QAAI,MAAM,YAAY,KAAK,eAAe,WAAW,GAAG;AACtD,YAAM;AAAA,QACJE,MAAK,KAAK,MAAM,IAAI;AAAA,QACpB;AAAA,QACA;AAAA,QACA,eAAe;AAAA,MACjB;AAAA,IACF,WAAW,MAAM,OAAO,GAAG;AACzB,YAAM,SAAS,MAAM,KAAK,YAAY,GAAG;AACzC,UAAI,SAAS,GAAG;AACd,cAAM,MAAM,MAAM,KAAK,MAAM,MAAM;AACnC,cAAM,OAAO,QAAQ,GAAG;AACxB,YAAI,MAAM;AACR,iBAAO,IAAI,OAAO,OAAO,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;;;AClJA,SAAS,oBAAoB;AAC7B,SAAS,QAAAC,OAAM,SAAS,WAAAC,gBAAe;;;ACEhC,IAAM,eAAe;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;;;ADXA,IAAM,SAAyB;AAAA,EAC7B,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,8CAA8C;AAAA;AAAA,EAEzD;AAAA;AAAA;AAAA,EAGA,eAAe,SAA2B;AACxC,UAAM,UAAoB,CAAC;AAG3B,UAAM,YAAY;AAClB,QAAI;AACJ,YAAQ,QAAQ,UAAU,KAAK,OAAO,OAAO,MAAM;AACjD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAGA,UAAM,cAAc;AACpB,YAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,YAAM,UAAU,MAAM,CAAC,EAAE,MAAM,GAAG;AAClC,iBAAW,OAAO,SAAS;AACzB,cAAM,UAAU,IAAI,KAAK;AACzB,YAAI,QAAS,SAAQ,KAAK,OAAO;AAAA,MACnC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,QAAI,WAAW,WAAW,GAAG,GAAG;AAC9B,YAAM,OAAO,WAAW,MAAM,MAAM,IAAI,CAAC,EAAE,UAAU;AACrD,UAAI,OAAO,QAAQ,UAAU;AAC7B,eAAS,IAAI,GAAG,IAAI,MAAM,IAAK,QAAO,QAAQ,IAAI;AAClD,YAAM,OAAO,WAAW,MAAM,IAAI,EAAE,QAAQ,OAAO,GAAG;AACtD,aAAO,iBAAiBC,MAAK,MAAM,IAAI,GAAG,YAAY;AAAA,IACxD;AAEA,UAAM,QAAQ,WAAW,QAAQ,OAAO,GAAG;AAC3C,WAAO,iBAAiBA,MAAK,SAAS,KAAK,GAAG,YAAY;AAAA,EAC5D;AAAA,EACA,gBAAgB,CAAC,eAAe,WAAW,QAAQ,eAAe,QAAQ,OAAO;AACnF;AAEA,SAAS,iBAAiB,MAAc,cAA0C;AAEhF,MAAI,aAAa,IAAI,OAAO,KAAK,EAAG,QAAO,OAAO;AAElD,MAAI,aAAa,IAAIA,MAAK,MAAM,aAAa,CAAC,EAAG,QAAOA,MAAK,MAAM,aAAa;AAChF,SAAO;AACT;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAA2B;AACxC,UAAM,UAAoB,CAAC;AAG3B,UAAM,WAAW;AACjB,QAAI;AACJ,YAAQ,QAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAChD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAGA,UAAM,WAAW;AACjB,YAAQ,QAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAChD,YAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,0BAAoB,MAAM,IAAI,OAAO;AAAA,IACvC;AAGA,UAAM,gBAAgB;AACtB,YAAQ,QAAQ,cAAc,KAAK,OAAO,OAAO,MAAM;AACrD,YAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,0BAAoB,MAAM,IAAI,SAAS,OAAO;AAAA,IAChD;AAGA,UAAM,eAAe;AACrB,YAAQ,QAAQ,aAAa,KAAK,OAAO,OAAO,MAAM;AACpD,YAAM,OAAO,MAAM,CAAC,EAAE,KAAK;AAC3B,0BAAoB,MAAM,IAAI,SAAS,MAAM;AAAA,IAC/C;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAC3D,UAAM,SAASA,MAAK,SAAS,KAAK;AAElC,QAAI,CAAC,WAAW,SAAS,IAAI,GAAG;AAE9B,YAAM,YAAY,QAAQ,UAAU;AACpC,YAAM,SAASA,MAAK,WAAW,aAAa,KAAK;AACjD,UAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,YAAM,QAAQA,MAAK,WAAW,YAAY,QAAQ;AAClD,UAAI,aAAa,IAAI,KAAK,EAAG,QAAO;AACpC,aAAO;AAAA,IACT;AAGA,QAAI,WAAW,WAAW,SAAS,GAAG;AACpC,YAAM,YAAY,QAAQ,QAAQ,UAAU,CAAC;AAC7C,YAAMC,YAAW,WAAW,MAAM,UAAU,MAAM,EAAE,MAAM,IAAI;AAC9D,eAAS,IAAIA,UAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,cAAM,OAAOA,UAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C,cAAM,SAASD,MAAK,WAAW,OAAO,KAAK;AAC3C,YAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,cAAM,QAAQA,MAAK,WAAW,MAAM,QAAQ;AAC5C,YAAI,aAAa,IAAI,KAAK,EAAG,QAAO;AAAA,MACtC;AACA,aAAO;AAAA,IACT;AAGA,QAAI,WAAW,WAAW,QAAQ,GAAG;AACnC,YAAM,UAAU,QAAQ,UAAU;AAClC,YAAMC,YAAW,WAAW,MAAM,SAAS,MAAM,EAAE,MAAM,IAAI;AAC7D,eAAS,IAAIA,UAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,cAAM,OAAOA,UAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C,cAAM,SAASD,MAAK,SAAS,OAAO,KAAK;AACzC,YAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,cAAM,QAAQA,MAAK,SAAS,MAAM,QAAQ;AAC1C,YAAI,aAAa,IAAI,KAAK,EAAG,QAAO;AAAA,MACtC;AACA,aAAO;AAAA,IACT;AAGA,UAAM,WAAW,WAAW,MAAM,IAAI;AAEtC,aAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,YAAM,OAAO,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC1C,YAAM,SAASA,MAAK,QAAQ,OAAO,KAAK;AACxC,UAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,YAAM,QAAQA,MAAK,QAAQ,MAAM,QAAQ;AACzC,UAAI,aAAa,IAAI,KAAK,EAAG,QAAO;AAAA,IACtC;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,QAAQ;AAC3B;AAQA,SAAS,oBAAoB,MAAc,QAAgB,SAAmB,YAA2B;AACvG,QAAM,UAAU,KAAK,KAAK;AAG1B,QAAM,aAAa,QAAQ,QAAQ,GAAG;AACtC,MAAI,eAAe,IAAI;AAErB,QAAI,OAAO,SAAS,GAAG,MAAM,KAAK,OAAO,KAAK;AAC9C,QAAI,YAAY;AACd,aAAO,GAAG,UAAU,KAAK,IAAI;AAAA,IAC/B;AAGA,QAAI,QAAQ,CAAC,KAAK,SAAS,GAAG,GAAG;AAC/B,cAAQ,KAAK,IAAI;AAAA,IACnB;AACA;AAAA,EACF;AAGA,MAAI,aAAa,QAAQ,MAAM,GAAG,UAAU,EAAE,KAAK;AACnD,MAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,iBAAa,WAAW,MAAM,GAAG,EAAE;AAAA,EACrC;AACA,QAAM,aAAa,SAAS,GAAG,MAAM,KAAK,UAAU,KAAK;AAGzD,MAAI,QAAQ;AACZ,MAAI,WAAW;AACf,WAAS,IAAI,YAAY,IAAI,QAAQ,QAAQ,KAAK;AAChD,QAAI,QAAQ,CAAC,MAAM,IAAK;AAAA,aACf,QAAQ,CAAC,MAAM,KAAK;AAC3B;AACA,UAAI,UAAU,GAAG;AAAE,mBAAW;AAAG;AAAA,MAAO;AAAA,IAC1C;AAAA,EACF;AACA,MAAI,aAAa,GAAI;AAGrB,QAAM,QAAQ,QAAQ,MAAM,aAAa,GAAG,QAAQ,EAAE,KAAK;AAC3D,QAAM,QAAQ,qBAAqB,KAAK;AAExC,aAAW,QAAQ,OAAO;AACxB,UAAM,UAAU,KAAK,KAAK;AAC1B,QAAI,YAAY,QAAQ;AACtB,YAAM,WAAW,aAAa,GAAG,UAAU,KAAK,UAAU,KAAK;AAC/D,cAAQ,KAAK,QAAQ;AAAA,IACvB,WAAW,SAAS;AAClB,0BAAoB,SAAS,YAAY,SAAS,UAAU;AAAA,IAC9D;AAAA,EACF;AACF;AAGA,SAAS,qBAAqB,GAAqB;AACjD,QAAM,QAAkB,CAAC;AACzB,MAAI,QAAQ;AACZ,MAAI,QAAQ;AACZ,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,QAAI,EAAE,CAAC,MAAM,IAAK;AAAA,aACT,EAAE,CAAC,MAAM,IAAK;AAAA,aACd,EAAE,CAAC,MAAM,OAAO,UAAU,GAAG;AACpC,YAAM,KAAK,EAAE,MAAM,OAAO,CAAC,CAAC;AAC5B,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AACA,QAAM,KAAK,EAAE,MAAM,KAAK,CAAC;AACzB,SAAO;AACT;AAGA,IAAM,KAAqB;AAAA,EACzB,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAA2B;AACxC,UAAM,UAAoB,CAAC;AAG3B,UAAM,cAAc;AACpB,QAAI;AACJ,YAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAGA,UAAM,aAAa;AACnB,YAAQ,QAAQ,WAAW,KAAK,OAAO,OAAO,MAAM;AAClD,YAAM,QAAQ,MAAM,CAAC;AACrB,YAAM,aAAa;AACnB,UAAI;AACJ,cAAQ,QAAQ,WAAW,KAAK,KAAK,OAAO,MAAM;AAChD,gBAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,MACvB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAG5D,UAAM,YAAY,eAAe,OAAO;AACxC,QAAI,CAAC,aAAa,CAAC,WAAW,WAAW,SAAS,EAAG,QAAO;AAE5D,UAAM,UAAU,WAAW,MAAM,UAAU,SAAS,CAAC;AAErD,UAAM,SAASA,MAAK,SAAS,OAAO;AACpC,eAAW,KAAK,cAAc;AAC5B,UAAI,EAAE,WAAW,SAAS,GAAG,KAAK,EAAE,SAAS,KAAK,EAAG,QAAO;AAAA,IAC9D;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,QAAQ;AAC3B;AAMA,IAAM,aAAa,oBAAI,IAA2B;AAClD,SAAS,eAAe,SAAgC;AACtD,MAAI,WAAW,IAAI,OAAO,EAAG,QAAO,WAAW,IAAI,OAAO;AAC1D,MAAI;AACF,UAAM,UAAU,aAAaA,MAAK,SAAS,QAAQ,GAAG,OAAO;AAC7D,UAAM,QAAQ,QAAQ,MAAM,kBAAkB;AAC9C,UAAM,SAAS,QAAQ,MAAM,CAAC,EAAE,KAAK,IAAI;AACzC,eAAW,IAAI,SAAS,MAAM;AAC9B,WAAO;AAAA,EACT,QAAQ;AACN,eAAW,IAAI,SAAS,IAAI;AAC5B,WAAO;AAAA,EACT;AACF;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,OAAO;AAAA,EACpB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA;AAAA,IAGd,EAAE,OAAO,uCAAuC;AAAA,EAClD;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAE5D,QAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,aAAO;AAAA,IACT;AAKA,UAAM,WAAW,WAAW,MAAM,GAAG;AACrC,aAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,YAAM,WAAW,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI;AAClD,iBAAW,WAAW,CAAC,IAAI,kBAAkB,QAAQ,oBAAoB,GAAG;AAC1E,cAAM,OAAOA,MAAK,SAAS,SAAS,QAAQ;AAC5C,YAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,MACrC;AAAA,IACF;AAGA,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,WAAW,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;AAC/C,iBAAW,WAAW,CAAC,IAAI,kBAAkB,QAAQ,oBAAoB,GAAG;AAC1E,cAAM,OAAOA,MAAK,SAAS,SAAS,QAAQ;AAC5C,YAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,MACrC;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,SAAS,UAAU,aAAa,SAAS;AAC5D;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,MAAM,QAAQ,OAAO,QAAQ,MAAM,MAAM;AAAA,EACtD,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,0BAA0B;AAAA,EACrC;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,UAAM,aAAaE,SAAQ,QAAQ,UAAU,GAAG,UAAU;AAC1D,QAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AAEzC,UAAM,WAAWF,MAAK,SAAS,UAAU;AACzC,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AAEvC,eAAW,UAAU,CAAC,WAAW,KAAK,GAAG;AACvC,YAAM,OAAOA,MAAK,SAAS,QAAQ,UAAU;AAC7C,UAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,IACrC;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,SAAS,eAAe,SAAS,SAAS;AAC7D;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,0CAA0C;AAAA;AAAA,IAEnD,EAAE,OAAO,iCAAiC;AAAA,EAC5C;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAC3D,UAAM,UAAU,WAAW,SAAS,KAAK,IAAI,aAAa,aAAa;AAEvE,UAAM,aAAaE,SAAQ,QAAQ,UAAU,GAAG,OAAO;AACvD,QAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AAEzC,UAAM,WAAWF,MAAK,SAAS,OAAO;AACtC,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,UAAM,UAAUA,MAAK,SAAS,OAAO,OAAO;AAC5C,QAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AACtC,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,UAAU,WAAW;AACxC;AAGA,IAAM,MAAsB;AAAA,EAC1B,IAAI;AAAA,EACJ,YAAY,CAAC,MAAM;AAAA,EACnB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,uDAAuD;AAAA;AAAA,IAEhE,EAAE,OAAO,sEAAsE;AAAA;AAAA,IAE/E,EAAE,OAAO,8CAA8C;AAAA,EACzD;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,QAAI,iBAAiB;AACrB,QAAI,eAAe,WAAW,GAAG,GAAG;AAClC,uBAAiB,eAAe,MAAM,CAAC;AAAA,IACzC;AAEA,QAAI,eAAe,SAAS,GAAG,KAAK,eAAe,SAAS,MAAM,GAAG;AACnE,YAAM,UAAU,eAAe,SAAS,MAAM,IAAI,iBAAiB,iBAAiB;AACpF,YAAM,aAAaE,SAAQ,QAAQ,UAAU,GAAG,OAAO;AACvD,UAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AACzC,YAAMC,YAAWH,MAAK,SAAS,OAAO;AACtC,UAAI,aAAa,IAAIG,SAAQ,EAAG,QAAOA;AACvC,aAAO;AAAA,IACT;AAEA,UAAM,WAAW,WAAW,QAAQ,OAAO,GAAG,IAAI;AAClD,UAAM,WAAWH,MAAK,SAAS,QAAQ;AACvC,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,UAAM,UAAUA,MAAK,SAAS,OAAO,QAAQ;AAC7C,QAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AACtC,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,QAAQ;AAC3B;AAIA,IAAM,mBAAmB,oBAAI,IAAI,CAAC,WAAW,QAAQ,eAAe,eAAe,CAAC;AAEpF,IAAM,QAAwB;AAAA,EAC5B,IAAI;AAAA,EACJ,YAAY,CAAC,QAAQ;AAAA,EACrB,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAAiB,UAAkB,UAAkB,cAAqC;AACvG,UAAM,UAAoB,CAAC;AAG3B,UAAM,cAAc;AACpB,QAAI;AACJ,YAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAIA,UAAM,UAAU,oBAAI,IAAoB;AACxC,eAAW,KAAK,cAAc;AAC5B,UAAI,MAAM,YAAY,CAAC,EAAE,SAAS,QAAQ,EAAG;AAC7C,YAAM,WAAW,EAAE,MAAM,GAAG,EAAE,IAAI,EAAG,QAAQ,YAAY,EAAE;AAC3D,UAAI,CAAC,YAAY,iBAAiB,IAAI,QAAQ,EAAG;AACjD,cAAQ,IAAI,UAAU,CAAC;AAAA,IACzB;AAEA,QAAI,QAAQ,OAAO,GAAG;AACpB,YAAM,UAAU,CAAC,GAAG,QAAQ,KAAK,CAAC,EAAE;AAAA,QAAI,CAAC,MACvC,EAAE,QAAQ,uBAAuB,MAAM;AAAA,MACzC;AACA,YAAM,WAAW,IAAI,OAAO,OAAO,QAAQ,KAAK,GAAG,CAAC,QAAQ,GAAG;AAC/D,YAAM,UAAU,oBAAI,IAAY;AAChC,cAAQ,QAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAChD,cAAM,WAAW,MAAM,CAAC;AACxB,cAAM,aAAa,QAAQ,IAAI,QAAQ;AACvC,YAAI,cAAc,CAAC,QAAQ,IAAI,UAAU,GAAG;AAC1C,kBAAQ,IAAI,UAAU;AACtB,kBAAQ,KAAK,UAAU;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAE5D,QAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AAGzC,UAAM,SAASA,MAAK,SAAS,WAAW,UAAU;AAClD,eAAW,KAAK,cAAc;AAC5B,UAAI,EAAE,WAAW,SAAS,GAAG,KAAK,EAAE,SAAS,QAAQ,EAAG,QAAO;AAAA,IACjE;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,YAAY,aAAa;AAC5C;AAGA,IAAM,SAAyB;AAAA,EAC7B,IAAI;AAAA,EACJ,YAAY,CAAC,OAAO,MAAM;AAAA,EAC1B,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,wBAAwB;AAAA,EACnC;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAE5D,QAAI,WAAW,SAAS,IAAI,GAAG;AAC7B,aAAO;AAAA,IACT;AAGA,QAAI,YAAY;AAChB,QAAI,UAAU,SAAS,GAAG,GAAG;AAC3B,kBAAY,UAAU,MAAM,GAAG,EAAE;AAAA,IACnC;AAGA,UAAM,WAAW,UAAU,MAAM,GAAG;AACpC,UAAM,WAAW,SAAS,KAAK,GAAG;AAClC,eAAW,OAAO,CAAC,OAAO,MAAM,GAAG;AACjC,iBAAW,WAAW;AAAA,QACpB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,GAAG;AACD,cAAM,OAAOA,MAAK,SAAS,SAAS,WAAW,GAAG;AAClD,YAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,MACrC;AAAA,IACF;AAEA,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,aAAa,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG;AAC7C,iBAAW,OAAO,CAAC,OAAO,MAAM,GAAG;AACjC,mBAAW,WAAW;AAAA,UACpB;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF,GAAG;AACD,gBAAM,OAAOA,MAAK,SAAS,SAAS,aAAa,GAAG;AACpD,cAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,SAAS,aAAa,SAAS;AAClD;AAIA,IAAM,qBAAqB,oBAAI,IAAI,CAAC,gBAAgB,cAAc,CAAC;AAEnE,IAAM,SAAyB;AAAA,EAC7B,IAAI;AAAA,EACJ,YAAY,CAAC,KAAK;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAAS,UAAU,UAAU,cAAc;AACxD,UAAM,UAAoB,CAAC;AAG3B,UAAM,aAAa;AACnB,QAAI;AACJ,YAAQ,QAAQ,WAAW,KAAK,OAAO,OAAO,MAAM;AAClD,cAAQ,KAAK,MAAM,CAAC,CAAC;AAAA,IACvB;AAIA,UAAM,WAAW,oBAAI,IAAoB;AACzC,eAAW,KAAK,cAAc;AAC5B,UAAI,MAAM,SAAU;AACpB,UAAI,CAAC,EAAE,SAAS,KAAK,EAAG;AACxB,YAAM,WAAW,EAAE,MAAM,GAAG,EAAE,IAAI;AAClC,YAAM,YAAY,SAAS,QAAQ,gBAAgB,EAAE,EAAE,QAAQ,UAAU,EAAE;AAC3E,UAAI,CAAC,aAAa,mBAAmB,IAAI,SAAS,EAAG;AACrD,eAAS,IAAI,WAAW,CAAC;AAAA,IAC3B;AAEA,QAAI,SAAS,OAAO,GAAG;AAErB,YAAM,UAAU,CAAC,GAAG,SAAS,KAAK,CAAC,EAAE;AAAA,QAAI,CAAC,MACxC,EAAE,QAAQ,uBAAuB,MAAM;AAAA,MACzC;AACA,YAAM,WAAW,IAAI,OAAO,OAAO,QAAQ,KAAK,GAAG,CAAC,QAAQ,GAAG;AAC/D,YAAM,UAAU,oBAAI,IAAY;AAChC,cAAQ,QAAQ,SAAS,KAAK,OAAO,OAAO,MAAM;AAChD,cAAM,YAAY,MAAM,CAAC;AACzB,cAAM,aAAa,SAAS,IAAI,SAAS;AACzC,YAAI,cAAc,CAAC,QAAQ,IAAI,UAAU,GAAG;AAC1C,kBAAQ,IAAI,UAAU;AACtB,kBAAQ,KAAK,UAAU;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAE5D,QAAI,aAAa,IAAI,UAAU,EAAG,QAAO;AAGzC,UAAM,WAAW,WAAW,MAAM,GAAG;AAGrC,aAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,YAAM,WAAW,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI;AAClD,iBAAW,WAAW,CAAC,IAAI,QAAQ,MAAM,GAAG;AAC1C,cAAM,OAAOA,MAAK,SAAS,SAAS,QAAQ;AAC5C,YAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,MACrC;AAAA,IACF;AAIA,aAAS,QAAQ,GAAG,QAAQ,SAAS,QAAQ,SAAS;AACpD,YAAM,UAAU,SAAS,MAAM,KAAK,EAAE,KAAK,GAAG;AAC9C,iBAAW,WAAW,CAAC,IAAI,QAAQ,MAAM,GAAG;AAC1C,cAAM,SAASA,MAAK,SAAS,SAAS,OAAO,IAAI;AACjD,mBAAW,KAAK,cAAc;AAC5B,cAAI,EAAE,WAAW,MAAM,KAAK,EAAE,SAAS,KAAK,EAAG,QAAO;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,OAAO,OAAO,SAAS,YAAY,aAAa;AACnE;AAGA,IAAM,OAAuB;AAAA,EAC3B,IAAI;AAAA,EACJ,YAAY,CAAC,OAAO;AAAA,EACpB,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,+BAA+B;AAAA;AAAA,IAExC,EAAE,OAAO,+BAA+B;AAAA,EAC1C;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,QAAI,WAAW,WAAW,OAAO,EAAG,QAAO;AAG3C,QAAI,WAAW,WAAW,UAAU,GAAG;AACrC,YAAM,aAAa,gBAAgB,OAAO;AAC1C,UAAI,CAAC,WAAY,QAAO;AACxB,YAAM,SAAS,WAAW,UAAU;AACpC,UAAI,CAAC,WAAW,WAAW,MAAM,EAAG,QAAO;AAC3C,YAAM,UAAU,WAAW,MAAM,OAAO,MAAM;AAE9C,YAAM,UAAUA,MAAK,SAAS,OAAO,OAAO;AAC5C,UAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AAEtC,YAAM,WAAWA,MAAK,SAAS,OAAO;AACtC,UAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,aAAO;AAAA,IACT;AAGA,UAAM,WAAWE,SAAQ,QAAQ,UAAU,GAAG,UAAU;AACxD,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AACvC,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,gBAAgB,SAAS,aAAa;AACzD;AAEA,IAAM,mBAAmB,oBAAI,IAA2B;AACxD,SAAS,gBAAgB,SAAgC;AACvD,MAAI,iBAAiB,IAAI,OAAO,EAAG,QAAO,iBAAiB,IAAI,OAAO;AACtE,MAAI;AACF,UAAM,UAAU,aAAaF,MAAK,SAAS,cAAc,GAAG,OAAO;AACnE,UAAM,QAAQ,QAAQ,MAAM,iBAAiB;AAC7C,UAAM,OAAO,QAAQ,MAAM,CAAC,IAAI;AAChC,qBAAiB,IAAI,SAAS,IAAI;AAClC,WAAO;AAAA,EACT,QAAQ;AACN,qBAAiB,IAAI,SAAS,IAAI;AAClC,WAAO;AAAA,EACT;AACF;AAGA,IAAM,QAAwB;AAAA,EAC5B,IAAI;AAAA,EACJ,YAAY,CAAC,UAAU,KAAK;AAAA,EAC5B,cAAc;AAAA,EACd,gBAAgB,CAAC;AAAA;AAAA,EACjB,eAAe,SAA2B;AACxC,UAAM,UAAoB,CAAC;AAI3B,UAAM,cAAc;AACpB,QAAI;AACJ,YAAQ,QAAQ,YAAY,KAAK,OAAO,OAAO,MAAM;AACnD,YAAM,OAAO,MAAM,CAAC;AAEpB,YAAM,aAAa,KAAK,MAAM,yBAAyB;AACvD,UAAI,YAAY;AACd,cAAM,SAAS,WAAW,CAAC;AAC3B,cAAM,QAAQ,WAAW,CAAC,EAAE,MAAM,GAAG;AACrC,mBAAW,QAAQ,OAAO;AACxB,gBAAM,UAAU,KAAK,KAAK,EAAE,MAAM,KAAK,EAAE,CAAC;AAC1C,cAAI,YAAY,IAAK;AACrB,kBAAQ,KAAK,GAAG,MAAM,IAAI,OAAO,EAAE;AAAA,QACrC;AAAA,MACF,WAAW,KAAK,SAAS,IAAI,GAAG;AAE9B;AAAA,MACF,OAAO;AACL,gBAAQ,KAAK,IAAI;AAAA,MACnB;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EACA,cAAc,YAAY,aAAa,SAAS,cAAc;AAC5D,UAAM,WAAW,WAAW,MAAM,GAAG;AAErC,aAAS,IAAI,SAAS,QAAQ,IAAI,GAAG,KAAK;AACxC,YAAM,WAAW,SAAS,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG;AAC9C,iBAAW,OAAO,CAAC,UAAU,KAAK,GAAG;AACnC,mBAAW,WAAW,CAAC,IAAI,mBAAmB,QAAQ,MAAM,GAAG;AAC7D,gBAAM,OAAOA,MAAK,SAAS,SAAS,WAAW,GAAG;AAClD,cAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AAEA,aAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,YAAM,aAAa,SAAS,MAAM,CAAC,EAAE,KAAK,GAAG;AAC7C,iBAAW,OAAO,CAAC,UAAU,KAAK,GAAG;AACnC,mBAAW,WAAW,CAAC,IAAI,mBAAmB,QAAQ,MAAM,GAAG;AAC7D,gBAAM,OAAOA,MAAK,SAAS,SAAS,aAAa,GAAG;AACpD,cAAI,aAAa,IAAI,IAAI,EAAG,QAAO;AAAA,QACrC;AAAA,MACF;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,UAAU,UAAU,aAAa,UAAU;AAC9D;AAGA,IAAM,aAA6B;AAAA,EACjC,IAAI;AAAA,EACJ,YAAY,CAAC,OAAO,QAAQ,OAAO,QAAQ,QAAQ,MAAM;AAAA,EACzD,cAAc;AAAA,EACd,gBAAgB;AAAA;AAAA,IAEd,EAAE,OAAO,mEAAmE;AAAA;AAAA,IAE5E,EAAE,OAAO,yCAAyC;AAAA;AAAA,IAElD,EAAE,OAAO,mFAAmF;AAAA;AAAA,IAE5F,EAAE,OAAO,0CAA0C;AAAA,EACrD;AAAA,EACA,cAAc,YAAY,YAAY,SAAS,cAAc;AAE3D,QAAI,WAAW,WAAW,OAAO,EAAG,QAAO;AAC3C,QAAI,CAAC,WAAW,WAAW,GAAG,EAAG,QAAO;AAGxC,UAAM,WAAWE,SAAQ,QAAQ,UAAU,GAAG,UAAU;AAGxD,QAAI,aAAa,IAAI,QAAQ,EAAG,QAAO;AAGvC,QAAI,SAAS,SAAS,KAAK,GAAG;AAC5B,YAAM,SAAS,SAAS,MAAM,GAAG,EAAE,IAAI;AACvC,UAAI,aAAa,IAAI,MAAM,EAAG,QAAO;AACrC,YAAM,UAAU,SAAS,MAAM,GAAG,EAAE,IAAI;AACxC,UAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AAAA,IACxC;AACA,QAAI,SAAS,SAAS,MAAM,GAAG;AAC7B,YAAM,UAAU,SAAS,MAAM,GAAG,EAAE,IAAI;AACxC,UAAI,aAAa,IAAI,OAAO,EAAG,QAAO;AAAA,IACxC;AAGA,eAAW,OAAO,CAAC,OAAO,QAAQ,OAAO,QAAQ,QAAQ,MAAM,GAAG;AAChE,UAAI,aAAa,IAAI,WAAW,GAAG,EAAG,QAAO,WAAW;AAAA,IAC1D;AAGA,eAAW,OAAO,CAAC,aAAa,cAAc,aAAa,YAAY,GAAG;AACxE,UAAI,aAAa,IAAI,WAAW,GAAG,EAAG,QAAO,WAAW;AAAA,IAC1D;AAEA,WAAO;AAAA,EACT;AAAA,EACA,gBAAgB,CAAC,gBAAgB,cAAc,QAAQ,SAAS,UAAU;AAC5E;AAGA,IAAM,mBAA8D;AAAA,EAClE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,SAAS;AAAA,EACT,WAAW;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEO,SAAS,kBAAkB,IAAuC;AACvE,SAAO,iBAAiB,EAAE,KAAK;AACjC;;;AL/xBA,eAAsB,eACpB,SACA,UAA0B,CAAC,GACD;AAC1B,QAAM,aAAaE,SAAQ,OAAO;AAGlC,MAAI;AACF,UAAM,IAAI,MAAMC,MAAK,UAAU;AAC/B,QAAI,CAAC,EAAE,YAAY,GAAG;AACpB,YAAM,IAAI,cAAc,oBAAoB,UAAU,EAAE;AAAA,IAC1D;AAAA,EACF,SAAS,OAAO;AACd,QAAI,iBAAiB,cAAe,OAAM;AAC1C,UAAM,IAAI,cAAc,wBAAwB,UAAU,IAAI,EAAE,OAAO,MAAM,CAAC;AAAA,EAChF;AAEA,QAAM,WAAW,QAAQ,YAAa,MAAM,eAAe,UAAU;AAErE,MAAI;AACF,UAAM,SAAS,kBAAkB,QAAQ;AACzC,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,cAAc,oCAAoC,QAAQ,EAAE;AAAA,IACxE;AAEA,WAAO,MAAM,IAAI,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO;AAAA,EAClE,SAAS,OAAO;AACd,QAAI,iBAAiB,cAAe,OAAM;AAC1C,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACrE,UAAM,IAAI,cAAc,SAAS,EAAE,OAAO,MAAM,CAAC;AAAA,EACnD;AACF;AAGO,IAAM,gBAAN,cAA4B,MAAM;AAAA,EACvC,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACd;AACF;;;AO3DA,IAAI,gBAAwB,aAAa;AAGlC,SAAS,YAAoB;AAClC,SAAO;AACT;AAGO,SAAS,UAAU,QAAsB;AAC9C,kBAAgB;AAClB;AAGA,SAAS,eAAuB;AAC9B,QAAM,MAAM,QAAQ,IAAI,UAAU,QAAQ,IAAI,QAAQ;AACtD,MAAI,IAAI,WAAW,IAAI,EAAG,QAAO;AACjC,SAAO;AACT;AAGO,SAAS,EAAE,KAAa,MAAgD;AAC7E,QAAM,WAAW,kBAAkB,OAAO,KAAK;AAC/C,MAAI,MAAO,SAAoC,GAAG,KAAM,GAA8B,GAAG,KAAK;AAE9F,MAAI,MAAM;AACR,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,IAAI,GAAG;AACzC,YAAM,IAAI,WAAW,IAAI,CAAC,KAAK,OAAO,CAAC,CAAC;AAAA,IAC1C;AAAA,EACF;AACA,SAAO;AACT;AAIA,IAAM,KAAK;AAAA;AAAA,EAET,mBAAmB;AAAA,EACnB,qBAAqB;AAAA;AAAA,EAGrB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,2BAA2B;AAAA;AAAA,EAG3B,uBAAuB;AAAA;AAAA,EAGvB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,4BAA4B;AAAA;AAAA,EAG5B,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA,EAGlB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA;AAAA,EAGhB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,qBAAqB;AAAA;AAAA,EAGrB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,yBAAyB;AAAA;AAAA,EAGzB,gBAAgB;AAAA;AAAA,EAGhB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA;AAAA,EAGjB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA;AAAA,EAGhB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,4BAA4B;AAAA;AAAA,EAG5B,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,wBAAwB;AAC1B;AAEA,IAAM,KAAK;AAAA;AAAA,EAET,mBAAmB;AAAA,EACnB,qBAAqB;AAAA;AAAA,EAGrB,uBAAuB;AAAA,EACvB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,2BAA2B;AAAA;AAAA,EAG3B,uBAAuB;AAAA;AAAA,EAGvB,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,cAAc;AAAA,EACd,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,sBAAsB;AAAA,EACtB,uBAAuB;AAAA,EACvB,oBAAoB;AAAA,EACpB,oBAAoB;AAAA,EACpB,4BAA4B;AAAA;AAAA,EAG5B,oBAAoB;AAAA,EACpB,mBAAmB;AAAA,EACnB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,kBAAkB;AAAA;AAAA,EAGlB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,kBAAkB;AAAA,EAClB,kBAAkB;AAAA,EAClB,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,gBAAgB;AAAA;AAAA,EAGhB,uBAAuB;AAAA,EACvB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,gBAAgB;AAAA,EAChB,wBAAwB;AAAA,EACxB,qBAAqB;AAAA;AAAA,EAGrB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,EACtB,yBAAyB;AAAA,EACzB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,uBAAuB;AAAA,EACvB,yBAAyB;AAAA,EACzB,wBAAwB;AAAA,EACxB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,oBAAoB;AAAA,EACpB,yBAAyB;AAAA;AAAA,EAGzB,gBAAgB;AAAA;AAAA,EAGhB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,mBAAmB;AAAA,EACnB,iBAAiB;AAAA;AAAA,EAGjB,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,YAAY;AAAA,EACZ,gBAAgB;AAAA,EAChB,iBAAiB;AAAA,EACjB,gBAAgB;AAAA;AAAA,EAGhB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,iBAAiB;AAAA,EACjB,4BAA4B;AAAA;AAAA,EAG5B,kBAAkB;AAAA,EAClB,iBAAiB;AAAA,EACjB,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,sBAAsB;AAAA,EACtB,qBAAqB;AAAA,EACrB,qBAAqB;AAAA,EACrB,wBAAwB;AAC1B;;;AC5PO,SAAS,qBACd,OACA,UAA6B,CAAC,GACtB;AACR,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,QAAkB,CAAC;AACzB,QAAM,QAAQ,OAAO,OAAO,MAAM,KAAK;AAGvC,QAAM,KAAK,EAAE,eAAe,CAAC;AAC7B,QAAM,KAAK,EAAE,kBAAkB,CAAC;AAChC,QAAM,KAAK,EAAE,sBAAsB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC/D,QAAM,KAAK,EAAE,sBAAsB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC/D,QAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC,CAAC;AAGnF,QAAM,WAAW,MACd,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,CAAC,EACrC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM,EACxD,MAAM,GAAG,IAAI;AAEhB,MAAI,SAAS,SAAS,GAAG;AACvB,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,SAAS,OAAO,CAAC,CAAC;AACjE,eAAW,KAAK,UAAU;AACxB,YAAM,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,WAAW,OAAO,CAAC,CAAC;AAAA,IACpF;AAAA,EACF;AAGA,MAAI,MAAM,qBAAqB,SAAS,GAAG;AACzC,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC,CAAC;AACnF,eAAW,KAAK,MAAM,sBAAsB;AAC1C,YAAM,KAAK,EAAE,wBAAwB,EAAE,OAAO,EAAE,MAAM,KAAK,UAAK,EAAE,CAAC,CAAC;AAAA,IACtE;AAAA,EACF;AAGA,QAAM,eAAe,MAClB,OAAO,CAAC,MAAM,EAAE,aAAa,SAAS,CAAC,EACvC,KAAK,CAAC,GAAG,MAAM,EAAE,aAAa,SAAS,EAAE,aAAa,MAAM,EAC5D,MAAM,GAAG,IAAI;AAEhB,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,KAAK,EAAE,yBAAyB,EAAE,OAAO,aAAa,OAAO,CAAC,CAAC;AACrE,eAAW,KAAK,cAAc;AAC5B,YAAM,KAAK,EAAE,wBAAwB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,aAAa,OAAO,CAAC,CAAC;AAAA,IACtF;AAAA,EACF;AAGA,QAAM,UAAU,MAAM;AAAA,IACpB,CAAC,MAAM,EAAE,WAAW,WAAW,KAAK,EAAE,aAAa,WAAW;AAAA,EAChE;AAEA,MAAI,QAAQ,SAAS,GAAG;AACtB,UAAM,KAAK,EAAE,uBAAuB,EAAE,OAAO,QAAQ,OAAO,CAAC,CAAC;AAC9D,eAAW,KAAK,SAAS;AACvB,YAAM,KAAK,KAAK,EAAE,IAAI,EAAE;AAAA,IAC1B;AAAA,EACF;AAGA,QAAM,YAAY,oBAAI,IAAoB;AAC1C,aAAW,KAAK,OAAO;AACrB,UAAM,MAAM,EAAE,KAAK,SAAS,GAAG,IAAI,EAAE,KAAK,UAAU,GAAG,EAAE,KAAK,YAAY,GAAG,CAAC,IAAI;AAClF,cAAU,IAAI,MAAM,UAAU,IAAI,GAAG,KAAK,KAAK,CAAC;AAAA,EAClD;AAEA,MAAI,UAAU,OAAO,GAAG;AACtB,UAAM,KAAK,EAAE,oBAAoB,CAAC;AAClC,UAAM,SAAS,CAAC,GAAG,UAAU,QAAQ,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AAClE,eAAW,CAAC,KAAK,KAAK,KAAK,QAAQ;AACjC,YAAM,KAAK,EAAE,qBAAqB,EAAE,KAAK,MAAM,CAAC,CAAC;AAAA,IACnD;AAAA,EACF;AAGA,MAAI,MAAM,qBAAqB,WAAW,KAAK,QAAQ,WAAW,GAAG;AACnE,UAAM,KAAK,EAAE,kBAAkB,CAAC;AAAA,EAClC;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;;;AC5FA,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,gBAAAC,qBAAoB;AAY7B,IAAM,eAAe;AAAA,EACnB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAQA,eAAsB,kBACpB,aACA,WACA,eAC0B;AAC1B,QAAM,SAA0C,CAAC;AACjD,QAAM,gBAAiC,CAAC;AAExC,WAAS,MAAM,GAAG,MAAM,UAAU,QAAQ,OAAO;AAC/C,UAAM,MAAM,UAAU,GAAG;AACzB,UAAM,YAAYC,SAAQ,aAAa,IAAI,SAAS;AAEpD,UAAM,QAAQ,MAAM,eAAe,WAAW;AAAA,MAC5C,SAAS,IAAI,WAAW;AAAA,MACxB,UAAU,IAAI;AAAA,IAChB,CAAC;AAGD,UAAM,WAAW,IAAI,YAAa,MAAM,eAAe,SAAS,KAAM;AAEtE,WAAO,IAAI,IAAI,IAAI;AAEnB,kBAAc,KAAK;AAAA,MACjB,MAAM,IAAI;AAAA,MACV,iBAAiB,MAAM;AAAA,MACvB;AAAA,MACA,OAAO,IAAI,SAAS,aAAa,MAAM,aAAa,MAAM;AAAA,MAC1D,aAAa,IAAI;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,IACnB,CAAC;AAAA,EACH;AAEA,QAAM,SAAS,iBAAiB,aAAa,MAAM;AAEnD,SAAO,EAAE,QAAQ,eAAe,OAAO;AACzC;AAiBO,SAAS,4BACd,QACA,WACwB;AACxB,QAAM,kBAAkB;AACxB,QAAM,sBAAsB;AAG5B,QAAM,mBAAmB,oBAAI,IAAiC;AAC9D,aAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACvD,UAAM,cAAc,oBAAI,IAAoB;AAC5C,eAAW,YAAY,OAAO,KAAK,MAAM,KAAK,GAAG;AAC/C,YAAM,WAAW,SAAS,MAAM,GAAG,EAAE,IAAI;AACzC,YAAM,YAAY,SAAS,QAAQ,YAAY,EAAE;AACjD,UAAI,UAAU,SAAS,mBAAmB,kBAAkB,IAAI,UAAU,YAAY,CAAC,EAAG;AAC1F,kBAAY,IAAI,WAAW,QAAQ;AAAA,IACrC;AACA,qBAAiB,IAAI,WAAW,WAAW;AAAA,EAC7C;AAGA,QAAM,iBAAiB,oBAAI,IAAoB;AAC/C,aAAW,CAAC,EAAE,GAAG,KAAK,kBAAkB;AACtC,eAAW,QAAQ,IAAI,KAAK,GAAG;AAC7B,qBAAe,IAAI,OAAO,eAAe,IAAI,IAAI,KAAK,KAAK,CAAC;AAAA,IAC9D;AAAA,EACF;AAGA,QAAM,WAAW,oBAAI,IAA2D;AAEhF,WAAS,OAAO,SAAiB,MAA4B,OAAe;AAC1E,QAAI,QAAQ,oBAAqB;AACjC,UAAM,WAAW,SAAS,IAAI,OAAO;AACrC,QAAI,CAAC,YAAY,QAAQ,SAAS,OAAO;AACvC,eAAS,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAAA,IACvC;AAAA,EACF;AAGA,WAAS,cAAc,SAAiB,MAAuB;AAC7D,UAAM,cAAc;AAAA,MAClB,IAAI,OAAO,8DAA8D,YAAY,IAAI,CAAC,KAAK;AAAA,MAC/F,IAAI,OAAO,6BAA6B,YAAY,IAAI,CAAC,KAAK;AAAA,MAC9D,IAAI,OAAO,MAAM,YAAY,IAAI,CAAC,6CAA6C;AAAA,IACjF;AACA,WAAO,YAAY,KAAK,CAAC,OAAO,GAAG,KAAK,OAAO,CAAC;AAAA,EAClD;AAGA,WAAS,kBAAkB,SAAiB,MAAuB;AACjE,UAAM,QAAQ,IAAI,OAAO,MAAM,YAAY,IAAI,CAAC,OAAO,GAAG;AAC1D,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAI,gBAAgB;AACpB,eAAW,QAAQ,OAAO;AACxB,UAAI,CAAC,MAAM,KAAK,IAAI,EAAG;AACvB,YAAM,YAAY;AAElB,YAAM,gBAAgB,4EAA4E,KAAK,IAAI;AAC3G,UAAI,CAAC,eAAe;AAClB,wBAAgB;AAChB;AAAA,MACF;AAAA,IACF;AACA,WAAO,CAAC;AAAA,EACV;AAEA,aAAW,CAAC,aAAa,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACzD,UAAM,WAAW,iBAAiB,IAAI,WAAW,KAAK,oBAAI,IAAI;AAE9D,eAAW,YAAY,OAAO,KAAK,MAAM,KAAK,GAAG;AAC/C,YAAM,UAAUC,MAAK,MAAM,SAAS,QAAQ;AAC5C,UAAI;AACJ,UAAI;AACF,kBAAUC,cAAa,SAAS,OAAO;AAAA,MACzC,QAAQ;AAAE;AAAA,MAAU;AAGpB,iBAAW,CAAC,aAAa,SAAS,KAAK,kBAAkB;AACvD,YAAI,gBAAgB,YAAa;AACjC,mBAAW,CAAC,YAAY,UAAU,KAAK,WAAW;AAEhD,cAAI,SAAS,IAAI,UAAU,EAAG;AAE9B,eAAK,eAAe,IAAI,UAAU,KAAK,KAAK,EAAG;AAC/C,cAAI,CAAC,QAAQ,SAAS,UAAU,EAAG;AACnC,gBAAM,QAAQ,IAAI,OAAO,MAAM,YAAY,UAAU,CAAC,KAAK;AAC3D,cAAI,CAAC,MAAM,KAAK,OAAO,EAAG;AAE1B,cAAI,cAAc,SAAS,UAAU,EAAG;AAExC,cAAI,kBAAkB,SAAS,UAAU,EAAG;AAE5C,gBAAM,UAAU,GAAG,WAAW,SAAI,WAAW;AAE7C,gBAAM,eAAe,cAAc,KAAK,UAAU;AAClD,gBAAM,YAAY,WAAW,UAAU,eAAe,IAAI;AAC1D,iBAAO,SAAS;AAAA,YACd,WAAW;AAAA,YACX,UAAU;AAAA,YACV,SAAS;AAAA,YACT,QAAQ;AAAA,YACR,MAAM;AAAA,YACN,OAAO;AAAA,UACT,GAAG,SAAS;AAAA,QACd;AAAA,MACF;AAGA,iBAAW,OAAO,WAAW;AAC3B,YAAI,IAAI,SAAS,YAAa;AAC9B,cAAM,UAAU,GAAG,WAAW,SAAI,IAAI,IAAI;AAC1C,cAAM,YAAY,IAAI;AACtB,cAAM,WAAW,CAAC,UAAU,WAAW,OAAO,WAAW,YAAY,WAAW,WAAW,SAAS,WAAW,WAAW;AAC1H,cAAM,UAAU,IAAI,OAAO,MAAM,YAAY,SAAS,CAAC,MAAM,SAAS,KAAK,GAAG,CAAC,MAAM;AACrF,YAAI,QAAQ,KAAK,OAAO,GAAG;AACzB,gBAAM,cAAc,OAAO,IAAI,IAAI;AACnC,cAAI,CAAC,YAAa;AAClB,gBAAM,YAAY,eAAe,WAAW;AAC5C,cAAI,WAAW;AACb,mBAAO,SAAS;AAAA,cACd,WAAW;AAAA,cACX,UAAU;AAAA,cACV,SAAS,IAAI;AAAA,cACb,QAAQ;AAAA,cACR,MAAM;AAAA,cACN,OAAO,GAAG,SAAS;AAAA,YACrB,GAAG,EAAE;AAAA,UACP;AAAA,QACF;AAAA,MACF;AAGA,iBAAW,OAAO,WAAW;AAC3B,YAAI,IAAI,SAAS,YAAa;AAC9B,cAAM,UAAU,GAAG,WAAW,SAAI,IAAI,IAAI;AAC1C,cAAM,UAAU,IAAI,UAAU,MAAM,GAAG,EAAE,IAAI;AAC7C,cAAM,cAAc,QAAQ,UAAU;AAEtC,cAAM,WAA4C,CAAC;AAEnD,YAAI,CAAC,aAAa;AAGhB,mBAAS,KAAK,EAAE,IAAI,IAAI,OAAO,uCAAuC,YAAY,OAAO,CAAC,OAAO,GAAG,GAAG,OAAO,GAAG,CAAC;AAElH,mBAAS,KAAK,EAAE,IAAI,IAAI,OAAO,UAAU,YAAY,OAAO,CAAC,UAAU,GAAG,GAAG,OAAO,GAAG,CAAC;AAAA,QAC1F,OAAO;AAGL,mBAAS,KAAK,EAAE,IAAI,IAAI,OAAO,qCAAqC,YAAY,OAAO,CAAC,KAAK,GAAG,GAAG,OAAO,GAAG,CAAC;AAE9G,mBAAS,KAAK,EAAE,IAAI,IAAI,OAAO,kCAAkC,YAAY,OAAO,CAAC,UAAU,GAAG,GAAG,OAAO,GAAG,CAAC;AAAA,QAClH;AAEA,mBAAW,EAAE,IAAI,MAAM,KAAK,UAAU;AACpC,cAAI,GAAG,KAAK,OAAO,GAAG;AACpB,kBAAM,cAAc,OAAO,IAAI,IAAI;AACnC,gBAAI,CAAC,YAAa;AAClB,kBAAM,YAAY,eAAe,WAAW;AAC5C,gBAAI,WAAW;AACb,qBAAO,SAAS;AAAA,gBACd,WAAW;AAAA,gBACX,UAAU;AAAA,gBACV,SAAS,IAAI;AAAA,gBACb,QAAQ;AAAA,gBACR,MAAM;AAAA,gBACN,OAAO,UAAK,IAAI,IAAI;AAAA,cACtB,GAAG,KAAK;AAAA,YACV;AACA;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,CAAC,GAAG,SAAS,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AACjD;AAEA,SAAS,YAAY,GAAmB;AACtC,SAAO,EAAE,QAAQ,uBAAuB,MAAM;AAChD;AAGA,SAAS,eAAe,OAAuC;AAC7D,QAAM,QAAQ,OAAO,OAAO,MAAM,KAAK;AACvC,MAAI,MAAM,WAAW,EAAG,QAAO;AAG/B,QAAM,SAAS,MAAM,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM;AAC7E,MAAI,OAAO,CAAC,EAAE,WAAW,SAAS,EAAG,QAAO,OAAO,CAAC,EAAE;AAGtD,QAAM,aAAa,CAAC,QAAQ,SAAS,OAAO,UAAU,OAAO,KAAK;AAClE,aAAW,QAAQ,YAAY;AAC7B,UAAM,QAAQ,MAAM,KAAK,CAAC,MAAM;AAC9B,YAAM,WAAW,EAAE,KAAK,MAAM,GAAG,EAAE,IAAI,EAAG,QAAQ,YAAY,EAAE,EAAE,YAAY;AAC9E,aAAO,aAAa;AAAA,IACtB,CAAC;AACD,QAAI,MAAO,QAAO,MAAM;AAAA,EAC1B;AAEA,SAAO,MAAM,CAAC,EAAE;AAClB;AAGA,IAAM,oBAAoB,oBAAI,IAAI;AAAA;AAAA,EAEhC;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAU;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAO;AAAA,EAC1D;AAAA,EAAS;AAAA,EAAY;AAAA,EAAc;AAAA,EAAY;AAAA,EAAW;AAAA;AAAA,EAE1D;AAAA,EAAS;AAAA,EAAW;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAe;AAAA,EAC/D;AAAA,EAAO;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AAAA,EAAW;AAAA;AAAA,EAE3D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAc;AAAA,EAAY;AAAA,EACxD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAa;AAAA,EAAU;AAAA,EAC/D;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAAW;AAAA,EAC5D;AAAA,EAAS;AAAA,EAAS;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAW;AAAA;AAAA,EAExD;AAAA,EAAS;AAAA,EAAU;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAC5D;AAAA,EAAU;AAAA,EAAY;AAAA,EAAa;AAAA,EAAU;AAAA,EAAU;AAAA,EACvD;AAAA,EAAU;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA,EAAU;AAAA,EAAS;AAAA;AAAA,EAE5D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAc;AAAA,EACzD;AAAA,EAAU;AAAA,EAAY;AAAA,EAAY;AAAA,EAAe;AAAA,EAAU;AAAA,EAC3D;AAAA,EAAU;AAAA,EAAU;AAAA,EAAa;AAAA,EAAW;AAAA,EAAW;AAAA;AAAA,EAEvD;AAAA,EAAS;AAAA,EAAY;AAAA,EAAU;AAAA,EAAS;AAAA,EAAW;AAAA,EACnD;AAAA,EAAY;AAAA,EAAc;AAAA,EAAW;AAAA,EAAW;AAAA;AAAA,EAEhD;AAAA,EAAW;AAAA,EAAY;AAAA,EAAY;AAAA,EAAc;AAAA,EAAW;AAAA,EAC5D;AAAA,EAAS;AAAA,EAAa;AAAA,EAAU;AAAA,EAAW;AAAA,EAAU;AAAA;AAAA,EAErD;AAAA,EAAU;AAAA,EAAa;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EACpD;AAAA,EAAc;AAAA,EAAW;AAAA,EAAU;AAAA,EAAc;AAAA;AAAA,EAEjD;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAW;AAC/C,CAAC;AAMD,SAAS,iBACP,aACA,QACiB;AACjB,QAAM,cAAwC,CAAC;AAC/C,QAAM,cAAgC,CAAC;AACvC,QAAM,iBAAwC,CAAC;AAE/C,aAAW,CAAC,WAAW,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACvD,eAAW,CAAC,UAAU,IAAI,KAAK,OAAO,QAAQ,MAAM,KAAK,GAAG;AAC1D,YAAM,eAAe,GAAG,SAAS,IAAI,QAAQ;AAC7C,kBAAY,YAAY,IAAI;AAAA,QAC1B,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,QACb,cAAc,KAAK,aAAa,IAAI,CAAC,MAAM,GAAG,SAAS,IAAI,CAAC,EAAE;AAAA,QAC9D,YAAY,KAAK,WAAW,IAAI,CAAC,MAAM,GAAG,SAAS,IAAI,CAAC,EAAE;AAAA,MAC5D;AAAA,IACF;AAEA,eAAW,QAAQ,MAAM,OAAO;AAC9B,kBAAY,KAAK;AAAA,QACf,QAAQ,GAAG,SAAS,IAAI,KAAK,MAAM;AAAA,QACnC,QAAQ,GAAG,SAAS,IAAI,KAAK,MAAM;AAAA,QACnC,MAAM,KAAK;AAAA,MACb,CAAC;AAAA,IACH;AAEA,eAAW,QAAQ,MAAM,sBAAsB;AAC7C,qBAAe,KAAK;AAAA,QAClB,OAAO,KAAK,MAAM,IAAI,CAAC,MAAM,GAAG,SAAS,IAAI,CAAC,EAAE;AAAA,MAClD,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO;AAAA,IACL,SAASF,SAAQ,WAAW;AAAA,IAC5B,OAAO;AAAA,IACP,OAAO;AAAA,IACP,sBAAsB;AAAA,IACtB,YAAY,OAAO,KAAK,WAAW,EAAE;AAAA,IACrC,YAAY,YAAY;AAAA,EAC1B;AACF;;;ACnXA,SAAS,YAAAG,WAAU,WAAW,aAAa;AAC3C,SAAS,QAAAC,aAAY;AACrB,SAAS,SAAS;AAIlB,IAAM,kBAAkB;AACxB,IAAM,cAAc;AAEpB,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,MAAM,EACH,OAAO,EACP,IAAI,CAAC,EACL;AAAA,IACC;AAAA,IACA;AAAA,EACF;AAAA,EACF,WAAW,EAAE,OAAO,EAAE,IAAI,CAAC;AAAA,EAC3B,UAAU,EAAE,KAAK,YAAY,EAAE,SAAS;AAAA,EACxC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS;AAAA,EACtC,OAAO,EAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,aAAa,EAAE,OAAO,EAAE,SAAS;AACnC,CAAC;AAED,IAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,WAAW,EAAE,OAAO;AAAA,EACpB,UAAU,EAAE,OAAO;AAAA,EACnB,SAAS,EAAE,OAAO;AAAA,EAClB,QAAQ,EAAE,OAAO;AAAA,EACjB,MAAM,EAAE,KAAK,CAAC,YAAY,SAAS,aAAa,QAAQ,CAAC;AAAA,EACzD,OAAO,EAAE,OAAO,EAAE,SAAS;AAC7B,CAAC;AAEM,IAAM,oBAAoB,EAAE,OAAO;AAAA,EACxC,SAAS,EAAE,QAAQ,KAAK;AAAA,EACxB,QAAQ,EACL,MAAM,qBAAqB,EAC3B,IAAI,CAAC,EACL;AAAA,IACC,CAAC,WAAW;AACV,YAAM,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI;AACtC,aAAO,IAAI,IAAI,KAAK,EAAE,SAAS,MAAM;AAAA,IACvC;AAAA,IACA,EAAE,SAAS,6BAA6B;AAAA,EAC1C;AAAA,EACF,aAAa,EAAE,MAAM,0BAA0B,EAAE,SAAS;AAC5D,CAAC;AAMD,eAAsB,gBACpB,aAC6B;AAC7B,QAAM,WAAWC,MAAK,aAAa,iBAAiB,WAAW;AAE/D,MAAI;AACJ,MAAI;AACF,UAAM,MAAMC,UAAS,UAAU,OAAO;AAAA,EACxC,SAAS,OAAgB;AACvB,QAAI,YAAY,KAAK,KAAK,MAAM,SAAS,UAAU;AACjD,aAAO;AAAA,IACT;AACA,UAAM,IAAI,MAAM,kBAAkB,QAAQ,EAAE;AAAA,EAC9C;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI,MAAM,mBAAmB,QAAQ,EAAE;AAAA,EAC/C;AAEA,QAAM,SAAS,kBAAkB,UAAU,MAAM;AACjD,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAChD,MAAM,GAAG,CAAC,EACV,KAAK,IAAI;AACZ,UAAM,IAAI,MAAM;AAAA,EAAmC,MAAM,EAAE;AAAA,EAC7D;AAEA,SAAO,OAAO;AAChB;AAKA,eAAsB,gBACpB,aACA,QACe;AACf,QAAM,UAAUD,MAAK,aAAa,eAAe;AACjD,QAAM,WAAWA,MAAK,SAAS,WAAW;AAC1C,QAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,UAAU,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAC3E;AAEA,SAAS,YAAY,OAAgD;AACnE,SAAO,iBAAiB,SAAS,UAAU;AAC7C;;;AC5FA,SAAS,YAAAE,WAAU,aAAAC,YAAW,SAAAC,QAAO,QAAAC,aAAY;AACjD,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AAC9B,SAAS,WAAAC,gBAAe;AACxB,SAAS,kBAAkB;AAG3B,IAAM,aAAa;AACnB,IAAMC,mBAAkB;AASxB,eAAe,SAAS,UAAmC;AACzD,QAAM,UAAU,MAAMP,UAAS,QAAQ;AACvC,SAAO,WAAW,QAAQ,EAAE,OAAO,OAAO,EAAE,OAAO,KAAK;AAC1D;AAGA,eAAe,wBACb,KACA,UAAoB,CAAC,GACqB;AAC1C,QAAM,eAAgD,CAAC;AACvD,QAAM,iBAAiB,QAAQ,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC;AAEvD,iBAAe,KAAK,YAAmC;AACrD,QAAI;AACJ,QAAI;AACF,gBAAU,MAAMM,SAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AAAA,IAC7D,QAAQ;AACN;AAAA,IACF;AACA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAWF,MAAK,YAAY,MAAM,IAAI;AAC5C,YAAM,eAAe,SAAS,MAAM,IAAI,SAAS,CAAC;AAGlD,UAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,SAAS,eAAgB;AACjE,UAAI,eAAe,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,CAAC,EAAG;AAEtD,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,KAAK,QAAQ;AAAA,MACrB,WAAW,MAAM,OAAO,GAAG;AACzB,YAAI;AACF,gBAAM,IAAI,MAAMD,MAAK,QAAQ;AAC7B,gBAAM,OAAO,MAAM,SAAS,QAAQ;AACpC,uBAAa,YAAY,IAAI,EAAE,OAAO,EAAE,SAAS,KAAK;AAAA,QACxD,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,GAAG;AACd,SAAO;AACT;AAGA,eAAsB,eACpB,aACA,OACA,SAMA,OAKe;AACf,QAAM,UAAUE,SAAQ,WAAW;AACnC,QAAM,MAAMD,MAAK,SAASG,gBAAe;AACzC,QAAML,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAIpC,MAAI;AACJ,MAAI,OAAO,WAAW,QAAQ;AAC5B,mBAAe,CAAC;AAChB,eAAW,YAAY,MAAM,WAAW;AACtC,YAAM,YAAYG,SAAQ,SAAS,QAAQ;AAC3C,YAAM,UAAU,MAAM,wBAAwB,WAAW,QAAQ,OAAO;AACxE,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,GAAG;AAClD,qBAAa,GAAG,QAAQ,IAAI,GAAG,EAAE,IAAI;AAAA,MACvC;AAAA,IACF;AAAA,EACF,OAAO;AACL,mBAAe,MAAM,4BAA4B,SAAS,OAAO;AAAA,EACnE;AAGA,MAAI;AACJ,MAAI;AACF,qBAAiB,MAAM,SAASD,MAAK,KAAK,aAAa,CAAC;AAAA,EAC1D,QAAQ;AAAA,EAAkC;AAG1C,QAAM,SAAiC,CAAC;AACxC,aAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,YAAY,GAAG;AACjD,WAAO,CAAC,IAAI,EAAE;AAAA,EAChB;AAEA,QAAM,QAAoB;AAAA,IACxB,SAAS;AAAA,IACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,SAAS;AAAA,MACP,WAAW,QAAQ;AAAA,MACnB,aAAa,QAAQ;AAAA,MACrB,UAAU,QAAQ;AAAA,MAClB,SAAS,QAAQ;AAAA,IACnB;AAAA,IACA,YAAY;AAAA,IACZ,YAAY,OAAO;AAAA,MACjB,OAAO,QAAQ,YAAY,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC;AAAA,IAC1D;AAAA,IACA;AAAA,IACA,YAAY,OAAO;AAAA,IACnB,eAAe,OAAO;AAAA,IACtB,WAAW,OAAO;AAAA,IAClB;AAAA,EACF;AAEA,QAAMH,WAAUG,MAAK,KAAK,UAAU,GAAG,KAAK,UAAU,KAAK,GAAG,OAAO;AACvE;AAGA,eAAsB,eACpB,aAC4B;AAC5B,QAAM,UAAUC,SAAQ,WAAW;AACnC,QAAM,WAAWD,MAAK,SAASG,kBAAiB,UAAU;AAC1D,MAAI;AACF,UAAM,MAAM,MAAMP,UAAS,UAAU,OAAO;AAC5C,UAAM,OAAO,KAAK,MAAM,GAAG;AAC3B,QAAI,MAAM,YAAY,SAAS,CAAC,MAAM,SAAS,CAAC,MAAM,YAAY;AAChE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAUA,eAAsB,kBACpB,OACA,aACA,SAMkB;AAElB,MACE,MAAM,QAAQ,cAAc,QAAQ,aACpC,MAAM,QAAQ,gBAAgB,QAAQ,gBACrC,MAAM,QAAQ,YAAY,SAAS,QAAQ,YAAY,OACxD,KAAK,UAAU,MAAM,QAAQ,WAAW,CAAC,CAAC,MAAM,KAAK,UAAU,QAAQ,WAAW,CAAC,CAAC,GACpF;AACA,WAAO;AAAA,EACT;AAEA,QAAM,UAAUK,SAAQ,WAAW;AACnC,QAAM,aAAaA,SAAQ,SAAS,QAAQ,SAAS;AACrD,QAAM,gBAAgB,MAAM,WAAW,UAAU,KAAK;AAGtD,MAAI;AACJ,MAAI;AACF,wBAAoB,MAAM,SAASD,MAAK,SAASG,kBAAiB,aAAa,CAAC;AAAA,EAClF,QAAQ;AAAA,EAAkC;AAC1C,OAAK,MAAM,kBAAkB,SAAS,qBAAqB,IAAK,QAAO;AAGvE,MAAI;AACJ,MAAI,cAAc;AAChB,oBAAgB,CAAC;AACjB,eAAW,YAAY,MAAM,WAAY;AACvC,YAAM,YAAYF,SAAQ,SAAS,QAAQ;AAC3C,YAAM,YAAY,MAAM,kBAAkB,WAAW,QAAQ,OAAO;AACpE,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,SAAS,GAAG;AACpD,sBAAc,GAAG,QAAQ,IAAI,GAAG,EAAE,IAAI;AAAA,MACxC;AAAA,IACF;AAAA,EACF,OAAO;AACL,oBAAgB,MAAM,kBAAkB,YAAY,QAAQ,OAAO;AAAA,EACrE;AAGA,QAAM,aAAa,IAAI,IAAI,OAAO,KAAK,MAAM,UAAU,CAAC;AACxD,QAAM,cAAc,IAAI,IAAI,OAAO,KAAK,aAAa,CAAC;AACtD,MAAI,WAAW,SAAS,YAAY,KAAM,QAAO;AACjD,aAAW,OAAO,YAAY;AAC5B,QAAI,CAAC,YAAY,IAAI,GAAG,EAAG,QAAO;AAAA,EACpC;AAGA,QAAM,eAAyB,CAAC;AAChC,aAAW,OAAO,YAAY;AAC5B,QAAI,KAAK,IAAI,MAAM,WAAW,GAAG,IAAI,cAAc,GAAG,CAAC,IAAI,GAAG;AAC5D,mBAAa,KAAK,GAAG;AAAA,IACvB;AAAA,EACF;AAGA,MAAI,aAAa,WAAW,EAAG,QAAO;AAItC,MAAI,CAAC,MAAM,WAAY,QAAO;AAE9B,aAAW,OAAO,cAAc;AAC9B,UAAM,aAAa,MAAM,WAAW,GAAG;AACvC,QAAI,CAAC,WAAY,QAAO;AAExB,QAAI;AAGF,YAAM,WAAW,eACbD,MAAK,SAAS,GAAG,IACjBA,MAAK,YAAY,GAAG;AACxB,YAAM,cAAc,MAAM,SAAS,QAAQ;AAC3C,UAAI,gBAAgB,WAAY,QAAO;AAAA,IACzC,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAGA,SAAO;AACT;AAGA,eAAe,kBACb,KACA,SACiC;AACjC,QAAM,SAAiC,CAAC;AACxC,QAAM,kBAAkB,WAAW,CAAC,GAAG,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC;AAE/D,iBAAe,KAAK,YAAmC;AACrD,QAAI;AACJ,QAAI;AACF,gBAAU,MAAME,SAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AAAA,IAC7D,QAAQ;AACN;AAAA,IACF;AACA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAWF,MAAK,YAAY,MAAM,IAAI;AAC5C,YAAM,eAAe,SAAS,MAAM,IAAI,SAAS,CAAC;AAElD,UAAI,MAAM,KAAK,WAAW,GAAG,KAAK,MAAM,SAAS,eAAgB;AACjE,UAAI,eAAe,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,CAAC,EAAG;AAEtD,UAAI,MAAM,YAAY,GAAG;AACvB,cAAM,KAAK,QAAQ;AAAA,MACrB,WAAW,MAAM,OAAO,GAAG;AACzB,YAAI;AACF,gBAAM,IAAI,MAAMD,MAAK,QAAQ;AAC7B,iBAAO,YAAY,IAAI,EAAE;AAAA,QAC3B,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,KAAK,GAAG;AACd,SAAO;AACT;AAGA,eAAe,4BACb,SACA,SAI0C;AAC1C,QAAM,aAAaE,SAAQ,SAAS,QAAQ,SAAS;AACrD,SAAO,wBAAwB,YAAY,QAAQ,OAAO;AAC5D;;;ACpRA,eAAsB,aAAa,MAMR;AAEzB,MAAI,CAAC,KAAK,SAAS;AACjB,UAAM,QAAQ,MAAM,eAAe,KAAK,WAAW;AACnD,QAAI,OAAO;AACT,YAAM,QAAQ,MAAM,kBAAkB,OAAO,KAAK,aAAa;AAAA,QAC7D,WAAW,KAAK;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,UAAU,KAAK;AAAA,QACf,SAAS,KAAK;AAAA,MAChB,CAAC;AACD,UAAI,OAAO;AAET,cAAMG,UAAwB;AAAA,UAC5B,OAAO,MAAM;AAAA,UACb,YAAY,MAAM;AAAA,UAClB,eAAe,MAAM;AAAA,UACrB,WAAW;AAAA,QACb;AACA,YAAI,MAAM,YAAY;AACpB,gBAAMC,eAAc,MAAM,gBAAgB,KAAK,WAAW;AAC1D,cAAIA,cAAa;AACf,kBAAM,kBAAkB;AAAA,cACtB,MAAM,WAAW;AAAA,cAAQA,aAAY;AAAA,YACvC;AACA,kBAAM,oBAAoBA,aAAY,eAAe,CAAC;AACtD,kBAAM,aAAa,IAAI,IAAI,kBAAkB;AAAA,cAC3C,CAAC,MAAM,GAAG,EAAE,SAAS,IAAI,EAAE,QAAQ,SAAI,EAAE,OAAO,IAAI,EAAE,MAAM;AAAA,YAC9D,CAAC;AACD,YAAAD,QAAO,kBAAkB;AAAA,cACvB,GAAG;AAAA,cACH,GAAG,gBAAgB;AAAA,gBACjB,CAAC,MAAM,CAAC,WAAW,IAAI,GAAG,EAAE,SAAS,IAAI,EAAE,QAAQ,SAAI,EAAE,OAAO,IAAI,EAAE,MAAM,EAAE;AAAA,cAChF;AAAA,YACF;AAAA,UACF;AAAA,QACF;AACA,eAAOA;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAGA,QAAM,cAAc,MAAM,gBAAgB,KAAK,WAAW;AAC1D,MAAI;AAEJ,MAAI,aAAa;AACf,UAAM,QAAQ,MAAM,kBAAkB,KAAK,aAAa,YAAY,QAAQ,KAAK,OAAO;AACxF,UAAM,kBAAkB,4BAA4B,MAAM,QAAQ,YAAY,MAAM;AACpF,UAAM,oBAAoB,YAAY,eAAe,CAAC;AACtD,UAAM,aAAa,IAAI,IAAI,kBAAkB;AAAA,MAC3C,CAAC,MAAM,GAAG,EAAE,SAAS,IAAI,EAAE,QAAQ,SAAI,EAAE,OAAO,IAAI,EAAE,MAAM;AAAA,IAC9D,CAAC;AACD,UAAM,SAAS;AAAA,MACb,GAAG;AAAA,MACH,GAAG,gBAAgB;AAAA,QACjB,CAAC,MAAM,CAAC,WAAW,IAAI,GAAG,EAAE,SAAS,IAAI,EAAE,QAAQ,SAAI,EAAE,OAAO,IAAI,EAAE,MAAM,EAAE;AAAA,MAChF;AAAA,IACF;AACA,aAAS;AAAA,MACP,OAAO,MAAM;AAAA,MACb,YAAY;AAAA,MACZ,eAAe,MAAM;AAAA,MACrB,iBAAiB;AAAA,IACnB;AAAA,EACF,OAAO;AACL,UAAM,QAAQ,MAAM,eAAe,KAAK,WAAW;AAAA,MACjD,SAAS,KAAK;AAAA,MACd,UAAU,KAAK;AAAA,IACjB,CAAC;AACD,aAAS,EAAE,MAAM;AAAA,EACnB;AAGA,MAAI;AACF,UAAM;AAAA,MACJ,KAAK;AAAA,MACL,OAAO;AAAA,MACP;AAAA,QACE,WAAW,KAAK;AAAA,QAChB,aAAa,KAAK;AAAA,QAClB,UAAU,KAAK;AAAA,QACf,SAAS,KAAK;AAAA,MAChB;AAAA,MACA;AAAA,QACE,YAAY,OAAO;AAAA,QACnB,eAAe,OAAO;AAAA,QACtB,WAAW,aAAa,OAAO,IAAI,CAAC,MAAM,EAAE,SAAS;AAAA,MACvD;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;;;ACpIA,SAAS,SAAAE,QAAO,aAAAC,YAAW,YAAAC,WAAU,WAAAC,UAAS,cAAc;AAC5D,SAAS,QAAAC,aAAY;AACrB,SAAS,KAAAC,UAAS;;;ACGX,IAAM,iBAAiB;;;ADG9B,IAAMC,mBAAkB;AACxB,IAAM,gBAAgB;AACtB,IAAM,cAAc;AAGpB,IAAM,iBAAiBC,GAAE,OAAO;AAAA,EAC9B,MAAMA,GAAE,OAAO;AAAA,EACf,QAAQA,GAAE,QAAQ;AAAA,EAClB,cAAcA,GAAE,MAAMA,GAAE,OAAO,CAAC;AAAA,EAChC,YAAYA,GAAE,MAAMA,GAAE,OAAO,CAAC;AAChC,CAAC;AAED,IAAM,wBAAwBA,GAAE,OAAO;AAAA,EACrC,SAASA,GAAE,OAAO;AAAA,EAClB,OAAOA,GAAE,OAAOA,GAAE,OAAO,GAAG,cAAc;AAAA,EAC1C,OAAOA,GAAE,MAAMA,GAAE,OAAO;AAAA,IACtB,QAAQA,GAAE,OAAO;AAAA,IACjB,QAAQA,GAAE,OAAO;AAAA,IACjB,MAAMA,GAAE,KAAK,CAAC,UAAU,WAAW,WAAW,CAAC;AAAA,EACjD,CAAC,CAAC;AAAA,EACF,sBAAsBA,GAAE,MAAMA,GAAE,OAAO,EAAE,OAAOA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,CAAC,CAAC;AAAA,EACtE,YAAYA,GAAE,OAAO;AAAA,EACrB,YAAYA,GAAE,OAAO;AACvB,CAAC;AAED,IAAM,iBAAiBA,GAAE,OAAO;AAAA,EAC9B,SAASA,GAAE,KAAK,CAAC,gBAAgB,KAAK,CAAC;AAAA,EACvC,WAAWA,GAAE,OAAO;AAAA,EACpB,SAASA,GAAE,OAAO;AAAA,EAClB,OAAO;AACT,CAAC;AAQD,eAAsB,aACpB,aACA,OACA,YACA,iBAKuB;AACvB,QAAM,UAAUC,MAAK,aAAaF,gBAAe;AACjD,QAAM,WAAWE,MAAK,SAAS,aAAa;AAE5C,QAAM,WAAyB;AAAA,IAC7B,SAAS;AAAA,IACT,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,SAAS,MAAM;AAAA,IACf;AAAA,IACA,GAAI,aAAa,EAAE,WAAW,IAAI,CAAC;AAAA,IACnC,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC;AAAA,EAC/C;AAEA,QAAMC,OAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AACxC,QAAM,OAAO,KAAK,UAAU,UAAU,MAAM,CAAC;AAC7C,QAAMC,WAAU,UAAU,MAAM,OAAO;AAGvC,MAAI;AACF,UAAM,cAAcF,MAAK,SAAS,WAAW;AAC7C,UAAMC,OAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AAC5C,UAAM,SAAS,SAAS,UAAU,QAAQ,SAAS,GAAG;AACtD,UAAMC,WAAUF,MAAK,aAAa,GAAG,MAAM,OAAO,GAAG,MAAM,OAAO;AAAA,EACpE,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAQA,eAAsB,aACpB,aAC8B;AAC9B,QAAM,WAAWA,MAAK,aAAaF,kBAAiB,aAAa;AAGjE,MAAI;AACJ,MAAI;AACF,UAAM,MAAMK,UAAS,UAAU,OAAO;AAAA,EACxC,SAAS,OAAgB;AACvB,QAAIC,aAAY,KAAK,KAAK,MAAM,SAAS,UAAU;AACjD,aAAO;AAAA,IACT;AACA,UAAM,IAAI;AAAA,MACR,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAAA,MAC1C,EAAE,OAAO,MAAM;AAAA,IACjB;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,GAAG;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI;AAAA,MACR,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAAA,IAC7C;AAAA,EACF;AAGA,QAAM,SAAS,eAAe,UAAU,MAAM;AAC9C,MAAI,CAAC,OAAO,SAAS;AACnB,UAAM,SAAS,OAAO,MAAM,OACzB,IAAI,CAAC,MAAM,KAAK,EAAE,KAAK,KAAK,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAChD,MAAM,GAAG,CAAC,EACV,KAAK,IAAI;AACZ,UAAM,IAAI;AAAA,MACR,EAAE,yBAAyB,EAAE,OAAO,CAAC;AAAA,IACvC;AAAA,EACF;AAEA,SAAO,OAAO;AAChB;AAMA,eAAsB,cACpB,aAC4B;AAC5B,QAAM,cAAcJ,MAAK,aAAaF,kBAAiB,WAAW;AAClE,MAAI;AACJ,MAAI;AACF,cAAU,MAAMO,SAAQ,WAAW;AAAA,EACrC,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,YAAY,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC,EAAE,KAAK,EAAE,QAAQ;AAC5E,QAAM,YAA+B,CAAC;AAEtC,aAAW,QAAQ,WAAW;AAC5B,QAAI;AACF,YAAM,MAAM,MAAMF,UAASH,MAAK,aAAa,IAAI,GAAG,OAAO;AAC3D,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,YAAM,SAAS,eAAe,UAAU,MAAM;AAC9C,UAAI,OAAO,SAAS;AAClB,cAAM,OAAO,OAAO;AACpB,kBAAU,KAAK;AAAA,UACb,WAAW,KAAK;AAAA,UAChB,YAAY,KAAK,MAAM;AAAA,UACvB,YAAY,KAAK,MAAM;AAAA,UACvB,cAAc,KAAK,MAAM,qBAAqB;AAAA,UAC9C,eAAe,gBAAgB,UAAU,OAAO,cAAc;AAAA,QAChE,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAMA,eAAsB,wBACpB,aACA,WAC8B;AAC9B,QAAM,cAAcA,MAAK,aAAaF,kBAAiB,WAAW;AAClE,MAAI;AACJ,MAAI;AACF,cAAU,MAAMO,SAAQ,WAAW;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,QAAQ,OAAO,CAAC,MAAM,EAAE,SAAS,OAAO,CAAC,EAAE,KAAK;AAGlE,QAAM,SAAS,UAAU,QAAQ,SAAS,GAAG;AAC7C,QAAM,aAAa,UAAU,KAAK,CAAC,MAAM,MAAM,GAAG,MAAM,OAAO;AAC/D,MAAI,YAAY;AACd,WAAO,gBAAgBL,MAAK,aAAa,UAAU,CAAC;AAAA,EACtD;AAGA,QAAM,cAAc,UAAU,KAAK,CAAC,MAAM,EAAE,WAAW,OAAO,MAAM,GAAG,EAAE,CAAC,CAAC;AAC3E,MAAI,aAAa;AACf,WAAO,gBAAgBA,MAAK,aAAa,WAAW,CAAC;AAAA,EACvD;AAEA,SAAO;AACT;AAEA,eAAe,gBAAgB,UAAgD;AAC7E,MAAI;AACF,UAAM,MAAM,MAAMG,UAAS,UAAU,OAAO;AAC5C,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAM,SAAS,eAAe,UAAU,MAAM;AAC9C,QAAI,OAAO,QAAS,QAAO,OAAO;AAClC,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeO,IAAM,eAAN,cAA2B,MAAM;AAAA,EACtC,YAAY,SAAiB,SAAwB;AACnD,UAAM,SAAS,OAAO;AACtB,SAAK,OAAO;AAAA,EACd;AACF;AAEA,SAASG,aAAY,OAAgD;AACnE,SAAO,iBAAiB,SAAS,UAAU;AAC7C;;;AEvOO,SAAS,YACd,UACA,UACU;AACV,QAAM,WAAW,IAAI,IAAI,OAAO,KAAK,SAAS,KAAK,CAAC;AACpD,QAAM,WAAW,IAAI,IAAI,OAAO,KAAK,SAAS,KAAK,CAAC;AAGpD,QAAM,QAAQ,CAAC,GAAG,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAG1D,QAAM,UAAU,CAAC,GAAG,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,SAAS,IAAI,CAAC,CAAC;AAG5D,QAAM,WAAqB,CAAC;AAC5B,aAAW,QAAQ,UAAU;AAC3B,QAAI,CAAC,SAAS,IAAI,IAAI,EAAG;AAEzB,UAAM,UAAU,SAAS,MAAM,IAAI,EAAE,aAAa,MAAM,EAAE,KAAK;AAC/D,UAAM,UAAU,SAAS,MAAM,IAAI,EAAE,aAAa,MAAM,EAAE,KAAK;AAE/D,QAAI,CAAC,YAAY,SAAS,OAAO,GAAG;AAClC,eAAS,KAAK,IAAI;AAAA,IACpB;AAAA,EACF;AAGA,QAAM,aAAa,IAAI,IAAI,OAAO;AAClC,QAAM,eAAe,oBAAI,IAAI,CAAC,GAAG,SAAS,GAAG,QAAQ,CAAC;AACtD,QAAM,qBAAqD,CAAC;AAC5D,QAAM,eAAe,oBAAI,IAAY;AAErC,aAAW,eAAe,cAAc;AAGtC,UAAM,QAAQ,WAAW,IAAI,WAAW,IAAI,WAAW;AACvD,UAAM,OAAO,MAAM,MAAM,WAAW;AACpC,QAAI,CAAC,KAAM;AAEX,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,MAAM,GAAG,SAAS,SAAI,WAAW;AACvC,UAAI,aAAa,IAAI,GAAG,EAAG;AAC3B,mBAAa,IAAI,GAAG;AAEpB,YAAM,SAAS,WAAW,IAAI,WAAW,IACrC,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC,IAC7C,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAElD,yBAAmB,KAAK;AAAA,QACtB,MAAM;AAAA,QACN;AAAA,QACA,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAIA,aAAW,aAAa,OAAO;AAC7B,UAAM,OAAO,SAAS,MAAM,SAAS;AACrC,QAAI,CAAC,KAAM;AAEX,eAAW,aAAa,KAAK,YAAY;AACvC,YAAM,MAAM,GAAG,SAAS,SAAI,SAAS;AACrC,UAAI,aAAa,IAAI,GAAG,EAAG;AAC3B,mBAAa,IAAI,GAAG;AAEpB,yBAAmB,KAAK;AAAA,QACtB,MAAM;AAAA,QACN,QAAQ,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAAA,QACjD,WAAW;AAAA,MACb,CAAC;AAAA,IACH;AAAA,EACF;AAEA,SAAO,EAAE,OAAO,SAAS,UAAU,mBAAmB;AACxD;AAGA,SAAS,gBAAgB,MAAuB;AAC9C,SAAO,+DAA+D,KAAK,IAAI;AACjF;AAGA,SAAS,UAAa,KAAU,MAAwC;AACtE,QAAM,MAAW,CAAC;AAClB,QAAM,KAAU,CAAC;AACjB,aAAW,QAAQ,KAAK;AACtB,KAAC,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,IAAI;AAAA,EACnC;AACA,SAAO,CAAC,KAAK,EAAE;AACjB;AAGO,SAAS,iBAAiB,MAAwB;AACvD,QAAM,QAAkB,CAAC;AAEzB,QAAM,KAAK,EAAE,YAAY,CAAC;AAE1B,MAAI,KAAK,MAAM,WAAW,KAAK,KAAK,QAAQ,WAAW,KAAK,KAAK,SAAS,WAAW,GAAG;AACtF,UAAM,KAAK,EAAE,gBAAgB,CAAC;AAC9B,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAEA,MAAI,KAAK,MAAM,SAAS,GAAG;AACzB,UAAM,CAAC,WAAW,QAAQ,IAAI,UAAU,KAAK,OAAO,eAAe;AACnE,UAAM,KAAK,EAAE,cAAc,EAAE,OAAO,KAAK,MAAM,OAAO,CAAC,CAAC;AACxD,eAAW,KAAK,UAAU;AACxB,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,KAAK,EAAE,oBAAoB,EAAE,OAAO,UAAU,OAAO,CAAC,CAAC;AAAA,IAC/D;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,UAAM,CAAC,WAAW,QAAQ,IAAI,UAAU,KAAK,SAAS,eAAe;AACrE,UAAM,KAAK,EAAE,gBAAgB,EAAE,OAAO,KAAK,QAAQ,OAAO,CAAC,CAAC;AAC5D,eAAW,KAAK,UAAU;AACxB,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,KAAK,EAAE,oBAAoB,EAAE,OAAO,UAAU,OAAO,CAAC,CAAC;AAAA,IAC/D;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,UAAM,CAAC,WAAW,QAAQ,IAAI,UAAU,KAAK,UAAU,eAAe;AACtE,UAAM,KAAK,EAAE,iBAAiB,EAAE,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC;AAC9D,eAAW,KAAK,UAAU;AACxB,YAAM,KAAK,OAAO,CAAC,EAAE;AAAA,IACvB;AACA,QAAI,UAAU,SAAS,GAAG;AACxB,YAAM,KAAK,EAAE,oBAAoB,EAAE,OAAO,UAAU,OAAO,CAAC,CAAC;AAAA,IAC/D;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,MAAI,KAAK,mBAAmB,SAAS,GAAG;AACtC,UAAM,CAAC,aAAa,UAAU,IAAI;AAAA,MAChC,KAAK;AAAA,MACL,CAAC,MAAM,gBAAgB,EAAE,IAAI,KAAK,gBAAgB,EAAE,SAAS;AAAA,IAC/D;AACA,UAAM,KAAK,EAAE,iBAAiB,EAAE,OAAO,KAAK,mBAAmB,OAAO,CAAC,CAAC;AACxE,eAAW,KAAK,YAAY;AAC1B,YAAM,KAAK,OAAO,EAAE,IAAI,EAAE;AAC1B,YAAM,KAAK,OAAO,EAAE,MAAM,EAAE;AAAA,IAC9B;AACA,QAAI,YAAY,SAAS,GAAG;AAC1B,YAAM,KAAK,EAAE,4BAA4B,EAAE,OAAO,YAAY,OAAO,CAAC,CAAC;AAAA,IACzE;AACA,UAAM,KAAK,EAAE;AAAA,EACf;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAEA,SAAS,YAAY,GAAa,GAAsB;AACtD,MAAI,EAAE,WAAW,EAAE,OAAQ,QAAO;AAClC,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,QAAI,EAAE,CAAC,MAAM,EAAE,CAAC,EAAG,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;;;ACjLA,SAAS,oBAAoB;;;ACA7B,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AAC9B,SAAS,qBAAqB;;;ACEvB,SAAS,cAAsB;AACpC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0JT;;;AC3JO,SAAS,kBAA0B;AACxC,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0KT;;;ACvKO,SAAS,WAAW,GAAmB;AAC5C,SAAO,EACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,OAAO;AAC1B;;;AHIA,IAAI,YAA2B;AAE/B,SAAS,cAAsB;AAC7B,MAAI,CAAC,WAAW;AACd,UAAM,UAAUC,SAAQ,cAAc,YAAY,GAAG,CAAC;AAEtD,UAAM,aAAa;AAAA,MACjBC,MAAK,SAAS,MAAM,OAAO,WAAW;AAAA;AAAA,MACtCA,MAAK,SAAS,MAAM,MAAM,QAAQ,OAAO,WAAW;AAAA;AAAA,IACtD;AACA,eAAW,KAAK,YAAY;AAC1B,UAAI;AAAE,oBAAYC,cAAa,GAAG,OAAO;AAAG;AAAA,MAAO,QAC7C;AAAA,MAAiB;AAAA,IACzB;AACA,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,wDAAwD;AAAA,EAC1F;AACA,SAAO;AACT;AAMO,SAAS,eAAe,OAAwB,UAAyB,CAAC,GAAW;AAC1F,QAAM,SAAS,QAAQ,UAAU;AACjC,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,SAAS,QAAQ,iBAAiB;AACxC,QAAM,aAAa,QAAQ,mBAAmB;AAC9C,QAAM,QAAQ,OAAO,OAAO,MAAM,KAAK;AACvC,QAAM,QAAQ,MAAM,IAAI,CAAC,OAAO;AAAA,IAC9B,IAAI,EAAE;AAAA,IACN,MAAM,EAAE,aAAa;AAAA,IACrB,YAAY,EAAE,WAAW;AAAA,IACzB,cAAc,EAAE;AAAA,IAChB,gBAAgB,EAAE;AAAA,IAClB,UAAU,EAAE,aAAa,WAAW,KAAK,EAAE,WAAW,WAAW;AAAA,IACjE,KAAK,EAAE,KAAK,SAAS,GAAG,IAAI,EAAE,KAAK,UAAU,GAAG,EAAE,KAAK,YAAY,GAAG,CAAC,IAAI;AAAA,IAC3E,OAAO,UAAU,EAAE,KAAK,SAAS,GAAG,IAAI,EAAE,KAAK,UAAU,GAAG,EAAE,KAAK,QAAQ,GAAG,CAAC,IAAI;AAAA,EACrF,EAAE;AAEF,QAAM,QAAQ,MAAM,MAAM,IAAI,CAAC,OAAO;AAAA,IACpC,QAAQ,EAAE;AAAA,IACV,QAAQ,EAAE;AAAA,IACV,MAAM,EAAE;AAAA,EACV,EAAE;AAEF,QAAM,gBAAgB,oBAAI,IAAY;AACtC,aAAW,KAAK,MAAM,sBAAsB;AAC1C,eAAW,KAAK,EAAE,MAAO,eAAc,IAAI,CAAC;AAAA,EAC9C;AAEA,QAAM,OAAO,CAAC,GAAG,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,EAAE,KAAK;AACxD,QAAM,cAAc,MAAM,QAAQ,MAAM,GAAG,EAAE,OAAO,OAAO,EAAE,IAAI,KAAK;AACtE,QAAM,WAAW,OAAO,KAAK,UAAU,IAAI,IAAI;AAC/C,QAAM,aAAa,SAAS,KAAK,UAAU,MAAM,IAAI;AACrD,QAAM,iBAAiB,aAAa,KAAK,UAAU,UAAU,IAAI;AACjE,QAAM,YAAY,KAAK,UAAU,EAAE,OAAO,OAAO,eAAe,CAAC,GAAG,aAAa,GAAG,MAAM,YAAY,CAAC;AAEvG,QAAM,WAAW,YAAY;AAE7B;AAAA;AAAA,IAAkB;AAAA,cACN,MAAM;AAAA;AAAA;AAAA;AAAA,SAIX,WAAW,WAAW,CAAC;AAAA,EAC9B,YAAY,CAAC;AAAA;AAAA;AAAA,EAGb,gBAAgB,CAAC;AAAA;AAAA;AAAA;AAAA,UAIT,SAAS;AAAA,YACP,UAAU;AAAA,gBACN,cAAc;AAAA,UACpB,QAAQ;AAAA,aACL,MAAM;AAAA;AAAA;AAAA,UAGT,QAAQ;AAAA;AAAA;AAAA;AAGlB;;;ADxFO,SAAS,YACd,OACA,UAMI,CAAC,GACgC;AACrC,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,SAAS,QAAQ,UAAU,UAAU;AAE3C,QAAM,OAAO,eAAe,OAAO;AAAA,IACjC;AAAA,IACA,MAAM,QAAQ;AAAA,IACd,eAAe,QAAQ;AAAA,IACvB,iBAAiB,QAAQ;AAAA,EAC3B,CAAC;AACD,QAAM,YAAY,KAAK,UAAU,KAAK;AAEtC,QAAM,SAAS,aAAa,CAAC,KAAK,QAAQ;AACxC,QAAI,IAAI,QAAQ,cAAc;AAC5B,UAAI,UAAU,KAAK,EAAE,gBAAgB,mBAAmB,CAAC;AACzD,UAAI,IAAI,SAAS;AACjB;AAAA,IACF;AAEA,QAAI,UAAU,KAAK,EAAE,gBAAgB,2BAA2B,CAAC;AACjE,QAAI,IAAI,IAAI;AAAA,EACd,CAAC;AAED,SAAO,OAAO,IAAI;AAElB,SAAO;AAAA,IACL;AAAA,IACA,OAAO,MAAM,OAAO,MAAM;AAAA,EAC5B;AACF;;;AKpDA,SAAS,gBAAAC,qBAAoB;AAC7B,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AAC9B,SAAS,iBAAAC,sBAAqB;AAG9B,SAAS,cAAsB;AAE7B,MAAI,MAAMD,SAAQC,eAAc,YAAY,GAAG,CAAC;AAChD,WAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC1B,QAAI;AACF,YAAM,MAAM,KAAK,MAAMH,cAAaC,MAAK,KAAK,cAAc,GAAG,OAAO,CAAC;AACvE,aAAO,IAAI;AAAA,IACb,QAAQ;AACN,YAAMC,SAAQ,GAAG;AAAA,IACnB;AAAA,EACF;AACA,SAAO;AACT;AAEO,IAAM,UAAU,YAAY;;;AtBGnC,IAAM,kBAAkB;AAGxB,eAAe,gBAAgB,MAM5B;AACD,SAAO,aAAa;AAAA,IAClB,WAAW,KAAK;AAAA,IAChB,aAAa,KAAK;AAAA,IAClB,SAAS,KAAK;AAAA,IACd,UAAU,KAAK;AAAA,IACf,SAAS,KAAK;AAAA,EAChB,CAAC;AACH;AAEA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,aAAa,EAClB;AAAA,EACC;AACF,EACC,QAAQ,OAAO,EACf,OAAO,mBAAmB,+CAA+C,EACzE,KAAK,aAAa,CAAC,gBAAgB;AAClC,QAAM,OAAO,YAAY,KAAK,EAAE;AAChC,MAAI,SAAS,QAAQ,SAAS,MAAM;AAClC,cAAU,IAAc;AAAA,EAC1B;AACF,CAAC;AAIH,QACG,QAAQ,MAAM,EACd,YAAY,qDAAqD,EACjE,OAAO,sBAAsB,oBAAoB,KAAK,EACtD,OAAO,oBAAoB,gBAAgB,GAAG,EAC9C;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,yBAAyB,oBAAoB,aAAa,KAAK,IAAI,CAAC,GAAG,EAC9E,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,WAAW,iBAAiB,KAAK,QAAQ;AAC/C,YAAQ,IAAI,EAAE,eAAe,CAAC;AAC9B,UAAM,EAAE,OAAO,WAAW,IAAI,MAAM,gBAAgB;AAAA,MAClD,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd;AAAA,IACF,CAAC;AAED,UAAM,WAAW,MAAM,aAAa,KAAK,MAAM,OAAO,YAAY;AAAA,MAChE,WAAW,KAAK;AAAA,MAAQ;AAAA,MAAU,SAAS,KAAK;AAAA,IAClD,CAAC;AAED,YAAQ,IAAI,EAAE,mBAAmB,CAAC;AAClC,YAAQ,IAAI,EAAE,iBAAiB,EAAE,IAAI,SAAS,UAAU,CAAC,CAAC;AAC1D,YAAQ,IAAI,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC3D,YAAQ,IAAI,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAE3D,QAAI,MAAM,qBAAqB,SAAS,GAAG;AACzC,cAAQ;AAAA,QACN,EAAE,qBAAqB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC;AAAA,MACrE;AAAA,IACF;AAGA,UAAM,MAAM,OAAO,OAAO,MAAM,KAAK,EAClC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM,EACxD,MAAM,GAAG,CAAC;AAEb,QAAI,IAAI,SAAS,KAAK,IAAI,CAAC,EAAE,WAAW,SAAS,GAAG;AAClD,cAAQ,IAAI,EAAE,mBAAmB,CAAC;AAClC,iBAAW,KAAK,KAAK;AACnB,YAAI,EAAE,WAAW,WAAW,EAAG;AAC/B,gBAAQ,IAAI,KAAK,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,OAAO,EAAE,WAAW,OAAO,CAAC,CAAC,EAAE;AAAA,MACtF;AAAA,IACF;AAAA,EACF,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAIH,QACG,QAAQ,SAAS,EACjB;AAAA,EACC;AACF,EACC,OAAO,sBAAsB,oBAAoB,KAAK,EACtD,OAAO,oBAAoB,gBAAgB,GAAG,EAC9C;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,sBAAsB,oCAAoC,IAAI,EACrE,OAAO,UAAU,qCAAqC,EACtD,OAAO,cAAc,2CAA2C,EAChE,OAAO,yBAAyB,oBAAoB,aAAa,KAAK,IAAI,CAAC,GAAG,EAC9E,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,WAAW,iBAAiB,KAAK,QAAQ;AAC/C,YAAQ,IAAI,EAAE,eAAe,CAAC;AAC9B,UAAM,EAAE,OAAO,WAAW,IAAI,MAAM,gBAAgB;AAAA,MAClD,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd;AAAA,MACA,SAAS,KAAK;AAAA,IAChB,CAAC;AAED,UAAM,SAAS,qBAAqB,OAAO,EAAE,MAAM,SAAS,KAAK,KAAK,EAAE,EAAE,CAAC;AAC3E,YAAQ,IAAI,MAAM;AAElB,QAAI,KAAK,MAAM;AACb,YAAM,aAAa,KAAK,MAAM,OAAO,YAAY;AAAA,QAC/C,WAAW,KAAK;AAAA,QAAQ;AAAA,QAAU,SAAS,KAAK;AAAA,MAClD,CAAC;AACD,cAAQ,IAAI,EAAE,uBAAuB,CAAC;AAAA,IACxC;AAAA,EACF,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAIH,QACG,QAAQ,OAAO,EACf;AAAA,EACC;AACF,EACC,OAAO,sBAAsB,oBAAoB,KAAK,EACtD,OAAO,oBAAoB,gBAAgB,GAAG,EAC9C,OAAO,QAAQ,8CAA8C,EAC7D,OAAO,sBAAsB,kFAAkF,EAC/G,OAAO,cAAc,2CAA2C,EAChE,OAAO,yBAAyB,oBAAoB,aAAa,KAAK,IAAI,CAAC,GAAG,EAC9E,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,WAAW,iBAAiB,KAAK,QAAQ;AAE/C,QAAI;AACJ,QAAI,KAAK,MAAM;AACb,yBAAmB,MAAM,wBAAwB,KAAK,MAAM,KAAK,IAAI;AACrE,UAAI,CAAC,kBAAkB;AACrB,gBAAQ,IAAI,EAAE,4BAA4B,EAAE,IAAI,KAAK,KAAK,CAAC,CAAC;AAC5D,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF,OAAO;AACL,yBAAmB,MAAM,aAAa,KAAK,IAAI;AAC/C,UAAI,CAAC,kBAAkB;AACrB,gBAAQ,IAAI,EAAE,gBAAgB,CAAC;AAC/B,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAEA,YAAQ,IAAI,EAAE,eAAe,CAAC;AAC9B,UAAM,EAAE,OAAO,aAAa,IAAI,MAAM,gBAAgB;AAAA,MACpD,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX;AAAA,MACA,SAAS,KAAK;AAAA,IAChB,CAAC;AACD,UAAM,OAAO,YAAY,iBAAiB,OAAO,YAAY;AAC7D,UAAM,SAAS,iBAAiB,IAAI;AAEpC,YAAQ,IAAI,MAAM;AAGlB,QAAI,KAAK,MAAM,KAAK,mBAAmB,SAAS,GAAG;AACjD,cAAQ,IAAI,EAAE,gBAAgB,EAAE,OAAO,KAAK,mBAAmB,OAAO,CAAC,CAAC;AACxE,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAIH,QACG,QAAQ,SAAS,EACjB;AAAA,EACC;AACF,EACC,OAAO,sBAAsB,oBAAoB,KAAK,EACtD,OAAO,oBAAoB,gBAAgB,GAAG,EAC9C,OAAO,UAAU,uBAAuB,EACxC,OAAO,cAAc,2CAA2C,EAChE,OAAO,yBAAyB,oBAAoB,aAAa,KAAK,IAAI,CAAC,GAAG,EAC9E,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,WAAW,iBAAiB,KAAK,QAAQ;AAC/C,QAAI,WAAW,MAAM,aAAa,KAAK,IAAI;AAE3C,QAAI,CAAC,UAAU;AACb,cAAQ,IAAI,EAAE,oBAAoB,CAAC;AACnC,YAAM,SAAS,MAAM,gBAAgB;AAAA,QACnC,QAAQ,KAAK;AAAA,QACb,MAAM,KAAK;AAAA,QACX;AAAA,QACA,SAAS,KAAK;AAAA,MAChB,CAAC;AACD,iBAAW,MAAM,aAAa,KAAK,MAAM,OAAO,OAAO,OAAO,YAAY;AAAA,QACxE,WAAW,KAAK;AAAA,QAAQ;AAAA,MAC1B,CAAC;AAAA,IACH;AAEA,UAAM,QAAQ,SAAS;AAEvB,QAAI,KAAK,MAAM;AACb,YAAM,UAAU;AAAA,QACd,YAAY,OAAO,KAAK,MAAM,KAAK,EAAE,KAAK;AAAA,QAC1C,mBAAmB,SAAS;AAAA,QAC5B,YAAY,MAAM;AAAA,QAClB,YAAY,MAAM;AAAA,QAClB,sBAAsB,MAAM,qBAAqB;AAAA,QACjD,eAAe,OAAO,OAAO,MAAM,KAAK,EACrC,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,SAAS,EAAE,WAAW,MAAM,EACxD,MAAM,GAAG,EAAE,EACX,IAAI,CAAC,OAAO;AAAA,UACX,MAAM,EAAE;AAAA,UACR,gBAAgB,EAAE,WAAW;AAAA,UAC7B,iBAAiB,EAAE,aAAa;AAAA,QAClC,EAAE;AAAA,MACN;AACA,cAAQ,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAC5C;AAAA,IACF;AAEA,YAAQ,IAAI,EAAE,eAAe,EAAE,MAAM,MAAM,QAAQ,CAAC,CAAC;AACrD,YAAQ,IAAI,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC3D,YAAQ,IAAI,EAAE,iBAAiB,EAAE,OAAO,MAAM,WAAW,CAAC,CAAC;AAC3D,YAAQ,IAAI,EAAE,qBAAqB,EAAE,OAAO,MAAM,qBAAqB,OAAO,CAAC,CAAC;AAChF,YAAQ,IAAI,EAAE,gBAAgB,EAAE,IAAI,SAAS,UAAU,CAAC,CAAC;AAEzD,YAAQ,IAAI,EAAE,gBAAgB,CAAC;AAC/B,eAAW,KAAK,OAAO,KAAK,MAAM,KAAK,EAAE,KAAK,GAAG;AAC/C,cAAQ,IAAI,KAAK,CAAC,EAAE;AAAA,IACtB;AAAA,EACF,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAIH,QACG,QAAQ,OAAO,EACf;AAAA,EACC;AACF,EACC,OAAO,sBAAsB,oBAAoB,KAAK,EACtD,OAAO,oBAAoB,gBAAgB,GAAG,EAC9C,OAAO,uBAAuB,eAAe,MAAM,EACnD;AAAA,EACC;AAAA,EACA;AACF,EACC,OAAO,eAAe,wCAAwC,EAC9D,OAAO,cAAc,2CAA2C,EAChE,OAAO,yBAAyB,oBAAoB,aAAa,KAAK,IAAI,CAAC,GAAG,EAC9E,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,WAAW,iBAAiB,KAAK,QAAQ;AAC/C,YAAQ,IAAI,EAAE,cAAc,CAAC;AAC7B,YAAQ,IAAI,EAAE,eAAe,CAAC;AAG9B,QAAI,OAAO;AACX,UAAM,SAAS,MAAM,gBAAgB;AAAA,MACnC,QAAQ,KAAK;AAAA,MACb,MAAM,KAAK;AAAA,MACX,SAAS,KAAK;AAAA,MACd;AAAA,MACA,SAAS,KAAK;AAAA,IAChB,CAAC;AACD,UAAM,WAAW,MAAM,aAAa,KAAK,IAAI;AAC7C,QAAI,UAAU;AACZ,aAAO,YAAY,SAAS,OAAO,OAAO,KAAK;AAAA,IACjD;AAEA,UAAM,OAAO,SAAS,KAAK,MAAM,EAAE;AACnC,UAAM,SAAS,YAAY,OAAO,OAAO;AAAA,MACvC;AAAA,MACA;AAAA,MACA,eAAe,OAAO;AAAA,MACtB,iBAAiB,OAAO;AAAA,IAC1B,CAAC;AAED,YAAQ,IAAI,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC;AACxC,YAAQ,IAAI,EAAE,UAAU,CAAC;AAEzB,QAAI,KAAK,OAAO;AACd,cAAQ,IAAI,EAAE,gBAAgB,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC;AACnD,UAAI,WAAiD;AACrD,YAAM,KAAK,QAAQ,EAAE,WAAW,KAAK,GAAG,MAAM;AAC5C,YAAI,SAAU,cAAa,QAAQ;AACnC,mBAAW,WAAW,YAAY;AAChC,cAAI;AACF,oBAAQ,IAAI,EAAE,eAAe,CAAC;AAC9B,kBAAM,YAAY,MAAM,gBAAgB;AAAA,cACtC,QAAQ,KAAK;AAAA,cACb,MAAM,KAAK;AAAA,cACX,SAAS,KAAK;AAAA,cACd;AAAA,cACA,SAAS;AAAA;AAAA,YACX,CAAC;AACD,mBAAO,MAAM;AACb,wBAAY,UAAU,OAAO;AAAA,cAC3B;AAAA,cACA,eAAe,UAAU;AAAA,cACzB,iBAAiB,UAAU;AAAA,YAC7B,CAAC;AACD,oBAAQ,IAAI,EAAE,cAAc,CAAC;AAAA,UAC/B,QAAQ;AAAA,UAAkD;AAAA,QAC5D,GAAG,GAAG;AAAA,MACR,CAAC;AAAA,IACH;AAAA,EACF,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAIH,QACG,QAAQ,UAAU,EAClB;AAAA,EACC;AACF,EACC,OAAO,sBAAsB,oBAAoB,KAAK,EACtD,OAAO,OAAO,SAAS;AACtB,QAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CAeyB,KAAK,MAAM;AAAA;AAErD,MAAI;AACF,UAAM,MAAME,OAAK,WAAW,WAAW;AACvC,UAAMC,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACpC,UAAM,OAAOD,OAAK,KAAK,gBAAgB;AACvC,UAAME,WAAU,MAAM,UAAU,OAAO;AACvC,YAAQ,IAAI,EAAE,gBAAgB,EAAE,KAAK,CAAC,CAAC;AAAA,EACzC,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAIH,QACG,QAAQ,SAAS,EACjB,YAAY,uCAAuC,EACnD,OAAO,oBAAoB,gBAAgB,GAAG,EAC9C,OAAO,wBAAwB,uBAAuB,IAAI,EAC1D,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,YAAY,MAAM,cAAc,KAAK,IAAI;AAE/C,QAAI,UAAU,WAAW,GAAG;AAC1B,cAAQ,IAAI,EAAE,eAAe,CAAC;AAC9B;AAAA,IACF;AAEA,UAAM,QAAQ,SAAS,KAAK,OAAO,EAAE;AACrC,UAAM,QAAQ,UAAU,MAAM,GAAG,KAAK;AAEtC,YAAQ,IAAI,EAAE,eAAe,CAAC;AAC9B,YAAQ,IAAI,EAAE,iBAAiB,EAAE,OAAO,UAAU,OAAO,CAAC,CAAC;AAC3D,YAAQ,IAAI,EAAE;AACd,eAAW,KAAK,OAAO;AACrB,YAAM,SAAS,EAAE,gBAAgB,mBAAmB;AACpD,cAAQ,IAAI,EAAE,iBAAiB;AAAA,QAC7B,IAAI,EAAE;AAAA,QACN,OAAO,EAAE;AAAA,QACT,OAAO,EAAE;AAAA,QACT,UAAU,EAAE;AAAA,QACZ;AAAA,MACF,CAAC,CAAC;AAAA,IACJ;AAAA,EACF,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAIH,IAAM,YAAY,QACf,QAAQ,QAAQ,EAChB,YAAY,+CAA+C;AAE9D,UACG,QAAQ,MAAM,EACd,YAAY,4CAA4C,EACxD,OAAO,oBAAoB,gBAAgB,GAAG,EAC9C,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,WAAW,MAAM,gBAAgB,KAAK,IAAI;AAChD,QAAI,UAAU;AACZ,cAAQ,IAAI,EAAE,sBAAsB,CAAC;AACrC;AAAA,IACF;AAEA,UAAM,SAAS;AAAA,MACb,SAAS;AAAA,MACT,QAAQ;AAAA,QACN,EAAE,MAAM,YAAY,WAAW,YAAY,aAAa,WAAW;AAAA,QACnE,EAAE,MAAM,WAAW,WAAW,WAAW,aAAa,YAAY;AAAA,MACpE;AAAA,IACF;AAEA,UAAM,gBAAgB,KAAK,MAAM,MAAM;AACvC,YAAQ,IAAI,EAAE,gBAAgB,CAAC;AAAA,EACjC,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAEH,UACG,QAAQ,MAAM,EACd,YAAY,wBAAwB,EACpC,OAAO,oBAAoB,gBAAgB,GAAG,EAC9C,OAAO,OAAO,SAAS;AACtB,MAAI;AACF,UAAM,SAAS,MAAM,gBAAgB,KAAK,IAAI;AAC9C,QAAI,CAAC,QAAQ;AACX,cAAQ,IAAI,EAAE,iBAAiB,CAAC;AAChC;AAAA,IACF;AAEA,YAAQ,IAAI,EAAE,iBAAiB,EAAE,OAAO,OAAO,OAAO,OAAO,CAAC,CAAC;AAC/D,eAAW,SAAS,OAAO,QAAQ;AACjC,YAAM,OAAO,MAAM,WAAW,KAAK,MAAM,QAAQ,MAAM;AACvD,YAAM,OAAO,MAAM,cAAc,WAAM,MAAM,WAAW,KAAK;AAC7D,cAAQ,IAAI,KAAK,MAAM,IAAI,KAAK,MAAM,SAAS,GAAG,IAAI,GAAG,IAAI,EAAE;AAAA,IACjE;AAAA,EACF,SAAS,OAAO;AACd,gBAAY,KAAK;AAAA,EACnB;AACF,CAAC;AAIH,SAAS,iBAAiB,MAAuC;AAC/D,MAAI,CAAC,KAAM,QAAO;AAClB,MAAI,gBAAgB,SAAS,IAAI,EAAG,QAAO;AAC3C,UAAQ,MAAM,qBAAqB,IAAI,EAAE;AACzC,UAAQ,MAAM,oBAAoB,aAAa,KAAK,IAAI,CAAC,EAAE;AAC3D,UAAQ,KAAK,CAAC;AAChB;AAEA,SAAS,YAAY,OAAuB;AAC1C,MAAI,iBAAiB,eAAe;AAClC,YAAQ,MAAM,EAAE,sBAAsB,EAAE,SAAS,MAAM,QAAQ,CAAC,CAAC;AAAA,EACnE,WAAW,iBAAiB,cAAc;AACxC,YAAQ,MAAM,EAAE,qBAAqB,EAAE,SAAS,MAAM,QAAQ,CAAC,CAAC;AAAA,EAClE,WAAW,iBAAiB,OAAO;AACjC,YAAQ,MAAM,EAAE,qBAAqB,EAAE,SAAS,MAAM,QAAQ,CAAC,CAAC;AAAA,EAClE,OAAO;AACL,YAAQ,MAAM,EAAE,wBAAwB,EAAE,SAAS,OAAO,KAAK,EAAE,CAAC,CAAC;AAAA,EACrE;AACA,UAAQ,KAAK,CAAC;AAChB;AAEA,QAAQ,MAAM;","names":["writeFile","mkdir","join","resolve","stat","readdir","stat","join","join","resolve","join","segments","resolve","fromRoot","resolve","stat","resolve","join","readFileSync","resolve","join","readFileSync","readFile","join","join","readFile","readFile","writeFile","mkdir","stat","join","resolve","readdir","ARCHTRACKER_DIR","result","layerConfig","mkdir","writeFile","readFile","readdir","join","z","ARCHTRACKER_DIR","z","join","mkdir","writeFile","readFile","isNodeError","readdir","isNodeError","readFileSync","join","dirname","dirname","join","readFileSync","readFileSync","join","dirname","fileURLToPath","join","mkdir","writeFile"]}