@gettymade/roux 0.1.3 → 0.2.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.
- package/README.md +24 -41
- package/dist/cli/index.js +2058 -1226
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +278 -54
- package/dist/index.js +1365 -575
- package/dist/index.js.map +1 -1
- package/package.json +6 -2
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../node_modules/string-similarity/src/index.js","../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/commands/status.ts","../../src/providers/docstore/cache.ts","../../src/providers/vector/sqlite.ts","../../src/cli/commands/serve.ts","../../src/providers/docstore/index.ts","../../src/providers/docstore/parser.ts","../../src/graph/builder.ts","../../src/graph/operations.ts","../../src/providers/embedding/transformers.ts","../../src/core/graphcore.ts","../../src/mcp/server.ts","../../src/mcp/types.ts","../../src/mcp/truncate.ts","../../src/mcp/transforms.ts","../../src/mcp/handlers.ts","../../src/cli/commands/viz.ts"],"sourcesContent":["module.exports = {\n\tcompareTwoStrings:compareTwoStrings,\n\tfindBestMatch:findBestMatch\n};\n\nfunction compareTwoStrings(first, second) {\n\tfirst = first.replace(/\\s+/g, '')\n\tsecond = second.replace(/\\s+/g, '')\n\n\tif (first === second) return 1; // identical or empty\n\tif (first.length < 2 || second.length < 2) return 0; // if either is a 0-letter or 1-letter string\n\n\tlet firstBigrams = new Map();\n\tfor (let i = 0; i < first.length - 1; i++) {\n\t\tconst bigram = first.substring(i, i + 2);\n\t\tconst count = firstBigrams.has(bigram)\n\t\t\t? firstBigrams.get(bigram) + 1\n\t\t\t: 1;\n\n\t\tfirstBigrams.set(bigram, count);\n\t};\n\n\tlet intersectionSize = 0;\n\tfor (let i = 0; i < second.length - 1; i++) {\n\t\tconst bigram = second.substring(i, i + 2);\n\t\tconst count = firstBigrams.has(bigram)\n\t\t\t? firstBigrams.get(bigram)\n\t\t\t: 0;\n\n\t\tif (count > 0) {\n\t\t\tfirstBigrams.set(bigram, count - 1);\n\t\t\tintersectionSize++;\n\t\t}\n\t}\n\n\treturn (2.0 * intersectionSize) / (first.length + second.length - 2);\n}\n\nfunction findBestMatch(mainString, targetStrings) {\n\tif (!areArgsValid(mainString, targetStrings)) throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');\n\t\n\tconst ratings = [];\n\tlet bestMatchIndex = 0;\n\n\tfor (let i = 0; i < targetStrings.length; i++) {\n\t\tconst currentTargetString = targetStrings[i];\n\t\tconst currentRating = compareTwoStrings(mainString, currentTargetString)\n\t\tratings.push({target: currentTargetString, rating: currentRating})\n\t\tif (currentRating > ratings[bestMatchIndex].rating) {\n\t\t\tbestMatchIndex = i\n\t\t}\n\t}\n\t\n\t\n\tconst bestMatch = ratings[bestMatchIndex]\n\t\n\treturn { ratings: ratings, bestMatch: bestMatch, bestMatchIndex: bestMatchIndex };\n}\n\nfunction areArgsValid(mainString, targetStrings) {\n\tif (typeof mainString !== 'string') return false;\n\tif (!Array.isArray(targetStrings)) return false;\n\tif (!targetStrings.length) return false;\n\tif (targetStrings.find( function (s) { return typeof s !== 'string'})) return false;\n\treturn true;\n}\n","#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { resolve } from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { initCommand } from './commands/init.js';\nimport { statusCommand } from './commands/status.js';\nimport { serveCommand } from './commands/serve.js';\nimport { vizCommand } from './commands/viz.js';\n\nconst program = new Command();\n\nprogram\n .name('roux')\n .description('Graph Programming Interface for knowledge bases')\n .version('0.1.0');\n\nprogram\n .command('init')\n .description('Initialize Roux in a directory')\n .argument('[directory]', 'Directory to initialize', '.')\n .action(async (directory: string) => {\n const resolvedDir = resolve(directory);\n const result = await initCommand(resolvedDir);\n\n if (result.created) {\n console.log(`Initialized Roux in ${resolvedDir}`);\n console.log(` Config: ${result.configPath}`);\n if (result.hooksInstalled) {\n console.log(` Claude hooks: installed`);\n }\n } else {\n if (result.hooksInstalled) {\n console.log(`Upgraded Roux in ${resolvedDir}`);\n console.log(` Claude hooks: installed`);\n } else {\n console.log(`Already initialized: ${result.configPath}`);\n }\n }\n });\n\nprogram\n .command('status')\n .description('Show graph statistics')\n .argument('[directory]', 'Directory to check', '.')\n .action(async (directory: string) => {\n const resolvedDir = resolve(directory);\n\n try {\n const result = await statusCommand(resolvedDir);\n console.log('Graph Status:');\n console.log(` Nodes: ${result.nodeCount}`);\n console.log(` Edges: ${result.edgeCount}`);\n console.log(` Embeddings: ${result.embeddingCount}/${result.nodeCount}`);\n console.log(\n ` Coverage: ${(result.embeddingCoverage * 100).toFixed(1)}%`\n );\n } catch (error) {\n console.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n process.exit(1);\n }\n });\n\nprogram\n .command('serve')\n .description('Start MCP server with file watching')\n .argument('[directory]', 'Directory to serve', '.')\n .option('--no-watch', 'Disable file watching')\n .action(async (directory: string, options: { watch: boolean }) => {\n const resolvedDir = resolve(directory);\n\n try {\n console.log('Starting Roux server...');\n\n const handle = await serveCommand(resolvedDir, {\n watch: options.watch,\n onProgress: (current, total) => {\n process.stdout.write(\n `\\r[${current}/${total}] Generating embeddings...`\n );\n if (current === total) {\n console.log(' Done.');\n }\n },\n });\n\n console.log(`Serving ${handle.nodeCount} nodes`);\n if (handle.isWatching) {\n console.log('Watching for file changes...');\n }\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log('\\nShutting down...');\n await handle.stop();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n // Keep process alive\n await new Promise(() => {});\n } catch (error) {\n console.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n process.exit(1);\n }\n });\n\nprogram\n .command('viz')\n .description('Generate graph visualization')\n .argument('[directory]', 'Directory to visualize', '.')\n .option('-o, --output <path>', 'Output file path')\n .option('--open', 'Open in browser after generation')\n .action(\n async (directory: string, options: { output?: string; open?: boolean }) => {\n const resolvedDir = resolve(directory);\n\n try {\n const result = await vizCommand(resolvedDir, {\n output: options.output,\n open: options.open,\n });\n\n console.log(\n `Generated visualization: ${result.nodeCount} nodes, ${result.edgeCount} edges`\n );\n console.log(` Output: ${result.outputPath}`);\n\n if (result.shouldOpen) {\n const openCmd =\n process.platform === 'darwin'\n ? 'open'\n : process.platform === 'win32'\n ? 'start'\n : 'xdg-open';\n execFile(openCmd, [result.outputPath]);\n }\n } catch (error) {\n console.error(\n error instanceof Error ? error.message : 'Unknown error'\n );\n process.exit(1);\n }\n }\n );\n\nprogram.parse();\n","import { mkdir, writeFile, readFile, access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface InitResult {\n created: boolean;\n configPath: string;\n hooksInstalled: boolean;\n}\n\ninterface McpServerConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n type?: string;\n url?: string;\n}\n\ninterface McpConfig {\n mcpServers?: Record<string, McpServerConfig>;\n}\n\ninterface ClaudeHook {\n type: string;\n command: string;\n}\n\ninterface ClaudeHookEntry {\n matcher: string;\n hooks: ClaudeHook[];\n}\n\ninterface ClaudeSettings {\n hooks?: {\n PreToolUse?: ClaudeHookEntry[];\n PostToolUse?: ClaudeHookEntry[];\n [key: string]: ClaudeHookEntry[] | undefined;\n };\n [key: string]: unknown;\n}\n\nconst DEFAULT_CONFIG = `providers:\n store:\n type: docstore\n`;\n\nconst ROUX_MCP_CONFIG: McpServerConfig = {\n command: 'npx',\n args: ['roux', 'serve', '.'],\n env: {},\n};\n\n/** Marker to identify roux's hook for idempotent updates */\nexport const HOOK_MARKER = 'roux-enforce-mcp';\n\n/** Hook command that rejects direct file operations on .md files */\nconst ENFORCE_MCP_HOOK_COMMAND = `node -e \"/* ${HOOK_MARKER} */ const d=JSON.parse(require('fs').readFileSync(0,'utf8'));const p=d.tool_input?.file_path||'';if(p.endsWith('.md')){console.error('Use mcp__roux__* tools for markdown files instead of Read/Edit/Write');process.exit(2)}\"`;\n\nconst ROUX_HOOK_ENTRY: ClaudeHookEntry = {\n matcher: 'Read|Edit|Write',\n hooks: [{ type: 'command', command: ENFORCE_MCP_HOOK_COMMAND }],\n};\n\nexport async function initCommand(directory: string): Promise<InitResult> {\n const configPath = join(directory, 'roux.yaml');\n const rouxDir = join(directory, '.roux');\n\n // Check if already initialized\n let configExists = false;\n try {\n await access(configPath);\n configExists = true;\n } catch {\n // File doesn't exist\n }\n\n // Create .roux directory regardless\n await mkdir(rouxDir, { recursive: true });\n\n // Create/update MCP config\n await updateMcpConfig(directory);\n\n // Determine store type and update Claude hooks if docstore\n const storeType = await getStoreType(directory, configExists);\n let hooksInstalled = false;\n if (storeType === 'docstore') {\n hooksInstalled = await updateClaudeSettings(directory);\n }\n\n if (configExists) {\n return { created: false, configPath, hooksInstalled };\n }\n\n // Create roux.yaml with minimal defaults\n await writeFile(configPath, DEFAULT_CONFIG, 'utf-8');\n return { created: true, configPath, hooksInstalled };\n}\n\nasync function updateMcpConfig(directory: string): Promise<void> {\n const mcpPath = join(directory, '.mcp.json');\n\n let config: McpConfig = {};\n\n // Try to read existing config\n try {\n const content = await readFile(mcpPath, 'utf-8');\n config = JSON.parse(content) as McpConfig;\n } catch {\n // File doesn't exist or invalid JSON — start fresh\n }\n\n // Ensure mcpServers object exists\n if (!config.mcpServers) {\n config.mcpServers = {};\n }\n\n // Add/update roux entry\n config.mcpServers.roux = ROUX_MCP_CONFIG;\n\n // Write back\n await writeFile(mcpPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nasync function getStoreType(\n directory: string,\n configExists: boolean\n): Promise<string> {\n if (!configExists) {\n return 'docstore'; // Default\n }\n\n try {\n const configPath = join(directory, 'roux.yaml');\n const content = await readFile(configPath, 'utf-8');\n // Simple YAML parsing for store type\n const typeMatch = content.match(/store:\\s*\\n\\s*type:\\s*(\\w+)/);\n if (typeMatch?.[1]) {\n return typeMatch[1];\n }\n } catch {\n // Can't read config, assume default\n }\n\n return 'docstore';\n}\n\n/** Returns true if hooks were installed, false if skipped */\nasync function updateClaudeSettings(directory: string): Promise<boolean> {\n const claudeDir = join(directory, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n // Ensure .claude directory exists\n await mkdir(claudeDir, { recursive: true });\n\n let config: ClaudeSettings = {};\n let existingContent: string | null = null;\n\n // Try to read existing settings\n try {\n existingContent = await readFile(settingsPath, 'utf-8');\n config = JSON.parse(existingContent) as ClaudeSettings;\n } catch (err) {\n if (existingContent !== null) {\n // File exists but is malformed JSON — don't touch it\n return false;\n }\n // File doesn't exist — start fresh\n }\n\n // Ensure hooks.PreToolUse array exists\n if (!config.hooks) {\n config.hooks = {};\n }\n if (!config.hooks.PreToolUse) {\n config.hooks.PreToolUse = [];\n }\n\n // Check if our hook already exists\n const hasRouxHook = config.hooks.PreToolUse.some((entry) =>\n entry.hooks?.some((h) => h.command?.includes(HOOK_MARKER))\n );\n\n if (hasRouxHook) {\n return false; // Already installed, don't duplicate\n }\n\n // Add our hook\n config.hooks.PreToolUse.push(ROUX_HOOK_ENTRY);\n\n // Write back\n await writeFile(settingsPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n return true;\n}\n","import { access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { Cache } from '../../providers/docstore/cache.js';\nimport { SqliteVectorProvider } from '../../providers/vector/sqlite.js';\n\nexport interface StatusResult {\n nodeCount: number;\n edgeCount: number;\n embeddingCount: number;\n embeddingCoverage: number;\n}\n\nexport async function statusCommand(directory: string): Promise<StatusResult> {\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n const cacheDir = join(directory, '.roux');\n const cache = new Cache(cacheDir);\n const vectorProvider = new SqliteVectorProvider(cacheDir);\n\n try {\n const stats = cache.getStats();\n const embeddingCount = vectorProvider.getEmbeddingCount();\n\n const embeddingCoverage =\n stats.nodeCount === 0 ? 1 : embeddingCount / stats.nodeCount;\n\n return {\n nodeCount: stats.nodeCount,\n edgeCount: stats.edgeCount,\n embeddingCount,\n embeddingCoverage,\n };\n } finally {\n cache.close();\n vectorProvider.close();\n }\n}\n","import Database from 'better-sqlite3';\nimport { join } from 'node:path';\nimport { mkdirSync } from 'node:fs';\nimport stringSimilarity from 'string-similarity';\nimport type { Node, SourceRef } from '../../types/node.js';\nimport type {\n TagMode,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../../types/provider.js';\n\nexport interface EmbeddingRecord {\n model: string;\n vector: number[];\n}\n\nexport interface CentralityRecord {\n pagerank: number;\n inDegree: number;\n outDegree: number;\n computedAt: number;\n}\n\ninterface NodeRow {\n id: string;\n title: string;\n content: string;\n tags: string;\n outgoing_links: string;\n properties: string;\n source_type: string;\n source_path: string;\n source_modified: number;\n}\n\ninterface EmbeddingRow {\n node_id: string;\n model: string;\n vector: Buffer;\n}\n\ninterface CentralityRow {\n node_id: string;\n pagerank: number;\n in_degree: number;\n out_degree: number;\n computed_at: number;\n}\n\nexport class Cache {\n private db: Database.Database;\n\n constructor(cacheDir: string) {\n mkdirSync(cacheDir, { recursive: true });\n const dbPath = join(cacheDir, 'cache.db');\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.initSchema();\n }\n\n private initSchema(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS nodes (\n id TEXT PRIMARY KEY,\n title TEXT,\n content TEXT,\n tags TEXT,\n outgoing_links TEXT,\n properties TEXT,\n source_type TEXT,\n source_path TEXT,\n source_modified INTEGER\n );\n\n CREATE TABLE IF NOT EXISTS embeddings (\n node_id TEXT PRIMARY KEY,\n model TEXT,\n vector BLOB,\n FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE\n );\n\n CREATE TABLE IF NOT EXISTS centrality (\n node_id TEXT PRIMARY KEY,\n pagerank REAL,\n in_degree INTEGER,\n out_degree INTEGER,\n computed_at INTEGER,\n FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE\n );\n\n CREATE INDEX IF NOT EXISTS idx_nodes_source_path ON nodes(source_path);\n `);\n // Enable foreign key enforcement for cascade deletes\n this.db.pragma('foreign_keys = ON');\n }\n\n getTableNames(): string[] {\n const rows = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table'\")\n .all() as Array<{ name: string }>;\n return rows.map((r) => r.name);\n }\n\n upsertNode(\n node: Node,\n sourceType: string,\n sourcePath: string,\n sourceModified: number\n ): void {\n const stmt = this.db.prepare(`\n INSERT INTO nodes (id, title, content, tags, outgoing_links, properties, source_type, source_path, source_modified)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n title = excluded.title,\n content = excluded.content,\n tags = excluded.tags,\n outgoing_links = excluded.outgoing_links,\n properties = excluded.properties,\n source_type = excluded.source_type,\n source_path = excluded.source_path,\n source_modified = excluded.source_modified\n `);\n\n stmt.run(\n node.id,\n node.title,\n node.content,\n JSON.stringify(node.tags),\n JSON.stringify(node.outgoingLinks),\n JSON.stringify(node.properties),\n sourceType,\n sourcePath,\n sourceModified\n );\n }\n\n getNode(id: string): Node | null {\n const row = this.db\n .prepare('SELECT * FROM nodes WHERE id = ?')\n .get(id) as NodeRow | undefined;\n\n if (!row) return null;\n return this.rowToNode(row);\n }\n\n getNodes(ids: string[]): Node[] {\n if (ids.length === 0) return [];\n\n // Fetch all nodes then order by input ids\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT * FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as NodeRow[];\n\n const nodeMap = new Map<string, Node>();\n for (const row of rows) {\n nodeMap.set(row.id, this.rowToNode(row));\n }\n\n // Return in requested order\n const result: Node[] = [];\n for (const id of ids) {\n const node = nodeMap.get(id);\n if (node) result.push(node);\n }\n return result;\n }\n\n deleteNode(id: string): void {\n this.db.prepare('DELETE FROM nodes WHERE id = ?').run(id);\n }\n\n getAllNodes(): Node[] {\n const rows = this.db.prepare('SELECT * FROM nodes').all() as NodeRow[];\n return rows.map((row) => this.rowToNode(row));\n }\n\n searchByTags(tags: string[], mode: TagMode): Node[] {\n if (tags.length === 0) return [];\n\n const allNodes = this.getAllNodes();\n const lowerTags = tags.map((t) => t.toLowerCase());\n\n return allNodes.filter((node) => {\n const nodeTags = node.tags.map((t) => t.toLowerCase());\n if (mode === 'any') {\n return lowerTags.some((t) => nodeTags.includes(t));\n } else {\n return lowerTags.every((t) => nodeTags.includes(t));\n }\n });\n }\n\n getModifiedTime(sourcePath: string): number | null {\n const row = this.db\n .prepare('SELECT source_modified FROM nodes WHERE source_path = ?')\n .get(sourcePath) as { source_modified: number } | undefined;\n\n return row?.source_modified ?? null;\n }\n\n getNodeByPath(sourcePath: string): Node | null {\n const row = this.db\n .prepare('SELECT * FROM nodes WHERE source_path = ?')\n .get(sourcePath) as NodeRow | undefined;\n\n if (!row) return null;\n return this.rowToNode(row);\n }\n\n getAllTrackedPaths(): Set<string> {\n const rows = this.db\n .prepare('SELECT source_path FROM nodes')\n .all() as Array<{ source_path: string }>;\n\n return new Set(rows.map((r) => r.source_path));\n }\n\n resolveTitles(ids: string[]): Map<string, string> {\n if (ids.length === 0) return new Map();\n\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT id, title FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as Array<{ id: string; title: string }>;\n\n const result = new Map<string, string>();\n for (const row of rows) {\n result.set(row.id, row.title);\n }\n return result;\n }\n\n nodesExist(ids: string[]): Map<string, boolean> {\n if (ids.length === 0) return new Map();\n\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT id FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as Array<{ id: string }>;\n\n const existingIds = new Set(rows.map((r) => r.id));\n const result = new Map<string, boolean>();\n for (const id of ids) {\n result.set(id, existingIds.has(id));\n }\n return result;\n }\n\n listNodes(filter: ListFilter, options?: ListOptions): ListNodesResult {\n const limit = Math.min(options?.limit ?? 100, 1000);\n const offset = options?.offset ?? 0;\n\n // Build query dynamically based on filters\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (filter.tag) {\n // Case-insensitive tag match - tags stored as JSON array\n conditions.push(\"EXISTS (SELECT 1 FROM json_each(tags) WHERE LOWER(json_each.value) = LOWER(?))\");\n params.push(filter.tag);\n }\n\n if (filter.path) {\n conditions.push(\"id LIKE ? || '%'\");\n params.push(filter.path);\n }\n\n const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n\n // Get total count of matching nodes (without limit/offset)\n const countQuery = `SELECT COUNT(*) as count FROM nodes ${whereClause}`;\n const countRow = this.db.prepare(countQuery).get(...params) as { count: number };\n const total = countRow.count;\n\n // Get paginated results\n const query = `SELECT id, title FROM nodes ${whereClause} LIMIT ? OFFSET ?`;\n const rows = this.db.prepare(query).all(...params, limit, offset) as Array<{ id: string; title: string }>;\n\n const nodes = rows.map((row) => ({ id: row.id, title: row.title }));\n return { nodes, total };\n }\n\n resolveNodes(names: string[], options?: ResolveOptions): ResolveResult[] {\n if (names.length === 0) return [];\n\n const strategy = options?.strategy ?? 'fuzzy';\n const threshold = options?.threshold ?? 0.7;\n\n // Build filter without undefined values\n const filter: ListFilter = {};\n if (options?.tag) filter.tag = options.tag;\n if (options?.path) filter.path = options.path;\n\n // Get candidate nodes (applying tag/path filters)\n const { nodes: candidates } = this.listNodes(filter, { limit: 1000 });\n\n if (candidates.length === 0) {\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n const candidateTitles = candidates.map((c) => c.title.toLowerCase());\n const titleToId = new Map<string, string>();\n for (const c of candidates) {\n titleToId.set(c.title.toLowerCase(), c.id);\n }\n\n return names.map((query): ResolveResult => {\n const queryLower = query.toLowerCase();\n\n if (strategy === 'exact') {\n // Exact case-insensitive title match\n const matchedId = titleToId.get(queryLower);\n if (matchedId) {\n return { query, match: matchedId, score: 1 };\n }\n return { query, match: null, score: 0 };\n }\n\n // Fuzzy strategy using string-similarity\n if (strategy === 'fuzzy') {\n const result = stringSimilarity.findBestMatch(queryLower, candidateTitles);\n const bestMatch = result.bestMatch;\n\n if (bestMatch.rating >= threshold) {\n // bestMatch.target is guaranteed to exist in titleToId since both come from candidates\n const matchedId = titleToId.get(bestMatch.target)!;\n return { query, match: matchedId, score: bestMatch.rating };\n }\n return { query, match: null, score: 0 };\n }\n\n // Semantic strategy - not supported at cache level, return no match\n // DocStore will handle semantic by using embedding provider\n return { query, match: null, score: 0 };\n });\n }\n\n updateOutgoingLinks(nodeId: string, links: string[]): void {\n this.db\n .prepare('UPDATE nodes SET outgoing_links = ? WHERE id = ?')\n .run(JSON.stringify(links), nodeId);\n }\n\n storeEmbedding(nodeId: string, vector: number[], model: string): void {\n const buffer = Buffer.from(new Float32Array(vector).buffer);\n this.db\n .prepare(\n `\n INSERT INTO embeddings (node_id, model, vector)\n VALUES (?, ?, ?)\n ON CONFLICT(node_id) DO UPDATE SET\n model = excluded.model,\n vector = excluded.vector\n `\n )\n .run(nodeId, model, buffer);\n }\n\n getEmbedding(nodeId: string): EmbeddingRecord | null {\n const row = this.db\n .prepare('SELECT model, vector FROM embeddings WHERE node_id = ?')\n .get(nodeId) as EmbeddingRow | undefined;\n\n if (!row) return null;\n\n const float32 = new Float32Array(\n row.vector.buffer,\n row.vector.byteOffset,\n row.vector.length / 4\n );\n return {\n model: row.model,\n vector: Array.from(float32),\n };\n }\n\n storeCentrality(\n nodeId: string,\n pagerank: number,\n inDegree: number,\n outDegree: number,\n computedAt: number\n ): void {\n this.db\n .prepare(\n `\n INSERT INTO centrality (node_id, pagerank, in_degree, out_degree, computed_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(node_id) DO UPDATE SET\n pagerank = excluded.pagerank,\n in_degree = excluded.in_degree,\n out_degree = excluded.out_degree,\n computed_at = excluded.computed_at\n `\n )\n .run(nodeId, pagerank, inDegree, outDegree, computedAt);\n }\n\n getCentrality(nodeId: string): CentralityRecord | null {\n const row = this.db\n .prepare('SELECT * FROM centrality WHERE node_id = ?')\n .get(nodeId) as CentralityRow | undefined;\n\n if (!row) return null;\n\n return {\n pagerank: row.pagerank,\n inDegree: row.in_degree,\n outDegree: row.out_degree,\n computedAt: row.computed_at,\n };\n }\n\n getStats(): { nodeCount: number; embeddingCount: number; edgeCount: number } {\n const nodeCount = this.db\n .prepare('SELECT COUNT(*) as count FROM nodes')\n .get() as { count: number };\n\n const embeddingCount = this.db\n .prepare('SELECT COUNT(*) as count FROM embeddings')\n .get() as { count: number };\n\n // Sum all in_degree values to get edge count\n const edgeSum = this.db\n .prepare('SELECT SUM(in_degree) as total FROM centrality')\n .get() as { total: number | null };\n\n return {\n nodeCount: nodeCount.count,\n embeddingCount: embeddingCount.count,\n edgeCount: edgeSum.total ?? 0,\n };\n }\n\n clear(): void {\n this.db.exec('DELETE FROM centrality');\n this.db.exec('DELETE FROM embeddings');\n this.db.exec('DELETE FROM nodes');\n }\n\n close(): void {\n this.db.close();\n }\n\n private rowToNode(row: NodeRow): Node {\n const sourceRef: SourceRef = {\n type: row.source_type as SourceRef['type'],\n path: row.source_path,\n lastModified: new Date(row.source_modified),\n };\n\n return {\n id: row.id,\n title: row.title,\n content: row.content,\n tags: JSON.parse(row.tags) as string[],\n outgoingLinks: JSON.parse(row.outgoing_links) as string[],\n properties: JSON.parse(row.properties) as Record<string, unknown>,\n sourceRef,\n };\n }\n}\n","import Database from 'better-sqlite3';\nimport type { Database as DatabaseType } from 'better-sqlite3';\nimport { join } from 'node:path';\nimport type { VectorProvider, VectorSearchResult } from '../../types/provider.js';\n\nexport class SqliteVectorProvider implements VectorProvider {\n private db: DatabaseType;\n private ownsDb: boolean;\n\n constructor(pathOrDb: string | DatabaseType) {\n if (typeof pathOrDb === 'string') {\n this.db = new Database(join(pathOrDb, 'vectors.db'));\n this.ownsDb = true;\n } else {\n this.db = pathOrDb;\n this.ownsDb = false;\n }\n this.init();\n }\n\n private init(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS vectors (\n id TEXT PRIMARY KEY,\n model TEXT NOT NULL,\n vector BLOB NOT NULL\n )\n `);\n }\n\n async store(id: string, vector: number[], model: string): Promise<void> {\n if (vector.length === 0) {\n throw new Error('Cannot store empty vector');\n }\n for (const v of vector) {\n if (!Number.isFinite(v)) {\n throw new Error(`Invalid vector value: ${v}`);\n }\n }\n\n // Validate dimension consistency (exclude self for overwrites)\n const existing = this.db\n .prepare('SELECT LENGTH(vector) / 4 as dim FROM vectors WHERE id != ? LIMIT 1')\n .get(id) as { dim: number } | undefined;\n\n if (existing && existing.dim !== vector.length) {\n throw new Error(\n `Dimension mismatch: cannot store ${vector.length}-dim vector, existing vectors have ${existing.dim} dimensions`\n );\n }\n\n const blob = Buffer.from(new Float32Array(vector).buffer);\n this.db\n .prepare(\n `INSERT OR REPLACE INTO vectors (id, model, vector) VALUES (?, ?, ?)`\n )\n .run(id, model, blob);\n }\n\n async search(vector: number[], limit: number): Promise<VectorSearchResult[]> {\n if (vector.length === 0) {\n throw new Error('Cannot search with empty vector');\n }\n for (const v of vector) {\n if (!Number.isFinite(v)) {\n throw new Error(`Invalid vector value: ${v}`);\n }\n }\n if (limit <= 0) {\n return [];\n }\n\n const rows = this.db\n .prepare('SELECT id, vector FROM vectors')\n .all() as Array<{ id: string; vector: Buffer }>;\n\n if (rows.length === 0) {\n return [];\n }\n\n // Check dimension mismatch against first stored vector\n const firstStoredDim = rows[0]!.vector.byteLength / 4;\n if (vector.length !== firstStoredDim) {\n throw new Error(\n `Dimension mismatch: query has ${vector.length} dimensions, stored vectors have ${firstStoredDim}`\n );\n }\n\n const queryVec = new Float32Array(vector);\n const results: VectorSearchResult[] = [];\n\n for (const row of rows) {\n const storedVec = new Float32Array(\n row.vector.buffer,\n row.vector.byteOffset,\n row.vector.byteLength / 4\n );\n const distance = cosineDistance(queryVec, storedVec);\n results.push({ id: row.id, distance });\n }\n\n results.sort((a, b) => a.distance - b.distance);\n return results.slice(0, limit);\n }\n\n async delete(id: string): Promise<void> {\n this.db.prepare('DELETE FROM vectors WHERE id = ?').run(id);\n }\n\n async getModel(id: string): Promise<string | null> {\n const row = this.db\n .prepare('SELECT model FROM vectors WHERE id = ?')\n .get(id) as { model: string } | undefined;\n return row?.model ?? null;\n }\n\n hasEmbedding(id: string): boolean {\n const row = this.db\n .prepare('SELECT 1 FROM vectors WHERE id = ?')\n .get(id);\n return row !== undefined;\n }\n\n /** For testing: get table names */\n getTableNames(): string[] {\n const rows = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table'\")\n .all() as Array<{ name: string }>;\n return rows.map((r) => r.name);\n }\n\n /** For testing: get vector blob size */\n getVectorBlobSize(id: string): number | null {\n const row = this.db\n .prepare('SELECT LENGTH(vector) as size FROM vectors WHERE id = ?')\n .get(id) as { size: number } | undefined;\n return row?.size ?? null;\n }\n\n /** Get total number of stored embeddings */\n getEmbeddingCount(): number {\n const row = this.db\n .prepare('SELECT COUNT(*) as count FROM vectors')\n .get() as { count: number };\n return row.count;\n }\n\n close(): void {\n if (this.ownsDb) {\n this.db.close();\n }\n }\n}\n\nfunction cosineDistance(a: Float32Array, b: Float32Array): number {\n let dotProduct = 0;\n let magnitudeA = 0;\n let magnitudeB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!;\n magnitudeA += a[i]! * a[i]!;\n magnitudeB += b[i]! * b[i]!;\n }\n\n magnitudeA = Math.sqrt(magnitudeA);\n magnitudeB = Math.sqrt(magnitudeB);\n\n if (magnitudeA === 0 || magnitudeB === 0) {\n return 1; // No similarity for zero vectors\n }\n\n const similarity = dotProduct / (magnitudeA * magnitudeB);\n return 1 - similarity;\n}\n","import { access, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { DocStore } from '../../providers/docstore/index.js';\nimport { TransformersEmbeddingProvider } from '../../providers/embedding/transformers.js';\nimport { GraphCoreImpl } from '../../core/graphcore.js';\nimport { McpServer, type TransportFactory } from '../../mcp/server.js';\nimport type { RouxConfig } from '../../types/config.js';\n\nexport interface ServeOptions {\n watch?: boolean;\n transportFactory?: TransportFactory;\n onProgress?: (current: number, total: number) => void;\n}\n\nexport interface ServeHandle {\n stop: () => Promise<void>;\n isWatching: boolean;\n nodeCount: number;\n}\n\nexport async function serveCommand(\n directory: string,\n options: ServeOptions = {}\n): Promise<ServeHandle> {\n const { watch = true, transportFactory, onProgress } = options;\n\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n // Load config\n const configContent = await readFile(configPath, 'utf-8');\n const config = parseYaml(configContent) as RouxConfig;\n\n // Create providers\n const sourcePath = config.source?.path ?? '.';\n const resolvedSourcePath = join(directory, sourcePath);\n const cachePath = config.cache?.path ?? '.roux';\n const resolvedCachePath = join(directory, cachePath);\n\n const store = new DocStore(resolvedSourcePath, resolvedCachePath);\n const embedding = new TransformersEmbeddingProvider(\n config.providers?.embedding?.type === 'local'\n ? config.providers.embedding.model\n : undefined\n );\n\n // Sync cache\n await store.sync();\n\n // Generate embeddings for nodes without them\n const allNodeIds = await store.getAllNodeIds();\n const total = allNodeIds.length;\n\n for (let i = 0; i < allNodeIds.length; i++) {\n const id = allNodeIds[i]!;\n\n if (!hasExistingEmbedding(store, id)) {\n const node = await store.getNode(id);\n if (node && node.content) {\n const vector = await embedding.embed(node.content);\n await store.storeEmbedding(id, vector, embedding.modelId());\n }\n }\n\n if (onProgress) {\n onProgress(i + 1, total);\n }\n }\n\n // Create GraphCore\n const core = new GraphCoreImpl();\n core.registerStore(store);\n core.registerEmbedding(embedding);\n\n // Start MCP server\n const mcpServer = new McpServer({\n core,\n store,\n hasEmbedding: true,\n });\n\n await mcpServer.start(transportFactory);\n\n // Start file watcher if enabled\n if (watch) {\n try {\n await store.startWatching(async (changedIds) => {\n // Generate embeddings for changed nodes\n for (const id of changedIds) {\n const node = await store.getNode(id);\n if (node && node.content) {\n const vector = await embedding.embed(node.content);\n await store.storeEmbedding(id, vector, embedding.modelId());\n }\n }\n });\n } catch (err) {\n console.warn(\n 'File watching disabled:',\n (err as Error).message || 'Unknown error'\n );\n }\n }\n\n return {\n stop: async () => {\n store.stopWatching();\n store.close();\n await mcpServer.close();\n },\n isWatching: store.isWatching(),\n nodeCount: allNodeIds.length,\n };\n}\n\nfunction hasExistingEmbedding(store: DocStore, id: string): boolean {\n return store.hasEmbedding(id);\n}\n","import { readFile, writeFile, stat, readdir, mkdir, rm } from 'node:fs/promises';\nimport { join, relative, dirname, resolve } from 'node:path';\nimport { watch, type FSWatcher } from 'chokidar';\nimport type { DirectedGraph } from 'graphology';\nimport type { Node } from '../../types/node.js';\nimport type {\n StoreProvider,\n NeighborOptions,\n Metric,\n TagMode,\n VectorSearchResult,\n VectorProvider,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../../types/provider.js';\nimport { Cache } from './cache.js';\nimport { SqliteVectorProvider } from '../vector/sqlite.js';\nimport {\n parseMarkdown,\n extractWikiLinks,\n normalizeId,\n titleFromPath,\n serializeToMarkdown,\n} from './parser.js';\nimport { buildGraph } from '../../graph/builder.js';\nimport {\n getNeighborIds,\n findPath as graphFindPath,\n getHubs as graphGetHubs,\n computeCentrality,\n} from '../../graph/operations.js';\n\nexport class DocStore implements StoreProvider {\n private cache: Cache;\n private sourceRoot: string;\n private graph: DirectedGraph | null = null;\n private vectorProvider: VectorProvider;\n private ownsVectorProvider: boolean;\n\n private watcher: FSWatcher | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private pendingChanges: Map<string, 'add' | 'change' | 'unlink'> = new Map();\n private onChangeCallback: ((changedIds: string[]) => void) | undefined;\n\n constructor(\n sourceRoot: string,\n cacheDir: string,\n vectorProvider?: VectorProvider\n ) {\n this.sourceRoot = sourceRoot;\n this.cache = new Cache(cacheDir);\n this.ownsVectorProvider = !vectorProvider;\n this.vectorProvider = vectorProvider ?? new SqliteVectorProvider(cacheDir);\n }\n\n async sync(): Promise<void> {\n const currentPaths = await this.collectMarkdownFiles(this.sourceRoot);\n const trackedPaths = this.cache.getAllTrackedPaths();\n\n // Process new/modified files\n for (const filePath of currentPaths) {\n try {\n const mtime = await this.getFileMtime(filePath);\n const cachedMtime = this.cache.getModifiedTime(filePath);\n\n if (cachedMtime === null || mtime > cachedMtime) {\n const node = await this.fileToNode(filePath);\n this.cache.upsertNode(node, 'file', filePath, mtime);\n }\n } catch (err) {\n // File may have been deleted between readdir and stat — skip it\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n continue;\n }\n throw err;\n }\n }\n\n // Remove deleted files\n const currentSet = new Set(currentPaths);\n for (const tracked of trackedPaths) {\n if (!currentSet.has(tracked)) {\n const node = this.cache.getNodeByPath(tracked);\n if (node) {\n this.cache.deleteNode(node.id);\n }\n }\n }\n\n // Resolve wiki-links after all nodes are cached\n const filenameIndex = this.buildFilenameIndex();\n this.resolveOutgoingLinks(filenameIndex);\n\n // Rebuild graph from all nodes\n this.rebuildGraph();\n }\n\n async createNode(node: Node): Promise<void> {\n const normalizedId = normalizeId(node.id);\n this.validatePathWithinSource(normalizedId);\n\n const existing = this.cache.getNode(normalizedId);\n if (existing) {\n throw new Error(`Node already exists: ${normalizedId}`);\n }\n\n const filePath = join(this.sourceRoot, normalizedId);\n const dir = dirname(filePath);\n await mkdir(dir, { recursive: true });\n\n const parsed = {\n title: node.title,\n tags: node.tags,\n properties: node.properties,\n content: node.content,\n };\n const markdown = serializeToMarkdown(parsed);\n await writeFile(filePath, markdown, 'utf-8');\n\n const mtime = await this.getFileMtime(filePath);\n const normalizedNode = { ...node, id: normalizedId };\n this.cache.upsertNode(normalizedNode, 'file', filePath, mtime);\n\n // Rebuild graph to include new node\n this.rebuildGraph();\n }\n\n async updateNode(id: string, updates: Partial<Node>): Promise<void> {\n const normalizedId = normalizeId(id);\n const existing = this.cache.getNode(normalizedId);\n if (!existing) {\n throw new Error(`Node not found: ${id}`);\n }\n\n // If content is updated, reparse wiki-links\n let outgoingLinks = updates.outgoingLinks;\n if (updates.content !== undefined && outgoingLinks === undefined) {\n const rawLinks = extractWikiLinks(updates.content);\n outgoingLinks = rawLinks.map((link) => this.normalizeWikiLink(link));\n }\n\n const updated: Node = {\n ...existing,\n ...updates,\n outgoingLinks: outgoingLinks ?? existing.outgoingLinks,\n id: existing.id, // ID cannot be changed\n };\n\n const filePath = join(this.sourceRoot, existing.id);\n const parsed = {\n title: updated.title,\n tags: updated.tags,\n properties: updated.properties,\n content: updated.content,\n };\n const markdown = serializeToMarkdown(parsed);\n await writeFile(filePath, markdown, 'utf-8');\n\n const mtime = await this.getFileMtime(filePath);\n this.cache.upsertNode(updated, 'file', filePath, mtime);\n\n // Rebuild graph if links changed\n if (outgoingLinks !== undefined || updates.outgoingLinks !== undefined) {\n this.rebuildGraph();\n }\n }\n\n async deleteNode(id: string): Promise<void> {\n const normalizedId = normalizeId(id);\n const existing = this.cache.getNode(normalizedId);\n if (!existing) {\n throw new Error(`Node not found: ${id}`);\n }\n\n const filePath = join(this.sourceRoot, existing.id);\n await rm(filePath);\n this.cache.deleteNode(existing.id);\n await this.vectorProvider.delete(existing.id);\n\n // Rebuild graph without deleted node\n this.rebuildGraph();\n }\n\n async getNode(id: string): Promise<Node | null> {\n // Normalize ID for case-insensitive lookup\n const normalizedId = normalizeId(id);\n return this.cache.getNode(normalizedId);\n }\n\n async getNodes(ids: string[]): Promise<Node[]> {\n const normalizedIds = ids.map(normalizeId);\n return this.cache.getNodes(normalizedIds);\n }\n\n async getAllNodeIds(): Promise<string[]> {\n const nodes = this.cache.getAllNodes();\n return nodes.map((n) => n.id);\n }\n\n async searchByTags(tags: string[], mode: TagMode): Promise<Node[]> {\n return this.cache.searchByTags(tags, mode);\n }\n\n async getRandomNode(tags?: string[]): Promise<Node | null> {\n let candidates: Node[];\n\n if (tags && tags.length > 0) {\n candidates = await this.searchByTags(tags, 'any');\n } else {\n candidates = this.cache.getAllNodes();\n }\n\n if (candidates.length === 0) {\n return null;\n }\n\n const randomIndex = Math.floor(Math.random() * candidates.length);\n // Safe: randomIndex is always 0 to length-1 when length > 0\n return candidates[randomIndex]!;\n }\n\n async resolveTitles(ids: string[]): Promise<Map<string, string>> {\n return this.cache.resolveTitles(ids);\n }\n\n async listNodes(\n filter: ListFilter,\n options?: ListOptions\n ): Promise<ListNodesResult> {\n return this.cache.listNodes(filter, options);\n }\n\n async resolveNodes(\n names: string[],\n options?: ResolveOptions\n ): Promise<ResolveResult[]> {\n // For exact and fuzzy, delegate to cache\n const strategy = options?.strategy ?? 'fuzzy';\n if (strategy === 'exact' || strategy === 'fuzzy') {\n return this.cache.resolveNodes(names, options);\n }\n\n // Semantic strategy: use vector search\n // This requires embedding provider which DocStore doesn't have direct access to\n // Return unmatched for now - GraphCore will handle semantic with embedding provider\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n async nodesExist(ids: string[]): Promise<Map<string, boolean>> {\n const normalizedIds = ids.map(normalizeId);\n return this.cache.nodesExist(normalizedIds);\n }\n\n async getNeighbors(id: string, options: NeighborOptions): Promise<Node[]> {\n this.ensureGraph();\n const neighborIds = getNeighborIds(this.graph!, id, options);\n return this.cache.getNodes(neighborIds);\n }\n\n async findPath(source: string, target: string): Promise<string[] | null> {\n this.ensureGraph();\n return graphFindPath(this.graph!, source, target);\n }\n\n async getHubs(metric: Metric, limit: number): Promise<Array<[string, number]>> {\n this.ensureGraph();\n return graphGetHubs(this.graph!, metric, limit);\n }\n\n async storeEmbedding(\n id: string,\n vector: number[],\n model: string\n ): Promise<void> {\n return this.vectorProvider.store(id, vector, model);\n }\n\n async searchByVector(\n vector: number[],\n limit: number\n ): Promise<VectorSearchResult[]> {\n return this.vectorProvider.search(vector, limit);\n }\n\n hasEmbedding(id: string): boolean {\n return this.vectorProvider.hasEmbedding(id);\n }\n\n close(): void {\n this.stopWatching();\n this.cache.close();\n if (this.ownsVectorProvider && 'close' in this.vectorProvider) {\n (this.vectorProvider as { close: () => void }).close();\n }\n }\n\n startWatching(onChange?: (changedIds: string[]) => void): Promise<void> {\n if (this.watcher) {\n throw new Error('Already watching. Call stopWatching() first.');\n }\n\n this.onChangeCallback = onChange;\n\n return new Promise((resolve, reject) => {\n this.watcher = watch(this.sourceRoot, {\n ignoreInitial: true,\n ignored: [...DocStore.EXCLUDED_DIRS].map((dir) => `**/${dir}/**`),\n awaitWriteFinish: {\n stabilityThreshold: 100,\n },\n followSymlinks: false,\n });\n\n this.watcher\n .on('ready', () => resolve())\n .on('add', (path) => this.queueChange(path, 'add'))\n .on('change', (path) => this.queueChange(path, 'change'))\n .on('unlink', (path) => this.queueChange(path, 'unlink'))\n .on('error', (err) => {\n if ((err as NodeJS.ErrnoException).code === 'EMFILE') {\n console.error(\n 'File watcher hit file descriptor limit. ' +\n 'Try: ulimit -n 65536 or reduce watched files.'\n );\n }\n reject(err);\n });\n });\n }\n\n stopWatching(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.pendingChanges.clear();\n\n if (this.watcher) {\n this.watcher.close();\n this.watcher = null;\n }\n }\n\n isWatching(): boolean {\n return this.watcher !== null;\n }\n\n private queueChange(filePath: string, event: 'add' | 'change' | 'unlink'): void {\n const relativePath = relative(this.sourceRoot, filePath);\n const id = normalizeId(relativePath);\n\n // Check exclusions\n if (!filePath.endsWith('.md')) {\n return;\n }\n\n // Check if path contains any excluded directory\n const pathParts = relativePath.split('/');\n for (const part of pathParts) {\n if (DocStore.EXCLUDED_DIRS.has(part)) {\n return;\n }\n }\n\n // Apply coalescing rules\n const existing = this.pendingChanges.get(id);\n\n if (existing) {\n if (existing === 'add' && event === 'change') {\n // add + change = add (keep as add)\n return;\n } else if (existing === 'add' && event === 'unlink') {\n // add + unlink = remove from queue\n this.pendingChanges.delete(id);\n } else if (existing === 'change' && event === 'unlink') {\n // change + unlink = unlink\n this.pendingChanges.set(id, 'unlink');\n }\n // change + change = change (already set, no action needed)\n } else {\n this.pendingChanges.set(id, event);\n }\n\n // Reset debounce timer\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.processQueue();\n }, 1000);\n }\n\n private async processQueue(): Promise<void> {\n const changes = new Map(this.pendingChanges);\n this.pendingChanges.clear();\n this.debounceTimer = null;\n\n const processedIds: string[] = [];\n\n for (const [id, event] of changes) {\n try {\n if (event === 'unlink') {\n const existing = this.cache.getNode(id);\n if (existing) {\n this.cache.deleteNode(id);\n await this.vectorProvider.delete(id);\n processedIds.push(id);\n }\n } else {\n // add or change\n const filePath = join(this.sourceRoot, id);\n const node = await this.fileToNode(filePath);\n const mtime = await this.getFileMtime(filePath);\n this.cache.upsertNode(node, 'file', filePath, mtime);\n processedIds.push(id);\n }\n } catch (err) {\n console.warn(`Failed to process file change for ${id}:`, err);\n }\n }\n\n // Resolve wiki-links and rebuild graph after processing all changes\n if (processedIds.length > 0) {\n const filenameIndex = this.buildFilenameIndex();\n this.resolveOutgoingLinks(filenameIndex);\n this.rebuildGraph();\n }\n\n // Call callback if provided\n if (this.onChangeCallback && processedIds.length > 0) {\n this.onChangeCallback(processedIds);\n }\n }\n\n private buildFilenameIndex(): Map<string, string[]> {\n const index = new Map<string, string[]>();\n for (const node of this.cache.getAllNodes()) {\n const basename = node.id.split('/').pop()!;\n const existing = index.get(basename) ?? [];\n existing.push(node.id);\n index.set(basename, existing);\n }\n // Sort each array alphabetically for deterministic first-match\n for (const paths of index.values()) {\n paths.sort();\n }\n return index;\n }\n\n private resolveOutgoingLinks(filenameIndex: Map<string, string[]>): void {\n // Build set of valid node IDs for quick lookup\n const validNodeIds = new Set<string>();\n for (const paths of filenameIndex.values()) {\n for (const path of paths) {\n validNodeIds.add(path);\n }\n }\n\n for (const node of this.cache.getAllNodes()) {\n const resolved = node.outgoingLinks.map((link) => {\n // If link already exists as a valid node ID, keep it\n if (validNodeIds.has(link)) {\n return link;\n }\n // Only resolve bare filenames (no path separators)\n // Partial paths like \"folder/target.md\" stay literal\n if (link.includes('/')) {\n return link;\n }\n // Try basename lookup for bare filenames\n const matches = filenameIndex.get(link);\n if (matches && matches.length > 0) {\n return matches[0]!;\n }\n return link;\n });\n\n // Only update if something changed\n if (resolved.some((r, i) => r !== node.outgoingLinks[i])) {\n this.cache.updateOutgoingLinks(node.id, resolved);\n }\n }\n }\n\n private ensureGraph(): void {\n if (!this.graph) {\n this.rebuildGraph();\n }\n }\n\n private rebuildGraph(): void {\n const nodes = this.cache.getAllNodes();\n this.graph = buildGraph(nodes);\n\n // Cache centrality metrics\n const centrality = computeCentrality(this.graph);\n const now = Date.now();\n for (const [id, metrics] of centrality) {\n this.cache.storeCentrality(id, 0, metrics.inDegree, metrics.outDegree, now);\n }\n }\n\n private static readonly EXCLUDED_DIRS = new Set(['.roux', 'node_modules', '.git', '.obsidian']);\n\n private async collectMarkdownFiles(dir: string): Promise<string[]> {\n const results: string[] = [];\n\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n // Directory doesn't exist yet\n return results;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Skip excluded directories\n if (DocStore.EXCLUDED_DIRS.has(entry.name)) {\n continue;\n }\n const nested = await this.collectMarkdownFiles(fullPath);\n results.push(...nested);\n } else if (entry.isFile() && entry.name.endsWith('.md')) {\n results.push(fullPath);\n }\n }\n\n return results;\n }\n\n private async getFileMtime(filePath: string): Promise<number> {\n const stats = await stat(filePath);\n return stats.mtimeMs;\n }\n\n private async fileToNode(filePath: string): Promise<Node> {\n const raw = await readFile(filePath, 'utf-8');\n const parsed = parseMarkdown(raw);\n\n const relativePath = relative(this.sourceRoot, filePath);\n const id = normalizeId(relativePath);\n\n // Derive title from path if not in frontmatter\n const title = parsed.title ?? titleFromPath(id);\n\n // Extract and normalize wiki links\n const rawLinks = extractWikiLinks(parsed.content);\n const outgoingLinks = rawLinks.map((link) => this.normalizeWikiLink(link));\n\n return {\n id,\n title,\n content: parsed.content,\n tags: parsed.tags,\n outgoingLinks,\n properties: parsed.properties,\n sourceRef: {\n type: 'file',\n path: filePath,\n lastModified: new Date(await this.getFileMtime(filePath)),\n },\n };\n }\n\n /**\n * Normalize a wiki-link target to an ID.\n * - If it has a file extension, normalize as-is\n * - If no extension, add .md\n * - Lowercase, forward slashes\n */\n private normalizeWikiLink(target: string): string {\n let normalized = target.toLowerCase().replace(/\\\\/g, '/');\n\n // Add .md if no file extension present\n // File extension = dot followed by 1-4 alphanumeric chars at end\n if (!this.hasFileExtension(normalized)) {\n normalized += '.md';\n }\n\n return normalized;\n }\n\n private hasFileExtension(path: string): boolean {\n // Match common file extensions: .md, .txt, .png, .json, etc.\n // Extension must contain at least one letter (to exclude .2024, .123, etc.)\n const match = path.match(/\\.([a-z0-9]{1,4})$/i);\n if (!match?.[1]) return false;\n // Require at least one letter in the extension\n return /[a-z]/i.test(match[1]);\n }\n\n private validatePathWithinSource(id: string): void {\n const resolvedPath = resolve(this.sourceRoot, id);\n const resolvedRoot = resolve(this.sourceRoot);\n\n if (!resolvedPath.startsWith(resolvedRoot + '/')) {\n throw new Error(`Path traversal detected: ${id} resolves outside source root`);\n }\n }\n}\n\nexport { Cache } from './cache.js';\nexport {\n parseMarkdown,\n extractWikiLinks,\n normalizeId,\n titleFromPath,\n serializeToMarkdown,\n} from './parser.js';\n","import matter from 'gray-matter';\n\nexport interface ParsedMarkdown {\n title: string | undefined;\n tags: string[];\n properties: Record<string, unknown>;\n content: string;\n}\n\n/**\n * Parse markdown with YAML frontmatter.\n * Handles missing/malformed frontmatter gracefully.\n */\nexport function parseMarkdown(raw: string): ParsedMarkdown {\n let parsed: matter.GrayMatterFile<string>;\n try {\n parsed = matter(raw);\n } catch {\n // Malformed frontmatter - return content as-is\n return {\n title: undefined,\n tags: [],\n properties: {},\n content: raw,\n };\n }\n\n const data = parsed.data as Record<string, unknown>;\n\n // Extract title\n const title = typeof data['title'] === 'string' ? data['title'] : undefined;\n\n // Extract tags - must be an array of strings\n let tags: string[] = [];\n if (Array.isArray(data['tags'])) {\n tags = data['tags'].filter((t): t is string => typeof t === 'string');\n }\n\n // Extract other properties (excluding title and tags)\n const properties: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (key !== 'title' && key !== 'tags') {\n properties[key] = value;\n }\n }\n\n return {\n title,\n tags,\n properties,\n content: parsed.content.trim(),\n };\n}\n\n/**\n * Extract wiki-link targets from markdown content.\n * Ignores links inside code blocks and inline code.\n * Deduplicates results.\n */\nexport function extractWikiLinks(content: string): string[] {\n // Remove code blocks first\n const withoutCodeBlocks = content.replace(/```[\\s\\S]*?```/g, '');\n\n // Remove inline code\n const withoutInlineCode = withoutCodeBlocks.replace(/`[^`]+`/g, '');\n\n // Match wiki links: [[target]] or [[target|display]]\n const linkRegex = /\\[\\[([^\\]|]+)(?:\\|[^\\]]+)?\\]\\]/g;\n const seen = new Set<string>();\n const links: string[] = [];\n\n let match;\n while ((match = linkRegex.exec(withoutInlineCode)) !== null) {\n const target = match[1]?.trim();\n if (target && !seen.has(target)) {\n seen.add(target);\n links.push(target);\n }\n }\n\n return links;\n}\n\n/**\n * Normalize a file path to a consistent ID format.\n * - Lowercased\n * - Forward slashes only\n * - Preserves extension\n */\nexport function normalizeId(path: string): string {\n return path.toLowerCase().replace(/\\\\/g, '/');\n}\n\n/**\n * Derive a human-readable title from a file path.\n * - Removes directory prefix\n * - Removes extension\n * - Replaces hyphens/underscores with spaces\n * - Title-cases words\n */\nexport function titleFromPath(path: string): string {\n // Get filename without directory\n const parts = path.split(/[/\\\\]/);\n // parts is always non-empty (even '' splits to [''])\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const filename = parts.at(-1)!;\n\n // Remove extension\n const withoutExt = filename.replace(/\\.[^.]+$/, '');\n\n // Replace hyphens and underscores with spaces, collapse multiples\n const spaced = withoutExt.replace(/[-_]+/g, ' ').toLowerCase();\n\n // Title-case each word\n return spaced\n .split(' ')\n .filter((w) => w.length > 0)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/**\n * Serialize parsed markdown back to a string with YAML frontmatter.\n * Omits frontmatter if no metadata is present.\n */\nexport function serializeToMarkdown(parsed: ParsedMarkdown): string {\n const hasFrontmatter =\n parsed.title !== undefined ||\n parsed.tags.length > 0 ||\n Object.keys(parsed.properties).length > 0;\n\n if (!hasFrontmatter) {\n return parsed.content;\n }\n\n // Build frontmatter object\n const frontmatter: Record<string, unknown> = {};\n\n if (parsed.title !== undefined) {\n frontmatter['title'] = parsed.title;\n }\n\n if (parsed.tags.length > 0) {\n frontmatter['tags'] = parsed.tags;\n }\n\n // Add other properties\n for (const [key, value] of Object.entries(parsed.properties)) {\n frontmatter[key] = value;\n }\n\n // Use gray-matter to stringify\n return matter.stringify(parsed.content, frontmatter);\n}\n","import { DirectedGraph } from 'graphology';\nimport type { Node } from '../types/node.js';\n\n/**\n * Build a directed graph from an array of nodes.\n * Edges are derived from each node's outgoingLinks.\n * Links to non-existent nodes are ignored.\n */\nexport function buildGraph(nodes: Node[]): DirectedGraph {\n const graph = new DirectedGraph();\n\n // First pass: add all nodes\n const nodeIds = new Set<string>();\n for (const node of nodes) {\n graph.addNode(node.id);\n nodeIds.add(node.id);\n }\n\n // Second pass: add edges (only to existing nodes)\n for (const node of nodes) {\n const seen = new Set<string>();\n for (const target of node.outgoingLinks) {\n // Skip if target doesn't exist or we've already added this edge\n if (!nodeIds.has(target) || seen.has(target)) {\n continue;\n }\n seen.add(target);\n graph.addDirectedEdge(node.id, target);\n }\n }\n\n return graph;\n}\n","import type { DirectedGraph } from 'graphology';\nimport { bidirectional } from 'graphology-shortest-path';\nimport type {\n NeighborOptions,\n Metric,\n CentralityMetrics,\n} from '../types/provider.js';\n\n/**\n * Get neighbor IDs based on direction.\n * Returns empty array if node doesn't exist.\n */\nexport function getNeighborIds(\n graph: DirectedGraph,\n id: string,\n options: NeighborOptions\n): string[] {\n if (!graph.hasNode(id)) {\n return [];\n }\n\n let neighbors: string[];\n\n switch (options.direction) {\n case 'in':\n neighbors = graph.inNeighbors(id);\n break;\n case 'out':\n neighbors = graph.outNeighbors(id);\n break;\n case 'both':\n neighbors = graph.neighbors(id);\n break;\n }\n\n if (options.limit !== undefined) {\n if (options.limit <= 0) {\n return [];\n }\n if (options.limit < neighbors.length) {\n return neighbors.slice(0, options.limit);\n }\n }\n\n return neighbors;\n}\n\n/**\n * Find shortest path between two nodes.\n * Returns array of node IDs or null if no path exists.\n */\nexport function findPath(\n graph: DirectedGraph,\n source: string,\n target: string\n): string[] | null {\n if (!graph.hasNode(source) || !graph.hasNode(target)) {\n return null;\n }\n\n if (source === target) {\n return [source];\n }\n\n const path = bidirectional(graph, source, target);\n return path;\n}\n\n/**\n * Get top nodes by centrality metric.\n * Returns array of [id, score] tuples sorted descending.\n */\nexport function getHubs(\n graph: DirectedGraph,\n metric: Metric,\n limit: number\n): Array<[string, number]> {\n if (limit <= 0) {\n return [];\n }\n\n const scores: Array<[string, number]> = [];\n\n graph.forEachNode((id) => {\n let score: number;\n switch (metric) {\n case 'in_degree':\n score = graph.inDegree(id);\n break;\n case 'out_degree':\n score = graph.outDegree(id);\n break;\n case 'pagerank':\n // PageRank is post-MVP, use in_degree as fallback\n score = graph.inDegree(id);\n break;\n }\n scores.push([id, score]);\n });\n\n scores.sort((a, b) => b[1] - a[1]);\n return scores.slice(0, limit);\n}\n\n/**\n * Compute centrality metrics for all nodes.\n * For MVP, computes in_degree and out_degree only.\n */\nexport function computeCentrality(\n graph: DirectedGraph\n): Map<string, CentralityMetrics> {\n const result = new Map<string, CentralityMetrics>();\n\n graph.forEachNode((id) => {\n result.set(id, {\n inDegree: graph.inDegree(id),\n outDegree: graph.outDegree(id),\n });\n });\n\n return result;\n}\n","import { pipeline, type FeatureExtractionPipeline } from '@xenova/transformers';\nimport type { EmbeddingProvider } from '../../types/provider.js';\n\nconst DEFAULT_MODEL = 'Xenova/all-MiniLM-L6-v2';\nconst DEFAULT_DIMENSIONS = 384;\n\nexport class TransformersEmbeddingProvider implements EmbeddingProvider {\n private model: string;\n private dims: number;\n private pipe: FeatureExtractionPipeline | null = null;\n\n constructor(model = DEFAULT_MODEL, dimensions = DEFAULT_DIMENSIONS) {\n this.model = model;\n this.dims = dimensions;\n }\n\n private async getPipeline(): Promise<FeatureExtractionPipeline> {\n if (!this.pipe) {\n this.pipe = await pipeline('feature-extraction', this.model);\n }\n return this.pipe;\n }\n\n async embed(text: string): Promise<number[]> {\n const pipe = await this.getPipeline();\n const output = await pipe(text, { pooling: 'mean', normalize: true });\n return Array.from(output.data as Float32Array);\n }\n\n async embedBatch(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) {\n return [];\n }\n return Promise.all(texts.map((t) => this.embed(t)));\n }\n\n dimensions(): number {\n return this.dims;\n }\n\n modelId(): string {\n return this.model;\n }\n}\n","import type { Node, NodeWithContext } from '../types/node.js';\nimport type {\n GraphCore,\n SearchOptions,\n} from '../types/graphcore.js';\nimport type {\n StoreProvider,\n EmbeddingProvider,\n Metric,\n TagMode,\n NeighborOptions,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../types/provider.js';\nimport type { RouxConfig } from '../types/config.js';\nimport { DocStore } from '../providers/docstore/index.js';\nimport { TransformersEmbeddingProvider } from '../providers/embedding/transformers.js';\n\nexport class GraphCoreImpl implements GraphCore {\n private store: StoreProvider | null = null;\n private embedding: EmbeddingProvider | null = null;\n\n registerStore(provider: StoreProvider): void {\n if (!provider) {\n throw new Error('Store provider is required');\n }\n this.store = provider;\n }\n\n registerEmbedding(provider: EmbeddingProvider): void {\n if (!provider) {\n throw new Error('Embedding provider is required');\n }\n this.embedding = provider;\n }\n\n private requireStore(): StoreProvider {\n if (!this.store) {\n throw new Error('StoreProvider not registered');\n }\n return this.store;\n }\n\n private requireEmbedding(): EmbeddingProvider {\n if (!this.embedding) {\n throw new Error('EmbeddingProvider not registered');\n }\n return this.embedding;\n }\n\n async search(query: string, options?: SearchOptions): Promise<Node[]> {\n const store = this.requireStore();\n const embedding = this.requireEmbedding();\n\n const limit = options?.limit ?? 10;\n const vector = await embedding.embed(query);\n const results = await store.searchByVector(vector, limit);\n\n // Results are already sorted by distance ascending\n const ids = results.map((r) => r.id);\n return store.getNodes(ids);\n }\n\n async getNode(id: string, depth?: number): Promise<NodeWithContext | null> {\n const store = this.requireStore();\n const node = await store.getNode(id);\n\n if (!node) {\n return null;\n }\n\n if (!depth || depth === 0) {\n return node;\n }\n\n // Fetch neighbors for context\n const [incomingNeighbors, outgoingNeighbors] = await Promise.all([\n store.getNeighbors(id, { direction: 'in' }),\n store.getNeighbors(id, { direction: 'out' }),\n ]);\n\n // Deduplicate neighbors (same node could be both incoming and outgoing)\n const neighborMap = new Map<string, Node>();\n for (const n of [...incomingNeighbors, ...outgoingNeighbors]) {\n neighborMap.set(n.id, n);\n }\n\n const result: NodeWithContext = {\n ...node,\n neighbors: Array.from(neighborMap.values()),\n incomingCount: incomingNeighbors.length,\n outgoingCount: outgoingNeighbors.length,\n };\n\n return result;\n }\n\n async createNode(partial: Partial<Node>): Promise<Node> {\n const store = this.requireStore();\n\n if (!partial.id || partial.id.trim() === '') {\n throw new Error('Node id is required and cannot be empty');\n }\n if (!partial.title) {\n throw new Error('Node title is required');\n }\n\n const node: Node = {\n id: partial.id,\n title: partial.title,\n content: partial.content ?? '',\n tags: partial.tags ?? [],\n outgoingLinks: partial.outgoingLinks ?? [],\n properties: partial.properties ?? {},\n ...(partial.sourceRef && { sourceRef: partial.sourceRef }),\n };\n\n await store.createNode(node);\n return (await store.getNode(node.id)) ?? node;\n }\n\n async updateNode(id: string, updates: Partial<Node>): Promise<Node> {\n const store = this.requireStore();\n await store.updateNode(id, updates);\n const updated = await store.getNode(id);\n if (!updated) {\n throw new Error(`Node not found after update: ${id}`);\n }\n return updated;\n }\n\n async deleteNode(id: string): Promise<boolean> {\n const store = this.requireStore();\n try {\n await store.deleteNode(id);\n return true;\n } catch (err) {\n // Only swallow \"not found\" errors - propagate everything else\n if (err instanceof Error && /not found/i.test(err.message)) {\n return false;\n }\n throw err;\n }\n }\n\n async getNeighbors(id: string, options: NeighborOptions): Promise<Node[]> {\n const store = this.requireStore();\n return store.getNeighbors(id, options);\n }\n\n async findPath(source: string, target: string): Promise<string[] | null> {\n const store = this.requireStore();\n return store.findPath(source, target);\n }\n\n async getHubs(\n metric: Metric,\n limit: number\n ): Promise<Array<[string, number]>> {\n const store = this.requireStore();\n return store.getHubs(metric, limit);\n }\n\n async searchByTags(\n tags: string[],\n mode: TagMode,\n limit?: number\n ): Promise<Node[]> {\n const store = this.requireStore();\n const results = await store.searchByTags(tags, mode);\n if (limit !== undefined) {\n return results.slice(0, limit);\n }\n return results;\n }\n\n async getRandomNode(tags?: string[]): Promise<Node | null> {\n const store = this.requireStore();\n return store.getRandomNode(tags);\n }\n\n async listNodes(\n filter: ListFilter,\n options?: ListOptions\n ): Promise<ListNodesResult> {\n return this.requireStore().listNodes(filter, options);\n }\n\n async resolveNodes(\n names: string[],\n options?: ResolveOptions\n ): Promise<ResolveResult[]> {\n const store = this.requireStore();\n const strategy = options?.strategy ?? 'fuzzy';\n\n // Semantic strategy requires embedding provider\n if (strategy === 'semantic') {\n if (!this.embedding) {\n throw new Error('Semantic resolution requires EmbeddingProvider');\n }\n\n // Build filter without undefined values\n const filter: ListFilter = {};\n if (options?.tag) filter.tag = options.tag;\n if (options?.path) filter.path = options.path;\n\n // Get candidates from store with filters\n const { nodes: candidates } = await store.listNodes(filter, { limit: 1000 });\n\n if (candidates.length === 0 || names.length === 0) {\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n const threshold = options?.threshold ?? 0.7;\n\n // Embed all queries in batch\n const queryVectors = await this.embedding.embedBatch(names);\n\n // Embed all candidate titles in batch\n const candidateTitles = candidates.map((c) => c.title);\n const candidateVectors = await this.embedding.embedBatch(candidateTitles);\n\n // Validate dimensions match\n if (queryVectors.length > 0 && candidateVectors.length > 0) {\n const queryDim = queryVectors[0]!.length;\n const candidateDim = candidateVectors[0]!.length;\n if (queryDim !== candidateDim) {\n throw new Error(\n `Embedding dimension mismatch: query=${queryDim}, candidate=${candidateDim}`\n );\n }\n }\n\n // For each query, find best matching candidate by cosine similarity\n return names.map((query, qIdx): ResolveResult => {\n const queryVector = queryVectors[qIdx]!;\n let bestScore = 0;\n let bestMatch: string | null = null;\n\n for (let cIdx = 0; cIdx < candidates.length; cIdx++) {\n const similarity = this.cosineSimilarity(queryVector, candidateVectors[cIdx]!);\n if (similarity > bestScore) {\n bestScore = similarity;\n bestMatch = candidates[cIdx]!.id;\n }\n }\n\n if (bestScore >= threshold) {\n return { query, match: bestMatch, score: bestScore };\n }\n return { query, match: null, score: 0 };\n });\n }\n\n // Exact and fuzzy delegate to store\n return store.resolveNodes(names, options);\n }\n\n private cosineSimilarity(a: number[], b: number[]): number {\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!;\n normA += a[i]! * a[i]!;\n normB += b[i]! * b[i]!;\n }\n if (normA === 0 || normB === 0) return 0;\n return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));\n }\n\n static fromConfig(config: RouxConfig): GraphCoreImpl {\n if (!config.providers?.store) {\n throw new Error('StoreProvider configuration is required');\n }\n\n const core = new GraphCoreImpl();\n\n // Create store based on config\n if (config.providers.store.type === 'docstore') {\n const sourcePath = config.source?.path ?? '.';\n const cachePath = config.cache?.path ?? '.roux';\n const store = new DocStore(sourcePath, cachePath);\n core.registerStore(store);\n } else {\n throw new Error(\n `Unsupported store provider type: ${config.providers.store.type}. Supported: docstore`\n );\n }\n\n // Create embedding provider (defaults to local transformers)\n const embeddingConfig = config.providers.embedding;\n if (!embeddingConfig || embeddingConfig.type === 'local') {\n const model = embeddingConfig?.model;\n const embedding = new TransformersEmbeddingProvider(model);\n core.registerEmbedding(embedding);\n } else {\n throw new Error(\n `Unsupported embedding provider type: ${embeddingConfig.type}. Supported: local`\n );\n }\n\n return core;\n }\n}\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type Tool,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport type { GraphCore } from '../types/graphcore.js';\nimport type { StoreProvider } from '../types/provider.js';\nimport { McpError } from './types.js';\nimport { dispatchTool, type HandlerContext } from './handlers.js';\n\nexport interface McpServerOptions {\n /** GraphCore instance for operations */\n core: GraphCore;\n /** StoreProvider for link resolution and direct store access */\n store: StoreProvider;\n /** Whether embedding provider is available (enables search tool) */\n hasEmbedding: boolean;\n}\n\n/** MCP Transport interface for server connection. */\nexport interface McpTransport {\n start?(): Promise<void>;\n close?(): Promise<void>;\n}\n\n/** Factory function to create a transport. Defaults to StdioServerTransport. */\nexport type TransportFactory = () => McpTransport;\n\n/** Tool input schemas as JSON Schema objects */\nconst TOOL_SCHEMAS = {\n search: {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Natural language search query',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 10,\n description: 'Maximum results to return',\n },\n include_content: {\n type: 'boolean',\n default: false,\n description:\n 'Include node content in results. Default false returns metadata only (id, title, tags, properties, links). Set true to include truncated content.',\n },\n },\n required: ['query'],\n },\n\n get_node: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID (file path for DocStore). ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n depth: {\n type: 'integer',\n minimum: 0,\n maximum: 1,\n default: 0,\n description: '0 = node only, 1 = include neighbors',\n },\n },\n required: ['id'],\n },\n\n get_neighbors: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Source node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n direction: {\n type: 'string',\n enum: ['in', 'out', 'both'],\n default: 'both',\n description: 'in = nodes linking here, out = nodes linked to, both = all',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 20,\n description: 'Maximum neighbors to return',\n },\n include_content: {\n type: 'boolean',\n default: false,\n description:\n 'Include node content in results. Default false returns metadata only (id, title, tags, properties, links). Set true to include truncated content.',\n },\n },\n required: ['id'],\n },\n\n find_path: {\n type: 'object',\n properties: {\n source: {\n type: 'string',\n description:\n 'Start node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n target: {\n type: 'string',\n description:\n 'End node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['source', 'target'],\n },\n\n get_hubs: {\n type: 'object',\n properties: {\n metric: {\n type: 'string',\n enum: ['in_degree', 'out_degree'],\n default: 'in_degree',\n description: 'Centrality metric',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 10,\n description: 'Maximum results',\n },\n },\n },\n\n search_by_tags: {\n type: 'object',\n properties: {\n tags: {\n type: 'array',\n items: { type: 'string' },\n minItems: 1,\n description: 'Tags to match',\n },\n mode: {\n type: 'string',\n enum: ['any', 'all'],\n default: 'any',\n description: 'any = OR matching, all = AND matching',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 100,\n default: 20,\n description: 'Maximum results',\n },\n },\n required: ['tags'],\n },\n\n random_node: {\n type: 'object',\n properties: {\n tags: {\n type: 'array',\n items: { type: 'string' },\n description: 'Optional: limit to nodes with these tags (any match)',\n },\n },\n },\n\n create_node: {\n type: 'object',\n properties: {\n title: {\n type: 'string',\n description:\n 'Node title (becomes filename for DocStore). Returned ID will be normalized to lowercase.',\n },\n content: {\n type: 'string',\n description: 'Full text content (markdown)',\n },\n tags: {\n type: 'array',\n items: { type: 'string' },\n default: [],\n description: 'Classification tags',\n },\n directory: {\n type: 'string',\n description: \"Optional: subdirectory path (e.g., 'notes/drafts')\",\n },\n },\n required: ['title', 'content'],\n },\n\n update_node: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID to update. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n title: {\n type: 'string',\n description: 'New title (renames file for DocStore)',\n },\n content: {\n type: 'string',\n description: 'New content (replaces entirely)',\n },\n tags: {\n type: 'array',\n items: { type: 'string' },\n description: 'New tags (replaces existing)',\n },\n },\n required: ['id'],\n },\n\n delete_node: {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID to delete. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['id'],\n },\n\n list_nodes: {\n type: 'object',\n properties: {\n tag: {\n type: 'string',\n description:\n 'Filter by tag from the \"tags\" frontmatter array (case-insensitive). Does NOT search other frontmatter fields like \"type\" or \"category\".',\n },\n path: {\n type: 'string',\n description: 'Filter by path prefix (startsWith, case-insensitive)',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 1000,\n default: 100,\n description: 'Maximum results to return',\n },\n offset: {\n type: 'integer',\n minimum: 0,\n default: 0,\n description: 'Skip this many results (for pagination)',\n },\n },\n },\n\n resolve_nodes: {\n type: 'object',\n properties: {\n names: {\n type: 'array',\n items: { type: 'string' },\n description: 'Names to resolve to existing nodes',\n },\n strategy: {\n type: 'string',\n enum: ['exact', 'fuzzy', 'semantic'],\n default: 'fuzzy',\n description:\n 'How to match names to nodes. \"exact\": case-insensitive title equality. \"fuzzy\": string similarity (Dice coefficient) — use for typos, misspellings, partial matches. \"semantic\": embedding cosine similarity — use for synonyms or related concepts (NOT typos). Misspellings embed poorly because they produce unrelated vectors.',\n },\n threshold: {\n type: 'number',\n minimum: 0,\n maximum: 1,\n default: 0.7,\n description:\n 'Minimum similarity score (0-1). Lower values match more loosely. For typo tolerance, use fuzzy with threshold 0.5-0.6. Ignored for exact strategy.',\n },\n tag: {\n type: 'string',\n description:\n 'Filter candidates by tag from \"tags\" frontmatter array (case-insensitive)',\n },\n path: {\n type: 'string',\n description: 'Filter candidates by path prefix (case-insensitive)',\n },\n },\n required: ['names'],\n },\n\n nodes_exist: {\n type: 'object',\n properties: {\n ids: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Node IDs to check existence. IDs are normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['ids'],\n },\n} as const;\n\n/** Tool definitions for MCP protocol */\nexport function getToolDefinitions(hasEmbedding: boolean): Tool[] {\n const tools: Tool[] = [\n {\n name: 'get_node',\n description:\n 'Retrieve a single node by ID with optional neighbor context',\n inputSchema: TOOL_SCHEMAS.get_node,\n },\n {\n name: 'get_neighbors',\n description: 'Get nodes linked to or from a specific node',\n inputSchema: TOOL_SCHEMAS.get_neighbors,\n },\n {\n name: 'find_path',\n description: 'Find the shortest path between two nodes',\n inputSchema: TOOL_SCHEMAS.find_path,\n },\n {\n name: 'get_hubs',\n description: 'Get the most central nodes by graph metric',\n inputSchema: TOOL_SCHEMAS.get_hubs,\n },\n {\n name: 'search_by_tags',\n description: 'Filter nodes by tags (AND or OR matching)',\n inputSchema: TOOL_SCHEMAS.search_by_tags,\n },\n {\n name: 'random_node',\n description: 'Get a random node for discovery, optionally filtered by tags',\n inputSchema: TOOL_SCHEMAS.random_node,\n },\n {\n name: 'create_node',\n description: 'Create a new node (writes file for DocStore)',\n inputSchema: TOOL_SCHEMAS.create_node,\n },\n {\n name: 'update_node',\n description:\n 'Update an existing node. Title changes rejected if incoming links exist.',\n inputSchema: TOOL_SCHEMAS.update_node,\n },\n {\n name: 'delete_node',\n description: 'Delete a node by ID',\n inputSchema: TOOL_SCHEMAS.delete_node,\n },\n {\n name: 'list_nodes',\n description:\n 'List nodes with optional filters and pagination. Tag filter searches the \"tags\" frontmatter array only. All IDs returned are lowercase.',\n inputSchema: TOOL_SCHEMAS.list_nodes,\n },\n {\n name: 'resolve_nodes',\n description:\n 'Batch resolve names to existing node IDs. Strategy selection: \"exact\" for known titles, \"fuzzy\" for typos/misspellings (e.g., \"chikken\" -> \"chicken\"), \"semantic\" for synonyms/concepts (e.g., \"poultry leg meat\" -> \"chicken thigh\"). Semantic does NOT handle typos — misspellings produce garbage embeddings.',\n inputSchema: TOOL_SCHEMAS.resolve_nodes,\n },\n {\n name: 'nodes_exist',\n description:\n 'Batch check if node IDs exist. IDs are normalized to lowercase before checking.',\n inputSchema: TOOL_SCHEMAS.nodes_exist,\n },\n ];\n\n if (hasEmbedding) {\n tools.unshift({\n name: 'search',\n description: 'Semantic similarity search across all nodes',\n inputSchema: TOOL_SCHEMAS.search,\n });\n }\n\n return tools;\n}\n\nexport class McpServer {\n private server: Server;\n private ctx: HandlerContext;\n\n constructor(options: McpServerOptions) {\n this.ctx = {\n core: options.core,\n store: options.store,\n hasEmbedding: options.hasEmbedding,\n };\n\n this.server = new Server(\n { name: 'roux', version: '0.1.0' },\n { capabilities: { tools: {} } }\n );\n\n this.setupHandlers();\n }\n\n /* v8 ignore start - MCP SDK callbacks tested via integration in Phase 11 */\n private setupHandlers(): void {\n this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: getToolDefinitions(this.ctx.hasEmbedding),\n }));\n\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n const result = await dispatchTool(this.ctx, name, args ?? {});\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n };\n } catch (error) {\n if (error instanceof McpError) {\n return {\n content: [\n { type: 'text', text: JSON.stringify(error.toResponse()) },\n ],\n isError: true,\n };\n }\n const mcpError = new McpError(\n 'PROVIDER_ERROR',\n error instanceof Error ? error.message : 'Unknown error'\n );\n return {\n content: [\n { type: 'text', text: JSON.stringify(mcpError.toResponse()) },\n ],\n isError: true,\n };\n }\n });\n }\n /* v8 ignore stop */\n\n /**\n * Start the server with optional transport factory.\n * @param transportFactory Factory to create transport. Defaults to StdioServerTransport.\n */\n async start(transportFactory?: TransportFactory): Promise<void> {\n /* v8 ignore start - Default stdio transport tested via integration */\n const transport = transportFactory\n ? transportFactory()\n : new StdioServerTransport();\n /* v8 ignore stop */\n await this.server.connect(transport as Parameters<typeof this.server.connect>[0]);\n }\n\n async close(): Promise<void> {\n await this.server.close();\n }\n}\n\n/**\n * Create and start an MCP server.\n * @param options Server configuration\n * @param transportFactory Optional transport factory for testing\n */\nexport async function createMcpServer(\n options: McpServerOptions,\n transportFactory?: TransportFactory\n): Promise<McpServer> {\n const server = new McpServer(options);\n await server.start(transportFactory);\n return server;\n}\n","import type { LinkInfo } from '../types/provider.js';\n\n/** Link with resolved human-readable title. Re-export for MCP layer. */\nexport type { LinkInfo };\n\n/** Metadata-only response for browsing operations (search, get_neighbors). */\nexport interface NodeMetadataResponse {\n id: string;\n title: string;\n tags: string[];\n links: LinkInfo[];\n properties: Record<string, unknown>;\n}\n\n/** Full response including content (for get_node, create, update, etc.). */\nexport interface NodeResponse extends NodeMetadataResponse {\n content: string;\n}\n\n/** Extended response for get_node with depth > 0. */\nexport interface NodeWithContextResponse extends NodeResponse {\n incomingNeighbors: NodeResponse[];\n outgoingNeighbors: NodeResponse[];\n incomingCount: number;\n outgoingCount: number;\n}\n\n/** Search results include similarity score. Content optional based on include_content param. */\nexport interface SearchResultResponse extends NodeMetadataResponse {\n score: number;\n content?: string;\n}\n\n/** Hub results pair ID with metric value. */\nexport interface HubResponse {\n id: string;\n title: string;\n score: number;\n}\n\n/** Path results are ordered node IDs. */\nexport interface PathResponse {\n path: string[];\n length: number;\n}\n\n/** Delete operation result. */\nexport interface DeleteResponse {\n deleted: boolean;\n}\n\n/** Standard error response shape. */\nexport interface ErrorResponse {\n error: {\n code: ErrorCode;\n message: string;\n };\n}\n\n/** Known error codes. */\nexport type ErrorCode =\n | 'INVALID_PARAMS'\n | 'NODE_EXISTS'\n | 'NODE_NOT_FOUND'\n | 'LINK_INTEGRITY'\n | 'PROVIDER_ERROR';\n\n/** Custom error class for MCP operations. */\nexport class McpError extends Error {\n constructor(\n public readonly code: ErrorCode,\n message: string\n ) {\n super(message);\n this.name = 'McpError';\n }\n\n toResponse(): ErrorResponse {\n return {\n error: {\n code: this.code,\n message: this.message,\n },\n };\n }\n}\n","/** Content truncation limits by context. */\nexport const TRUNCATION_LIMITS = {\n /** Primary node (get_node, single result) */\n primary: 10_000,\n /** List results (search, neighbors) */\n list: 500,\n /** Neighbor nodes in context */\n neighbor: 200,\n} as const;\n\nexport type TruncationContext = keyof typeof TRUNCATION_LIMITS;\n\nconst TRUNCATION_SUFFIX = '... [truncated]';\n\n/**\n * Truncate content to limit, appending suffix if truncated.\n * Returns original content if within limit.\n */\nexport function truncateContent(\n content: string,\n context: TruncationContext\n): string {\n const limit = TRUNCATION_LIMITS[context];\n\n if (content.length <= limit) {\n return content;\n }\n\n // Account for suffix length when truncating, guard against negative\n const truncatedLength = Math.max(0, limit - TRUNCATION_SUFFIX.length);\n return content.slice(0, truncatedLength) + TRUNCATION_SUFFIX;\n}\n\n/**\n * Check if content was truncated (ends with truncation suffix).\n */\nexport function isTruncated(content: string): boolean {\n return content.endsWith(TRUNCATION_SUFFIX);\n}\n","import type { Node } from '../types/node.js';\nimport type { LinkInfo, StoreProvider } from '../types/provider.js';\nimport type {\n NodeResponse,\n NodeMetadataResponse,\n NodeWithContextResponse,\n SearchResultResponse,\n HubResponse,\n PathResponse,\n} from './types.js';\nimport { truncateContent, type TruncationContext } from './truncate.js';\n\n/** Maximum neighbors to include in NodeWithContextResponse. */\nexport const MAX_NEIGHBORS = 20;\n\n/** Maximum links to resolve titles for (prevents OOM on nodes with massive link counts). */\nexport const MAX_LINKS_TO_RESOLVE = 100;\n\n/**\n * Transform a Node to NodeResponse, resolving link titles.\n */\nexport async function nodeToResponse(\n node: Node,\n store: StoreProvider,\n truncation: TruncationContext\n): Promise<NodeResponse> {\n // Limit links to prevent OOM on nodes with massive outgoing link counts\n const linksToResolve = node.outgoingLinks.slice(0, MAX_LINKS_TO_RESOLVE);\n const titles = await store.resolveTitles(linksToResolve);\n\n const links: LinkInfo[] = linksToResolve.map((id) => ({\n id,\n title: titles.get(id) ?? id,\n }));\n\n return {\n id: node.id,\n title: node.title,\n content: truncateContent(node.content, truncation),\n tags: node.tags,\n links,\n properties: node.properties,\n };\n}\n\n/**\n * Transform multiple nodes to responses, resolving all link titles in batch.\n * When includeContent is false, returns NodeMetadataResponse[] (no content field).\n * When includeContent is true, returns NodeResponse[] (with truncated content).\n */\nexport async function nodesToResponses(\n nodes: Node[],\n store: StoreProvider,\n truncation: TruncationContext,\n includeContent: boolean\n): Promise<NodeResponse[] | NodeMetadataResponse[]> {\n // Collect unique link IDs, limiting per-node to prevent OOM\n const allLinkIds = new Set<string>();\n const nodeLinkLimits = new Map<string, string[]>();\n\n for (const node of nodes) {\n const limitedLinks = node.outgoingLinks.slice(0, MAX_LINKS_TO_RESOLVE);\n nodeLinkLimits.set(node.id, limitedLinks);\n for (const linkId of limitedLinks) {\n allLinkIds.add(linkId);\n }\n }\n\n // Batch resolve titles\n const titles = await store.resolveTitles(Array.from(allLinkIds));\n\n // Transform nodes using limited links\n return nodes.map((node) => {\n // Defensive fallback - all node IDs are populated in the loop above\n const limitedLinks = nodeLinkLimits.get(node.id) /* v8 ignore next */ ?? [];\n const base: NodeMetadataResponse = {\n id: node.id,\n title: node.title,\n tags: node.tags,\n links: limitedLinks.map((id) => ({\n id,\n title: titles.get(id) ?? id,\n })),\n properties: node.properties,\n };\n\n if (includeContent) {\n return {\n ...base,\n content: truncateContent(node.content, truncation),\n } as NodeResponse;\n }\n\n return base;\n });\n}\n\n/**\n * Transform a Node with neighbor context to NodeWithContextResponse.\n */\nexport async function nodeToContextResponse(\n node: Node,\n incomingNeighbors: Node[],\n outgoingNeighbors: Node[],\n store: StoreProvider\n): Promise<NodeWithContextResponse> {\n // Get primary node response\n const primary = await nodeToResponse(node, store, 'primary');\n\n // Limit neighbors\n const limitedIncoming = incomingNeighbors.slice(0, MAX_NEIGHBORS);\n const limitedOutgoing = outgoingNeighbors.slice(0, MAX_NEIGHBORS);\n\n // Transform neighbors with neighbor truncation\n // Context responses always include content (truncated for neighbors)\n const [incomingResponses, outgoingResponses] = await Promise.all([\n nodesToResponses(limitedIncoming, store, 'neighbor', true) as Promise<NodeResponse[]>,\n nodesToResponses(limitedOutgoing, store, 'neighbor', true) as Promise<NodeResponse[]>,\n ]);\n\n return {\n ...primary,\n incomingNeighbors: incomingResponses,\n outgoingNeighbors: outgoingResponses,\n incomingCount: incomingNeighbors.length,\n outgoingCount: outgoingNeighbors.length,\n };\n}\n\n/**\n * Transform search results with scores.\n * When includeContent is false, content field is omitted.\n * When includeContent is true, content is truncated and included.\n */\nexport async function nodesToSearchResults(\n nodes: Node[],\n scores: Map<string, number>,\n store: StoreProvider,\n includeContent: boolean\n): Promise<SearchResultResponse[]> {\n const responses = await nodesToResponses(nodes, store, 'list', includeContent);\n\n return responses.map((response) => ({\n ...response,\n score: scores.get(response.id) ?? 0,\n }));\n}\n\n/**\n * Transform hub results (id, score pairs) to HubResponse[].\n */\nexport async function hubsToResponses(\n hubs: Array<[string, number]>,\n store: StoreProvider\n): Promise<HubResponse[]> {\n const ids = hubs.map(([id]) => id);\n const titles = await store.resolveTitles(ids);\n\n return hubs.map(([id, score]) => ({\n id,\n title: titles.get(id) ?? id,\n score,\n }));\n}\n\n/**\n * Transform a path (array of node IDs) to PathResponse.\n */\nexport function pathToResponse(path: string[]): PathResponse {\n return {\n path,\n length: path.length - 1,\n };\n}\n","import type { GraphCore } from '../types/graphcore.js';\nimport type {\n StoreProvider,\n Metric,\n TagMode,\n ListFilter,\n ResolveOptions,\n ResolveStrategy,\n NodeSummary,\n ResolveResult,\n} from '../types/provider.js';\nimport type { Node } from '../types/node.js';\nimport {\n McpError,\n type NodeResponse,\n type NodeMetadataResponse,\n type NodeWithContextResponse,\n type SearchResultResponse,\n type HubResponse,\n type PathResponse,\n type DeleteResponse,\n} from './types.js';\nimport {\n nodeToResponse,\n nodesToResponses,\n nodeToContextResponse,\n nodesToSearchResults,\n hubsToResponses,\n pathToResponse,\n} from './transforms.js';\n\nexport interface HandlerContext {\n core: GraphCore;\n store: StoreProvider;\n hasEmbedding: boolean;\n}\n\nfunction coerceLimit(value: unknown, defaultValue: number): number {\n if (value === undefined || value === null) {\n return defaultValue;\n }\n const num = Number(value);\n if (Number.isNaN(num)) {\n return defaultValue;\n }\n const floored = Math.floor(num);\n if (floored < 1) {\n throw new McpError('INVALID_PARAMS', 'limit must be at least 1');\n }\n return floored;\n}\n\nfunction coerceOffset(value: unknown, defaultValue: number): number {\n if (value === undefined || value === null) {\n return defaultValue;\n }\n const num = Number(value);\n if (Number.isNaN(num)) {\n return defaultValue;\n }\n const floored = Math.floor(num);\n if (floored < 0) {\n throw new McpError('INVALID_PARAMS', 'offset must be at least 0');\n }\n return floored;\n}\n\nexport interface ListNodesResponse {\n nodes: NodeSummary[];\n total: number;\n}\n\nexport interface NodesExistResponse {\n [id: string]: boolean;\n}\n\nexport type ToolResult =\n | NodeResponse\n | NodeMetadataResponse\n | NodeWithContextResponse\n | SearchResultResponse[]\n | NodeResponse[]\n | NodeMetadataResponse[]\n | HubResponse[]\n | PathResponse\n | DeleteResponse\n | ListNodesResponse\n | ResolveResult[]\n | NodesExistResponse\n | null;\n\nexport async function handleSearch(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<SearchResultResponse[]> {\n if (!ctx.hasEmbedding) {\n throw new McpError('PROVIDER_ERROR', 'Search requires embedding provider');\n }\n\n const query = args.query;\n const limit = coerceLimit(args.limit, 10);\n const includeContent = args.include_content === true;\n\n if (typeof query !== 'string' || query.trim() === '') {\n throw new McpError('INVALID_PARAMS', 'query is required and must be a non-empty string');\n }\n\n const nodes = await ctx.core.search(query, { limit });\n\n // Approximate scores for display. Results are sorted by actual similarity (distance-based).\n // Score is a UI hint (higher = better match), not the raw similarity metric.\n const scores = new Map<string, number>();\n nodes.forEach((node, index) => {\n scores.set(node.id, Math.max(0, 1 - index * 0.05));\n });\n\n return nodesToSearchResults(nodes, scores, ctx.store, includeContent);\n}\n\nfunction coerceDepth(value: unknown): number {\n if (value === undefined || value === null) {\n return 0;\n }\n const num = Number(value);\n if (Number.isNaN(num)) {\n return 0;\n }\n return num >= 1 ? 1 : 0;\n}\n\nexport async function handleGetNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse | NodeWithContextResponse | null> {\n const id = args.id as string;\n const depth = coerceDepth(args.depth);\n\n if (!id || typeof id !== 'string') {\n throw new McpError('INVALID_PARAMS', 'id is required and must be a string');\n }\n\n const node = await ctx.core.getNode(id, depth);\n if (!node) {\n return null;\n }\n\n if (depth === 0) {\n return nodeToResponse(node, ctx.store, 'primary');\n }\n\n const [incomingNeighbors, outgoingNeighbors] = await Promise.all([\n ctx.core.getNeighbors(id, { direction: 'in' }),\n ctx.core.getNeighbors(id, { direction: 'out' }),\n ]);\n\n return nodeToContextResponse(node, incomingNeighbors, outgoingNeighbors, ctx.store);\n}\n\nconst VALID_DIRECTIONS = ['in', 'out', 'both'] as const;\n\nexport async function handleGetNeighbors(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse[] | NodeMetadataResponse[]> {\n const id = args.id as string;\n const directionRaw = args.direction ?? 'both';\n const limit = coerceLimit(args.limit, 20);\n const includeContent = args.include_content === true;\n\n if (!id || typeof id !== 'string') {\n throw new McpError('INVALID_PARAMS', 'id is required and must be a string');\n }\n\n if (!VALID_DIRECTIONS.includes(directionRaw as (typeof VALID_DIRECTIONS)[number])) {\n throw new McpError(\n 'INVALID_PARAMS',\n `direction must be one of: ${VALID_DIRECTIONS.join(', ')}`\n );\n }\n const direction = directionRaw as 'in' | 'out' | 'both';\n\n const neighbors = await ctx.core.getNeighbors(id, { direction, limit });\n return nodesToResponses(neighbors, ctx.store, 'list', includeContent);\n}\n\nexport async function handleFindPath(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<PathResponse | null> {\n const source = args.source as string;\n const target = args.target as string;\n\n if (!source || typeof source !== 'string') {\n throw new McpError('INVALID_PARAMS', 'source is required and must be a string');\n }\n if (!target || typeof target !== 'string') {\n throw new McpError('INVALID_PARAMS', 'target is required and must be a string');\n }\n\n const path = await ctx.core.findPath(source, target);\n if (!path) {\n return null;\n }\n\n return pathToResponse(path);\n}\n\nconst VALID_METRICS = ['pagerank', 'in_degree', 'out_degree'] as const;\n\nexport async function handleGetHubs(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<HubResponse[]> {\n const metricRaw = args.metric ?? 'in_degree';\n const limit = coerceLimit(args.limit, 10);\n\n if (!VALID_METRICS.includes(metricRaw as Metric)) {\n throw new McpError(\n 'INVALID_PARAMS',\n `metric must be one of: ${VALID_METRICS.join(', ')}`\n );\n }\n const metric = metricRaw as Metric;\n\n const hubs = await ctx.core.getHubs(metric, limit);\n return hubsToResponses(hubs, ctx.store);\n}\n\nconst VALID_TAG_MODES = ['any', 'all'] as const;\n\nexport async function handleSearchByTags(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse[]> {\n const tags = args.tags;\n const modeRaw = args.mode ?? 'any';\n const limit = coerceLimit(args.limit, 20);\n\n if (!Array.isArray(tags) || tags.length === 0) {\n throw new McpError('INVALID_PARAMS', 'tags is required and must be a non-empty array');\n }\n\n // Validate all elements are strings\n if (!tags.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n\n if (!VALID_TAG_MODES.includes(modeRaw as TagMode)) {\n throw new McpError(\n 'INVALID_PARAMS',\n `mode must be one of: ${VALID_TAG_MODES.join(', ')}`\n );\n }\n const mode = modeRaw as TagMode;\n\n const nodes = await ctx.core.searchByTags(tags, mode, limit);\n // searchByTags always includes content (not a browsing operation like search/get_neighbors)\n return nodesToResponses(nodes, ctx.store, 'list', true) as Promise<NodeResponse[]>;\n}\n\nexport async function handleRandomNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse | null> {\n const tags = args.tags;\n\n if (tags !== undefined) {\n if (!Array.isArray(tags) || !tags.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n }\n\n const node = await ctx.core.getRandomNode(tags as string[] | undefined);\n if (!node) {\n return null;\n }\n\n return nodeToResponse(node, ctx.store, 'primary');\n}\n\nexport async function handleCreateNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse> {\n const title = args.title as string;\n const content = args.content as string;\n const tagsRaw = args.tags;\n const directory = args.directory as string | undefined;\n\n if (!title || typeof title !== 'string') {\n throw new McpError('INVALID_PARAMS', 'title is required and must be a string');\n }\n if (!content || typeof content !== 'string') {\n throw new McpError('INVALID_PARAMS', 'content is required and must be a string');\n }\n\n let tags: string[] = [];\n if (tagsRaw !== undefined) {\n if (!Array.isArray(tagsRaw) || !tagsRaw.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n tags = tagsRaw;\n }\n\n const filename = sanitizeFilename(title) + '.md';\n const id = directory ? `${directory}/${filename}` : filename;\n\n const existing = await ctx.core.getNode(id);\n if (existing) {\n throw new McpError('NODE_EXISTS', `Node already exists: ${id}`);\n }\n\n const node = await ctx.core.createNode({\n id,\n title,\n content,\n tags,\n });\n\n return nodeToResponse(node, ctx.store, 'primary');\n}\n\nexport async function handleUpdateNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse> {\n const id = args.id as string;\n const title = args.title as string | undefined;\n const content = args.content as string | undefined;\n const tagsRaw = args.tags;\n\n if (!id || typeof id !== 'string') {\n throw new McpError('INVALID_PARAMS', 'id is required and must be a string');\n }\n\n if (title === undefined && content === undefined && tagsRaw === undefined) {\n throw new McpError(\n 'INVALID_PARAMS',\n 'At least one of title, content, or tags must be provided'\n );\n }\n\n let tags: string[] | undefined;\n if (tagsRaw !== undefined) {\n if (!Array.isArray(tagsRaw) || !tagsRaw.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n tags = tagsRaw;\n }\n\n const existing = await ctx.core.getNode(id);\n if (!existing) {\n throw new McpError('NODE_NOT_FOUND', `Node not found: ${id}`);\n }\n\n if (title !== undefined && title !== existing.title) {\n const incomingNeighbors = await ctx.core.getNeighbors(id, { direction: 'in' });\n if (incomingNeighbors.length > 0) {\n throw new McpError(\n 'LINK_INTEGRITY',\n `Cannot rename node with ${incomingNeighbors.length} incoming links`\n );\n }\n }\n\n const updates: Partial<Node> = {};\n if (title !== undefined) updates.title = title;\n if (content !== undefined) updates.content = content;\n if (tags !== undefined) updates.tags = tags;\n\n const updated = await ctx.core.updateNode(id, updates);\n return nodeToResponse(updated, ctx.store, 'primary');\n}\n\nexport async function handleDeleteNode(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<DeleteResponse> {\n const id = args.id as string;\n\n if (!id || typeof id !== 'string') {\n throw new McpError('INVALID_PARAMS', 'id is required and must be a string');\n }\n\n const deleted = await ctx.core.deleteNode(id);\n return { deleted };\n}\n\nconst VALID_STRATEGIES = ['exact', 'fuzzy', 'semantic'] as const;\n\nexport async function handleListNodes(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<ListNodesResponse> {\n const tag = args.tag as string | undefined;\n const path = args.path as string | undefined;\n const limit = coerceLimit(args.limit, 100);\n const offset = coerceOffset(args.offset, 0);\n\n const filter: ListFilter = {};\n if (tag) filter.tag = tag;\n if (path) filter.path = path;\n\n return ctx.core.listNodes(filter, { limit, offset });\n}\n\nexport async function handleResolveNodes(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<ResolveResult[]> {\n const names = args.names;\n const strategy = args.strategy as ResolveStrategy | undefined;\n const threshold = args.threshold as number | undefined;\n const tag = args.tag as string | undefined;\n const path = args.path as string | undefined;\n\n if (!Array.isArray(names)) {\n throw new McpError('INVALID_PARAMS', 'names is required and must be an array');\n }\n\n if (strategy !== undefined && !VALID_STRATEGIES.includes(strategy as ResolveStrategy)) {\n throw new McpError(\n 'INVALID_PARAMS',\n `strategy must be one of: ${VALID_STRATEGIES.join(', ')}`\n );\n }\n\n if (strategy === 'semantic' && !ctx.hasEmbedding) {\n throw new McpError('PROVIDER_ERROR', 'Semantic resolution requires embedding provider');\n }\n\n const options: ResolveOptions = {};\n if (strategy) options.strategy = strategy;\n if (threshold !== undefined) options.threshold = threshold;\n if (tag) options.tag = tag;\n if (path) options.path = path;\n\n return ctx.core.resolveNodes(names as string[], options);\n}\n\nexport async function handleNodesExist(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodesExistResponse> {\n const ids = args.ids;\n\n if (!Array.isArray(ids)) {\n throw new McpError('INVALID_PARAMS', 'ids is required and must be an array');\n }\n\n const result = await ctx.store.nodesExist(ids as string[]);\n\n // Convert Map to plain object\n const response: NodesExistResponse = {};\n for (const [id, exists] of result) {\n response[id] = exists;\n }\n return response;\n}\n\nexport function sanitizeFilename(title: string): string {\n const sanitized = title\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^-+|-+$/g, '');\n\n return sanitized || 'untitled';\n}\n\nexport async function dispatchTool(\n ctx: HandlerContext,\n name: string,\n args: Record<string, unknown>\n): Promise<ToolResult> {\n switch (name) {\n case 'search':\n return handleSearch(ctx, args);\n case 'get_node':\n return handleGetNode(ctx, args);\n case 'get_neighbors':\n return handleGetNeighbors(ctx, args);\n case 'find_path':\n return handleFindPath(ctx, args);\n case 'get_hubs':\n return handleGetHubs(ctx, args);\n case 'search_by_tags':\n return handleSearchByTags(ctx, args);\n case 'random_node':\n return handleRandomNode(ctx, args);\n case 'create_node':\n return handleCreateNode(ctx, args);\n case 'update_node':\n return handleUpdateNode(ctx, args);\n case 'delete_node':\n return handleDeleteNode(ctx, args);\n case 'list_nodes':\n return handleListNodes(ctx, args);\n case 'resolve_nodes':\n return handleResolveNodes(ctx, args);\n case 'nodes_exist':\n return handleNodesExist(ctx, args);\n default:\n throw new McpError('INVALID_PARAMS', `Unknown tool: ${name}`);\n }\n}\n","import { access, writeFile, mkdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { Cache } from '../../providers/docstore/cache.js';\n\nexport interface VizOptions {\n output?: string | undefined;\n open?: boolean | undefined;\n}\n\nexport interface VizResult {\n outputPath: string;\n nodeCount: number;\n edgeCount: number;\n shouldOpen: boolean;\n}\n\ninterface GraphNode {\n id: string;\n title: string;\n inDegree: number;\n}\n\ninterface GraphEdge {\n source: string;\n target: string;\n}\n\nexport async function vizCommand(\n directory: string,\n options: VizOptions = {}\n): Promise<VizResult> {\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n const cacheDir = join(directory, '.roux');\n const outputPath = options.output ?? join(cacheDir, 'graph.html');\n\n const cache = new Cache(cacheDir);\n\n try {\n const nodes = cache.getAllNodes();\n const graphNodes: GraphNode[] = [];\n const graphEdges: GraphEdge[] = [];\n\n // Build set of existing node IDs for edge filtering\n const existingNodeIds = new Set(nodes.map((n) => n.id));\n\n // Build node list with degree info\n for (const node of nodes) {\n const centrality = cache.getCentrality(node.id);\n graphNodes.push({\n id: node.id,\n title: node.title,\n inDegree: centrality?.inDegree ?? 0,\n });\n\n // Build edges from outgoing links, filtering to existing targets only\n for (const target of node.outgoingLinks) {\n if (existingNodeIds.has(target)) {\n graphEdges.push({\n source: node.id,\n target,\n });\n }\n }\n }\n\n // Generate HTML\n const html = generateHtml(graphNodes, graphEdges);\n\n // Ensure output directory exists\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, html, 'utf-8');\n\n return {\n outputPath,\n nodeCount: graphNodes.length,\n edgeCount: graphEdges.length,\n shouldOpen: options.open ?? false,\n };\n } finally {\n cache.close();\n }\n}\n\nfunction generateHtml(nodes: GraphNode[], edges: GraphEdge[]): string {\n const nodesJson = JSON.stringify(nodes);\n const edgesJson = JSON.stringify(edges);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Roux Graph Visualization</title>\n <script src=\"https://d3js.org/d3.v7.min.js\"></script>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, sans-serif; background: #1a1a2e; overflow: hidden; }\n svg { display: block; width: 100vw; height: 100vh; }\n .node circle { cursor: pointer; }\n .node text { fill: #e0e0e0; font-size: 10px; pointer-events: none; }\n .link { stroke: #4a4a6a; stroke-opacity: 0.6; fill: none; }\n .tooltip {\n position: absolute;\n background: #16213e;\n color: #e0e0e0;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: 12px;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s;\n border: 1px solid #4a4a6a;\n }\n </style>\n</head>\n<body>\n <div class=\"tooltip\" id=\"tooltip\"></div>\n <svg></svg>\n <script>\n const nodes = ${nodesJson};\n const links = ${edgesJson};\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n const svg = d3.select(\"svg\")\n .attr(\"viewBox\", [0, 0, width, height]);\n\n const g = svg.append(\"g\");\n\n // Zoom behavior\n svg.call(d3.zoom()\n .scaleExtent([0.1, 4])\n .on(\"zoom\", (event) => g.attr(\"transform\", event.transform)));\n\n // Node size based on in-degree\n const maxDegree = Math.max(1, ...nodes.map(n => n.inDegree));\n const nodeRadius = d => 5 + (d.inDegree / maxDegree) * 15;\n\n // Force simulation\n const simulation = d3.forceSimulation(nodes)\n .force(\"link\", d3.forceLink(links).id(d => d.id).distance(100))\n .force(\"charge\", d3.forceManyBody().strength(-200))\n .force(\"center\", d3.forceCenter(width / 2, height / 2))\n .force(\"collision\", d3.forceCollide().radius(d => nodeRadius(d) + 5));\n\n // Draw links with arrows\n svg.append(\"defs\").append(\"marker\")\n .attr(\"id\", \"arrow\")\n .attr(\"viewBox\", \"0 -5 10 10\")\n .attr(\"refX\", 20)\n .attr(\"refY\", 0)\n .attr(\"markerWidth\", 6)\n .attr(\"markerHeight\", 6)\n .attr(\"orient\", \"auto\")\n .append(\"path\")\n .attr(\"fill\", \"#4a4a6a\")\n .attr(\"d\", \"M0,-5L10,0L0,5\");\n\n const link = g.append(\"g\")\n .selectAll(\"line\")\n .data(links)\n .join(\"line\")\n .attr(\"class\", \"link\")\n .attr(\"marker-end\", \"url(#arrow)\");\n\n // Draw nodes\n const node = g.append(\"g\")\n .selectAll(\".node\")\n .data(nodes)\n .join(\"g\")\n .attr(\"class\", \"node\")\n .call(d3.drag()\n .on(\"start\", dragstarted)\n .on(\"drag\", dragged)\n .on(\"end\", dragended));\n\n node.append(\"circle\")\n .attr(\"r\", nodeRadius)\n .attr(\"fill\", \"#0f4c75\")\n .attr(\"stroke\", \"#3282b8\")\n .attr(\"stroke-width\", 2);\n\n node.append(\"text\")\n .attr(\"dx\", d => nodeRadius(d) + 5)\n .attr(\"dy\", 4)\n .text(d => d.title.length > 20 ? d.title.slice(0, 17) + \"...\" : d.title);\n\n // Tooltip\n const tooltip = d3.select(\"#tooltip\");\n node\n .on(\"mouseover\", (event, d) => {\n tooltip\n .style(\"opacity\", 1)\n .html(\\`<strong>\\${d.title}</strong><br>ID: \\${d.id}<br>Incoming links: \\${d.inDegree}\\`);\n })\n .on(\"mousemove\", (event) => {\n tooltip\n .style(\"left\", (event.pageX + 10) + \"px\")\n .style(\"top\", (event.pageY - 10) + \"px\");\n })\n .on(\"mouseout\", () => tooltip.style(\"opacity\", 0));\n\n // Simulation tick\n simulation.on(\"tick\", () => {\n link\n .attr(\"x1\", d => d.source.x)\n .attr(\"y1\", d => d.source.y)\n .attr(\"x2\", d => d.target.x)\n .attr(\"y2\", d => d.target.y);\n\n node.attr(\"transform\", d => \\`translate(\\${d.x},\\${d.y})\\`);\n });\n\n function dragstarted(event, d) {\n if (!event.active) simulation.alphaTarget(0.3).restart();\n d.fx = d.x;\n d.fy = d.y;\n }\n\n function dragged(event, d) {\n d.fx = event.x;\n d.fy = event.y;\n }\n\n function dragended(event, d) {\n if (!event.active) simulation.alphaTarget(0);\n d.fx = null;\n d.fy = null;\n }\n </script>\n</body>\n</html>`;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA,WAAO,UAAU;AAAA,MAChB;AAAA,MACA;AAAA,IACD;AAEA,aAAS,kBAAkB,OAAO,QAAQ;AACzC,cAAQ,MAAM,QAAQ,QAAQ,EAAE;AAChC,eAAS,OAAO,QAAQ,QAAQ,EAAE;AAElC,UAAI,UAAU,OAAQ,QAAO;AAC7B,UAAI,MAAM,SAAS,KAAK,OAAO,SAAS,EAAG,QAAO;AAElD,UAAI,eAAe,oBAAI,IAAI;AAC3B,eAAS,IAAI,GAAG,IAAI,MAAM,SAAS,GAAG,KAAK;AAC1C,cAAM,SAAS,MAAM,UAAU,GAAG,IAAI,CAAC;AACvC,cAAM,QAAQ,aAAa,IAAI,MAAM,IAClC,aAAa,IAAI,MAAM,IAAI,IAC3B;AAEH,qBAAa,IAAI,QAAQ,KAAK;AAAA,MAC/B;AAAC;AAED,UAAI,mBAAmB;AACvB,eAAS,IAAI,GAAG,IAAI,OAAO,SAAS,GAAG,KAAK;AAC3C,cAAM,SAAS,OAAO,UAAU,GAAG,IAAI,CAAC;AACxC,cAAM,QAAQ,aAAa,IAAI,MAAM,IAClC,aAAa,IAAI,MAAM,IACvB;AAEH,YAAI,QAAQ,GAAG;AACd,uBAAa,IAAI,QAAQ,QAAQ,CAAC;AAClC;AAAA,QACD;AAAA,MACD;AAEA,aAAQ,IAAM,oBAAqB,MAAM,SAAS,OAAO,SAAS;AAAA,IACnE;AAEA,aAAS,cAAc,YAAY,eAAe;AACjD,UAAI,CAAC,aAAa,YAAY,aAAa,EAAG,OAAM,IAAI,MAAM,wFAAwF;AAEtJ,YAAM,UAAU,CAAC;AACjB,UAAI,iBAAiB;AAErB,eAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC9C,cAAM,sBAAsB,cAAc,CAAC;AAC3C,cAAM,gBAAgB,kBAAkB,YAAY,mBAAmB;AACvE,gBAAQ,KAAK,EAAC,QAAQ,qBAAqB,QAAQ,cAAa,CAAC;AACjE,YAAI,gBAAgB,QAAQ,cAAc,EAAE,QAAQ;AACnD,2BAAiB;AAAA,QAClB;AAAA,MACD;AAGA,YAAM,YAAY,QAAQ,cAAc;AAExC,aAAO,EAAE,SAAkB,WAAsB,eAA+B;AAAA,IACjF;AAEA,aAAS,aAAa,YAAY,eAAe;AAChD,UAAI,OAAO,eAAe,SAAU,QAAO;AAC3C,UAAI,CAAC,MAAM,QAAQ,aAAa,EAAG,QAAO;AAC1C,UAAI,CAAC,cAAc,OAAQ,QAAO;AAClC,UAAI,cAAc,KAAM,SAAU,GAAG;AAAE,eAAO,OAAO,MAAM;AAAA,MAAQ,CAAC,EAAG,QAAO;AAC9E,aAAO;AAAA,IACR;AAAA;AAAA;;;AC/DA,SAAS,eAAe;AACxB,SAAS,WAAAA,gBAAe;AACxB,SAAS,gBAAgB;;;ACJzB,SAAS,OAAO,WAAW,UAAU,cAAc;AACnD,SAAS,YAAY;AAuCrB,IAAM,iBAAiB;AAAA;AAAA;AAAA;AAKvB,IAAM,kBAAmC;AAAA,EACvC,SAAS;AAAA,EACT,MAAM,CAAC,QAAQ,SAAS,GAAG;AAAA,EAC3B,KAAK,CAAC;AACR;AAGO,IAAM,cAAc;AAG3B,IAAM,2BAA2B,eAAe,WAAW;AAE3D,IAAM,kBAAmC;AAAA,EACvC,SAAS;AAAA,EACT,OAAO,CAAC,EAAE,MAAM,WAAW,SAAS,yBAAyB,CAAC;AAChE;AAEA,eAAsB,YAAY,WAAwC;AACxE,QAAM,aAAa,KAAK,WAAW,WAAW;AAC9C,QAAM,UAAU,KAAK,WAAW,OAAO;AAGvC,MAAI,eAAe;AACnB,MAAI;AACF,UAAM,OAAO,UAAU;AACvB,mBAAe;AAAA,EACjB,QAAQ;AAAA,EAER;AAGA,QAAM,MAAM,SAAS,EAAE,WAAW,KAAK,CAAC;AAGxC,QAAM,gBAAgB,SAAS;AAG/B,QAAM,YAAY,MAAM,aAAa,WAAW,YAAY;AAC5D,MAAI,iBAAiB;AACrB,MAAI,cAAc,YAAY;AAC5B,qBAAiB,MAAM,qBAAqB,SAAS;AAAA,EACvD;AAEA,MAAI,cAAc;AAChB,WAAO,EAAE,SAAS,OAAO,YAAY,eAAe;AAAA,EACtD;AAGA,QAAM,UAAU,YAAY,gBAAgB,OAAO;AACnD,SAAO,EAAE,SAAS,MAAM,YAAY,eAAe;AACrD;AAEA,eAAe,gBAAgB,WAAkC;AAC/D,QAAM,UAAU,KAAK,WAAW,WAAW;AAE3C,MAAI,SAAoB,CAAC;AAGzB,MAAI;AACF,UAAM,UAAU,MAAM,SAAS,SAAS,OAAO;AAC/C,aAAS,KAAK,MAAM,OAAO;AAAA,EAC7B,QAAQ;AAAA,EAER;AAGA,MAAI,CAAC,OAAO,YAAY;AACtB,WAAO,aAAa,CAAC;AAAA,EACvB;AAGA,SAAO,WAAW,OAAO;AAGzB,QAAM,UAAU,SAAS,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAC1E;AAEA,eAAe,aACb,WACA,cACiB;AACjB,MAAI,CAAC,cAAc;AACjB,WAAO;AAAA,EACT;AAEA,MAAI;AACF,UAAM,aAAa,KAAK,WAAW,WAAW;AAC9C,UAAM,UAAU,MAAM,SAAS,YAAY,OAAO;AAElD,UAAM,YAAY,QAAQ,MAAM,6BAA6B;AAC7D,QAAI,YAAY,CAAC,GAAG;AAClB,aAAO,UAAU,CAAC;AAAA,IACpB;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAGA,eAAe,qBAAqB,WAAqC;AACvE,QAAM,YAAY,KAAK,WAAW,SAAS;AAC3C,QAAM,eAAe,KAAK,WAAW,eAAe;AAGpD,QAAM,MAAM,WAAW,EAAE,WAAW,KAAK,CAAC;AAE1C,MAAI,SAAyB,CAAC;AAC9B,MAAI,kBAAiC;AAGrC,MAAI;AACF,sBAAkB,MAAM,SAAS,cAAc,OAAO;AACtD,aAAS,KAAK,MAAM,eAAe;AAAA,EACrC,SAAS,KAAK;AACZ,QAAI,oBAAoB,MAAM;AAE5B,aAAO;AAAA,IACT;AAAA,EAEF;AAGA,MAAI,CAAC,OAAO,OAAO;AACjB,WAAO,QAAQ,CAAC;AAAA,EAClB;AACA,MAAI,CAAC,OAAO,MAAM,YAAY;AAC5B,WAAO,MAAM,aAAa,CAAC;AAAA,EAC7B;AAGA,QAAM,cAAc,OAAO,MAAM,WAAW;AAAA,IAAK,CAAC,UAChD,MAAM,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,SAAS,WAAW,CAAC;AAAA,EAC3D;AAEA,MAAI,aAAa;AACf,WAAO;AAAA,EACT;AAGA,SAAO,MAAM,WAAW,KAAK,eAAe;AAG5C,QAAM,UAAU,cAAc,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAC7E,SAAO;AACT;;;AC/LA,SAAS,UAAAC,eAAc;AACvB,SAAS,QAAAC,aAAY;;;ACErB,+BAA6B;AAH7B,OAAO,cAAc;AACrB,SAAS,QAAAC,aAAY;AACrB,SAAS,iBAAiB;AAkDnB,IAAM,QAAN,MAAY;AAAA,EACT;AAAA,EAER,YAAY,UAAkB;AAC5B,cAAU,UAAU,EAAE,WAAW,KAAK,CAAC;AACvC,UAAM,SAASA,MAAK,UAAU,UAAU;AACxC,SAAK,KAAK,IAAI,SAAS,MAAM;AAC7B,SAAK,GAAG,OAAO,oBAAoB;AACnC,SAAK,WAAW;AAAA,EAClB;AAAA,EAEQ,aAAmB;AACzB,SAAK,GAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KA8BZ;AAED,SAAK,GAAG,OAAO,mBAAmB;AAAA,EACpC;AAAA,EAEA,gBAA0B;AACxB,UAAM,OAAO,KAAK,GACf,QAAQ,mDAAmD,EAC3D,IAAI;AACP,WAAO,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EAC/B;AAAA,EAEA,WACE,MACA,YACA,YACA,gBACM;AACN,UAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAY5B;AAED,SAAK;AAAA,MACH,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK,UAAU,KAAK,IAAI;AAAA,MACxB,KAAK,UAAU,KAAK,aAAa;AAAA,MACjC,KAAK,UAAU,KAAK,UAAU;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA,EAEA,QAAQ,IAAyB;AAC/B,UAAM,MAAM,KAAK,GACd,QAAQ,kCAAkC,EAC1C,IAAI,EAAE;AAET,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,KAAK,UAAU,GAAG;AAAA,EAC3B;AAAA,EAEA,SAAS,KAAuB;AAC9B,QAAI,IAAI,WAAW,EAAG,QAAO,CAAC;AAG9B,UAAM,eAAe,IAAI,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAChD,UAAM,OAAO,KAAK,GACf,QAAQ,oCAAoC,YAAY,GAAG,EAC3D,IAAI,GAAG,GAAG;AAEb,UAAM,UAAU,oBAAI,IAAkB;AACtC,eAAW,OAAO,MAAM;AACtB,cAAQ,IAAI,IAAI,IAAI,KAAK,UAAU,GAAG,CAAC;AAAA,IACzC;AAGA,UAAM,SAAiB,CAAC;AACxB,eAAW,MAAM,KAAK;AACpB,YAAM,OAAO,QAAQ,IAAI,EAAE;AAC3B,UAAI,KAAM,QAAO,KAAK,IAAI;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,IAAkB;AAC3B,SAAK,GAAG,QAAQ,gCAAgC,EAAE,IAAI,EAAE;AAAA,EAC1D;AAAA,EAEA,cAAsB;AACpB,UAAM,OAAO,KAAK,GAAG,QAAQ,qBAAqB,EAAE,IAAI;AACxD,WAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,UAAU,GAAG,CAAC;AAAA,EAC9C;AAAA,EAEA,aAAa,MAAgB,MAAuB;AAClD,QAAI,KAAK,WAAW,EAAG,QAAO,CAAC;AAE/B,UAAM,WAAW,KAAK,YAAY;AAClC,UAAM,YAAY,KAAK,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AAEjD,WAAO,SAAS,OAAO,CAAC,SAAS;AAC/B,YAAM,WAAW,KAAK,KAAK,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AACrD,UAAI,SAAS,OAAO;AAClB,eAAO,UAAU,KAAK,CAAC,MAAM,SAAS,SAAS,CAAC,CAAC;AAAA,MACnD,OAAO;AACL,eAAO,UAAU,MAAM,CAAC,MAAM,SAAS,SAAS,CAAC,CAAC;AAAA,MACpD;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,gBAAgB,YAAmC;AACjD,UAAM,MAAM,KAAK,GACd,QAAQ,yDAAyD,EACjE,IAAI,UAAU;AAEjB,WAAO,KAAK,mBAAmB;AAAA,EACjC;AAAA,EAEA,cAAc,YAAiC;AAC7C,UAAM,MAAM,KAAK,GACd,QAAQ,2CAA2C,EACnD,IAAI,UAAU;AAEjB,QAAI,CAAC,IAAK,QAAO;AACjB,WAAO,KAAK,UAAU,GAAG;AAAA,EAC3B;AAAA,EAEA,qBAAkC;AAChC,UAAM,OAAO,KAAK,GACf,QAAQ,+BAA+B,EACvC,IAAI;AAEP,WAAO,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;AAAA,EAC/C;AAAA,EAEA,cAAc,KAAoC;AAChD,QAAI,IAAI,WAAW,EAAG,QAAO,oBAAI,IAAI;AAErC,UAAM,eAAe,IAAI,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAChD,UAAM,OAAO,KAAK,GACf,QAAQ,4CAA4C,YAAY,GAAG,EACnE,IAAI,GAAG,GAAG;AAEb,UAAM,SAAS,oBAAI,IAAoB;AACvC,eAAW,OAAO,MAAM;AACtB,aAAO,IAAI,IAAI,IAAI,IAAI,KAAK;AAAA,IAC9B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,WAAW,KAAqC;AAC9C,QAAI,IAAI,WAAW,EAAG,QAAO,oBAAI,IAAI;AAErC,UAAM,eAAe,IAAI,IAAI,MAAM,GAAG,EAAE,KAAK,GAAG;AAChD,UAAM,OAAO,KAAK,GACf,QAAQ,qCAAqC,YAAY,GAAG,EAC5D,IAAI,GAAG,GAAG;AAEb,UAAM,cAAc,IAAI,IAAI,KAAK,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AACjD,UAAM,SAAS,oBAAI,IAAqB;AACxC,eAAW,MAAM,KAAK;AACpB,aAAO,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;AAAA,IACpC;AACA,WAAO;AAAA,EACT;AAAA,EAEA,UAAU,QAAoB,SAAwC;AACpE,UAAM,QAAQ,KAAK,IAAI,SAAS,SAAS,KAAK,GAAI;AAClD,UAAM,SAAS,SAAS,UAAU;AAGlC,UAAM,aAAuB,CAAC;AAC9B,UAAM,SAAoB,CAAC;AAE3B,QAAI,OAAO,KAAK;AAEd,iBAAW,KAAK,gFAAgF;AAChG,aAAO,KAAK,OAAO,GAAG;AAAA,IACxB;AAEA,QAAI,OAAO,MAAM;AACf,iBAAW,KAAK,kBAAkB;AAClC,aAAO,KAAK,OAAO,IAAI;AAAA,IACzB;AAEA,UAAM,cAAc,WAAW,SAAS,IAAI,SAAS,WAAW,KAAK,OAAO,CAAC,KAAK;AAGlF,UAAM,aAAa,uCAAuC,WAAW;AACrE,UAAM,WAAW,KAAK,GAAG,QAAQ,UAAU,EAAE,IAAI,GAAG,MAAM;AAC1D,UAAM,QAAQ,SAAS;AAGvB,UAAM,QAAQ,+BAA+B,WAAW;AACxD,UAAM,OAAO,KAAK,GAAG,QAAQ,KAAK,EAAE,IAAI,GAAG,QAAQ,OAAO,MAAM;AAEhE,UAAM,QAAQ,KAAK,IAAI,CAAC,SAAS,EAAE,IAAI,IAAI,IAAI,OAAO,IAAI,MAAM,EAAE;AAClE,WAAO,EAAE,OAAO,MAAM;AAAA,EACxB;AAAA,EAEA,aAAa,OAAiB,SAA2C;AACvE,QAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAEhC,UAAM,WAAW,SAAS,YAAY;AACtC,UAAM,YAAY,SAAS,aAAa;AAGxC,UAAM,SAAqB,CAAC;AAC5B,QAAI,SAAS,IAAK,QAAO,MAAM,QAAQ;AACvC,QAAI,SAAS,KAAM,QAAO,OAAO,QAAQ;AAGzC,UAAM,EAAE,OAAO,WAAW,IAAI,KAAK,UAAU,QAAQ,EAAE,OAAO,IAAK,CAAC;AAEpE,QAAI,WAAW,WAAW,GAAG;AAC3B,aAAO,MAAM,IAAI,CAAC,WAAW,EAAE,OAAO,OAAO,MAAM,OAAO,EAAE,EAAE;AAAA,IAChE;AAEA,UAAM,kBAAkB,WAAW,IAAI,CAAC,MAAM,EAAE,MAAM,YAAY,CAAC;AACnE,UAAM,YAAY,oBAAI,IAAoB;AAC1C,eAAW,KAAK,YAAY;AAC1B,gBAAU,IAAI,EAAE,MAAM,YAAY,GAAG,EAAE,EAAE;AAAA,IAC3C;AAEA,WAAO,MAAM,IAAI,CAAC,UAAyB;AACzC,YAAM,aAAa,MAAM,YAAY;AAErC,UAAI,aAAa,SAAS;AAExB,cAAM,YAAY,UAAU,IAAI,UAAU;AAC1C,YAAI,WAAW;AACb,iBAAO,EAAE,OAAO,OAAO,WAAW,OAAO,EAAE;AAAA,QAC7C;AACA,eAAO,EAAE,OAAO,OAAO,MAAM,OAAO,EAAE;AAAA,MACxC;AAGA,UAAI,aAAa,SAAS;AACxB,cAAM,SAAS,yBAAAC,QAAiB,cAAc,YAAY,eAAe;AACzE,cAAM,YAAY,OAAO;AAEzB,YAAI,UAAU,UAAU,WAAW;AAEjC,gBAAM,YAAY,UAAU,IAAI,UAAU,MAAM;AAChD,iBAAO,EAAE,OAAO,OAAO,WAAW,OAAO,UAAU,OAAO;AAAA,QAC5D;AACA,eAAO,EAAE,OAAO,OAAO,MAAM,OAAO,EAAE;AAAA,MACxC;AAIA,aAAO,EAAE,OAAO,OAAO,MAAM,OAAO,EAAE;AAAA,IACxC,CAAC;AAAA,EACH;AAAA,EAEA,oBAAoB,QAAgB,OAAuB;AACzD,SAAK,GACF,QAAQ,kDAAkD,EAC1D,IAAI,KAAK,UAAU,KAAK,GAAG,MAAM;AAAA,EACtC;AAAA,EAEA,eAAe,QAAgB,QAAkB,OAAqB;AACpE,UAAM,SAAS,OAAO,KAAK,IAAI,aAAa,MAAM,EAAE,MAAM;AAC1D,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOF,EACC,IAAI,QAAQ,OAAO,MAAM;AAAA,EAC9B;AAAA,EAEA,aAAa,QAAwC;AACnD,UAAM,MAAM,KAAK,GACd,QAAQ,wDAAwD,EAChE,IAAI,MAAM;AAEb,QAAI,CAAC,IAAK,QAAO;AAEjB,UAAM,UAAU,IAAI;AAAA,MAClB,IAAI,OAAO;AAAA,MACX,IAAI,OAAO;AAAA,MACX,IAAI,OAAO,SAAS;AAAA,IACtB;AACA,WAAO;AAAA,MACL,OAAO,IAAI;AAAA,MACX,QAAQ,MAAM,KAAK,OAAO;AAAA,IAC5B;AAAA,EACF;AAAA,EAEA,gBACE,QACA,UACA,UACA,WACA,YACM;AACN,SAAK,GACF;AAAA,MACC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASF,EACC,IAAI,QAAQ,UAAU,UAAU,WAAW,UAAU;AAAA,EAC1D;AAAA,EAEA,cAAc,QAAyC;AACrD,UAAM,MAAM,KAAK,GACd,QAAQ,4CAA4C,EACpD,IAAI,MAAM;AAEb,QAAI,CAAC,IAAK,QAAO;AAEjB,WAAO;AAAA,MACL,UAAU,IAAI;AAAA,MACd,UAAU,IAAI;AAAA,MACd,WAAW,IAAI;AAAA,MACf,YAAY,IAAI;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,WAA6E;AAC3E,UAAM,YAAY,KAAK,GACpB,QAAQ,qCAAqC,EAC7C,IAAI;AAEP,UAAM,iBAAiB,KAAK,GACzB,QAAQ,0CAA0C,EAClD,IAAI;AAGP,UAAM,UAAU,KAAK,GAClB,QAAQ,gDAAgD,EACxD,IAAI;AAEP,WAAO;AAAA,MACL,WAAW,UAAU;AAAA,MACrB,gBAAgB,eAAe;AAAA,MAC/B,WAAW,QAAQ,SAAS;AAAA,IAC9B;AAAA,EACF;AAAA,EAEA,QAAc;AACZ,SAAK,GAAG,KAAK,wBAAwB;AACrC,SAAK,GAAG,KAAK,wBAAwB;AACrC,SAAK,GAAG,KAAK,mBAAmB;AAAA,EAClC;AAAA,EAEA,QAAc;AACZ,SAAK,GAAG,MAAM;AAAA,EAChB;AAAA,EAEQ,UAAU,KAAoB;AACpC,UAAM,YAAuB;AAAA,MAC3B,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,cAAc,IAAI,KAAK,IAAI,eAAe;AAAA,IAC5C;AAEA,WAAO;AAAA,MACL,IAAI,IAAI;AAAA,MACR,OAAO,IAAI;AAAA,MACX,SAAS,IAAI;AAAA,MACb,MAAM,KAAK,MAAM,IAAI,IAAI;AAAA,MACzB,eAAe,KAAK,MAAM,IAAI,cAAc;AAAA,MAC5C,YAAY,KAAK,MAAM,IAAI,UAAU;AAAA,MACrC;AAAA,IACF;AAAA,EACF;AACF;;;ACjdA,OAAOC,eAAc;AAErB,SAAS,QAAAC,aAAY;AAGd,IAAM,uBAAN,MAAqD;AAAA,EAClD;AAAA,EACA;AAAA,EAER,YAAY,UAAiC;AAC3C,QAAI,OAAO,aAAa,UAAU;AAChC,WAAK,KAAK,IAAID,UAASC,MAAK,UAAU,YAAY,CAAC;AACnD,WAAK,SAAS;AAAA,IAChB,OAAO;AACL,WAAK,KAAK;AACV,WAAK,SAAS;AAAA,IAChB;AACA,SAAK,KAAK;AAAA,EACZ;AAAA,EAEQ,OAAa;AACnB,SAAK,GAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAMZ;AAAA,EACH;AAAA,EAEA,MAAM,MAAM,IAAY,QAAkB,OAA8B;AACtE,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AACA,eAAW,KAAK,QAAQ;AACtB,UAAI,CAAC,OAAO,SAAS,CAAC,GAAG;AACvB,cAAM,IAAI,MAAM,yBAAyB,CAAC,EAAE;AAAA,MAC9C;AAAA,IACF;AAGA,UAAM,WAAW,KAAK,GACnB,QAAQ,qEAAqE,EAC7E,IAAI,EAAE;AAET,QAAI,YAAY,SAAS,QAAQ,OAAO,QAAQ;AAC9C,YAAM,IAAI;AAAA,QACR,oCAAoC,OAAO,MAAM,sCAAsC,SAAS,GAAG;AAAA,MACrG;AAAA,IACF;AAEA,UAAM,OAAO,OAAO,KAAK,IAAI,aAAa,MAAM,EAAE,MAAM;AACxD,SAAK,GACF;AAAA,MACC;AAAA,IACF,EACC,IAAI,IAAI,OAAO,IAAI;AAAA,EACxB;AAAA,EAEA,MAAM,OAAO,QAAkB,OAA8C;AAC3E,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,IAAI,MAAM,iCAAiC;AAAA,IACnD;AACA,eAAW,KAAK,QAAQ;AACtB,UAAI,CAAC,OAAO,SAAS,CAAC,GAAG;AACvB,cAAM,IAAI,MAAM,yBAAyB,CAAC,EAAE;AAAA,MAC9C;AAAA,IACF;AACA,QAAI,SAAS,GAAG;AACd,aAAO,CAAC;AAAA,IACV;AAEA,UAAM,OAAO,KAAK,GACf,QAAQ,gCAAgC,EACxC,IAAI;AAEP,QAAI,KAAK,WAAW,GAAG;AACrB,aAAO,CAAC;AAAA,IACV;AAGA,UAAM,iBAAiB,KAAK,CAAC,EAAG,OAAO,aAAa;AACpD,QAAI,OAAO,WAAW,gBAAgB;AACpC,YAAM,IAAI;AAAA,QACR,iCAAiC,OAAO,MAAM,oCAAoC,cAAc;AAAA,MAClG;AAAA,IACF;AAEA,UAAM,WAAW,IAAI,aAAa,MAAM;AACxC,UAAM,UAAgC,CAAC;AAEvC,eAAW,OAAO,MAAM;AACtB,YAAM,YAAY,IAAI;AAAA,QACpB,IAAI,OAAO;AAAA,QACX,IAAI,OAAO;AAAA,QACX,IAAI,OAAO,aAAa;AAAA,MAC1B;AACA,YAAM,WAAW,eAAe,UAAU,SAAS;AACnD,cAAQ,KAAK,EAAE,IAAI,IAAI,IAAI,SAAS,CAAC;AAAA,IACvC;AAEA,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,WAAW,EAAE,QAAQ;AAC9C,WAAO,QAAQ,MAAM,GAAG,KAAK;AAAA,EAC/B;AAAA,EAEA,MAAM,OAAO,IAA2B;AACtC,SAAK,GAAG,QAAQ,kCAAkC,EAAE,IAAI,EAAE;AAAA,EAC5D;AAAA,EAEA,MAAM,SAAS,IAAoC;AACjD,UAAM,MAAM,KAAK,GACd,QAAQ,wCAAwC,EAChD,IAAI,EAAE;AACT,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA,EAEA,aAAa,IAAqB;AAChC,UAAM,MAAM,KAAK,GACd,QAAQ,oCAAoC,EAC5C,IAAI,EAAE;AACT,WAAO,QAAQ;AAAA,EACjB;AAAA;AAAA,EAGA,gBAA0B;AACxB,UAAM,OAAO,KAAK,GACf,QAAQ,mDAAmD,EAC3D,IAAI;AACP,WAAO,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EAC/B;AAAA;AAAA,EAGA,kBAAkB,IAA2B;AAC3C,UAAM,MAAM,KAAK,GACd,QAAQ,yDAAyD,EACjE,IAAI,EAAE;AACT,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA;AAAA,EAGA,oBAA4B;AAC1B,UAAM,MAAM,KAAK,GACd,QAAQ,uCAAuC,EAC/C,IAAI;AACP,WAAO,IAAI;AAAA,EACb;AAAA,EAEA,QAAc;AACZ,QAAI,KAAK,QAAQ;AACf,WAAK,GAAG,MAAM;AAAA,IAChB;AAAA,EACF;AACF;AAEA,SAAS,eAAe,GAAiB,GAAyB;AAChE,MAAI,aAAa;AACjB,MAAI,aAAa;AACjB,MAAI,aAAa;AAEjB,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,kBAAc,EAAE,CAAC,IAAK,EAAE,CAAC;AACzB,kBAAc,EAAE,CAAC,IAAK,EAAE,CAAC;AACzB,kBAAc,EAAE,CAAC,IAAK,EAAE,CAAC;AAAA,EAC3B;AAEA,eAAa,KAAK,KAAK,UAAU;AACjC,eAAa,KAAK,KAAK,UAAU;AAEjC,MAAI,eAAe,KAAK,eAAe,GAAG;AACxC,WAAO;AAAA,EACT;AAEA,QAAM,aAAa,cAAc,aAAa;AAC9C,SAAO,IAAI;AACb;;;AFlKA,eAAsB,cAAc,WAA0C;AAC5E,QAAM,aAAaC,MAAK,WAAW,WAAW;AAG9C,MAAI;AACF,UAAMC,QAAO,UAAU;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,QAAM,WAAWD,MAAK,WAAW,OAAO;AACxC,QAAM,QAAQ,IAAI,MAAM,QAAQ;AAChC,QAAM,iBAAiB,IAAI,qBAAqB,QAAQ;AAExD,MAAI;AACF,UAAM,QAAQ,MAAM,SAAS;AAC7B,UAAM,iBAAiB,eAAe,kBAAkB;AAExD,UAAM,oBACJ,MAAM,cAAc,IAAI,IAAI,iBAAiB,MAAM;AAErD,WAAO;AAAA,MACL,WAAW,MAAM;AAAA,MACjB,WAAW,MAAM;AAAA,MACjB;AAAA,MACA;AAAA,IACF;AAAA,EACF,UAAE;AACA,UAAM,MAAM;AACZ,mBAAe,MAAM;AAAA,EACvB;AACF;;;AG3CA,SAAS,UAAAE,SAAQ,YAAAC,iBAAgB;AACjC,SAAS,QAAAC,aAAY;AACrB,SAAS,SAAS,iBAAiB;;;ACFnC,SAAS,YAAAC,WAAU,aAAAC,YAAW,MAAM,SAAS,SAAAC,QAAO,UAAU;AAC9D,SAAS,QAAAC,OAAM,UAAU,SAAS,eAAe;AACjD,SAAS,aAA6B;;;ACFtC,OAAO,YAAY;AAaZ,SAAS,cAAc,KAA6B;AACzD,MAAI;AACJ,MAAI;AACF,aAAS,OAAO,GAAG;AAAA,EACrB,QAAQ;AAEN,WAAO;AAAA,MACL,OAAO;AAAA,MACP,MAAM,CAAC;AAAA,MACP,YAAY,CAAC;AAAA,MACb,SAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,OAAO,OAAO;AAGpB,QAAM,QAAQ,OAAO,KAAK,OAAO,MAAM,WAAW,KAAK,OAAO,IAAI;AAGlE,MAAI,OAAiB,CAAC;AACtB,MAAI,MAAM,QAAQ,KAAK,MAAM,CAAC,GAAG;AAC/B,WAAO,KAAK,MAAM,EAAE,OAAO,CAAC,MAAmB,OAAO,MAAM,QAAQ;AAAA,EACtE;AAGA,QAAM,aAAsC,CAAC;AAC7C,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,QAAI,QAAQ,WAAW,QAAQ,QAAQ;AACrC,iBAAW,GAAG,IAAI;AAAA,IACpB;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,OAAO,QAAQ,KAAK;AAAA,EAC/B;AACF;AAOO,SAAS,iBAAiB,SAA2B;AAE1D,QAAM,oBAAoB,QAAQ,QAAQ,mBAAmB,EAAE;AAG/D,QAAM,oBAAoB,kBAAkB,QAAQ,YAAY,EAAE;AAGlE,QAAM,YAAY;AAClB,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,QAAkB,CAAC;AAEzB,MAAI;AACJ,UAAQ,QAAQ,UAAU,KAAK,iBAAiB,OAAO,MAAM;AAC3D,UAAM,SAAS,MAAM,CAAC,GAAG,KAAK;AAC9B,QAAI,UAAU,CAAC,KAAK,IAAI,MAAM,GAAG;AAC/B,WAAK,IAAI,MAAM;AACf,YAAM,KAAK,MAAM;AAAA,IACnB;AAAA,EACF;AAEA,SAAO;AACT;AAQO,SAAS,YAAY,MAAsB;AAChD,SAAO,KAAK,YAAY,EAAE,QAAQ,OAAO,GAAG;AAC9C;AASO,SAAS,cAAc,MAAsB;AAElD,QAAM,QAAQ,KAAK,MAAM,OAAO;AAGhC,QAAM,WAAW,MAAM,GAAG,EAAE;AAG5B,QAAM,aAAa,SAAS,QAAQ,YAAY,EAAE;AAGlD,QAAM,SAAS,WAAW,QAAQ,UAAU,GAAG,EAAE,YAAY;AAG7D,SAAO,OACJ,MAAM,GAAG,EACT,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAC1B,IAAI,CAAC,SAAS,KAAK,OAAO,CAAC,EAAE,YAAY,IAAI,KAAK,MAAM,CAAC,CAAC,EAC1D,KAAK,GAAG;AACb;AAMO,SAAS,oBAAoB,QAAgC;AAClE,QAAM,iBACJ,OAAO,UAAU,UACjB,OAAO,KAAK,SAAS,KACrB,OAAO,KAAK,OAAO,UAAU,EAAE,SAAS;AAE1C,MAAI,CAAC,gBAAgB;AACnB,WAAO,OAAO;AAAA,EAChB;AAGA,QAAM,cAAuC,CAAC;AAE9C,MAAI,OAAO,UAAU,QAAW;AAC9B,gBAAY,OAAO,IAAI,OAAO;AAAA,EAChC;AAEA,MAAI,OAAO,KAAK,SAAS,GAAG;AAC1B,gBAAY,MAAM,IAAI,OAAO;AAAA,EAC/B;AAGA,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,OAAO,UAAU,GAAG;AAC5D,gBAAY,GAAG,IAAI;AAAA,EACrB;AAGA,SAAO,OAAO,UAAU,OAAO,SAAS,WAAW;AACrD;;;ACzJA,SAAS,qBAAqB;AAQvB,SAAS,WAAW,OAA8B;AACvD,QAAM,QAAQ,IAAI,cAAc;AAGhC,QAAM,UAAU,oBAAI,IAAY;AAChC,aAAW,QAAQ,OAAO;AACxB,UAAM,QAAQ,KAAK,EAAE;AACrB,YAAQ,IAAI,KAAK,EAAE;AAAA,EACrB;AAGA,aAAW,QAAQ,OAAO;AACxB,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,UAAU,KAAK,eAAe;AAEvC,UAAI,CAAC,QAAQ,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,GAAG;AAC5C;AAAA,MACF;AACA,WAAK,IAAI,MAAM;AACf,YAAM,gBAAgB,KAAK,IAAI,MAAM;AAAA,IACvC;AAAA,EACF;AAEA,SAAO;AACT;;;AC/BA,SAAS,qBAAqB;AAWvB,SAAS,eACd,OACA,IACA,SACU;AACV,MAAI,CAAC,MAAM,QAAQ,EAAE,GAAG;AACtB,WAAO,CAAC;AAAA,EACV;AAEA,MAAI;AAEJ,UAAQ,QAAQ,WAAW;AAAA,IACzB,KAAK;AACH,kBAAY,MAAM,YAAY,EAAE;AAChC;AAAA,IACF,KAAK;AACH,kBAAY,MAAM,aAAa,EAAE;AACjC;AAAA,IACF,KAAK;AACH,kBAAY,MAAM,UAAU,EAAE;AAC9B;AAAA,EACJ;AAEA,MAAI,QAAQ,UAAU,QAAW;AAC/B,QAAI,QAAQ,SAAS,GAAG;AACtB,aAAO,CAAC;AAAA,IACV;AACA,QAAI,QAAQ,QAAQ,UAAU,QAAQ;AACpC,aAAO,UAAU,MAAM,GAAG,QAAQ,KAAK;AAAA,IACzC;AAAA,EACF;AAEA,SAAO;AACT;AAMO,SAAS,SACd,OACA,QACA,QACiB;AACjB,MAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,CAAC,MAAM,QAAQ,MAAM,GAAG;AACpD,WAAO;AAAA,EACT;AAEA,MAAI,WAAW,QAAQ;AACrB,WAAO,CAAC,MAAM;AAAA,EAChB;AAEA,QAAM,OAAO,cAAc,OAAO,QAAQ,MAAM;AAChD,SAAO;AACT;AAMO,SAAS,QACd,OACA,QACA,OACyB;AACzB,MAAI,SAAS,GAAG;AACd,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,SAAkC,CAAC;AAEzC,QAAM,YAAY,CAAC,OAAO;AACxB,QAAI;AACJ,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,gBAAQ,MAAM,SAAS,EAAE;AACzB;AAAA,MACF,KAAK;AACH,gBAAQ,MAAM,UAAU,EAAE;AAC1B;AAAA,MACF,KAAK;AAEH,gBAAQ,MAAM,SAAS,EAAE;AACzB;AAAA,IACJ;AACA,WAAO,KAAK,CAAC,IAAI,KAAK,CAAC;AAAA,EACzB,CAAC;AAED,SAAO,KAAK,CAAC,GAAG,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;AACjC,SAAO,OAAO,MAAM,GAAG,KAAK;AAC9B;AAMO,SAAS,kBACd,OACgC;AAChC,QAAM,SAAS,oBAAI,IAA+B;AAElD,QAAM,YAAY,CAAC,OAAO;AACxB,WAAO,IAAI,IAAI;AAAA,MACb,UAAU,MAAM,SAAS,EAAE;AAAA,MAC3B,WAAW,MAAM,UAAU,EAAE;AAAA,IAC/B,CAAC;AAAA,EACH,CAAC;AAED,SAAO;AACT;;;AHtFO,IAAM,WAAN,MAAM,UAAkC;AAAA,EACrC;AAAA,EACA;AAAA,EACA,QAA8B;AAAA,EAC9B;AAAA,EACA;AAAA,EAEA,UAA4B;AAAA,EAC5B,gBAAsD;AAAA,EACtD,iBAA2D,oBAAI,IAAI;AAAA,EACnE;AAAA,EAER,YACE,YACA,UACA,gBACA;AACA,SAAK,aAAa;AAClB,SAAK,QAAQ,IAAI,MAAM,QAAQ;AAC/B,SAAK,qBAAqB,CAAC;AAC3B,SAAK,iBAAiB,kBAAkB,IAAI,qBAAqB,QAAQ;AAAA,EAC3E;AAAA,EAEA,MAAM,OAAsB;AAC1B,UAAM,eAAe,MAAM,KAAK,qBAAqB,KAAK,UAAU;AACpE,UAAM,eAAe,KAAK,MAAM,mBAAmB;AAGnD,eAAW,YAAY,cAAc;AACnC,UAAI;AACF,cAAM,QAAQ,MAAM,KAAK,aAAa,QAAQ;AAC9C,cAAM,cAAc,KAAK,MAAM,gBAAgB,QAAQ;AAEvD,YAAI,gBAAgB,QAAQ,QAAQ,aAAa;AAC/C,gBAAM,OAAO,MAAM,KAAK,WAAW,QAAQ;AAC3C,eAAK,MAAM,WAAW,MAAM,QAAQ,UAAU,KAAK;AAAA,QACrD;AAAA,MACF,SAAS,KAAK;AAEZ,YAAK,IAA8B,SAAS,UAAU;AACpD;AAAA,QACF;AACA,cAAM;AAAA,MACR;AAAA,IACF;AAGA,UAAM,aAAa,IAAI,IAAI,YAAY;AACvC,eAAW,WAAW,cAAc;AAClC,UAAI,CAAC,WAAW,IAAI,OAAO,GAAG;AAC5B,cAAM,OAAO,KAAK,MAAM,cAAc,OAAO;AAC7C,YAAI,MAAM;AACR,eAAK,MAAM,WAAW,KAAK,EAAE;AAAA,QAC/B;AAAA,MACF;AAAA,IACF;AAGA,UAAM,gBAAgB,KAAK,mBAAmB;AAC9C,SAAK,qBAAqB,aAAa;AAGvC,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAM,WAAW,MAA2B;AAC1C,UAAM,eAAe,YAAY,KAAK,EAAE;AACxC,SAAK,yBAAyB,YAAY;AAE1C,UAAM,WAAW,KAAK,MAAM,QAAQ,YAAY;AAChD,QAAI,UAAU;AACZ,YAAM,IAAI,MAAM,wBAAwB,YAAY,EAAE;AAAA,IACxD;AAEA,UAAM,WAAWC,MAAK,KAAK,YAAY,YAAY;AACnD,UAAM,MAAM,QAAQ,QAAQ;AAC5B,UAAMC,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAEpC,UAAM,SAAS;AAAA,MACb,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK;AAAA,MACX,YAAY,KAAK;AAAA,MACjB,SAAS,KAAK;AAAA,IAChB;AACA,UAAM,WAAW,oBAAoB,MAAM;AAC3C,UAAMC,WAAU,UAAU,UAAU,OAAO;AAE3C,UAAM,QAAQ,MAAM,KAAK,aAAa,QAAQ;AAC9C,UAAM,iBAAiB,EAAE,GAAG,MAAM,IAAI,aAAa;AACnD,SAAK,MAAM,WAAW,gBAAgB,QAAQ,UAAU,KAAK;AAG7D,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAM,WAAW,IAAY,SAAuC;AAClE,UAAM,eAAe,YAAY,EAAE;AACnC,UAAM,WAAW,KAAK,MAAM,QAAQ,YAAY;AAChD,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,mBAAmB,EAAE,EAAE;AAAA,IACzC;AAGA,QAAI,gBAAgB,QAAQ;AAC5B,QAAI,QAAQ,YAAY,UAAa,kBAAkB,QAAW;AAChE,YAAM,WAAW,iBAAiB,QAAQ,OAAO;AACjD,sBAAgB,SAAS,IAAI,CAAC,SAAS,KAAK,kBAAkB,IAAI,CAAC;AAAA,IACrE;AAEA,UAAM,UAAgB;AAAA,MACpB,GAAG;AAAA,MACH,GAAG;AAAA,MACH,eAAe,iBAAiB,SAAS;AAAA,MACzC,IAAI,SAAS;AAAA;AAAA,IACf;AAEA,UAAM,WAAWF,MAAK,KAAK,YAAY,SAAS,EAAE;AAClD,UAAM,SAAS;AAAA,MACb,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ;AAAA,MACd,YAAY,QAAQ;AAAA,MACpB,SAAS,QAAQ;AAAA,IACnB;AACA,UAAM,WAAW,oBAAoB,MAAM;AAC3C,UAAME,WAAU,UAAU,UAAU,OAAO;AAE3C,UAAM,QAAQ,MAAM,KAAK,aAAa,QAAQ;AAC9C,SAAK,MAAM,WAAW,SAAS,QAAQ,UAAU,KAAK;AAGtD,QAAI,kBAAkB,UAAa,QAAQ,kBAAkB,QAAW;AACtE,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,IAA2B;AAC1C,UAAM,eAAe,YAAY,EAAE;AACnC,UAAM,WAAW,KAAK,MAAM,QAAQ,YAAY;AAChD,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,mBAAmB,EAAE,EAAE;AAAA,IACzC;AAEA,UAAM,WAAWF,MAAK,KAAK,YAAY,SAAS,EAAE;AAClD,UAAM,GAAG,QAAQ;AACjB,SAAK,MAAM,WAAW,SAAS,EAAE;AACjC,UAAM,KAAK,eAAe,OAAO,SAAS,EAAE;AAG5C,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,MAAM,QAAQ,IAAkC;AAE9C,UAAM,eAAe,YAAY,EAAE;AACnC,WAAO,KAAK,MAAM,QAAQ,YAAY;AAAA,EACxC;AAAA,EAEA,MAAM,SAAS,KAAgC;AAC7C,UAAM,gBAAgB,IAAI,IAAI,WAAW;AACzC,WAAO,KAAK,MAAM,SAAS,aAAa;AAAA,EAC1C;AAAA,EAEA,MAAM,gBAAmC;AACvC,UAAM,QAAQ,KAAK,MAAM,YAAY;AACrC,WAAO,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE;AAAA,EAC9B;AAAA,EAEA,MAAM,aAAa,MAAgB,MAAgC;AACjE,WAAO,KAAK,MAAM,aAAa,MAAM,IAAI;AAAA,EAC3C;AAAA,EAEA,MAAM,cAAc,MAAuC;AACzD,QAAI;AAEJ,QAAI,QAAQ,KAAK,SAAS,GAAG;AAC3B,mBAAa,MAAM,KAAK,aAAa,MAAM,KAAK;AAAA,IAClD,OAAO;AACL,mBAAa,KAAK,MAAM,YAAY;AAAA,IACtC;AAEA,QAAI,WAAW,WAAW,GAAG;AAC3B,aAAO;AAAA,IACT;AAEA,UAAM,cAAc,KAAK,MAAM,KAAK,OAAO,IAAI,WAAW,MAAM;AAEhE,WAAO,WAAW,WAAW;AAAA,EAC/B;AAAA,EAEA,MAAM,cAAc,KAA6C;AAC/D,WAAO,KAAK,MAAM,cAAc,GAAG;AAAA,EACrC;AAAA,EAEA,MAAM,UACJ,QACA,SAC0B;AAC1B,WAAO,KAAK,MAAM,UAAU,QAAQ,OAAO;AAAA,EAC7C;AAAA,EAEA,MAAM,aACJ,OACA,SAC0B;AAE1B,UAAM,WAAW,SAAS,YAAY;AACtC,QAAI,aAAa,WAAW,aAAa,SAAS;AAChD,aAAO,KAAK,MAAM,aAAa,OAAO,OAAO;AAAA,IAC/C;AAKA,WAAO,MAAM,IAAI,CAAC,WAAW,EAAE,OAAO,OAAO,MAAM,OAAO,EAAE,EAAE;AAAA,EAChE;AAAA,EAEA,MAAM,WAAW,KAA8C;AAC7D,UAAM,gBAAgB,IAAI,IAAI,WAAW;AACzC,WAAO,KAAK,MAAM,WAAW,aAAa;AAAA,EAC5C;AAAA,EAEA,MAAM,aAAa,IAAY,SAA2C;AACxE,SAAK,YAAY;AACjB,UAAM,cAAc,eAAe,KAAK,OAAQ,IAAI,OAAO;AAC3D,WAAO,KAAK,MAAM,SAAS,WAAW;AAAA,EACxC;AAAA,EAEA,MAAM,SAAS,QAAgB,QAA0C;AACvE,SAAK,YAAY;AACjB,WAAO,SAAc,KAAK,OAAQ,QAAQ,MAAM;AAAA,EAClD;AAAA,EAEA,MAAM,QAAQ,QAAgB,OAAiD;AAC7E,SAAK,YAAY;AACjB,WAAO,QAAa,KAAK,OAAQ,QAAQ,KAAK;AAAA,EAChD;AAAA,EAEA,MAAM,eACJ,IACA,QACA,OACe;AACf,WAAO,KAAK,eAAe,MAAM,IAAI,QAAQ,KAAK;AAAA,EACpD;AAAA,EAEA,MAAM,eACJ,QACA,OAC+B;AAC/B,WAAO,KAAK,eAAe,OAAO,QAAQ,KAAK;AAAA,EACjD;AAAA,EAEA,aAAa,IAAqB;AAChC,WAAO,KAAK,eAAe,aAAa,EAAE;AAAA,EAC5C;AAAA,EAEA,QAAc;AACZ,SAAK,aAAa;AAClB,SAAK,MAAM,MAAM;AACjB,QAAI,KAAK,sBAAsB,WAAW,KAAK,gBAAgB;AAC7D,MAAC,KAAK,eAAyC,MAAM;AAAA,IACvD;AAAA,EACF;AAAA,EAEA,cAAc,UAA0D;AACtE,QAAI,KAAK,SAAS;AAChB,YAAM,IAAI,MAAM,8CAA8C;AAAA,IAChE;AAEA,SAAK,mBAAmB;AAExB,WAAO,IAAI,QAAQ,CAACG,UAAS,WAAW;AACtC,WAAK,UAAU,MAAM,KAAK,YAAY;AAAA,QACpC,eAAe;AAAA,QACf,SAAS,CAAC,GAAG,UAAS,aAAa,EAAE,IAAI,CAAC,QAAQ,MAAM,GAAG,KAAK;AAAA,QAChE,kBAAkB;AAAA,UAChB,oBAAoB;AAAA,QACtB;AAAA,QACA,gBAAgB;AAAA,MAClB,CAAC;AAED,WAAK,QACF,GAAG,SAAS,MAAMA,SAAQ,CAAC,EAC3B,GAAG,OAAO,CAAC,SAAS,KAAK,YAAY,MAAM,KAAK,CAAC,EACjD,GAAG,UAAU,CAAC,SAAS,KAAK,YAAY,MAAM,QAAQ,CAAC,EACvD,GAAG,UAAU,CAAC,SAAS,KAAK,YAAY,MAAM,QAAQ,CAAC,EACvD,GAAG,SAAS,CAAC,QAAQ;AACpB,YAAK,IAA8B,SAAS,UAAU;AACpD,kBAAQ;AAAA,YACN;AAAA,UAEF;AAAA,QACF;AACA,eAAO,GAAG;AAAA,MACZ,CAAC;AAAA,IACL,CAAC;AAAA,EACH;AAAA,EAEA,eAAqB;AACnB,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAC/B,WAAK,gBAAgB;AAAA,IACvB;AACA,SAAK,eAAe,MAAM;AAE1B,QAAI,KAAK,SAAS;AAChB,WAAK,QAAQ,MAAM;AACnB,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA,EAEA,aAAsB;AACpB,WAAO,KAAK,YAAY;AAAA,EAC1B;AAAA,EAEQ,YAAY,UAAkB,OAA0C;AAC9E,UAAM,eAAe,SAAS,KAAK,YAAY,QAAQ;AACvD,UAAM,KAAK,YAAY,YAAY;AAGnC,QAAI,CAAC,SAAS,SAAS,KAAK,GAAG;AAC7B;AAAA,IACF;AAGA,UAAM,YAAY,aAAa,MAAM,GAAG;AACxC,eAAW,QAAQ,WAAW;AAC5B,UAAI,UAAS,cAAc,IAAI,IAAI,GAAG;AACpC;AAAA,MACF;AAAA,IACF;AAGA,UAAM,WAAW,KAAK,eAAe,IAAI,EAAE;AAE3C,QAAI,UAAU;AACZ,UAAI,aAAa,SAAS,UAAU,UAAU;AAE5C;AAAA,MACF,WAAW,aAAa,SAAS,UAAU,UAAU;AAEnD,aAAK,eAAe,OAAO,EAAE;AAAA,MAC/B,WAAW,aAAa,YAAY,UAAU,UAAU;AAEtD,aAAK,eAAe,IAAI,IAAI,QAAQ;AAAA,MACtC;AAAA,IAEF,OAAO;AACL,WAAK,eAAe,IAAI,IAAI,KAAK;AAAA,IACnC;AAGA,QAAI,KAAK,eAAe;AACtB,mBAAa,KAAK,aAAa;AAAA,IACjC;AAEA,SAAK,gBAAgB,WAAW,MAAM;AACpC,WAAK,aAAa;AAAA,IACpB,GAAG,GAAI;AAAA,EACT;AAAA,EAEA,MAAc,eAA8B;AAC1C,UAAM,UAAU,IAAI,IAAI,KAAK,cAAc;AAC3C,SAAK,eAAe,MAAM;AAC1B,SAAK,gBAAgB;AAErB,UAAM,eAAyB,CAAC;AAEhC,eAAW,CAAC,IAAI,KAAK,KAAK,SAAS;AACjC,UAAI;AACF,YAAI,UAAU,UAAU;AACtB,gBAAM,WAAW,KAAK,MAAM,QAAQ,EAAE;AACtC,cAAI,UAAU;AACZ,iBAAK,MAAM,WAAW,EAAE;AACxB,kBAAM,KAAK,eAAe,OAAO,EAAE;AACnC,yBAAa,KAAK,EAAE;AAAA,UACtB;AAAA,QACF,OAAO;AAEL,gBAAM,WAAWH,MAAK,KAAK,YAAY,EAAE;AACzC,gBAAM,OAAO,MAAM,KAAK,WAAW,QAAQ;AAC3C,gBAAM,QAAQ,MAAM,KAAK,aAAa,QAAQ;AAC9C,eAAK,MAAM,WAAW,MAAM,QAAQ,UAAU,KAAK;AACnD,uBAAa,KAAK,EAAE;AAAA,QACtB;AAAA,MACF,SAAS,KAAK;AACZ,gBAAQ,KAAK,qCAAqC,EAAE,KAAK,GAAG;AAAA,MAC9D;AAAA,IACF;AAGA,QAAI,aAAa,SAAS,GAAG;AAC3B,YAAM,gBAAgB,KAAK,mBAAmB;AAC9C,WAAK,qBAAqB,aAAa;AACvC,WAAK,aAAa;AAAA,IACpB;AAGA,QAAI,KAAK,oBAAoB,aAAa,SAAS,GAAG;AACpD,WAAK,iBAAiB,YAAY;AAAA,IACpC;AAAA,EACF;AAAA,EAEQ,qBAA4C;AAClD,UAAM,QAAQ,oBAAI,IAAsB;AACxC,eAAW,QAAQ,KAAK,MAAM,YAAY,GAAG;AAC3C,YAAM,WAAW,KAAK,GAAG,MAAM,GAAG,EAAE,IAAI;AACxC,YAAM,WAAW,MAAM,IAAI,QAAQ,KAAK,CAAC;AACzC,eAAS,KAAK,KAAK,EAAE;AACrB,YAAM,IAAI,UAAU,QAAQ;AAAA,IAC9B;AAEA,eAAW,SAAS,MAAM,OAAO,GAAG;AAClC,YAAM,KAAK;AAAA,IACb;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,qBAAqB,eAA4C;AAEvE,UAAM,eAAe,oBAAI,IAAY;AACrC,eAAW,SAAS,cAAc,OAAO,GAAG;AAC1C,iBAAW,QAAQ,OAAO;AACxB,qBAAa,IAAI,IAAI;AAAA,MACvB;AAAA,IACF;AAEA,eAAW,QAAQ,KAAK,MAAM,YAAY,GAAG;AAC3C,YAAM,WAAW,KAAK,cAAc,IAAI,CAAC,SAAS;AAEhD,YAAI,aAAa,IAAI,IAAI,GAAG;AAC1B,iBAAO;AAAA,QACT;AAGA,YAAI,KAAK,SAAS,GAAG,GAAG;AACtB,iBAAO;AAAA,QACT;AAEA,cAAM,UAAU,cAAc,IAAI,IAAI;AACtC,YAAI,WAAW,QAAQ,SAAS,GAAG;AACjC,iBAAO,QAAQ,CAAC;AAAA,QAClB;AACA,eAAO;AAAA,MACT,CAAC;AAGD,UAAI,SAAS,KAAK,CAAC,GAAG,MAAM,MAAM,KAAK,cAAc,CAAC,CAAC,GAAG;AACxD,aAAK,MAAM,oBAAoB,KAAK,IAAI,QAAQ;AAAA,MAClD;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,cAAoB;AAC1B,QAAI,CAAC,KAAK,OAAO;AACf,WAAK,aAAa;AAAA,IACpB;AAAA,EACF;AAAA,EAEQ,eAAqB;AAC3B,UAAM,QAAQ,KAAK,MAAM,YAAY;AACrC,SAAK,QAAQ,WAAW,KAAK;AAG7B,UAAM,aAAa,kBAAkB,KAAK,KAAK;AAC/C,UAAM,MAAM,KAAK,IAAI;AACrB,eAAW,CAAC,IAAI,OAAO,KAAK,YAAY;AACtC,WAAK,MAAM,gBAAgB,IAAI,GAAG,QAAQ,UAAU,QAAQ,WAAW,GAAG;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,OAAwB,gBAAgB,oBAAI,IAAI,CAAC,SAAS,gBAAgB,QAAQ,WAAW,CAAC;AAAA,EAE9F,MAAc,qBAAqB,KAAgC;AACjE,UAAM,UAAoB,CAAC;AAE3B,QAAI;AACJ,QAAI;AACF,gBAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,IACtD,QAAQ;AAEN,aAAO;AAAA,IACT;AAEA,eAAW,SAAS,SAAS;AAC3B,YAAM,WAAWA,MAAK,KAAK,MAAM,IAAI;AAErC,UAAI,MAAM,YAAY,GAAG;AAEvB,YAAI,UAAS,cAAc,IAAI,MAAM,IAAI,GAAG;AAC1C;AAAA,QACF;AACA,cAAM,SAAS,MAAM,KAAK,qBAAqB,QAAQ;AACvD,gBAAQ,KAAK,GAAG,MAAM;AAAA,MACxB,WAAW,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,GAAG;AACvD,gBAAQ,KAAK,QAAQ;AAAA,MACvB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,aAAa,UAAmC;AAC5D,UAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,MAAc,WAAW,UAAiC;AACxD,UAAM,MAAM,MAAMI,UAAS,UAAU,OAAO;AAC5C,UAAM,SAAS,cAAc,GAAG;AAEhC,UAAM,eAAe,SAAS,KAAK,YAAY,QAAQ;AACvD,UAAM,KAAK,YAAY,YAAY;AAGnC,UAAM,QAAQ,OAAO,SAAS,cAAc,EAAE;AAG9C,UAAM,WAAW,iBAAiB,OAAO,OAAO;AAChD,UAAM,gBAAgB,SAAS,IAAI,CAAC,SAAS,KAAK,kBAAkB,IAAI,CAAC;AAEzE,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,MAAM,OAAO;AAAA,MACb;AAAA,MACA,YAAY,OAAO;AAAA,MACnB,WAAW;AAAA,QACT,MAAM;AAAA,QACN,MAAM;AAAA,QACN,cAAc,IAAI,KAAK,MAAM,KAAK,aAAa,QAAQ,CAAC;AAAA,MAC1D;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,kBAAkB,QAAwB;AAChD,QAAI,aAAa,OAAO,YAAY,EAAE,QAAQ,OAAO,GAAG;AAIxD,QAAI,CAAC,KAAK,iBAAiB,UAAU,GAAG;AACtC,oBAAc;AAAA,IAChB;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,iBAAiB,MAAuB;AAG9C,UAAM,QAAQ,KAAK,MAAM,qBAAqB;AAC9C,QAAI,CAAC,QAAQ,CAAC,EAAG,QAAO;AAExB,WAAO,SAAS,KAAK,MAAM,CAAC,CAAC;AAAA,EAC/B;AAAA,EAEQ,yBAAyB,IAAkB;AACjD,UAAM,eAAe,QAAQ,KAAK,YAAY,EAAE;AAChD,UAAM,eAAe,QAAQ,KAAK,UAAU;AAE5C,QAAI,CAAC,aAAa,WAAW,eAAe,GAAG,GAAG;AAChD,YAAM,IAAI,MAAM,4BAA4B,EAAE,+BAA+B;AAAA,IAC/E;AAAA,EACF;AACF;;;AI9lBA,SAAS,gBAAgD;AAGzD,IAAM,gBAAgB;AACtB,IAAM,qBAAqB;AAEpB,IAAM,gCAAN,MAAiE;AAAA,EAC9D;AAAA,EACA;AAAA,EACA,OAAyC;AAAA,EAEjD,YAAY,QAAQ,eAAe,aAAa,oBAAoB;AAClE,SAAK,QAAQ;AACb,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,MAAc,cAAkD;AAC9D,QAAI,CAAC,KAAK,MAAM;AACd,WAAK,OAAO,MAAM,SAAS,sBAAsB,KAAK,KAAK;AAAA,IAC7D;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,MAAM,MAAiC;AAC3C,UAAM,OAAO,MAAM,KAAK,YAAY;AACpC,UAAM,SAAS,MAAM,KAAK,MAAM,EAAE,SAAS,QAAQ,WAAW,KAAK,CAAC;AACpE,WAAO,MAAM,KAAK,OAAO,IAAoB;AAAA,EAC/C;AAAA,EAEA,MAAM,WAAW,OAAsC;AACrD,QAAI,MAAM,WAAW,GAAG;AACtB,aAAO,CAAC;AAAA,IACV;AACA,WAAO,QAAQ,IAAI,MAAM,IAAI,CAAC,MAAM,KAAK,MAAM,CAAC,CAAC,CAAC;AAAA,EACpD;AAAA,EAEA,aAAqB;AACnB,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAkB;AAChB,WAAO,KAAK;AAAA,EACd;AACF;;;ACtBO,IAAM,gBAAN,MAAM,eAAmC;AAAA,EACtC,QAA8B;AAAA,EAC9B,YAAsC;AAAA,EAE9C,cAAc,UAA+B;AAC3C,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,4BAA4B;AAAA,IAC9C;AACA,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,kBAAkB,UAAmC;AACnD,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,gCAAgC;AAAA,IAClD;AACA,SAAK,YAAY;AAAA,EACnB;AAAA,EAEQ,eAA8B;AACpC,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,8BAA8B;AAAA,IAChD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,mBAAsC;AAC5C,QAAI,CAAC,KAAK,WAAW;AACnB,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,OAAO,OAAe,SAA0C;AACpE,UAAM,QAAQ,KAAK,aAAa;AAChC,UAAM,YAAY,KAAK,iBAAiB;AAExC,UAAM,QAAQ,SAAS,SAAS;AAChC,UAAM,SAAS,MAAM,UAAU,MAAM,KAAK;AAC1C,UAAM,UAAU,MAAM,MAAM,eAAe,QAAQ,KAAK;AAGxD,UAAM,MAAM,QAAQ,IAAI,CAAC,MAAM,EAAE,EAAE;AACnC,WAAO,MAAM,SAAS,GAAG;AAAA,EAC3B;AAAA,EAEA,MAAM,QAAQ,IAAY,OAAiD;AACzE,UAAM,QAAQ,KAAK,aAAa;AAChC,UAAM,OAAO,MAAM,MAAM,QAAQ,EAAE;AAEnC,QAAI,CAAC,MAAM;AACT,aAAO;AAAA,IACT;AAEA,QAAI,CAAC,SAAS,UAAU,GAAG;AACzB,aAAO;AAAA,IACT;AAGA,UAAM,CAAC,mBAAmB,iBAAiB,IAAI,MAAM,QAAQ,IAAI;AAAA,MAC/D,MAAM,aAAa,IAAI,EAAE,WAAW,KAAK,CAAC;AAAA,MAC1C,MAAM,aAAa,IAAI,EAAE,WAAW,MAAM,CAAC;AAAA,IAC7C,CAAC;AAGD,UAAM,cAAc,oBAAI,IAAkB;AAC1C,eAAW,KAAK,CAAC,GAAG,mBAAmB,GAAG,iBAAiB,GAAG;AAC5D,kBAAY,IAAI,EAAE,IAAI,CAAC;AAAA,IACzB;AAEA,UAAM,SAA0B;AAAA,MAC9B,GAAG;AAAA,MACH,WAAW,MAAM,KAAK,YAAY,OAAO,CAAC;AAAA,MAC1C,eAAe,kBAAkB;AAAA,MACjC,eAAe,kBAAkB;AAAA,IACnC;AAEA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,SAAuC;AACtD,UAAM,QAAQ,KAAK,aAAa;AAEhC,QAAI,CAAC,QAAQ,MAAM,QAAQ,GAAG,KAAK,MAAM,IAAI;AAC3C,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AACA,QAAI,CAAC,QAAQ,OAAO;AAClB,YAAM,IAAI,MAAM,wBAAwB;AAAA,IAC1C;AAEA,UAAM,OAAa;AAAA,MACjB,IAAI,QAAQ;AAAA,MACZ,OAAO,QAAQ;AAAA,MACf,SAAS,QAAQ,WAAW;AAAA,MAC5B,MAAM,QAAQ,QAAQ,CAAC;AAAA,MACvB,eAAe,QAAQ,iBAAiB,CAAC;AAAA,MACzC,YAAY,QAAQ,cAAc,CAAC;AAAA,MACnC,GAAI,QAAQ,aAAa,EAAE,WAAW,QAAQ,UAAU;AAAA,IAC1D;AAEA,UAAM,MAAM,WAAW,IAAI;AAC3B,WAAQ,MAAM,MAAM,QAAQ,KAAK,EAAE,KAAM;AAAA,EAC3C;AAAA,EAEA,MAAM,WAAW,IAAY,SAAuC;AAClE,UAAM,QAAQ,KAAK,aAAa;AAChC,UAAM,MAAM,WAAW,IAAI,OAAO;AAClC,UAAM,UAAU,MAAM,MAAM,QAAQ,EAAE;AACtC,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,gCAAgC,EAAE,EAAE;AAAA,IACtD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,WAAW,IAA8B;AAC7C,UAAM,QAAQ,KAAK,aAAa;AAChC,QAAI;AACF,YAAM,MAAM,WAAW,EAAE;AACzB,aAAO;AAAA,IACT,SAAS,KAAK;AAEZ,UAAI,eAAe,SAAS,aAAa,KAAK,IAAI,OAAO,GAAG;AAC1D,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,IAAY,SAA2C;AACxE,UAAM,QAAQ,KAAK,aAAa;AAChC,WAAO,MAAM,aAAa,IAAI,OAAO;AAAA,EACvC;AAAA,EAEA,MAAM,SAAS,QAAgB,QAA0C;AACvE,UAAM,QAAQ,KAAK,aAAa;AAChC,WAAO,MAAM,SAAS,QAAQ,MAAM;AAAA,EACtC;AAAA,EAEA,MAAM,QACJ,QACA,OACkC;AAClC,UAAM,QAAQ,KAAK,aAAa;AAChC,WAAO,MAAM,QAAQ,QAAQ,KAAK;AAAA,EACpC;AAAA,EAEA,MAAM,aACJ,MACA,MACA,OACiB;AACjB,UAAM,QAAQ,KAAK,aAAa;AAChC,UAAM,UAAU,MAAM,MAAM,aAAa,MAAM,IAAI;AACnD,QAAI,UAAU,QAAW;AACvB,aAAO,QAAQ,MAAM,GAAG,KAAK;AAAA,IAC/B;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,cAAc,MAAuC;AACzD,UAAM,QAAQ,KAAK,aAAa;AAChC,WAAO,MAAM,cAAc,IAAI;AAAA,EACjC;AAAA,EAEA,MAAM,UACJ,QACA,SAC0B;AAC1B,WAAO,KAAK,aAAa,EAAE,UAAU,QAAQ,OAAO;AAAA,EACtD;AAAA,EAEA,MAAM,aACJ,OACA,SAC0B;AAC1B,UAAM,QAAQ,KAAK,aAAa;AAChC,UAAM,WAAW,SAAS,YAAY;AAGtC,QAAI,aAAa,YAAY;AAC3B,UAAI,CAAC,KAAK,WAAW;AACnB,cAAM,IAAI,MAAM,gDAAgD;AAAA,MAClE;AAGA,YAAM,SAAqB,CAAC;AAC5B,UAAI,SAAS,IAAK,QAAO,MAAM,QAAQ;AACvC,UAAI,SAAS,KAAM,QAAO,OAAO,QAAQ;AAGzC,YAAM,EAAE,OAAO,WAAW,IAAI,MAAM,MAAM,UAAU,QAAQ,EAAE,OAAO,IAAK,CAAC;AAE3E,UAAI,WAAW,WAAW,KAAK,MAAM,WAAW,GAAG;AACjD,eAAO,MAAM,IAAI,CAAC,WAAW,EAAE,OAAO,OAAO,MAAM,OAAO,EAAE,EAAE;AAAA,MAChE;AAEA,YAAM,YAAY,SAAS,aAAa;AAGxC,YAAM,eAAe,MAAM,KAAK,UAAU,WAAW,KAAK;AAG1D,YAAM,kBAAkB,WAAW,IAAI,CAAC,MAAM,EAAE,KAAK;AACrD,YAAM,mBAAmB,MAAM,KAAK,UAAU,WAAW,eAAe;AAGxE,UAAI,aAAa,SAAS,KAAK,iBAAiB,SAAS,GAAG;AAC1D,cAAM,WAAW,aAAa,CAAC,EAAG;AAClC,cAAM,eAAe,iBAAiB,CAAC,EAAG;AAC1C,YAAI,aAAa,cAAc;AAC7B,gBAAM,IAAI;AAAA,YACR,uCAAuC,QAAQ,eAAe,YAAY;AAAA,UAC5E;AAAA,QACF;AAAA,MACF;AAGA,aAAO,MAAM,IAAI,CAAC,OAAO,SAAwB;AAC/C,cAAM,cAAc,aAAa,IAAI;AACrC,YAAI,YAAY;AAChB,YAAI,YAA2B;AAE/B,iBAAS,OAAO,GAAG,OAAO,WAAW,QAAQ,QAAQ;AACnD,gBAAM,aAAa,KAAK,iBAAiB,aAAa,iBAAiB,IAAI,CAAE;AAC7E,cAAI,aAAa,WAAW;AAC1B,wBAAY;AACZ,wBAAY,WAAW,IAAI,EAAG;AAAA,UAChC;AAAA,QACF;AAEA,YAAI,aAAa,WAAW;AAC1B,iBAAO,EAAE,OAAO,OAAO,WAAW,OAAO,UAAU;AAAA,QACrD;AACA,eAAO,EAAE,OAAO,OAAO,MAAM,OAAO,EAAE;AAAA,MACxC,CAAC;AAAA,IACH;AAGA,WAAO,MAAM,aAAa,OAAO,OAAO;AAAA,EAC1C;AAAA,EAEQ,iBAAiB,GAAa,GAAqB;AACzD,QAAI,aAAa;AACjB,QAAI,QAAQ;AACZ,QAAI,QAAQ;AACZ,aAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AACjC,oBAAc,EAAE,CAAC,IAAK,EAAE,CAAC;AACzB,eAAS,EAAE,CAAC,IAAK,EAAE,CAAC;AACpB,eAAS,EAAE,CAAC,IAAK,EAAE,CAAC;AAAA,IACtB;AACA,QAAI,UAAU,KAAK,UAAU,EAAG,QAAO;AACvC,WAAO,cAAc,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,KAAK;AAAA,EACzD;AAAA,EAEA,OAAO,WAAW,QAAmC;AACnD,QAAI,CAAC,OAAO,WAAW,OAAO;AAC5B,YAAM,IAAI,MAAM,yCAAyC;AAAA,IAC3D;AAEA,UAAM,OAAO,IAAI,eAAc;AAG/B,QAAI,OAAO,UAAU,MAAM,SAAS,YAAY;AAC9C,YAAM,aAAa,OAAO,QAAQ,QAAQ;AAC1C,YAAM,YAAY,OAAO,OAAO,QAAQ;AACxC,YAAM,QAAQ,IAAI,SAAS,YAAY,SAAS;AAChD,WAAK,cAAc,KAAK;AAAA,IAC1B,OAAO;AACL,YAAM,IAAI;AAAA,QACR,oCAAoC,OAAO,UAAU,MAAM,IAAI;AAAA,MACjE;AAAA,IACF;AAGA,UAAM,kBAAkB,OAAO,UAAU;AACzC,QAAI,CAAC,mBAAmB,gBAAgB,SAAS,SAAS;AACxD,YAAM,QAAQ,iBAAiB;AAC/B,YAAM,YAAY,IAAI,8BAA8B,KAAK;AACzD,WAAK,kBAAkB,SAAS;AAAA,IAClC,OAAO;AACL,YAAM,IAAI;AAAA,QACR,wCAAwC,gBAAgB,IAAI;AAAA,MAC9D;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;;;ACnTA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OAEK;;;AC8DA,IAAM,WAAN,cAAuB,MAAM;AAAA,EAClC,YACkB,MAChB,SACA;AACA,UAAM,OAAO;AAHG;AAIhB,SAAK,OAAO;AAAA,EACd;AAAA,EAEA,aAA4B;AAC1B,WAAO;AAAA,MACL,OAAO;AAAA,QACL,MAAM,KAAK;AAAA,QACX,SAAS,KAAK;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;;;ACpFO,IAAM,oBAAoB;AAAA;AAAA,EAE/B,SAAS;AAAA;AAAA,EAET,MAAM;AAAA;AAAA,EAEN,UAAU;AACZ;AAIA,IAAM,oBAAoB;AAMnB,SAAS,gBACd,SACA,SACQ;AACR,QAAM,QAAQ,kBAAkB,OAAO;AAEvC,MAAI,QAAQ,UAAU,OAAO;AAC3B,WAAO;AAAA,EACT;AAGA,QAAM,kBAAkB,KAAK,IAAI,GAAG,QAAQ,kBAAkB,MAAM;AACpE,SAAO,QAAQ,MAAM,GAAG,eAAe,IAAI;AAC7C;;;AClBO,IAAM,gBAAgB;AAGtB,IAAM,uBAAuB;AAKpC,eAAsB,eACpB,MACA,OACA,YACuB;AAEvB,QAAM,iBAAiB,KAAK,cAAc,MAAM,GAAG,oBAAoB;AACvE,QAAM,SAAS,MAAM,MAAM,cAAc,cAAc;AAEvD,QAAM,QAAoB,eAAe,IAAI,CAAC,QAAQ;AAAA,IACpD;AAAA,IACA,OAAO,OAAO,IAAI,EAAE,KAAK;AAAA,EAC3B,EAAE;AAEF,SAAO;AAAA,IACL,IAAI,KAAK;AAAA,IACT,OAAO,KAAK;AAAA,IACZ,SAAS,gBAAgB,KAAK,SAAS,UAAU;AAAA,IACjD,MAAM,KAAK;AAAA,IACX;AAAA,IACA,YAAY,KAAK;AAAA,EACnB;AACF;AAOA,eAAsB,iBACpB,OACA,OACA,YACA,gBACkD;AAElD,QAAM,aAAa,oBAAI,IAAY;AACnC,QAAM,iBAAiB,oBAAI,IAAsB;AAEjD,aAAW,QAAQ,OAAO;AACxB,UAAM,eAAe,KAAK,cAAc,MAAM,GAAG,oBAAoB;AACrE,mBAAe,IAAI,KAAK,IAAI,YAAY;AACxC,eAAW,UAAU,cAAc;AACjC,iBAAW,IAAI,MAAM;AAAA,IACvB;AAAA,EACF;AAGA,QAAM,SAAS,MAAM,MAAM,cAAc,MAAM,KAAK,UAAU,CAAC;AAG/D,SAAO,MAAM,IAAI,CAAC,SAAS;AAEzB,UAAM,eAAe,eAAe,IAAI,KAAK,EAAE,KAA0B,CAAC;AAC1E,UAAM,OAA6B;AAAA,MACjC,IAAI,KAAK;AAAA,MACT,OAAO,KAAK;AAAA,MACZ,MAAM,KAAK;AAAA,MACX,OAAO,aAAa,IAAI,CAAC,QAAQ;AAAA,QAC/B;AAAA,QACA,OAAO,OAAO,IAAI,EAAE,KAAK;AAAA,MAC3B,EAAE;AAAA,MACF,YAAY,KAAK;AAAA,IACnB;AAEA,QAAI,gBAAgB;AAClB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS,gBAAgB,KAAK,SAAS,UAAU;AAAA,MACnD;AAAA,IACF;AAEA,WAAO;AAAA,EACT,CAAC;AACH;AAKA,eAAsB,sBACpB,MACA,mBACA,mBACA,OACkC;AAElC,QAAM,UAAU,MAAM,eAAe,MAAM,OAAO,SAAS;AAG3D,QAAM,kBAAkB,kBAAkB,MAAM,GAAG,aAAa;AAChE,QAAM,kBAAkB,kBAAkB,MAAM,GAAG,aAAa;AAIhE,QAAM,CAAC,mBAAmB,iBAAiB,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC/D,iBAAiB,iBAAiB,OAAO,YAAY,IAAI;AAAA,IACzD,iBAAiB,iBAAiB,OAAO,YAAY,IAAI;AAAA,EAC3D,CAAC;AAED,SAAO;AAAA,IACL,GAAG;AAAA,IACH,mBAAmB;AAAA,IACnB,mBAAmB;AAAA,IACnB,eAAe,kBAAkB;AAAA,IACjC,eAAe,kBAAkB;AAAA,EACnC;AACF;AAOA,eAAsB,qBACpB,OACA,QACA,OACA,gBACiC;AACjC,QAAM,YAAY,MAAM,iBAAiB,OAAO,OAAO,QAAQ,cAAc;AAE7E,SAAO,UAAU,IAAI,CAAC,cAAc;AAAA,IAClC,GAAG;AAAA,IACH,OAAO,OAAO,IAAI,SAAS,EAAE,KAAK;AAAA,EACpC,EAAE;AACJ;AAKA,eAAsB,gBACpB,MACA,OACwB;AACxB,QAAM,MAAM,KAAK,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE;AACjC,QAAM,SAAS,MAAM,MAAM,cAAc,GAAG;AAE5C,SAAO,KAAK,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO;AAAA,IAChC;AAAA,IACA,OAAO,OAAO,IAAI,EAAE,KAAK;AAAA,IACzB;AAAA,EACF,EAAE;AACJ;AAKO,SAAS,eAAe,MAA8B;AAC3D,SAAO;AAAA,IACL;AAAA,IACA,QAAQ,KAAK,SAAS;AAAA,EACxB;AACF;;;ACxIA,SAAS,YAAY,OAAgB,cAA8B;AACjE,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,OAAO,KAAK;AACxB,MAAI,OAAO,MAAM,GAAG,GAAG;AACrB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,MAAM,GAAG;AAC9B,MAAI,UAAU,GAAG;AACf,UAAM,IAAI,SAAS,kBAAkB,0BAA0B;AAAA,EACjE;AACA,SAAO;AACT;AAEA,SAAS,aAAa,OAAgB,cAA8B;AAClE,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,OAAO,KAAK;AACxB,MAAI,OAAO,MAAM,GAAG,GAAG;AACrB,WAAO;AAAA,EACT;AACA,QAAM,UAAU,KAAK,MAAM,GAAG;AAC9B,MAAI,UAAU,GAAG;AACf,UAAM,IAAI,SAAS,kBAAkB,2BAA2B;AAAA,EAClE;AACA,SAAO;AACT;AA0BA,eAAsB,aACpB,KACA,MACiC;AACjC,MAAI,CAAC,IAAI,cAAc;AACrB,UAAM,IAAI,SAAS,kBAAkB,oCAAoC;AAAA,EAC3E;AAEA,QAAM,QAAQ,KAAK;AACnB,QAAM,QAAQ,YAAY,KAAK,OAAO,EAAE;AACxC,QAAM,iBAAiB,KAAK,oBAAoB;AAEhD,MAAI,OAAO,UAAU,YAAY,MAAM,KAAK,MAAM,IAAI;AACpD,UAAM,IAAI,SAAS,kBAAkB,kDAAkD;AAAA,EACzF;AAEA,QAAM,QAAQ,MAAM,IAAI,KAAK,OAAO,OAAO,EAAE,MAAM,CAAC;AAIpD,QAAM,SAAS,oBAAI,IAAoB;AACvC,QAAM,QAAQ,CAAC,MAAM,UAAU;AAC7B,WAAO,IAAI,KAAK,IAAI,KAAK,IAAI,GAAG,IAAI,QAAQ,IAAI,CAAC;AAAA,EACnD,CAAC;AAED,SAAO,qBAAqB,OAAO,QAAQ,IAAI,OAAO,cAAc;AACtE;AAEA,SAAS,YAAY,OAAwB;AAC3C,MAAI,UAAU,UAAa,UAAU,MAAM;AACzC,WAAO;AAAA,EACT;AACA,QAAM,MAAM,OAAO,KAAK;AACxB,MAAI,OAAO,MAAM,GAAG,GAAG;AACrB,WAAO;AAAA,EACT;AACA,SAAO,OAAO,IAAI,IAAI;AACxB;AAEA,eAAsB,cACpB,KACA,MACwD;AACxD,QAAM,KAAK,KAAK;AAChB,QAAM,QAAQ,YAAY,KAAK,KAAK;AAEpC,MAAI,CAAC,MAAM,OAAO,OAAO,UAAU;AACjC,UAAM,IAAI,SAAS,kBAAkB,qCAAqC;AAAA,EAC5E;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK,QAAQ,IAAI,KAAK;AAC7C,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,MAAI,UAAU,GAAG;AACf,WAAO,eAAe,MAAM,IAAI,OAAO,SAAS;AAAA,EAClD;AAEA,QAAM,CAAC,mBAAmB,iBAAiB,IAAI,MAAM,QAAQ,IAAI;AAAA,IAC/D,IAAI,KAAK,aAAa,IAAI,EAAE,WAAW,KAAK,CAAC;AAAA,IAC7C,IAAI,KAAK,aAAa,IAAI,EAAE,WAAW,MAAM,CAAC;AAAA,EAChD,CAAC;AAED,SAAO,sBAAsB,MAAM,mBAAmB,mBAAmB,IAAI,KAAK;AACpF;AAEA,IAAM,mBAAmB,CAAC,MAAM,OAAO,MAAM;AAE7C,eAAsB,mBACpB,KACA,MACkD;AAClD,QAAM,KAAK,KAAK;AAChB,QAAM,eAAe,KAAK,aAAa;AACvC,QAAM,QAAQ,YAAY,KAAK,OAAO,EAAE;AACxC,QAAM,iBAAiB,KAAK,oBAAoB;AAEhD,MAAI,CAAC,MAAM,OAAO,OAAO,UAAU;AACjC,UAAM,IAAI,SAAS,kBAAkB,qCAAqC;AAAA,EAC5E;AAEA,MAAI,CAAC,iBAAiB,SAAS,YAAiD,GAAG;AACjF,UAAM,IAAI;AAAA,MACR;AAAA,MACA,6BAA6B,iBAAiB,KAAK,IAAI,CAAC;AAAA,IAC1D;AAAA,EACF;AACA,QAAM,YAAY;AAElB,QAAM,YAAY,MAAM,IAAI,KAAK,aAAa,IAAI,EAAE,WAAW,MAAM,CAAC;AACtE,SAAO,iBAAiB,WAAW,IAAI,OAAO,QAAQ,cAAc;AACtE;AAEA,eAAsB,eACpB,KACA,MAC8B;AAC9B,QAAM,SAAS,KAAK;AACpB,QAAM,SAAS,KAAK;AAEpB,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,SAAS,kBAAkB,yCAAyC;AAAA,EAChF;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,UAAU;AACzC,UAAM,IAAI,SAAS,kBAAkB,yCAAyC;AAAA,EAChF;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK,SAAS,QAAQ,MAAM;AACnD,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,SAAO,eAAe,IAAI;AAC5B;AAEA,IAAM,gBAAgB,CAAC,YAAY,aAAa,YAAY;AAE5D,eAAsB,cACpB,KACA,MACwB;AACxB,QAAM,YAAY,KAAK,UAAU;AACjC,QAAM,QAAQ,YAAY,KAAK,OAAO,EAAE;AAExC,MAAI,CAAC,cAAc,SAAS,SAAmB,GAAG;AAChD,UAAM,IAAI;AAAA,MACR;AAAA,MACA,0BAA0B,cAAc,KAAK,IAAI,CAAC;AAAA,IACpD;AAAA,EACF;AACA,QAAM,SAAS;AAEf,QAAM,OAAO,MAAM,IAAI,KAAK,QAAQ,QAAQ,KAAK;AACjD,SAAO,gBAAgB,MAAM,IAAI,KAAK;AACxC;AAEA,IAAM,kBAAkB,CAAC,OAAO,KAAK;AAErC,eAAsB,mBACpB,KACA,MACyB;AACzB,QAAM,OAAO,KAAK;AAClB,QAAM,UAAU,KAAK,QAAQ;AAC7B,QAAM,QAAQ,YAAY,KAAK,OAAO,EAAE;AAExC,MAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,KAAK,WAAW,GAAG;AAC7C,UAAM,IAAI,SAAS,kBAAkB,gDAAgD;AAAA,EACvF;AAGA,MAAI,CAAC,KAAK,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ,GAAG;AAC7C,UAAM,IAAI,SAAS,kBAAkB,gCAAgC;AAAA,EACvE;AAEA,MAAI,CAAC,gBAAgB,SAAS,OAAkB,GAAG;AACjD,UAAM,IAAI;AAAA,MACR;AAAA,MACA,wBAAwB,gBAAgB,KAAK,IAAI,CAAC;AAAA,IACpD;AAAA,EACF;AACA,QAAM,OAAO;AAEb,QAAM,QAAQ,MAAM,IAAI,KAAK,aAAa,MAAM,MAAM,KAAK;AAE3D,SAAO,iBAAiB,OAAO,IAAI,OAAO,QAAQ,IAAI;AACxD;AAEA,eAAsB,iBACpB,KACA,MAC8B;AAC9B,QAAM,OAAO,KAAK;AAElB,MAAI,SAAS,QAAW;AACtB,QAAI,CAAC,MAAM,QAAQ,IAAI,KAAK,CAAC,KAAK,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ,GAAG;AACrE,YAAM,IAAI,SAAS,kBAAkB,gCAAgC;AAAA,IACvE;AAAA,EACF;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK,cAAc,IAA4B;AACtE,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AAEA,SAAO,eAAe,MAAM,IAAI,OAAO,SAAS;AAClD;AAEA,eAAsB,iBACpB,KACA,MACuB;AACvB,QAAM,QAAQ,KAAK;AACnB,QAAM,UAAU,KAAK;AACrB,QAAM,UAAU,KAAK;AACrB,QAAM,YAAY,KAAK;AAEvB,MAAI,CAAC,SAAS,OAAO,UAAU,UAAU;AACvC,UAAM,IAAI,SAAS,kBAAkB,wCAAwC;AAAA,EAC/E;AACA,MAAI,CAAC,WAAW,OAAO,YAAY,UAAU;AAC3C,UAAM,IAAI,SAAS,kBAAkB,0CAA0C;AAAA,EACjF;AAEA,MAAI,OAAiB,CAAC;AACtB,MAAI,YAAY,QAAW;AACzB,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ,GAAG;AAC3E,YAAM,IAAI,SAAS,kBAAkB,gCAAgC;AAAA,IACvE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,iBAAiB,KAAK,IAAI;AAC3C,QAAM,KAAK,YAAY,GAAG,SAAS,IAAI,QAAQ,KAAK;AAEpD,QAAM,WAAW,MAAM,IAAI,KAAK,QAAQ,EAAE;AAC1C,MAAI,UAAU;AACZ,UAAM,IAAI,SAAS,eAAe,wBAAwB,EAAE,EAAE;AAAA,EAChE;AAEA,QAAM,OAAO,MAAM,IAAI,KAAK,WAAW;AAAA,IACrC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,SAAO,eAAe,MAAM,IAAI,OAAO,SAAS;AAClD;AAEA,eAAsB,iBACpB,KACA,MACuB;AACvB,QAAM,KAAK,KAAK;AAChB,QAAM,QAAQ,KAAK;AACnB,QAAM,UAAU,KAAK;AACrB,QAAM,UAAU,KAAK;AAErB,MAAI,CAAC,MAAM,OAAO,OAAO,UAAU;AACjC,UAAM,IAAI,SAAS,kBAAkB,qCAAqC;AAAA,EAC5E;AAEA,MAAI,UAAU,UAAa,YAAY,UAAa,YAAY,QAAW;AACzE,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,MAAI;AACJ,MAAI,YAAY,QAAW;AACzB,QAAI,CAAC,MAAM,QAAQ,OAAO,KAAK,CAAC,QAAQ,MAAM,CAAC,MAAM,OAAO,MAAM,QAAQ,GAAG;AAC3E,YAAM,IAAI,SAAS,kBAAkB,gCAAgC;AAAA,IACvE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,WAAW,MAAM,IAAI,KAAK,QAAQ,EAAE;AAC1C,MAAI,CAAC,UAAU;AACb,UAAM,IAAI,SAAS,kBAAkB,mBAAmB,EAAE,EAAE;AAAA,EAC9D;AAEA,MAAI,UAAU,UAAa,UAAU,SAAS,OAAO;AACnD,UAAM,oBAAoB,MAAM,IAAI,KAAK,aAAa,IAAI,EAAE,WAAW,KAAK,CAAC;AAC7E,QAAI,kBAAkB,SAAS,GAAG;AAChC,YAAM,IAAI;AAAA,QACR;AAAA,QACA,2BAA2B,kBAAkB,MAAM;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,UAAyB,CAAC;AAChC,MAAI,UAAU,OAAW,SAAQ,QAAQ;AACzC,MAAI,YAAY,OAAW,SAAQ,UAAU;AAC7C,MAAI,SAAS,OAAW,SAAQ,OAAO;AAEvC,QAAM,UAAU,MAAM,IAAI,KAAK,WAAW,IAAI,OAAO;AACrD,SAAO,eAAe,SAAS,IAAI,OAAO,SAAS;AACrD;AAEA,eAAsB,iBACpB,KACA,MACyB;AACzB,QAAM,KAAK,KAAK;AAEhB,MAAI,CAAC,MAAM,OAAO,OAAO,UAAU;AACjC,UAAM,IAAI,SAAS,kBAAkB,qCAAqC;AAAA,EAC5E;AAEA,QAAM,UAAU,MAAM,IAAI,KAAK,WAAW,EAAE;AAC5C,SAAO,EAAE,QAAQ;AACnB;AAEA,IAAM,mBAAmB,CAAC,SAAS,SAAS,UAAU;AAEtD,eAAsB,gBACpB,KACA,MAC4B;AAC5B,QAAM,MAAM,KAAK;AACjB,QAAM,OAAO,KAAK;AAClB,QAAM,QAAQ,YAAY,KAAK,OAAO,GAAG;AACzC,QAAM,SAAS,aAAa,KAAK,QAAQ,CAAC;AAE1C,QAAM,SAAqB,CAAC;AAC5B,MAAI,IAAK,QAAO,MAAM;AACtB,MAAI,KAAM,QAAO,OAAO;AAExB,SAAO,IAAI,KAAK,UAAU,QAAQ,EAAE,OAAO,OAAO,CAAC;AACrD;AAEA,eAAsB,mBACpB,KACA,MAC0B;AAC1B,QAAM,QAAQ,KAAK;AACnB,QAAM,WAAW,KAAK;AACtB,QAAM,YAAY,KAAK;AACvB,QAAM,MAAM,KAAK;AACjB,QAAM,OAAO,KAAK;AAElB,MAAI,CAAC,MAAM,QAAQ,KAAK,GAAG;AACzB,UAAM,IAAI,SAAS,kBAAkB,wCAAwC;AAAA,EAC/E;AAEA,MAAI,aAAa,UAAa,CAAC,iBAAiB,SAAS,QAA2B,GAAG;AACrF,UAAM,IAAI;AAAA,MACR;AAAA,MACA,4BAA4B,iBAAiB,KAAK,IAAI,CAAC;AAAA,IACzD;AAAA,EACF;AAEA,MAAI,aAAa,cAAc,CAAC,IAAI,cAAc;AAChD,UAAM,IAAI,SAAS,kBAAkB,iDAAiD;AAAA,EACxF;AAEA,QAAM,UAA0B,CAAC;AACjC,MAAI,SAAU,SAAQ,WAAW;AACjC,MAAI,cAAc,OAAW,SAAQ,YAAY;AACjD,MAAI,IAAK,SAAQ,MAAM;AACvB,MAAI,KAAM,SAAQ,OAAO;AAEzB,SAAO,IAAI,KAAK,aAAa,OAAmB,OAAO;AACzD;AAEA,eAAsB,iBACpB,KACA,MAC6B;AAC7B,QAAM,MAAM,KAAK;AAEjB,MAAI,CAAC,MAAM,QAAQ,GAAG,GAAG;AACvB,UAAM,IAAI,SAAS,kBAAkB,sCAAsC;AAAA,EAC7E;AAEA,QAAM,SAAS,MAAM,IAAI,MAAM,WAAW,GAAe;AAGzD,QAAM,WAA+B,CAAC;AACtC,aAAW,CAAC,IAAI,MAAM,KAAK,QAAQ;AACjC,aAAS,EAAE,IAAI;AAAA,EACjB;AACA,SAAO;AACT;AAEO,SAAS,iBAAiB,OAAuB;AACtD,QAAM,YAAY,MACf,YAAY,EACZ,QAAQ,iBAAiB,EAAE,EAC3B,QAAQ,QAAQ,GAAG,EACnB,QAAQ,OAAO,GAAG,EAClB,QAAQ,YAAY,EAAE;AAEzB,SAAO,aAAa;AACtB;AAEA,eAAsB,aACpB,KACA,MACA,MACqB;AACrB,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO,aAAa,KAAK,IAAI;AAAA,IAC/B,KAAK;AACH,aAAO,cAAc,KAAK,IAAI;AAAA,IAChC,KAAK;AACH,aAAO,mBAAmB,KAAK,IAAI;AAAA,IACrC,KAAK;AACH,aAAO,eAAe,KAAK,IAAI;AAAA,IACjC,KAAK;AACH,aAAO,cAAc,KAAK,IAAI;AAAA,IAChC,KAAK;AACH,aAAO,mBAAmB,KAAK,IAAI;AAAA,IACrC,KAAK;AACH,aAAO,iBAAiB,KAAK,IAAI;AAAA,IACnC,KAAK;AACH,aAAO,iBAAiB,KAAK,IAAI;AAAA,IACnC,KAAK;AACH,aAAO,iBAAiB,KAAK,IAAI;AAAA,IACnC,KAAK;AACH,aAAO,iBAAiB,KAAK,IAAI;AAAA,IACnC,KAAK;AACH,aAAO,gBAAgB,KAAK,IAAI;AAAA,IAClC,KAAK;AACH,aAAO,mBAAmB,KAAK,IAAI;AAAA,IACrC,KAAK;AACH,aAAO,iBAAiB,KAAK,IAAI;AAAA,IACnC;AACE,YAAM,IAAI,SAAS,kBAAkB,iBAAiB,IAAI,EAAE;AAAA,EAChE;AACF;;;AJ1dA,IAAM,eAAe;AAAA,EACnB,QAAQ;AAAA,IACN,MAAM;AAAA,IACN,YAAY;AAAA,MACV,OAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,iBAAiB;AAAA,QACf,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,UAAU,CAAC,OAAO;AAAA,EACpB;AAAA,EAEA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,MACV,IAAI;AAAA,QACF,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,IAAI;AAAA,EACjB;AAAA,EAEA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,YAAY;AAAA,MACV,IAAI;AAAA,QACF,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,MAAM,CAAC,MAAM,OAAO,MAAM;AAAA,QAC1B,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,iBAAiB;AAAA,QACf,MAAM;AAAA,QACN,SAAS;AAAA,QACT,aACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,UAAU,CAAC,IAAI;AAAA,EACjB;AAAA,EAEA,WAAW;AAAA,IACT,MAAM;AAAA,IACN,YAAY;AAAA,MACV,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,UAAU,CAAC,UAAU,QAAQ;AAAA,EAC/B;AAAA,EAEA,UAAU;AAAA,IACR,MAAM;AAAA,IACN,YAAY;AAAA,MACV,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,MAAM,CAAC,aAAa,YAAY;AAAA,QAChC,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EAEA,gBAAgB;AAAA,IACd,MAAM;AAAA,IACN,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,UAAU;AAAA,QACV,aAAa;AAAA,MACf;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,MAAM,CAAC,OAAO,KAAK;AAAA,QACnB,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,MAAM;AAAA,EACnB;AAAA,EAEA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,aAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EAEA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,OAAO;AAAA,QACL,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,SAAS,CAAC;AAAA,QACV,aAAa;AAAA,MACf;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,SAAS,SAAS;AAAA,EAC/B;AAAA,EAEA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,IAAI;AAAA,QACF,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,IAAI;AAAA,EACjB;AAAA,EAEA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,IAAI;AAAA,QACF,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,UAAU,CAAC,IAAI;AAAA,EACjB;AAAA,EAEA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,MACV,KAAK;AAAA,QACH,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,MACA,OAAO;AAAA,QACL,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,MACA,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AAAA,EAEA,eAAe;AAAA,IACb,MAAM;AAAA,IACN,YAAY;AAAA,MACV,OAAO;AAAA,QACL,MAAM;AAAA,QACN,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,aAAa;AAAA,MACf;AAAA,MACA,UAAU;AAAA,QACR,MAAM;AAAA,QACN,MAAM,CAAC,SAAS,SAAS,UAAU;AAAA,QACnC,SAAS;AAAA,QACT,aACE;AAAA,MACJ;AAAA,MACA,WAAW;AAAA,QACT,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS;AAAA,QACT,aACE;AAAA,MACJ;AAAA,MACA,KAAK;AAAA,QACH,MAAM;AAAA,QACN,aACE;AAAA,MACJ;AAAA,MACA,MAAM;AAAA,QACJ,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,OAAO;AAAA,EACpB;AAAA,EAEA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,MACV,KAAK;AAAA,QACH,MAAM;AAAA,QACN,OAAO,EAAE,MAAM,SAAS;AAAA,QACxB,aACE;AAAA,MACJ;AAAA,IACF;AAAA,IACA,UAAU,CAAC,KAAK;AAAA,EAClB;AACF;AAGO,SAAS,mBAAmB,cAA+B;AAChE,QAAM,QAAgB;AAAA,IACpB;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,aAAa,aAAa;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,aAAa,aAAa;AAAA,IAC5B;AAAA,EACF;AAEA,MAAI,cAAc;AAChB,UAAM,QAAQ;AAAA,MACZ,MAAM;AAAA,MACN,aAAa;AAAA,MACb,aAAa,aAAa;AAAA,IAC5B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAEO,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA;AAAA,EAER,YAAY,SAA2B;AACrC,SAAK,MAAM;AAAA,MACT,MAAM,QAAQ;AAAA,MACd,OAAO,QAAQ;AAAA,MACf,cAAc,QAAQ;AAAA,IACxB;AAEA,SAAK,SAAS,IAAI;AAAA,MAChB,EAAE,MAAM,QAAQ,SAAS,QAAQ;AAAA,MACjC,EAAE,cAAc,EAAE,OAAO,CAAC,EAAE,EAAE;AAAA,IAChC;AAEA,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA,EAGQ,gBAAsB;AAC5B,SAAK,OAAO,kBAAkB,wBAAwB,aAAa;AAAA,MACjE,OAAO,mBAAmB,KAAK,IAAI,YAAY;AAAA,IACjD,EAAE;AAEF,SAAK,OAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACtE,YAAM,EAAE,MAAM,WAAW,KAAK,IAAI,QAAQ;AAE1C,UAAI;AACF,cAAM,SAAS,MAAM,aAAa,KAAK,KAAK,MAAM,QAAQ,CAAC,CAAC;AAC5D,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,CAAC;AAAA,QACnE;AAAA,MACF,SAAS,OAAO;AACd,YAAI,iBAAiB,UAAU;AAC7B,iBAAO;AAAA,YACL,SAAS;AAAA,cACP,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,MAAM,WAAW,CAAC,EAAE;AAAA,YAC3D;AAAA,YACA,SAAS;AAAA,UACX;AAAA,QACF;AACA,cAAM,WAAW,IAAI;AAAA,UACnB;AAAA,UACA,iBAAiB,QAAQ,MAAM,UAAU;AAAA,QAC3C;AACA,eAAO;AAAA,UACL,SAAS;AAAA,YACP,EAAE,MAAM,QAAQ,MAAM,KAAK,UAAU,SAAS,WAAW,CAAC,EAAE;AAAA,UAC9D;AAAA,UACA,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,MAAM,kBAAoD;AAE9D,UAAM,YAAY,mBACd,iBAAiB,IACjB,IAAI,qBAAqB;AAE7B,UAAM,KAAK,OAAO,QAAQ,SAAsD;AAAA,EAClF;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,KAAK,OAAO,MAAM;AAAA,EAC1B;AACF;;;APtcA,eAAsB,aACpB,WACA,UAAwB,CAAC,GACH;AACtB,QAAM,EAAE,OAAAC,SAAQ,MAAM,kBAAkB,WAAW,IAAI;AAEvD,QAAM,aAAaC,MAAK,WAAW,WAAW;AAG9C,MAAI;AACF,UAAMC,QAAO,UAAU;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAGA,QAAM,gBAAgB,MAAMC,UAAS,YAAY,OAAO;AACxD,QAAM,SAAS,UAAU,aAAa;AAGtC,QAAM,aAAa,OAAO,QAAQ,QAAQ;AAC1C,QAAM,qBAAqBF,MAAK,WAAW,UAAU;AACrD,QAAM,YAAY,OAAO,OAAO,QAAQ;AACxC,QAAM,oBAAoBA,MAAK,WAAW,SAAS;AAEnD,QAAM,QAAQ,IAAI,SAAS,oBAAoB,iBAAiB;AAChE,QAAM,YAAY,IAAI;AAAA,IACpB,OAAO,WAAW,WAAW,SAAS,UAClC,OAAO,UAAU,UAAU,QAC3B;AAAA,EACN;AAGA,QAAM,MAAM,KAAK;AAGjB,QAAM,aAAa,MAAM,MAAM,cAAc;AAC7C,QAAM,QAAQ,WAAW;AAEzB,WAAS,IAAI,GAAG,IAAI,WAAW,QAAQ,KAAK;AAC1C,UAAM,KAAK,WAAW,CAAC;AAEvB,QAAI,CAAC,qBAAqB,OAAO,EAAE,GAAG;AACpC,YAAM,OAAO,MAAM,MAAM,QAAQ,EAAE;AACnC,UAAI,QAAQ,KAAK,SAAS;AACxB,cAAM,SAAS,MAAM,UAAU,MAAM,KAAK,OAAO;AACjD,cAAM,MAAM,eAAe,IAAI,QAAQ,UAAU,QAAQ,CAAC;AAAA,MAC5D;AAAA,IACF;AAEA,QAAI,YAAY;AACd,iBAAW,IAAI,GAAG,KAAK;AAAA,IACzB;AAAA,EACF;AAGA,QAAM,OAAO,IAAI,cAAc;AAC/B,OAAK,cAAc,KAAK;AACxB,OAAK,kBAAkB,SAAS;AAGhC,QAAM,YAAY,IAAI,UAAU;AAAA,IAC9B;AAAA,IACA;AAAA,IACA,cAAc;AAAA,EAChB,CAAC;AAED,QAAM,UAAU,MAAM,gBAAgB;AAGtC,MAAID,QAAO;AACT,QAAI;AACF,YAAM,MAAM,cAAc,OAAO,eAAe;AAE9C,mBAAW,MAAM,YAAY;AAC3B,gBAAM,OAAO,MAAM,MAAM,QAAQ,EAAE;AACnC,cAAI,QAAQ,KAAK,SAAS;AACxB,kBAAM,SAAS,MAAM,UAAU,MAAM,KAAK,OAAO;AACjD,kBAAM,MAAM,eAAe,IAAI,QAAQ,UAAU,QAAQ,CAAC;AAAA,UAC5D;AAAA,QACF;AAAA,MACF,CAAC;AAAA,IACH,SAAS,KAAK;AACZ,cAAQ;AAAA,QACN;AAAA,QACC,IAAc,WAAW;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,MAAM,YAAY;AAChB,YAAM,aAAa;AACnB,YAAM,MAAM;AACZ,YAAM,UAAU,MAAM;AAAA,IACxB;AAAA,IACA,YAAY,MAAM,WAAW;AAAA,IAC7B,WAAW,WAAW;AAAA,EACxB;AACF;AAEA,SAAS,qBAAqB,OAAiB,IAAqB;AAClE,SAAO,MAAM,aAAa,EAAE;AAC9B;;;AY5HA,SAAS,UAAAI,SAAQ,aAAAC,YAAW,SAAAC,cAAa;AACzC,SAAS,QAAAC,OAAM,WAAAC,gBAAe;AA0B9B,eAAsB,WACpB,WACA,UAAsB,CAAC,GACH;AACpB,QAAM,aAAaC,MAAK,WAAW,WAAW;AAG9C,MAAI;AACF,UAAMC,QAAO,UAAU;AAAA,EACzB,QAAQ;AACN,UAAM,IAAI,MAAM,mDAAmD;AAAA,EACrE;AAEA,QAAM,WAAWD,MAAK,WAAW,OAAO;AACxC,QAAM,aAAa,QAAQ,UAAUA,MAAK,UAAU,YAAY;AAEhE,QAAM,QAAQ,IAAI,MAAM,QAAQ;AAEhC,MAAI;AACF,UAAM,QAAQ,MAAM,YAAY;AAChC,UAAM,aAA0B,CAAC;AACjC,UAAM,aAA0B,CAAC;AAGjC,UAAM,kBAAkB,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAGtD,eAAW,QAAQ,OAAO;AACxB,YAAM,aAAa,MAAM,cAAc,KAAK,EAAE;AAC9C,iBAAW,KAAK;AAAA,QACd,IAAI,KAAK;AAAA,QACT,OAAO,KAAK;AAAA,QACZ,UAAU,YAAY,YAAY;AAAA,MACpC,CAAC;AAGD,iBAAW,UAAU,KAAK,eAAe;AACvC,YAAI,gBAAgB,IAAI,MAAM,GAAG;AAC/B,qBAAW,KAAK;AAAA,YACd,QAAQ,KAAK;AAAA,YACb;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAGA,UAAM,OAAO,aAAa,YAAY,UAAU;AAGhD,UAAME,OAAMC,SAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AACpD,UAAMC,WAAU,YAAY,MAAM,OAAO;AAEzC,WAAO;AAAA,MACL;AAAA,MACA,WAAW,WAAW;AAAA,MACtB,WAAW,WAAW;AAAA,MACtB,YAAY,QAAQ,QAAQ;AAAA,IAC9B;AAAA,EACF,UAAE;AACA,UAAM,MAAM;AAAA,EACd;AACF;AAEA,SAAS,aAAa,OAAoB,OAA4B;AACpE,QAAM,YAAY,KAAK,UAAU,KAAK;AACtC,QAAM,YAAY,KAAK,UAAU,KAAK;AAEtC,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,oBAgCW,SAAS;AAAA,oBACT,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiH7B;;;AjBvOA,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,MAAM,EACX,YAAY,iDAAiD,EAC7D,QAAQ,OAAO;AAElB,QACG,QAAQ,MAAM,EACd,YAAY,gCAAgC,EAC5C,SAAS,eAAe,2BAA2B,GAAG,EACtD,OAAO,OAAO,cAAsB;AACnC,QAAM,cAAcC,SAAQ,SAAS;AACrC,QAAM,SAAS,MAAM,YAAY,WAAW;AAE5C,MAAI,OAAO,SAAS;AAClB,YAAQ,IAAI,uBAAuB,WAAW,EAAE;AAChD,YAAQ,IAAI,aAAa,OAAO,UAAU,EAAE;AAC5C,QAAI,OAAO,gBAAgB;AACzB,cAAQ,IAAI,2BAA2B;AAAA,IACzC;AAAA,EACF,OAAO;AACL,QAAI,OAAO,gBAAgB;AACzB,cAAQ,IAAI,oBAAoB,WAAW,EAAE;AAC7C,cAAQ,IAAI,2BAA2B;AAAA,IACzC,OAAO;AACL,cAAQ,IAAI,wBAAwB,OAAO,UAAU,EAAE;AAAA,IACzD;AAAA,EACF;AACF,CAAC;AAEH,QACG,QAAQ,QAAQ,EAChB,YAAY,uBAAuB,EACnC,SAAS,eAAe,sBAAsB,GAAG,EACjD,OAAO,OAAO,cAAsB;AACnC,QAAM,cAAcA,SAAQ,SAAS;AAErC,MAAI;AACF,UAAM,SAAS,MAAM,cAAc,WAAW;AAC9C,YAAQ,IAAI,eAAe;AAC3B,YAAQ,IAAI,YAAY,OAAO,SAAS,EAAE;AAC1C,YAAQ,IAAI,YAAY,OAAO,SAAS,EAAE;AAC1C,YAAQ,IAAI,iBAAiB,OAAO,cAAc,IAAI,OAAO,SAAS,EAAE;AACxE,YAAQ;AAAA,MACN,gBAAgB,OAAO,oBAAoB,KAAK,QAAQ,CAAC,CAAC;AAAA,IAC5D;AAAA,EACF,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,OAAO,EACf,YAAY,qCAAqC,EACjD,SAAS,eAAe,sBAAsB,GAAG,EACjD,OAAO,cAAc,uBAAuB,EAC5C,OAAO,OAAO,WAAmB,YAAgC;AAChE,QAAM,cAAcA,SAAQ,SAAS;AAErC,MAAI;AACF,YAAQ,IAAI,yBAAyB;AAErC,UAAM,SAAS,MAAM,aAAa,aAAa;AAAA,MAC7C,OAAO,QAAQ;AAAA,MACf,YAAY,CAAC,SAAS,UAAU;AAC9B,gBAAQ,OAAO;AAAA,UACb,MAAM,OAAO,IAAI,KAAK;AAAA,QACxB;AACA,YAAI,YAAY,OAAO;AACrB,kBAAQ,IAAI,QAAQ;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC;AAED,YAAQ,IAAI,WAAW,OAAO,SAAS,QAAQ;AAC/C,QAAI,OAAO,YAAY;AACrB,cAAQ,IAAI,8BAA8B;AAAA,IAC5C;AAGA,UAAM,WAAW,YAAY;AAC3B,cAAQ,IAAI,oBAAoB;AAChC,YAAM,OAAO,KAAK;AAClB,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,YAAQ,GAAG,UAAU,QAAQ;AAC7B,YAAQ,GAAG,WAAW,QAAQ;AAG9B,UAAM,IAAI,QAAQ,MAAM;AAAA,IAAC,CAAC;AAAA,EAC5B,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,iBAAiB,QAAQ,MAAM,UAAU;AAAA,IAC3C;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,CAAC;AAEH,QACG,QAAQ,KAAK,EACb,YAAY,8BAA8B,EAC1C,SAAS,eAAe,0BAA0B,GAAG,EACrD,OAAO,uBAAuB,kBAAkB,EAChD,OAAO,UAAU,kCAAkC,EACnD;AAAA,EACC,OAAO,WAAmB,YAAiD;AACzE,UAAM,cAAcA,SAAQ,SAAS;AAErC,QAAI;AACF,YAAM,SAAS,MAAM,WAAW,aAAa;AAAA,QAC3C,QAAQ,QAAQ;AAAA,QAChB,MAAM,QAAQ;AAAA,MAChB,CAAC;AAED,cAAQ;AAAA,QACN,4BAA4B,OAAO,SAAS,WAAW,OAAO,SAAS;AAAA,MACzE;AACA,cAAQ,IAAI,aAAa,OAAO,UAAU,EAAE;AAE5C,UAAI,OAAO,YAAY;AACrB,cAAM,UACJ,QAAQ,aAAa,WACjB,SACA,QAAQ,aAAa,UACnB,UACA;AACR,iBAAS,SAAS,CAAC,OAAO,UAAU,CAAC;AAAA,MACvC;AAAA,IACF,SAAS,OAAO;AACd,cAAQ;AAAA,QACN,iBAAiB,QAAQ,MAAM,UAAU;AAAA,MAC3C;AACA,cAAQ,KAAK,CAAC;AAAA,IAChB;AAAA,EACF;AACF;AAEF,QAAQ,MAAM;","names":["resolve","access","join","join","stringSimilarity","Database","join","join","access","access","readFile","join","readFile","writeFile","mkdir","join","join","mkdir","writeFile","resolve","readFile","watch","join","access","readFile","access","writeFile","mkdir","join","dirname","join","access","mkdir","dirname","writeFile","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../../node_modules/string-similarity/src/index.js","../../src/cli/index.ts","../../src/cli/commands/init.ts","../../src/cli/commands/status.ts","../../src/providers/docstore/cache.ts","../../src/providers/docstore/cache/centrality.ts","../../src/providers/store/resolve.ts","../../src/providers/vector/sqlite.ts","../../src/utils/heap.ts","../../src/utils/math.ts","../../src/cli/commands/serve.ts","../../src/providers/docstore/index.ts","../../src/graph/builder.ts","../../src/graph/traversal.ts","../../src/graph/analysis.ts","../../src/graph/manager.ts","../../src/providers/store/index.ts","../../src/providers/docstore/parser.ts","../../src/providers/docstore/normalize.ts","../../src/providers/docstore/watcher.ts","../../src/providers/docstore/constants.ts","../../src/providers/docstore/links.ts","../../src/providers/docstore/file-operations.ts","../../src/providers/docstore/id.ts","../../src/providers/docstore/reader-registry.ts","../../src/providers/docstore/readers/markdown.ts","../../src/providers/embedding/transformers.ts","../../src/types/provider.ts","../../src/core/graphcore.ts","../../src/mcp/server.ts","../../src/types/config.ts","../../src/index.ts","../../src/mcp/types.ts","../../src/mcp/handlers/search.ts","../../src/mcp/validation.ts","../../src/mcp/truncate.ts","../../src/mcp/transforms.ts","../../src/mcp/handlers/get_node.ts","../../src/mcp/handlers/get_neighbors.ts","../../src/mcp/handlers/find_path.ts","../../src/mcp/handlers/get_hubs.ts","../../src/mcp/handlers/search_by_tags.ts","../../src/mcp/handlers/random_node.ts","../../src/mcp/handlers/create_node.ts","../../src/mcp/handlers/update_node.ts","../../src/mcp/handlers/delete_node.ts","../../src/mcp/handlers/list_nodes.ts","../../src/mcp/handlers/resolve_nodes.ts","../../src/mcp/handlers/nodes_exist.ts","../../src/mcp/handlers/index.ts","../../src/cli/commands/viz.ts"],"sourcesContent":["module.exports = {\n\tcompareTwoStrings:compareTwoStrings,\n\tfindBestMatch:findBestMatch\n};\n\nfunction compareTwoStrings(first, second) {\n\tfirst = first.replace(/\\s+/g, '')\n\tsecond = second.replace(/\\s+/g, '')\n\n\tif (first === second) return 1; // identical or empty\n\tif (first.length < 2 || second.length < 2) return 0; // if either is a 0-letter or 1-letter string\n\n\tlet firstBigrams = new Map();\n\tfor (let i = 0; i < first.length - 1; i++) {\n\t\tconst bigram = first.substring(i, i + 2);\n\t\tconst count = firstBigrams.has(bigram)\n\t\t\t? firstBigrams.get(bigram) + 1\n\t\t\t: 1;\n\n\t\tfirstBigrams.set(bigram, count);\n\t};\n\n\tlet intersectionSize = 0;\n\tfor (let i = 0; i < second.length - 1; i++) {\n\t\tconst bigram = second.substring(i, i + 2);\n\t\tconst count = firstBigrams.has(bigram)\n\t\t\t? firstBigrams.get(bigram)\n\t\t\t: 0;\n\n\t\tif (count > 0) {\n\t\t\tfirstBigrams.set(bigram, count - 1);\n\t\t\tintersectionSize++;\n\t\t}\n\t}\n\n\treturn (2.0 * intersectionSize) / (first.length + second.length - 2);\n}\n\nfunction findBestMatch(mainString, targetStrings) {\n\tif (!areArgsValid(mainString, targetStrings)) throw new Error('Bad arguments: First argument should be a string, second should be an array of strings');\n\t\n\tconst ratings = [];\n\tlet bestMatchIndex = 0;\n\n\tfor (let i = 0; i < targetStrings.length; i++) {\n\t\tconst currentTargetString = targetStrings[i];\n\t\tconst currentRating = compareTwoStrings(mainString, currentTargetString)\n\t\tratings.push({target: currentTargetString, rating: currentRating})\n\t\tif (currentRating > ratings[bestMatchIndex].rating) {\n\t\t\tbestMatchIndex = i\n\t\t}\n\t}\n\t\n\t\n\tconst bestMatch = ratings[bestMatchIndex]\n\t\n\treturn { ratings: ratings, bestMatch: bestMatch, bestMatchIndex: bestMatchIndex };\n}\n\nfunction areArgsValid(mainString, targetStrings) {\n\tif (typeof mainString !== 'string') return false;\n\tif (!Array.isArray(targetStrings)) return false;\n\tif (!targetStrings.length) return false;\n\tif (targetStrings.find( function (s) { return typeof s !== 'string'})) return false;\n\treturn true;\n}\n","#!/usr/bin/env node\n\nimport { Command } from 'commander';\nimport { resolve } from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { initCommand } from './commands/init.js';\nimport { statusCommand } from './commands/status.js';\nimport { serveCommand } from './commands/serve.js';\nimport { vizCommand } from './commands/viz.js';\nimport { VERSION } from '../index.js';\n\nfunction handleCliError(error: unknown): never {\n console.error(error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n}\n\nconst program = new Command();\n\nprogram\n .name('roux')\n .description('Graph Programming Interface for knowledge bases')\n .version(VERSION);\n\nprogram\n .command('init')\n .description('Initialize Roux in a directory')\n .argument('[directory]', 'Directory to initialize', '.')\n .action(async (directory: string) => {\n const resolvedDir = resolve(directory);\n const result = await initCommand(resolvedDir);\n\n if (result.created) {\n console.log(`Initialized Roux in ${resolvedDir}`);\n console.log(` Config: ${result.configPath}`);\n if (result.hooksInstalled) {\n console.log(` Claude hooks: installed`);\n }\n } else {\n if (result.hooksInstalled) {\n console.log(`Upgraded Roux in ${resolvedDir}`);\n console.log(` Claude hooks: installed`);\n } else {\n console.log(`Already initialized: ${result.configPath}`);\n }\n }\n });\n\nprogram\n .command('status')\n .description('Show graph statistics')\n .argument('[directory]', 'Directory to check', '.')\n .action(async (directory: string) => {\n const resolvedDir = resolve(directory);\n\n try {\n const result = await statusCommand(resolvedDir);\n console.log('Graph Status:');\n console.log(` Nodes: ${result.nodeCount}`);\n console.log(` Edges: ${result.edgeCount}`);\n console.log(` Embeddings: ${result.embeddingCount}/${result.nodeCount}`);\n console.log(\n ` Coverage: ${(result.embeddingCoverage * 100).toFixed(1)}%`\n );\n } catch (error) {\n handleCliError(error);\n }\n });\n\nprogram\n .command('serve')\n .description('Start MCP server with file watching')\n .argument('[directory]', 'Directory to serve', '.')\n .option('--no-watch', 'Disable file watching')\n .action(async (directory: string, options: { watch: boolean }) => {\n const resolvedDir = resolve(directory);\n\n try {\n console.log('Starting Roux server...');\n\n const handle = await serveCommand(resolvedDir, {\n watch: options.watch,\n onProgress: (current, total) => {\n process.stdout.write(\n `\\r[${current}/${total}] Generating embeddings...`\n );\n if (current === total) {\n console.log(' Done.');\n }\n },\n });\n\n console.log(`Serving ${handle.nodeCount} nodes`);\n if (handle.isWatching) {\n console.log('Watching for file changes...');\n }\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log('\\nShutting down...');\n await handle.stop();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n // Keep process alive\n await new Promise(() => {});\n } catch (error) {\n handleCliError(error);\n }\n });\n\nprogram\n .command('viz')\n .description('Generate graph visualization')\n .argument('[directory]', 'Directory to visualize', '.')\n .option('-o, --output <path>', 'Output file path')\n .option('--open', 'Open in browser after generation')\n .action(\n async (directory: string, options: { output?: string; open?: boolean }) => {\n const resolvedDir = resolve(directory);\n\n try {\n const result = await vizCommand(resolvedDir, {\n output: options.output,\n open: options.open,\n });\n\n console.log(\n `Generated visualization: ${result.nodeCount} nodes, ${result.edgeCount} edges`\n );\n console.log(` Output: ${result.outputPath}`);\n\n if (result.shouldOpen) {\n const openCmd =\n process.platform === 'darwin'\n ? 'open'\n : process.platform === 'win32'\n ? 'start'\n : 'xdg-open';\n execFile(openCmd, [result.outputPath]);\n }\n } catch (error) {\n handleCliError(error);\n }\n }\n );\n\nprogram.parse();\n","import { mkdir, writeFile, readFile, access } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport interface InitResult {\n created: boolean;\n configPath: string;\n hooksInstalled: boolean;\n}\n\ninterface McpServerConfig {\n command?: string;\n args?: string[];\n env?: Record<string, string>;\n type?: string;\n url?: string;\n}\n\ninterface McpConfig {\n mcpServers?: Record<string, McpServerConfig>;\n}\n\ninterface ClaudeHook {\n type: string;\n command: string;\n}\n\ninterface ClaudeHookEntry {\n matcher: string;\n hooks: ClaudeHook[];\n}\n\ninterface ClaudeSettings {\n hooks?: {\n PreToolUse?: ClaudeHookEntry[];\n PostToolUse?: ClaudeHookEntry[];\n [key: string]: ClaudeHookEntry[] | undefined;\n };\n [key: string]: unknown;\n}\n\nconst DEFAULT_CONFIG = `providers:\n store:\n type: docstore\n`;\n\nconst ROUX_MCP_CONFIG: McpServerConfig = {\n command: 'npx',\n args: ['roux', 'serve', '.'],\n env: {},\n};\n\n/** Marker to identify roux's hook for idempotent updates */\nexport const HOOK_MARKER = 'roux-enforce-mcp';\n\n/** Hook command that rejects direct file operations on .md files */\nconst ENFORCE_MCP_HOOK_COMMAND = `node -e \"/* ${HOOK_MARKER} */ const d=JSON.parse(require('fs').readFileSync(0,'utf8'));const p=d.tool_input?.file_path||'';if(p.endsWith('.md')){console.error('Use mcp__roux__* tools for markdown files instead of Read/Edit/Write');process.exit(2)}\"`;\n\nconst ROUX_HOOK_ENTRY: ClaudeHookEntry = {\n matcher: 'Read|Edit|Write',\n hooks: [{ type: 'command', command: ENFORCE_MCP_HOOK_COMMAND }],\n};\n\nexport async function initCommand(directory: string): Promise<InitResult> {\n const configPath = join(directory, 'roux.yaml');\n const rouxDir = join(directory, '.roux');\n\n // Check if already initialized\n let configExists = false;\n try {\n await access(configPath);\n configExists = true;\n } catch {\n // File doesn't exist\n }\n\n // Create .roux directory regardless\n await mkdir(rouxDir, { recursive: true });\n\n // Create/update MCP config\n await updateMcpConfig(directory);\n\n // Determine store type and update Claude hooks if docstore\n const storeType = await getStoreType(directory, configExists);\n let hooksInstalled = false;\n if (storeType === 'docstore') {\n hooksInstalled = await updateClaudeSettings(directory);\n }\n\n if (configExists) {\n return { created: false, configPath, hooksInstalled };\n }\n\n // Create roux.yaml with minimal defaults\n await writeFile(configPath, DEFAULT_CONFIG, 'utf-8');\n return { created: true, configPath, hooksInstalled };\n}\n\nasync function updateMcpConfig(directory: string): Promise<void> {\n const mcpPath = join(directory, '.mcp.json');\n\n let config: McpConfig = {};\n\n // Try to read existing config\n try {\n const content = await readFile(mcpPath, 'utf-8');\n config = JSON.parse(content) as McpConfig;\n } catch {\n // File doesn't exist or invalid JSON — start fresh\n }\n\n // Ensure mcpServers object exists\n if (!config.mcpServers) {\n config.mcpServers = {};\n }\n\n // Add/update roux entry\n config.mcpServers.roux = ROUX_MCP_CONFIG;\n\n // Write back\n await writeFile(mcpPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n}\n\nasync function getStoreType(\n directory: string,\n configExists: boolean\n): Promise<string> {\n if (!configExists) {\n return 'docstore'; // Default\n }\n\n try {\n const configPath = join(directory, 'roux.yaml');\n const content = await readFile(configPath, 'utf-8');\n // Simple YAML parsing for store type\n const typeMatch = content.match(/store:\\s*\\n\\s*type:\\s*(\\w+)/);\n if (typeMatch?.[1]) {\n return typeMatch[1];\n }\n } catch {\n // Can't read config, assume default\n }\n\n return 'docstore';\n}\n\n/** Returns true if hooks were installed, false if skipped */\nasync function updateClaudeSettings(directory: string): Promise<boolean> {\n const claudeDir = join(directory, '.claude');\n const settingsPath = join(claudeDir, 'settings.json');\n\n // Ensure .claude directory exists\n await mkdir(claudeDir, { recursive: true });\n\n let config: ClaudeSettings = {};\n let existingContent: string | null = null;\n\n // Try to read existing settings\n try {\n existingContent = await readFile(settingsPath, 'utf-8');\n config = JSON.parse(existingContent) as ClaudeSettings;\n } catch (err) {\n if (existingContent !== null) {\n // File exists but is malformed JSON — don't touch it\n return false;\n }\n // File doesn't exist — start fresh\n }\n\n // Ensure hooks.PreToolUse array exists\n if (!config.hooks) {\n config.hooks = {};\n }\n if (!config.hooks.PreToolUse) {\n config.hooks.PreToolUse = [];\n }\n\n // Check if our hook already exists\n const hasRouxHook = config.hooks.PreToolUse.some((entry) =>\n entry.hooks?.some((h) => h.command?.includes(HOOK_MARKER))\n );\n\n if (hasRouxHook) {\n return false; // Already installed, don't duplicate\n }\n\n // Add our hook\n config.hooks.PreToolUse.push(ROUX_HOOK_ENTRY);\n\n // Write back\n await writeFile(settingsPath, JSON.stringify(config, null, 2) + '\\n', 'utf-8');\n return true;\n}\n","import { access } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { Cache } from '../../providers/docstore/cache.js';\nimport { SqliteVectorIndex } from '../../providers/vector/sqlite.js';\n\nexport interface StatusResult {\n nodeCount: number;\n edgeCount: number;\n embeddingCount: number;\n embeddingCoverage: number;\n}\n\nexport async function statusCommand(directory: string): Promise<StatusResult> {\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n const cacheDir = join(directory, '.roux');\n const cache = new Cache(cacheDir);\n const vectorProvider = new SqliteVectorIndex(cacheDir);\n\n try {\n const stats = cache.getStats();\n const embeddingCount = vectorProvider.getEmbeddingCount();\n\n const embeddingCoverage =\n stats.nodeCount === 0 ? 1 : embeddingCount / stats.nodeCount;\n\n return {\n nodeCount: stats.nodeCount,\n edgeCount: stats.edgeCount,\n embeddingCount,\n embeddingCoverage,\n };\n } finally {\n cache.close();\n vectorProvider.close();\n }\n}\n","import Database from 'better-sqlite3';\nimport { join } from 'node:path';\nimport { mkdirSync } from 'node:fs';\nimport type { Node, SourceRef } from '../../types/node.js';\nimport type {\n TagMode,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../../types/provider.js';\nimport {\n type CentralityRecord,\n initCentralitySchema,\n storeCentrality,\n getCentrality,\n resolveNames,\n} from './cache/index.js';\n\nexport type { CentralityRecord };\n\ninterface NodeRow {\n id: string;\n title: string;\n content: string;\n tags: string;\n outgoing_links: string;\n properties: string;\n source_type: string;\n source_path: string;\n source_modified: number;\n}\n\n\nexport class Cache {\n private db: Database.Database;\n\n constructor(cacheDir: string) {\n mkdirSync(cacheDir, { recursive: true });\n const dbPath = join(cacheDir, 'cache.db');\n this.db = new Database(dbPath);\n this.db.pragma('journal_mode = WAL');\n this.initSchema();\n }\n\n private initSchema(): void {\n // Create nodes table (core schema owned by Cache)\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS nodes (\n id TEXT PRIMARY KEY,\n title TEXT,\n content TEXT,\n tags TEXT,\n outgoing_links TEXT,\n properties TEXT,\n source_type TEXT,\n source_path TEXT,\n source_modified INTEGER\n );\n\n CREATE INDEX IF NOT EXISTS idx_nodes_source_path ON nodes(source_path);\n `);\n\n // Delegate centrality schema to its module\n initCentralitySchema(this.db);\n\n // Enable foreign key enforcement for cascade deletes\n this.db.pragma('foreign_keys = ON');\n }\n\n getTableNames(): string[] {\n const rows = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table'\")\n .all() as Array<{ name: string }>;\n return rows.map((r) => r.name);\n }\n\n upsertNode(\n node: Node,\n sourceType: string,\n sourcePath: string,\n sourceModified: number\n ): void {\n const stmt = this.db.prepare(`\n INSERT INTO nodes (id, title, content, tags, outgoing_links, properties, source_type, source_path, source_modified)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n title = excluded.title,\n content = excluded.content,\n tags = excluded.tags,\n outgoing_links = excluded.outgoing_links,\n properties = excluded.properties,\n source_type = excluded.source_type,\n source_path = excluded.source_path,\n source_modified = excluded.source_modified\n `);\n\n stmt.run(\n node.id,\n node.title,\n node.content,\n JSON.stringify(node.tags),\n JSON.stringify(node.outgoingLinks),\n JSON.stringify(node.properties),\n sourceType,\n sourcePath,\n sourceModified\n );\n }\n\n getNode(id: string): Node | null {\n const row = this.db\n .prepare('SELECT * FROM nodes WHERE id = ?')\n .get(id) as NodeRow | undefined;\n\n if (!row) return null;\n return this.rowToNode(row);\n }\n\n getNodes(ids: string[]): Node[] {\n if (ids.length === 0) return [];\n\n // Fetch all nodes then order by input ids\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT * FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as NodeRow[];\n\n const nodeMap = new Map<string, Node>();\n for (const row of rows) {\n nodeMap.set(row.id, this.rowToNode(row));\n }\n\n // Return in requested order\n const result: Node[] = [];\n for (const id of ids) {\n const node = nodeMap.get(id);\n if (node) result.push(node);\n }\n return result;\n }\n\n deleteNode(id: string): void {\n this.db.prepare('DELETE FROM nodes WHERE id = ?').run(id);\n }\n\n getAllNodes(): Node[] {\n const rows = this.db.prepare('SELECT * FROM nodes').all() as NodeRow[];\n return rows.map((row) => this.rowToNode(row));\n }\n\n searchByTags(tags: string[], mode: TagMode, limit?: number): Node[] {\n if (tags.length === 0) return [];\n\n const lowerTags = tags.map((t) => t.toLowerCase());\n\n // Build SQL query with tag filtering in the database\n // Tags are stored as JSON array, so we use json_each to search\n let query: string;\n const params: unknown[] = [];\n\n if (mode === 'any') {\n // Match nodes that have ANY of the specified tags\n const tagConditions = lowerTags.map(() =>\n \"EXISTS (SELECT 1 FROM json_each(tags) WHERE LOWER(json_each.value) = ?)\"\n ).join(' OR ');\n query = `SELECT * FROM nodes WHERE ${tagConditions}`;\n params.push(...lowerTags);\n } else {\n // Match nodes that have ALL of the specified tags\n const tagConditions = lowerTags.map(() =>\n \"EXISTS (SELECT 1 FROM json_each(tags) WHERE LOWER(json_each.value) = ?)\"\n ).join(' AND ');\n query = `SELECT * FROM nodes WHERE ${tagConditions}`;\n params.push(...lowerTags);\n }\n\n if (limit !== undefined) {\n query += ' LIMIT ?';\n params.push(limit);\n }\n\n const rows = this.db.prepare(query).all(...params) as NodeRow[];\n return rows.map((row) => this.rowToNode(row));\n }\n\n getModifiedTime(sourcePath: string): number | null {\n const row = this.db\n .prepare('SELECT source_modified FROM nodes WHERE source_path = ?')\n .get(sourcePath) as { source_modified: number } | undefined;\n\n return row?.source_modified ?? null;\n }\n\n getNodeByPath(sourcePath: string): Node | null {\n // Case-insensitive path lookup for cross-platform compatibility\n const row = this.db\n .prepare('SELECT * FROM nodes WHERE LOWER(source_path) = LOWER(?)')\n .get(sourcePath) as NodeRow | undefined;\n\n if (!row) return null;\n return this.rowToNode(row);\n }\n\n getAllTrackedPaths(): Set<string> {\n const rows = this.db\n .prepare('SELECT source_path FROM nodes')\n .all() as Array<{ source_path: string }>;\n\n return new Set(rows.map((r) => r.source_path));\n }\n\n updateSourcePath(id: string, newPath: string): void {\n this.db\n .prepare('UPDATE nodes SET source_path = ? WHERE id = ?')\n .run(newPath, id);\n }\n\n getIdByPath(sourcePath: string): string | null {\n const row = this.db\n .prepare('SELECT id FROM nodes WHERE source_path = ?')\n .get(sourcePath) as { id: string } | undefined;\n\n return row?.id ?? null;\n }\n\n resolveTitles(ids: string[]): Map<string, string> {\n if (ids.length === 0) return new Map();\n\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT id, title FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as Array<{ id: string; title: string }>;\n\n const result = new Map<string, string>();\n for (const row of rows) {\n result.set(row.id, row.title);\n }\n return result;\n }\n\n nodesExist(ids: string[]): Map<string, boolean> {\n if (ids.length === 0) return new Map();\n\n const placeholders = ids.map(() => '?').join(',');\n const rows = this.db\n .prepare(`SELECT id FROM nodes WHERE id IN (${placeholders})`)\n .all(...ids) as Array<{ id: string }>;\n\n const existingIds = new Set(rows.map((r) => r.id));\n const result = new Map<string, boolean>();\n for (const id of ids) {\n result.set(id, existingIds.has(id));\n }\n return result;\n }\n\n listNodes(filter: ListFilter, options?: ListOptions): ListNodesResult {\n const limit = Math.min(options?.limit ?? 100, 1000);\n const offset = options?.offset ?? 0;\n\n // Build query dynamically based on filters\n const conditions: string[] = [];\n const params: unknown[] = [];\n\n if (filter.tag) {\n // Case-insensitive tag match - tags stored as JSON array\n conditions.push(\"EXISTS (SELECT 1 FROM json_each(tags) WHERE LOWER(json_each.value) = LOWER(?))\");\n params.push(filter.tag);\n }\n\n if (filter.path) {\n // Filter by source_path since node id is now a stable nanoid\n conditions.push(\"LOWER(source_path) LIKE '%' || LOWER(?) || '%'\");\n params.push(filter.path);\n }\n\n const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';\n\n // Get total count of matching nodes (without limit/offset)\n const countQuery = `SELECT COUNT(*) as count FROM nodes ${whereClause}`;\n const countRow = this.db.prepare(countQuery).get(...params) as { count: number };\n const total = countRow.count;\n\n // Get paginated results\n const query = `SELECT id, title FROM nodes ${whereClause} LIMIT ? OFFSET ?`;\n const rows = this.db.prepare(query).all(...params, limit, offset) as Array<{ id: string; title: string }>;\n\n const nodes = rows.map((row) => ({ id: row.id, title: row.title }));\n return { nodes, total };\n }\n\n resolveNodes(names: string[], options?: ResolveOptions): ResolveResult[] {\n if (names.length === 0) return [];\n\n const strategy = options?.strategy ?? 'fuzzy';\n const threshold = options?.threshold ?? 0.7;\n\n // Build filter without undefined values\n const filter: ListFilter = {};\n if (options?.tag) filter.tag = options.tag;\n if (options?.path) filter.path = options.path;\n\n // Get candidate nodes (applying tag/path filters)\n const { nodes: candidates } = this.listNodes(filter, { limit: 1000 });\n\n return resolveNames(names, candidates, { strategy, threshold });\n }\n\n updateOutgoingLinks(nodeId: string, links: string[]): void {\n this.db\n .prepare('UPDATE nodes SET outgoing_links = ? WHERE id = ?')\n .run(JSON.stringify(links), nodeId);\n }\n\n storeCentrality(\n nodeId: string,\n pagerank: number,\n inDegree: number,\n outDegree: number,\n computedAt: number\n ): void {\n storeCentrality(this.db, nodeId, pagerank, inDegree, outDegree, computedAt);\n }\n\n getCentrality(nodeId: string): CentralityRecord | null {\n return getCentrality(this.db, nodeId);\n }\n\n getStats(): { nodeCount: number; edgeCount: number } {\n const nodeCount = this.db\n .prepare('SELECT COUNT(*) as count FROM nodes')\n .get() as { count: number };\n\n // Sum all in_degree values to get edge count\n const edgeSum = this.db\n .prepare('SELECT SUM(in_degree) as total FROM centrality')\n .get() as { total: number | null };\n\n return {\n nodeCount: nodeCount.count,\n edgeCount: edgeSum.total ?? 0,\n };\n }\n\n clear(): void {\n this.db.exec('DELETE FROM centrality');\n this.db.exec('DELETE FROM nodes');\n }\n\n close(): void {\n this.db.close();\n }\n\n private rowToNode(row: NodeRow): Node {\n const sourceRef: SourceRef = {\n type: row.source_type as SourceRef['type'],\n path: row.source_path,\n lastModified: new Date(row.source_modified),\n };\n\n return {\n id: row.id,\n title: row.title,\n content: row.content,\n tags: JSON.parse(row.tags) as string[],\n outgoingLinks: JSON.parse(row.outgoing_links) as string[],\n properties: JSON.parse(row.properties) as Record<string, unknown>,\n sourceRef,\n };\n }\n}\n","import type Database from 'better-sqlite3';\n\nexport interface CentralityRecord {\n pagerank: number;\n inDegree: number;\n outDegree: number;\n computedAt: number;\n}\n\nexport function initCentralitySchema(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS centrality (\n node_id TEXT PRIMARY KEY,\n pagerank REAL,\n in_degree INTEGER,\n out_degree INTEGER,\n computed_at INTEGER,\n FOREIGN KEY (node_id) REFERENCES nodes(id) ON DELETE CASCADE\n )\n `);\n}\n\ninterface CentralityRow {\n node_id: string;\n pagerank: number;\n in_degree: number;\n out_degree: number;\n computed_at: number;\n}\n\nexport function storeCentrality(\n db: Database.Database,\n nodeId: string,\n pagerank: number,\n inDegree: number,\n outDegree: number,\n computedAt: number\n): void {\n db.prepare(\n `\n INSERT INTO centrality (node_id, pagerank, in_degree, out_degree, computed_at)\n VALUES (?, ?, ?, ?, ?)\n ON CONFLICT(node_id) DO UPDATE SET\n pagerank = excluded.pagerank,\n in_degree = excluded.in_degree,\n out_degree = excluded.out_degree,\n computed_at = excluded.computed_at\n `\n ).run(nodeId, pagerank, inDegree, outDegree, computedAt);\n}\n\nexport function getCentrality(\n db: Database.Database,\n nodeId: string\n): CentralityRecord | null {\n const row = db\n .prepare('SELECT * FROM centrality WHERE node_id = ?')\n .get(nodeId) as CentralityRow | undefined;\n\n if (!row) return null;\n\n return {\n pagerank: row.pagerank,\n inDegree: row.in_degree,\n outDegree: row.out_degree,\n computedAt: row.computed_at,\n };\n}\n","import stringSimilarity from 'string-similarity';\nimport type { ResolveResult, ResolveStrategy } from '../../types/provider.js';\n\nexport interface Candidate {\n id: string;\n title: string;\n}\n\nexport interface ResolveMatchOptions {\n strategy: ResolveStrategy;\n threshold: number;\n}\n\n/**\n * Resolve names to node IDs using exact or fuzzy matching.\n * Semantic strategy returns no matches (handled at higher level with embeddings).\n */\nexport function resolveNames(\n names: string[],\n candidates: Candidate[],\n options: ResolveMatchOptions\n): ResolveResult[] {\n if (names.length === 0) return [];\n\n const { strategy, threshold } = options;\n\n if (candidates.length === 0) {\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n const candidateTitles = candidates.map((c) => c.title.toLowerCase());\n const titleToId = new Map<string, string>();\n for (const c of candidates) {\n titleToId.set(c.title.toLowerCase(), c.id);\n }\n\n return names.map((query): ResolveResult => {\n const queryLower = query.toLowerCase();\n\n if (strategy === 'exact') {\n const matchedId = titleToId.get(queryLower);\n if (matchedId) {\n return { query, match: matchedId, score: 1 };\n }\n return { query, match: null, score: 0 };\n }\n\n if (strategy === 'fuzzy') {\n const result = stringSimilarity.findBestMatch(queryLower, candidateTitles);\n const bestMatch = result.bestMatch;\n\n if (bestMatch.rating >= threshold) {\n const matchedId = titleToId.get(bestMatch.target)!;\n return { query, match: matchedId, score: bestMatch.rating };\n }\n return { query, match: null, score: 0 };\n }\n\n // Semantic strategy - not supported at this level\n return { query, match: null, score: 0 };\n });\n}\n","import Database from 'better-sqlite3';\nimport type { Database as DatabaseType } from 'better-sqlite3';\nimport { join } from 'node:path';\nimport type { VectorIndex, VectorSearchResult } from '../../types/provider.js';\nimport { MinHeap } from '../../utils/heap.js';\nimport { cosineDistance } from '../../utils/math.js';\n\nexport class SqliteVectorIndex implements VectorIndex {\n private db: DatabaseType;\n private ownsDb: boolean;\n private modelMismatchWarned = false;\n\n constructor(pathOrDb: string | DatabaseType) {\n if (typeof pathOrDb === 'string') {\n this.db = new Database(join(pathOrDb, 'vectors.db'));\n this.ownsDb = true;\n } else {\n this.db = pathOrDb;\n this.ownsDb = false;\n }\n this.init();\n }\n\n private init(): void {\n this.db.exec(`\n CREATE TABLE IF NOT EXISTS vectors (\n id TEXT PRIMARY KEY,\n model TEXT NOT NULL,\n vector BLOB NOT NULL\n )\n `);\n }\n\n async store(id: string, vector: number[], model: string): Promise<void> {\n if (vector.length === 0) {\n throw new Error('Cannot store empty vector');\n }\n for (const v of vector) {\n if (!Number.isFinite(v)) {\n throw new Error(`Invalid vector value: ${v}`);\n }\n }\n\n // Validate dimension consistency (exclude self for overwrites)\n const existing = this.db\n .prepare('SELECT LENGTH(vector) / 4 as dim FROM vectors WHERE id != ? LIMIT 1')\n .get(id) as { dim: number } | undefined;\n\n if (existing && existing.dim !== vector.length) {\n throw new Error(\n `Dimension mismatch: cannot store ${vector.length}-dim vector, existing vectors have ${existing.dim} dimensions`\n );\n }\n\n const blob = Buffer.from(new Float32Array(vector).buffer);\n this.db\n .prepare(\n `INSERT OR REPLACE INTO vectors (id, model, vector) VALUES (?, ?, ?)`\n )\n .run(id, model, blob);\n }\n\n async search(vector: number[], limit: number): Promise<VectorSearchResult[]> {\n if (vector.length === 0) {\n throw new Error('Cannot search with empty vector');\n }\n for (const v of vector) {\n if (!Number.isFinite(v)) {\n throw new Error(`Invalid vector value: ${v}`);\n }\n }\n if (limit <= 0) {\n return [];\n }\n\n // Warn once if index contains mixed models\n if (!this.modelMismatchWarned) {\n const models = this.db\n .prepare('SELECT DISTINCT model FROM vectors')\n .all() as Array<{ model: string }>;\n if (models.length > 1) {\n console.warn(\n `Vector index contains embeddings from multiple models: ${models.map((m) => m.model).join(', ')}. ` +\n 'Search results may be unreliable. Re-sync to re-embed all documents with current model.'\n );\n this.modelMismatchWarned = true;\n }\n }\n\n const queryVec = new Float32Array(vector);\n const stmt = this.db.prepare('SELECT id, vector FROM vectors');\n\n // Max-heap by distance: largest distance at root for efficient eviction\n const heap = new MinHeap<VectorSearchResult>(\n (a, b) => b.distance - a.distance\n );\n\n let dimensionChecked = false;\n\n for (const row of stmt.iterate() as IterableIterator<{\n id: string;\n vector: Buffer;\n }>) {\n // Check dimension mismatch against first stored vector\n if (!dimensionChecked) {\n const storedDim = row.vector.byteLength / 4;\n if (vector.length !== storedDim) {\n throw new Error(\n `Dimension mismatch: query has ${vector.length} dimensions, stored vectors have ${storedDim}`\n );\n }\n dimensionChecked = true;\n }\n\n const storedVec = new Float32Array(\n row.vector.buffer,\n row.vector.byteOffset,\n row.vector.byteLength / 4\n );\n const distance = cosineDistance(queryVec, storedVec);\n\n if (heap.size() < limit) {\n heap.push({ id: row.id, distance });\n } else if (distance < heap.peek()!.distance) {\n heap.pop();\n heap.push({ id: row.id, distance });\n }\n }\n\n // Extract and sort by distance ascending\n return heap.toArray().sort((a, b) => a.distance - b.distance);\n }\n\n async delete(id: string): Promise<void> {\n this.db.prepare('DELETE FROM vectors WHERE id = ?').run(id);\n }\n\n async getModel(id: string): Promise<string | null> {\n const row = this.db\n .prepare('SELECT model FROM vectors WHERE id = ?')\n .get(id) as { model: string } | undefined;\n return row?.model ?? null;\n }\n\n hasEmbedding(id: string): boolean {\n const row = this.db\n .prepare('SELECT 1 FROM vectors WHERE id = ?')\n .get(id);\n return row !== undefined;\n }\n\n /** For testing: get table names */\n getTableNames(): string[] {\n const rows = this.db\n .prepare(\"SELECT name FROM sqlite_master WHERE type='table'\")\n .all() as Array<{ name: string }>;\n return rows.map((r) => r.name);\n }\n\n /** For testing: get vector blob size */\n getVectorBlobSize(id: string): number | null {\n const row = this.db\n .prepare('SELECT LENGTH(vector) as size FROM vectors WHERE id = ?')\n .get(id) as { size: number } | undefined;\n return row?.size ?? null;\n }\n\n /** Get total number of stored embeddings */\n getEmbeddingCount(): number {\n const row = this.db\n .prepare('SELECT COUNT(*) as count FROM vectors')\n .get() as { count: number };\n return row.count;\n }\n\n close(): void {\n if (this.ownsDb) {\n this.db.close();\n }\n }\n}\n","type Comparator<T> = (a: T, b: T) => number;\n\n/**\n * Min-heap implementation for top-k selection.\n * The smallest element (by comparator) is always at the root.\n */\nexport class MinHeap<T> {\n private data: T[] = [];\n private compare: Comparator<T>;\n\n constructor(comparator: Comparator<T>) {\n this.compare = comparator;\n }\n\n size(): number {\n return this.data.length;\n }\n\n peek(): T | undefined {\n return this.data[0];\n }\n\n push(value: T): void {\n this.data.push(value);\n this.bubbleUp(this.data.length - 1);\n }\n\n pop(): T | undefined {\n if (this.data.length === 0) return undefined;\n if (this.data.length === 1) return this.data.pop();\n\n const min = this.data[0];\n this.data[0] = this.data.pop()!;\n this.bubbleDown(0);\n return min;\n }\n\n toArray(): T[] {\n return [...this.data];\n }\n\n private bubbleUp(index: number): void {\n while (index > 0) {\n const parentIndex = Math.floor((index - 1) / 2);\n if (this.compare(this.data[index]!, this.data[parentIndex]!) >= 0) {\n break;\n }\n this.swap(index, parentIndex);\n index = parentIndex;\n }\n }\n\n private bubbleDown(index: number): void {\n const length = this.data.length;\n while (true) {\n const leftChild = 2 * index + 1;\n const rightChild = 2 * index + 2;\n let smallest = index;\n\n if (leftChild < length && this.compare(this.data[leftChild]!, this.data[smallest]!) < 0) {\n smallest = leftChild;\n }\n if (rightChild < length && this.compare(this.data[rightChild]!, this.data[smallest]!) < 0) {\n smallest = rightChild;\n }\n\n if (smallest === index) break;\n\n this.swap(index, smallest);\n index = smallest;\n }\n }\n\n private swap(i: number, j: number): void {\n const temp = this.data[i]!;\n this.data[i] = this.data[j]!;\n this.data[j] = temp;\n }\n}\n","type VectorLike = ArrayLike<number>;\n\n/**\n * Compute cosine similarity between two vectors.\n * Returns value in range [-1, 1] where 1 = identical direction, 0 = orthogonal, -1 = opposite.\n * Returns 0 if either vector has zero magnitude.\n * Throws if either vector is empty or dimensions differ.\n */\nexport function cosineSimilarity(a: VectorLike, b: VectorLike): number {\n if (a.length === 0 || b.length === 0) {\n throw new Error('Cannot compute similarity for empty vector');\n }\n if (a.length !== b.length) {\n throw new Error(`Dimension mismatch: ${a.length} vs ${b.length}`);\n }\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n for (let i = 0; i < a.length; i++) {\n dotProduct += a[i]! * b[i]!;\n normA += a[i]! * a[i]!;\n normB += b[i]! * b[i]!;\n }\n\n if (normA === 0 || normB === 0) return 0;\n\n return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));\n}\n\n/**\n * Compute cosine distance between two vectors.\n * Returns value in range [0, 2] where 0 = identical direction, 1 = orthogonal, 2 = opposite.\n * Returns 1 if either vector has zero magnitude.\n */\nexport function cosineDistance(a: VectorLike, b: VectorLike): number {\n const similarity = cosineSimilarity(a, b);\n if (similarity === 0 && (isZeroVector(a) || isZeroVector(b))) {\n return 1;\n }\n return 1 - similarity;\n}\n\nfunction isZeroVector(v: VectorLike): boolean {\n for (let i = 0; i < v.length; i++) {\n if (v[i] !== 0) return false;\n }\n return true;\n}\n","import { access, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport { DocStore } from '../../providers/docstore/index.js';\nimport { TransformersEmbedding } from '../../providers/embedding/transformers.js';\nimport { GraphCoreImpl } from '../../core/graphcore.js';\nimport { McpServer, type TransportFactory } from '../../mcp/server.js';\nimport { DEFAULT_NAMING, type RouxConfig } from '../../types/config.js';\n\nexport interface ServeOptions {\n watch?: boolean;\n transportFactory?: TransportFactory;\n onProgress?: (current: number, total: number) => void;\n}\n\nexport interface ServeHandle {\n stop: () => Promise<void>;\n isWatching: boolean;\n nodeCount: number;\n}\n\nexport async function serveCommand(\n directory: string,\n options: ServeOptions = {}\n): Promise<ServeHandle> {\n const { watch = true, transportFactory, onProgress } = options;\n\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n // Load config\n const configContent = await readFile(configPath, 'utf-8');\n const config = parseYaml(configContent) as RouxConfig;\n\n // Create providers\n const sourcePath = config.source?.path ?? '.';\n const resolvedSourcePath = join(directory, sourcePath);\n const cachePath = config.cache?.path ?? '.roux';\n const resolvedCachePath = join(directory, cachePath);\n\n const store = new DocStore({ sourceRoot: resolvedSourcePath, cacheDir: resolvedCachePath });\n const embeddingModel =\n config.providers?.embedding?.type === 'local'\n ? config.providers.embedding.model\n : undefined;\n const embedding = new TransformersEmbedding(\n embeddingModel ? { model: embeddingModel } : {}\n );\n\n // Create GraphCore and register providers (store.onRegister calls sync)\n const core = new GraphCoreImpl();\n await core.registerStore(store);\n await core.registerEmbedding(embedding);\n\n // Generate embeddings for nodes without them\n const allNodeIds = await store.getAllNodeIds();\n const total = allNodeIds.length;\n\n for (let i = 0; i < allNodeIds.length; i++) {\n const id = allNodeIds[i]!;\n\n if (!hasExistingEmbedding(store, id)) {\n const node = await store.getNode(id);\n if (node && node.content) {\n const vector = await embedding.embed(node.content);\n await store.storeEmbedding(id, vector, embedding.modelId());\n }\n }\n\n if (onProgress) {\n onProgress(i + 1, total);\n }\n }\n\n // Resolve naming conventions\n const naming = { ...DEFAULT_NAMING, ...config.naming };\n\n // Start MCP server\n const mcpServer = new McpServer({\n core,\n store,\n hasEmbedding: true,\n naming,\n });\n\n try {\n await mcpServer.start(transportFactory);\n } catch (err) {\n // Clean up providers on MCP server start failure\n await core.destroy();\n throw err;\n }\n\n // Start file watcher if enabled\n if (watch) {\n try {\n await store.startWatching(async (changedIds) => {\n // Generate embeddings for changed nodes\n for (const id of changedIds) {\n try {\n const node = await store.getNode(id);\n if (node && node.content) {\n const vector = await embedding.embed(node.content);\n await store.storeEmbedding(id, vector, embedding.modelId());\n }\n } catch (err) {\n // Log warning but continue processing other changed files\n console.warn(\n 'Failed to generate embedding for',\n id,\n ':',\n (err as Error).message || 'Unknown error'\n );\n }\n }\n });\n } catch (err) {\n console.warn(\n 'File watching disabled:',\n (err as Error).message || 'Unknown error'\n );\n }\n }\n\n return {\n stop: async () => {\n await core.destroy();\n await mcpServer.close();\n },\n isWatching: store.isWatching(),\n nodeCount: allNodeIds.length,\n };\n}\n\nfunction hasExistingEmbedding(store: DocStore, id: string): boolean {\n return store.hasEmbedding(id);\n}\n","import { writeFile, mkdir, rm, stat } from 'node:fs/promises';\nimport { mkdirSync } from 'node:fs';\nimport { join, relative, dirname, extname } from 'node:path';\nimport type { Node, NodeUpdates } from '../../types/node.js';\nimport type {\n TagMode,\n VectorIndex,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n CentralityMetrics,\n} from '../../types/provider.js';\nimport { StoreProvider } from '../store/index.js';\nimport { Cache } from './cache.js';\nimport { SqliteVectorIndex } from '../vector/sqlite.js';\nimport {\n parseMarkdown,\n extractWikiLinks,\n normalizeId,\n serializeToMarkdown,\n} from './parser.js';\nimport { FileWatcher, type FileEventType } from './watcher.js';\nimport {\n normalizeWikiLink,\n buildFilenameIndex,\n resolveLinks,\n} from './links.js';\nimport {\n getFileMtime,\n validatePathWithinSource,\n collectFiles,\n readFileContent,\n} from './file-operations.js';\nimport { ReaderRegistry } from './reader-registry.js';\nimport type { FileContext } from './types.js';\nimport { MarkdownReader } from './readers/index.js';\nimport { generateId } from './id.js';\n\n/**\n * Create a registry with default readers pre-registered.\n * Returns a new instance each call.\n */\nfunction createDefaultRegistry(): ReaderRegistry {\n const registry = new ReaderRegistry();\n registry.register(new MarkdownReader());\n return registry;\n}\n\nexport interface DocStoreOptions {\n sourceRoot: string;\n cacheDir: string;\n id?: string;\n vectorIndex?: VectorIndex;\n registry?: ReaderRegistry;\n /** Optional FileWatcher instance. If provided, DocStore uses it instead of creating one. */\n fileWatcher?: FileWatcher;\n}\n\nexport class DocStore extends StoreProvider {\n readonly id: string;\n private cache: Cache;\n private sourceRoot: string;\n private ownsVectorIndex: boolean;\n private registry: ReaderRegistry;\n\n private fileWatcher: FileWatcher | null = null;\n private onChangeCallback: ((changedIds: string[]) => void) | undefined;\n\n constructor(options: DocStoreOptions) {\n const {\n sourceRoot,\n cacheDir,\n id = 'docstore',\n vectorIndex,\n registry,\n fileWatcher,\n } = options;\n\n const ownsVector = !vectorIndex;\n // Ensure cacheDir exists before SqliteVectorIndex tries to open a DB inside it\n if (!vectorIndex) mkdirSync(cacheDir, { recursive: true });\n const vi = vectorIndex ?? new SqliteVectorIndex(cacheDir);\n super({ vectorIndex: vi });\n\n this.id = id;\n this.sourceRoot = sourceRoot;\n this.cache = new Cache(cacheDir);\n this.ownsVectorIndex = ownsVector;\n this.registry = registry ?? createDefaultRegistry();\n this.fileWatcher = fileWatcher ?? null;\n }\n\n async sync(): Promise<void> {\n // Pause watcher during sync to avoid processing our own writes\n if (this.fileWatcher?.isWatching()) {\n this.fileWatcher.pause();\n }\n\n try {\n const extensions = this.registry.getExtensions();\n const currentPaths = await collectFiles(this.sourceRoot, extensions);\n const trackedPaths = this.cache.getAllTrackedPaths();\n\n // Track IDs seen during this sync for duplicate detection\n const seenIds = new Map<string, string>(); // id -> first file path\n\n // Process new/modified files\n for (const filePath of currentPaths) {\n try {\n const mtime = await getFileMtime(filePath);\n const cachedMtime = this.cache.getModifiedTime(filePath);\n\n if (cachedMtime === null || mtime > cachedMtime) {\n const { node, needsIdWrite, newMtime } = await this.parseAndMaybeWriteId(filePath, mtime);\n\n // Check for duplicate ID\n const existingPath = seenIds.get(node.id);\n if (existingPath) {\n console.warn(\n `Duplicate ID ${node.id} found in ${filePath} (first seen in ${existingPath}):`,\n new Error('Skipping duplicate')\n );\n continue;\n }\n\n seenIds.set(node.id, filePath);\n\n // Use new mtime if we wrote back, otherwise original\n const finalMtime = needsIdWrite ? (newMtime ?? mtime) : mtime;\n this.cache.upsertNode(node, 'file', filePath, finalMtime);\n } else {\n // Even if not modified, track the ID for duplicate detection\n const existingNode = this.cache.getNodeByPath(filePath);\n if (existingNode) {\n const existingPath = seenIds.get(existingNode.id);\n if (existingPath) {\n console.warn(\n `Duplicate ID ${existingNode.id} found in ${filePath} (first seen in ${existingPath}):`,\n new Error('Skipping duplicate')\n );\n // Remove from cache since we're skipping this one\n this.cache.deleteNode(existingNode.id);\n } else {\n seenIds.set(existingNode.id, filePath);\n }\n }\n }\n } catch (err) {\n // File may have been deleted between readdir and stat — skip silently\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n continue;\n }\n // All other errors (parse failures, read errors): log and skip file\n // Graceful degradation - one bad file shouldn't crash the entire sync\n console.warn(`Failed to process file ${filePath}:`, err);\n continue;\n }\n }\n\n // Remove deleted files\n const currentSet = new Set(currentPaths);\n for (const tracked of trackedPaths) {\n if (!currentSet.has(tracked)) {\n const node = this.cache.getNodeByPath(tracked);\n if (node) {\n this.cache.deleteNode(node.id);\n }\n }\n }\n\n // Resolve wiki-links after all nodes are cached\n this.resolveAllLinks();\n\n // Rebuild graph from all nodes\n await this.syncGraph();\n } finally {\n // Always resume watcher, even if sync fails\n if (this.fileWatcher?.isWatching()) {\n this.fileWatcher.resume();\n }\n }\n }\n\n async createNode(node: Node): Promise<void> {\n // Use node.id as file path, not as the stable ID\n const normalizedPath = normalizeId(node.id);\n validatePathWithinSource(this.sourceRoot, normalizedPath);\n\n const existingByPath = this.cache.getNodeByPath(join(this.sourceRoot, normalizedPath));\n if (existingByPath) {\n throw new Error(`Node already exists: ${normalizedPath}`);\n }\n\n const filePath = join(this.sourceRoot, normalizedPath);\n const dir = dirname(filePath);\n await mkdir(dir, { recursive: true });\n\n // Generate fresh stable ID\n const stableId = generateId();\n\n const rawLinks = extractWikiLinks(node.content);\n const parsed = {\n id: stableId,\n title: node.title,\n tags: node.tags,\n properties: node.properties,\n content: node.content,\n rawLinks,\n };\n const markdown = serializeToMarkdown(parsed);\n await writeFile(filePath, markdown, 'utf-8');\n\n // Extract wikilinks from content\n let outgoingLinks = node.outgoingLinks;\n if (node.content && (!outgoingLinks || outgoingLinks.length === 0)) {\n outgoingLinks = rawLinks.map((link) => normalizeWikiLink(link));\n }\n\n const mtime = await getFileMtime(filePath);\n const createdNode: Node = {\n ...node,\n id: stableId,\n outgoingLinks,\n sourceRef: {\n type: 'file',\n path: filePath,\n lastModified: new Date(mtime),\n },\n };\n this.cache.upsertNode(createdNode, 'file', filePath, mtime);\n\n // Resolve wikilinks and rebuild graph\n this.resolveAllLinks();\n await this.syncGraph();\n }\n\n async updateNode(id: string, updates: NodeUpdates): Promise<void> {\n // Try to find node by ID directly, by normalized path, or by source path\n let existing = this.cache.getNode(id);\n if (!existing) {\n const normalizedId = normalizeId(id);\n existing = this.cache.getNode(normalizedId);\n }\n if (!existing && (id.includes('.') || id.includes('/'))) {\n const fullPath = join(this.sourceRoot, normalizeId(id));\n existing = this.cache.getNodeByPath(fullPath);\n }\n if (!existing) {\n throw new Error(`Node not found: ${id}`);\n }\n\n // Strip id from updates if present - original ID must be preserved\n const { ...safeUpdates } = updates;\n\n // Derive outgoingLinks from content (new or existing)\n const contentForLinks = safeUpdates.content ?? existing.content;\n const rawLinks = extractWikiLinks(contentForLinks);\n const outgoingLinks = rawLinks.map((link) => normalizeWikiLink(link));\n\n const updated: Node = {\n ...existing,\n ...safeUpdates,\n outgoingLinks,\n id: existing.id, // Preserve original ID\n };\n\n // Get file path from sourceRef (stable ID means path may differ from id)\n const filePath = existing.sourceRef?.path ?? join(this.sourceRoot, existing.id);\n\n const parsed = {\n id: existing.id, // Write the stable ID back to frontmatter\n title: updated.title,\n tags: updated.tags,\n properties: updated.properties,\n content: updated.content,\n rawLinks,\n };\n const markdown = serializeToMarkdown(parsed);\n await writeFile(filePath, markdown, 'utf-8');\n\n const mtime = await getFileMtime(filePath);\n this.cache.upsertNode(updated, 'file', filePath, mtime);\n\n // Resolve wikilinks and rebuild graph\n this.resolveAllLinks();\n await this.syncGraph();\n }\n\n async deleteNode(id: string): Promise<void> {\n // Try to find node by ID directly, by normalized path, or by source path\n let existing = this.cache.getNode(id);\n if (!existing) {\n const normalizedId = normalizeId(id);\n existing = this.cache.getNode(normalizedId);\n }\n if (!existing && (id.includes('.') || id.includes('/'))) {\n const fullPath = join(this.sourceRoot, normalizeId(id));\n existing = this.cache.getNodeByPath(fullPath);\n }\n if (!existing) {\n throw new Error(`Node not found: ${id}`);\n }\n\n // Get file path from sourceRef\n const filePath = existing.sourceRef?.path ?? join(this.sourceRoot, existing.id);\n await rm(filePath);\n this.cache.deleteNode(existing.id);\n if (this.vectorIndex) await this.vectorIndex.delete(existing.id);\n\n // Rebuild graph without deleted node\n await this.syncGraph();\n }\n\n async getNode(id: string): Promise<Node | null> {\n // Try exact ID first (stable IDs are case-sensitive)\n let node = this.cache.getNode(id);\n if (node) return node;\n\n // Fall back to normalized ID for path-based lookups\n const normalizedId = normalizeId(id);\n if (normalizedId !== id) {\n node = this.cache.getNode(normalizedId);\n if (node) return node;\n }\n\n // Try lookup by source path (for backwards compatibility with path-based queries)\n if (id.includes('.') || id.includes('/')) {\n const fullPath = join(this.sourceRoot, normalizedId);\n node = this.cache.getNodeByPath(fullPath);\n }\n\n return node;\n }\n\n async getNodes(ids: string[]): Promise<Node[]> {\n // Use getNode for each to leverage path-based fallback\n const results: Node[] = [];\n for (const id of ids) {\n const node = await this.getNode(id);\n if (node) results.push(node);\n }\n return results;\n }\n\n async getAllNodeIds(): Promise<string[]> {\n const nodes = this.cache.getAllNodes();\n return nodes.map((n) => n.id);\n }\n\n async searchByTags(tags: string[], mode: TagMode, limit?: number): Promise<Node[]> {\n return this.cache.searchByTags(tags, mode, limit);\n }\n\n async resolveTitles(ids: string[]): Promise<Map<string, string>> {\n return this.cache.resolveTitles(ids);\n }\n\n async listNodes(\n filter: ListFilter,\n options?: ListOptions\n ): Promise<ListNodesResult> {\n return this.cache.listNodes(filter, options);\n }\n\n async resolveNodes(\n names: string[],\n options?: ResolveOptions\n ): Promise<ResolveResult[]> {\n // For exact and fuzzy, delegate to cache\n const strategy = options?.strategy ?? 'fuzzy';\n if (strategy === 'exact' || strategy === 'fuzzy') {\n return this.cache.resolveNodes(names, options);\n }\n\n // Semantic strategy: use vector search\n // This requires embedding provider which DocStore doesn't have direct access to\n // Return unmatched for now - GraphCore will handle semantic with embedding provider\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n async nodesExist(ids: string[]): Promise<Map<string, boolean>> {\n const result = new Map<string, boolean>();\n for (const id of ids) {\n const node = await this.getNode(id);\n // Use normalized ID as key for consistency\n result.set(normalizeId(id), node !== null);\n }\n return result;\n }\n\n hasEmbedding(id: string): boolean {\n if (!this.vectorIndex) return false;\n return this.vectorIndex.hasEmbedding(id);\n }\n\n close(): void {\n this.stopWatching();\n this.cache.close();\n if (this.ownsVectorIndex && this.vectorIndex && 'close' in this.vectorIndex) {\n (this.vectorIndex as { close: () => void }).close();\n }\n }\n\n // Lifecycle hooks\n\n async onRegister(): Promise<void> {\n await this.sync();\n }\n\n async onUnregister(): Promise<void> {\n this.close();\n }\n\n startWatching(onChange?: (changedIds: string[]) => void): Promise<void> {\n if (this.fileWatcher?.isWatching()) {\n throw new Error('Already watching. Call stopWatching() first.');\n }\n\n this.onChangeCallback = onChange;\n\n if (!this.fileWatcher) {\n this.fileWatcher = new FileWatcher({\n root: this.sourceRoot,\n extensions: this.registry.getExtensions(),\n onBatch: (events) => this.handleWatcherBatch(events),\n });\n }\n\n return this.fileWatcher.start();\n }\n\n stopWatching(): void {\n if (this.fileWatcher) {\n this.fileWatcher.stop();\n }\n }\n\n isWatching(): boolean {\n return this.fileWatcher?.isWatching() ?? false;\n }\n\n private async handleWatcherBatch(events: Map<string, FileEventType>): Promise<void> {\n // Pause watcher during batch processing to avoid triggering events from ID writebacks\n this.fileWatcher?.pause();\n\n const processedIds: string[] = [];\n\n try {\n for (const [pathId, event] of events) {\n // pathId is the normalized file path from the watcher (e.g., \"folder/file.md\")\n const filePath = join(this.sourceRoot, pathId);\n\n try {\n if (event === 'unlink') {\n // Look up node by source path since pathId is a file path, not a stable ID\n const existing = this.cache.getNodeByPath(filePath);\n if (existing) {\n this.cache.deleteNode(existing.id);\n if (this.vectorIndex) {\n try {\n await this.vectorIndex.delete(existing.id);\n } catch (vectorErr) {\n console.warn(`Vector delete failed for ${pathId}:`, vectorErr);\n }\n }\n processedIds.push(existing.id);\n }\n } else {\n // add or change - use parseAndMaybeWriteId to ensure stable IDs\n const mtime = await getFileMtime(filePath);\n const { node, newMtime } = await this.parseAndMaybeWriteId(filePath, mtime);\n // Use new mtime if ID was written back, otherwise original\n const finalMtime = newMtime ?? mtime;\n\n // Check if there's an existing node with a different ID for this path\n // (can happen if file was modified externally and lost its ID)\n const existingByPath = this.cache.getNodeByPath(filePath);\n if (existingByPath && existingByPath.id !== node.id) {\n // Delete the old node to avoid duplicates\n this.cache.deleteNode(existingByPath.id);\n if (this.vectorIndex) {\n try {\n await this.vectorIndex.delete(existingByPath.id);\n } catch {\n // Ignore vector delete failures for cleanup\n }\n }\n }\n\n this.cache.upsertNode(node, 'file', filePath, finalMtime);\n processedIds.push(node.id);\n }\n } catch (err) {\n console.warn(`Failed to process file change for ${pathId}:`, err);\n }\n }\n\n // Resolve wiki-links and rebuild graph after processing all changes\n if (processedIds.length > 0) {\n this.resolveAllLinks();\n await this.syncGraph();\n }\n\n // Call callback if provided\n if (this.onChangeCallback && processedIds.length > 0) {\n this.onChangeCallback(processedIds);\n }\n } finally {\n // Resume watcher after batch processing\n this.fileWatcher?.resume();\n }\n }\n\n private resolveAllLinks(): void {\n const nodes = this.cache.getAllNodes();\n\n // Build title-based index for wikilink resolution\n // buildFilenameIndex indexes by title (primary) and filename (fallback)\n const filenameIndex = buildFilenameIndex(nodes);\n const validNodeIds = new Set(nodes.map((n) => n.id));\n\n // Build path-to-ID mapping for partial path resolution (e.g., [[folder/note]])\n const pathToId = new Map<string, string>();\n for (const node of nodes) {\n if (node.sourceRef?.path) {\n const relativePath = relative(this.sourceRoot, node.sourceRef.path);\n const normalizedPath = normalizeId(relativePath);\n pathToId.set(normalizedPath, node.id);\n }\n }\n\n // Resolve links for each node\n for (const node of nodes) {\n const resolvedIds = resolveLinks(\n node.outgoingLinks,\n filenameIndex,\n validNodeIds\n );\n\n // Second pass: resolve any remaining paths (like \"folder/target.md\") to stable IDs\n const finalIds = resolvedIds.map((link) => {\n // If already a valid stable ID, keep it\n if (validNodeIds.has(link)) {\n return link;\n }\n // Try path-based lookup for partial paths\n const stableId = pathToId.get(link);\n return stableId ?? link;\n });\n\n if (finalIds.some((r, i) => r !== node.outgoingLinks[i])) {\n this.cache.updateOutgoingLinks(node.id, finalIds);\n }\n }\n }\n\n // ── Graph operations (override for path-based lookup) ─────\n\n async getNeighbors(id: string, options: { direction: 'in' | 'out' | 'both'; limit?: number }): Promise<Node[]> {\n // Resolve path to stable ID if needed\n const node = await this.getNode(id);\n if (!node) return [];\n return super.getNeighbors(node.id, options);\n }\n\n async findPath(source: string, target: string): Promise<string[] | null> {\n // Resolve paths to stable IDs\n const sourceNode = await this.getNode(source);\n const targetNode = await this.getNode(target);\n if (!sourceNode || !targetNode) return null;\n return super.findPath(sourceNode.id, targetNode.id);\n }\n\n async getHubs(metric: 'in_degree' | 'out_degree', limit: number): Promise<Array<[string, number]>> {\n return super.getHubs(metric, limit);\n }\n\n // ── StoreProvider abstract method implementations ─────────\n\n protected async loadAllNodes(): Promise<Node[]> {\n return this.cache.getAllNodes();\n }\n\n protected async getNodesByIds(ids: string[]): Promise<Node[]> {\n return this.cache.getNodes(ids);\n }\n\n protected onCentralityComputed(centrality: Map<string, CentralityMetrics>): void {\n const now = Date.now();\n for (const [id, metrics] of centrality) {\n this.cache.storeCentrality(id, 0, metrics.inDegree, metrics.outDegree, now);\n }\n }\n\n /**\n * Parse a file and optionally write a generated ID back if missing.\n * Returns the node (with stable ID) and whether a write occurred.\n */\n private async parseAndMaybeWriteId(\n filePath: string,\n originalMtime: number\n ): Promise<{ node: Node; needsIdWrite: boolean; newMtime?: number }> {\n const content = await readFileContent(filePath);\n const relativePath = relative(this.sourceRoot, filePath);\n const ext = extname(filePath).toLowerCase();\n const actualMtime = new Date(originalMtime);\n\n const context: FileContext = {\n absolutePath: filePath,\n relativePath,\n extension: ext,\n mtime: actualMtime,\n };\n\n const { node, needsIdWrite } = this.registry.parse(content, context);\n\n if (!needsIdWrite) {\n return { node, needsIdWrite: false };\n }\n\n // Generate and write ID back to file\n const newId = generateId();\n const writebackSuccess = await this.writeIdBack(filePath, newId, originalMtime, content);\n\n if (!writebackSuccess) {\n // File was modified during sync, skip caching this file\n // Return the node but signal that caching should be skipped\n console.warn(`File modified during sync, skipping ID writeback: ${filePath}`);\n return { node, needsIdWrite: true };\n }\n\n // Update node with the new stable ID\n const updatedNode: Node = {\n ...node,\n id: newId,\n };\n\n // Get new mtime after write\n const newMtime = await getFileMtime(filePath);\n return { node: updatedNode, needsIdWrite: true, newMtime };\n }\n\n /**\n * Write a generated ID back to file's frontmatter.\n * Returns false if file was modified since originalMtime (race condition).\n */\n private async writeIdBack(\n filePath: string,\n nodeId: string,\n originalMtime: number,\n originalContent: string\n ): Promise<boolean> {\n // Check if file was modified\n const currentStat = await stat(filePath);\n if (currentStat.mtimeMs !== originalMtime) {\n return false;\n }\n\n // Parse and update frontmatter\n const parsed = parseMarkdown(originalContent);\n parsed.id = nodeId;\n const newContent = serializeToMarkdown(parsed);\n await writeFile(filePath, newContent, 'utf-8');\n return true;\n }\n}\n\nexport { Cache } from './cache.js';\nexport {\n parseMarkdown,\n extractWikiLinks,\n normalizeId,\n titleFromPath,\n serializeToMarkdown,\n} from './parser.js';\nexport { FileWatcher, EXCLUDED_DIRS, type FileEventType } from './watcher.js';\nexport {\n getFileMtime,\n validatePathWithinSource,\n collectFiles,\n readFileContent,\n} from './file-operations.js';\nexport type { FormatReader, FileContext } from './types.js';\nexport { ReaderRegistry } from './reader-registry.js';\nexport { MarkdownReader } from './readers/index.js';\nexport { createDefaultRegistry };\n","import { DirectedGraph } from 'graphology';\nimport type { Node } from '../types/node.js';\n\n/**\n * Build a directed graph from an array of nodes.\n * Edges are derived from each node's outgoingLinks.\n * Links to non-existent nodes are ignored.\n */\nexport function buildGraph(nodes: Node[]): DirectedGraph {\n const graph = new DirectedGraph();\n\n // First pass: add all nodes\n const nodeIds = new Set<string>();\n for (const node of nodes) {\n graph.addNode(node.id);\n nodeIds.add(node.id);\n }\n\n // Second pass: add edges (only to existing nodes)\n for (const node of nodes) {\n const seen = new Set<string>();\n for (const target of node.outgoingLinks) {\n // Skip if target doesn't exist or we've already added this edge\n if (!nodeIds.has(target) || seen.has(target)) {\n continue;\n }\n seen.add(target);\n graph.addDirectedEdge(node.id, target);\n }\n }\n\n return graph;\n}\n","import type { DirectedGraph } from 'graphology';\nimport { bidirectional } from 'graphology-shortest-path';\nimport type { NeighborOptions, Metric } from '../types/provider.js';\nimport { MinHeap } from '../utils/heap.js';\n\n/**\n * Get neighbor IDs based on direction.\n * Returns empty array if node doesn't exist.\n * Uses iterator-based traversal for early termination when limit is specified.\n */\nexport function getNeighborIds(\n graph: DirectedGraph,\n id: string,\n options: NeighborOptions\n): string[] {\n if (!graph.hasNode(id)) {\n return [];\n }\n\n const limit = options.limit;\n if (limit !== undefined && limit <= 0) {\n return [];\n }\n\n const maxCount = limit ?? Infinity;\n const direction = options.direction;\n\n // For 'both' direction, use graphology's neighborEntries which deduplicates\n if (direction === 'both') {\n const neighbors: string[] = [];\n for (const entry of graph.neighborEntries(id)) {\n if (neighbors.length >= maxCount) break;\n neighbors.push(entry.neighbor);\n }\n return neighbors;\n }\n\n // For single direction, iterate directly\n const neighbors: string[] = [];\n const iterator =\n direction === 'in'\n ? graph.inNeighborEntries(id)\n : graph.outNeighborEntries(id);\n\n for (const entry of iterator) {\n if (neighbors.length >= maxCount) break;\n neighbors.push(entry.neighbor);\n }\n\n return neighbors;\n}\n\n/**\n * Find shortest path between two nodes.\n * Returns array of node IDs or null if no path exists.\n */\nexport function findPath(\n graph: DirectedGraph,\n source: string,\n target: string\n): string[] | null {\n if (!graph.hasNode(source) || !graph.hasNode(target)) {\n return null;\n }\n\n if (source === target) {\n return [source];\n }\n\n const path = bidirectional(graph, source, target);\n return path;\n}\n\n/**\n * Get top nodes by centrality metric.\n * Returns array of [id, score] tuples sorted descending.\n * Uses min-heap for O(n log k) complexity instead of O(n log n).\n */\nexport function getHubs(\n graph: DirectedGraph,\n metric: Metric,\n limit: number\n): Array<[string, number]> {\n if (limit <= 0) {\n return [];\n }\n\n const heap = new MinHeap<[string, number]>((a, b) => a[1] - b[1]);\n\n graph.forEachNode((id) => {\n const score = metric === 'in_degree' ? graph.inDegree(id) : graph.outDegree(id);\n\n if (heap.size() < limit) {\n heap.push([id, score]);\n } else if (score > heap.peek()![1]) {\n heap.pop();\n heap.push([id, score]);\n }\n });\n\n // Sort by score descending, then by node ID ascending for deterministic tie-breaking\n return heap.toArray().sort((a, b) => {\n const scoreDiff = b[1] - a[1];\n if (scoreDiff !== 0) return scoreDiff;\n return a[0].localeCompare(b[0]);\n });\n}\n","import type { DirectedGraph } from 'graphology';\nimport type { CentralityMetrics } from '../types/provider.js';\n\n/**\n * Compute centrality metrics for all nodes.\n * For MVP, computes in_degree and out_degree only.\n */\nexport function computeCentrality(\n graph: DirectedGraph\n): Map<string, CentralityMetrics> {\n const result = new Map<string, CentralityMetrics>();\n\n graph.forEachNode((id) => {\n result.set(id, {\n inDegree: graph.inDegree(id),\n outDegree: graph.outDegree(id),\n });\n });\n\n return result;\n}\n","import { DirectedGraph } from 'graphology';\nimport type { Node } from '../types/node.js';\nimport type { NeighborOptions, Metric, CentralityMetrics } from '../types/provider.js';\nimport { buildGraph } from './builder.js';\nimport { getNeighborIds, findPath, getHubs } from './traversal.js';\nimport { computeCentrality } from './analysis.js';\n\nexport class GraphNotReadyError extends Error {\n constructor() {\n super('Graph not built. Call build() before querying.');\n this.name = 'GraphNotReadyError';\n }\n}\n\nexport class GraphManager {\n private graph: DirectedGraph | null = null;\n\n /** Build graph and return centrality metrics. Caller stores as needed. */\n build(nodes: Node[]): Map<string, CentralityMetrics> {\n this.graph = buildGraph(nodes);\n return computeCentrality(this.graph);\n }\n\n /** Throws GraphNotReadyError if not built. Returns graph for query use. */\n assertReady(): DirectedGraph {\n if (!this.graph) throw new GraphNotReadyError();\n return this.graph;\n }\n\n isReady(): boolean {\n return this.graph !== null;\n }\n\n getNeighborIds(id: string, options: NeighborOptions): string[] {\n return getNeighborIds(this.assertReady(), id, options);\n }\n\n findPath(source: string, target: string): string[] | null {\n return findPath(this.assertReady(), source, target);\n }\n\n getHubs(metric: Metric, limit: number): Array<[string, number]> {\n return getHubs(this.assertReady(), metric, limit);\n }\n}\n","import type { Node, NodeUpdates } from '../../types/node.js';\nimport type {\n Metric,\n CentralityMetrics,\n VectorIndex,\n VectorSearchResult,\n TagMode,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../../types/provider.js';\nimport type { NeighborOptions } from '../../types/edge.js';\nimport { GraphManager } from '../../graph/manager.js';\nimport { resolveNames } from './resolve.js';\n\nexport interface StoreProviderOptions {\n vectorIndex?: VectorIndex;\n}\n\nexport abstract class StoreProvider {\n protected readonly graphManager = new GraphManager();\n protected readonly vectorIndex: VectorIndex | null;\n\n constructor(options?: StoreProviderOptions) {\n this.vectorIndex = options?.vectorIndex ?? null;\n }\n\n // ── Abstract methods (subclasses MUST implement) ───────────\n\n protected abstract loadAllNodes(): Promise<Node[]>;\n protected abstract getNodesByIds(ids: string[]): Promise<Node[]>;\n abstract createNode(node: Node): Promise<void>;\n abstract updateNode(id: string, updates: NodeUpdates): Promise<void>;\n abstract deleteNode(id: string): Promise<void>;\n abstract getNode(id: string): Promise<Node | null>;\n abstract getNodes(ids: string[]): Promise<Node[]>;\n abstract close(): void;\n\n // ── Graph operations (delegate to GraphManager) ────────────\n\n async getNeighbors(id: string, options: NeighborOptions): Promise<Node[]> {\n if (!this.graphManager.isReady()) return [];\n const neighborIds = this.graphManager.getNeighborIds(id, options);\n return this.getNodesByIds(neighborIds);\n }\n\n async findPath(source: string, target: string): Promise<string[] | null> {\n if (!this.graphManager.isReady()) return null;\n return this.graphManager.findPath(source, target);\n }\n\n async getHubs(metric: Metric, limit: number): Promise<Array<[string, number]>> {\n if (!this.graphManager.isReady()) return [];\n return this.graphManager.getHubs(metric, limit);\n }\n\n // ── Vector operations (delegate to VectorIndex) ────────────\n\n async storeEmbedding(id: string, vector: number[], model: string): Promise<void> {\n if (!this.vectorIndex) throw new Error('No VectorIndex configured');\n return this.vectorIndex.store(id, vector, model);\n }\n\n async searchByVector(vector: number[], limit: number): Promise<VectorSearchResult[]> {\n if (!this.vectorIndex) throw new Error('No VectorIndex configured');\n return this.vectorIndex.search(vector, limit);\n }\n\n // ── Discovery ──────────────────────────────────────────────\n\n async getRandomNode(tags?: string[]): Promise<Node | null> {\n let candidates: Node[];\n if (tags && tags.length > 0) {\n candidates = await this.searchByTags(tags, 'any');\n } else {\n candidates = await this.loadAllNodes();\n }\n if (candidates.length === 0) return null;\n return candidates[Math.floor(Math.random() * candidates.length)]!;\n }\n\n // ── Default implementations (overridable) ──────────────────\n\n async searchByTags(tags: string[], mode: TagMode, limit?: number): Promise<Node[]> {\n const allNodes = await this.loadAllNodes();\n const lowerTags = tags.map(t => t.toLowerCase());\n let results = allNodes.filter(node => {\n const nodeTags = node.tags.map(t => t.toLowerCase());\n return mode === 'any'\n ? lowerTags.some(t => nodeTags.includes(t))\n : lowerTags.every(t => nodeTags.includes(t));\n });\n if (limit !== undefined) results = results.slice(0, limit);\n return results;\n }\n\n async listNodes(filter: ListFilter, options?: ListOptions): Promise<ListNodesResult> {\n let nodes = await this.loadAllNodes();\n if (filter.tag) {\n const lower = filter.tag.toLowerCase();\n nodes = nodes.filter(n => n.tags.some(t => t.toLowerCase() === lower));\n }\n if (filter.path) {\n const lowerPath = filter.path.toLowerCase();\n nodes = nodes.filter(n => n.id.startsWith(lowerPath));\n }\n const total = nodes.length;\n const offset = options?.offset ?? 0;\n const limit = Math.min(options?.limit ?? 100, 1000);\n const sliced = nodes.slice(offset, offset + limit);\n return {\n nodes: sliced.map(n => ({ id: n.id, title: n.title })),\n total,\n };\n }\n\n async nodesExist(ids: string[]): Promise<Map<string, boolean>> {\n if (ids.length === 0) return new Map();\n const found = await this.getNodesByIds(ids);\n const foundIds = new Set(found.map(n => n.id));\n const result = new Map<string, boolean>();\n for (const id of ids) {\n result.set(id, foundIds.has(id));\n }\n return result;\n }\n\n async resolveTitles(ids: string[]): Promise<Map<string, string>> {\n if (ids.length === 0) return new Map();\n const nodes = await this.getNodesByIds(ids);\n const result = new Map<string, string>();\n for (const node of nodes) {\n result.set(node.id, node.title);\n }\n return result;\n }\n\n async resolveNodes(names: string[], options?: ResolveOptions): Promise<ResolveResult[]> {\n const strategy = options?.strategy ?? 'fuzzy';\n if (strategy === 'semantic') {\n return names.map(query => ({ query, match: null, score: 0 }));\n }\n const allNodes = await this.loadAllNodes();\n let candidates = allNodes.map(n => ({ id: n.id, title: n.title }));\n if (options?.tag) {\n const lower = options.tag.toLowerCase();\n const filtered = allNodes.filter(n => n.tags.some(t => t.toLowerCase() === lower));\n candidates = filtered.map(n => ({ id: n.id, title: n.title }));\n }\n if (options?.path) {\n const lowerPath = options.path.toLowerCase();\n candidates = candidates.filter(c => c.id.startsWith(lowerPath));\n }\n return resolveNames(names, candidates, {\n strategy,\n threshold: options?.threshold ?? 0.7,\n });\n }\n\n // ── Graph lifecycle ────────────────────────────────────────\n\n protected async syncGraph(): Promise<void> {\n const nodes = await this.loadAllNodes();\n const centrality = this.graphManager.build(nodes);\n this.onCentralityComputed(centrality);\n }\n\n protected onCentralityComputed(_centrality: Map<string, CentralityMetrics>): void {\n // Default no-op. Subclasses override to persist centrality.\n }\n}\n","import matter from 'gray-matter';\n\n/** Reserved frontmatter keys that are extracted to dedicated fields */\nexport const RESERVED_FRONTMATTER_KEYS = ['id', 'title', 'tags'] as const;\n\nexport interface ParsedMarkdown {\n /** Stable frontmatter ID (12-char nanoid) */\n id?: string;\n title: string | undefined;\n tags: string[];\n properties: Record<string, unknown>;\n content: string;\n /** Raw wiki-link targets before normalization (e.g., [\"Other Note\", \"folder/file\"]) */\n rawLinks: string[];\n}\n\n/** Set of reserved keys for O(1) lookup */\nconst RESERVED_KEYS_SET = new Set<string>(RESERVED_FRONTMATTER_KEYS);\n\n/**\n * Parse markdown with YAML frontmatter.\n * Handles missing/malformed frontmatter gracefully.\n */\nexport function parseMarkdown(raw: string): ParsedMarkdown {\n let parsed: matter.GrayMatterFile<string>;\n try {\n parsed = matter(raw);\n } catch {\n // Malformed frontmatter - return content as-is\n return {\n title: undefined,\n tags: [],\n properties: {},\n content: raw,\n rawLinks: extractWikiLinks(raw),\n };\n }\n\n const data = parsed.data as Record<string, unknown>;\n\n // Extract id - must be a string\n const id = typeof data['id'] === 'string' ? data['id'] : undefined;\n\n // Extract title\n const title = typeof data['title'] === 'string' ? data['title'] : undefined;\n\n // Extract tags - must be an array of strings\n let tags: string[] = [];\n if (Array.isArray(data['tags'])) {\n tags = data['tags'].filter((t): t is string => typeof t === 'string');\n }\n\n // Extract other properties (excluding all reserved keys)\n const properties: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data)) {\n if (!RESERVED_KEYS_SET.has(key)) {\n properties[key] = value;\n }\n }\n\n const content = parsed.content.trim();\n\n const result: ParsedMarkdown = {\n title,\n tags,\n properties,\n content,\n rawLinks: extractWikiLinks(content),\n };\n\n // Only include id if it's a string (exactOptionalPropertyTypes)\n if (id !== undefined) {\n result.id = id;\n }\n\n return result;\n}\n\n/**\n * Extract wiki-link targets from markdown content.\n * Ignores links inside code blocks and inline code.\n * Deduplicates results.\n */\nexport function extractWikiLinks(content: string): string[] {\n // Remove code blocks first\n const withoutCodeBlocks = content.replace(/```[\\s\\S]*?```/g, '');\n\n // Remove inline code\n const withoutInlineCode = withoutCodeBlocks.replace(/`[^`]+`/g, '');\n\n // Match wiki links: [[target]] or [[target|display]]\n const linkRegex = /\\[\\[([^\\]|]+)(?:\\|[^\\]]+)?\\]\\]/g;\n const seen = new Set<string>();\n const links: string[] = [];\n\n let match;\n while ((match = linkRegex.exec(withoutInlineCode)) !== null) {\n const target = match[1]?.trim();\n if (target && !seen.has(target)) {\n seen.add(target);\n links.push(target);\n }\n }\n\n return links;\n}\n\nimport { normalizePath } from './normalize.js';\n\n/**\n * Normalize a file path to a consistent ID format.\n * @deprecated Use normalizePath from './normalize.js' directly.\n */\nexport const normalizeId = normalizePath;\n\n/**\n * Derive a human-readable title from a file path.\n * - Removes directory prefix\n * - Removes extension\n * - Replaces hyphens/underscores with spaces\n * - Title-cases words\n */\nexport function titleFromPath(path: string): string {\n // Get filename without directory\n const parts = path.split(/[/\\\\]/);\n // parts is always non-empty (even '' splits to [''])\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const filename = parts.at(-1)!;\n\n // Remove extension\n const withoutExt = filename.replace(/\\.[^.]+$/, '');\n\n // Replace hyphens and underscores with spaces, collapse multiples\n const spaced = withoutExt.replace(/[-_]+/g, ' ').toLowerCase();\n\n // Title-case each word\n return spaced\n .split(' ')\n .filter((w) => w.length > 0)\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\n/**\n * Serialize parsed markdown back to a string with YAML frontmatter.\n * Omits frontmatter if no metadata is present.\n * Places id FIRST in frontmatter for consistency.\n */\nexport function serializeToMarkdown(parsed: ParsedMarkdown): string {\n const hasFrontmatter =\n parsed.id !== undefined ||\n parsed.title !== undefined ||\n parsed.tags.length > 0 ||\n Object.keys(parsed.properties).length > 0;\n\n if (!hasFrontmatter) {\n return parsed.content;\n }\n\n // Build frontmatter object with id FIRST\n // Using insertion order which is preserved in modern JS\n const frontmatter: Record<string, unknown> = {};\n\n if (parsed.id !== undefined) {\n frontmatter['id'] = parsed.id;\n }\n\n if (parsed.title !== undefined) {\n frontmatter['title'] = parsed.title;\n }\n\n if (parsed.tags.length > 0) {\n frontmatter['tags'] = parsed.tags;\n }\n\n // Add other properties\n for (const [key, value] of Object.entries(parsed.properties)) {\n frontmatter[key] = value;\n }\n\n // Use gray-matter to stringify\n return matter.stringify(parsed.content, frontmatter);\n}\n","/**\n * Normalization utilities for file paths and wikilink targets.\n *\n * Single source of truth for path normalization in DocStore.\n */\n\n/**\n * Check if a path has a file extension.\n * Extension must be 1-4 chars and contain at least one letter\n * (excludes numeric-only like .2024, .123).\n */\nexport function hasFileExtension(path: string): boolean {\n const match = path.match(/\\.([a-z0-9]{1,4})$/i);\n if (!match?.[1]) return false;\n return /[a-z]/i.test(match[1]);\n}\n\n/**\n * Normalize a file path: lowercase, forward slashes.\n * Use for file system paths and node IDs.\n */\nexport function normalizePath(path: string): string {\n return path.toLowerCase().replace(/\\\\/g, '/');\n}\n\n/**\n * Normalize a wikilink target to a node ID.\n * Lowercases, converts backslashes, appends .md if no extension.\n * Use for resolving [[wikilinks]] to node IDs.\n */\nexport function normalizeLinkTarget(target: string): string {\n let normalized = target.trim().toLowerCase().replace(/\\\\/g, '/');\n\n if (!hasFileExtension(normalized)) {\n normalized += '.md';\n }\n\n return normalized;\n}\n","/**\n * FileWatcher - Pure file system event emitter\n *\n * Responsibilities:\n * - Wraps chokidar\n * - Filters (.md only, excluded dirs)\n * - Coalesces events\n * - Debounces\n * - Emits batched events via callback\n */\n\nimport { watch, type FSWatcher } from 'chokidar';\nimport { relative, extname } from 'node:path';\nimport { EXCLUDED_DIRS } from './constants.js';\n\n// Re-export for backwards compatibility\nexport { EXCLUDED_DIRS } from './constants.js';\n\nexport type FileEventType = 'add' | 'change' | 'unlink';\n\nexport interface FileWatcherOptions {\n root: string;\n /** File extensions to watch (e.g., new Set(['.md', '.markdown'])). Required. */\n extensions: ReadonlySet<string>;\n debounceMs?: number;\n /** Called after debounce with coalesced events. Exceptions (sync or async) are\n * logged and swallowed; watcher continues operating. */\n onBatch: (events: Map<string, FileEventType>) => void | Promise<void>;\n}\n\nconst DEFAULT_DEBOUNCE_MS = 1000;\n\nexport class FileWatcher {\n private readonly root: string;\n private readonly extensions: ReadonlySet<string>;\n private readonly debounceMs: number;\n private readonly onBatch: (events: Map<string, FileEventType>) => void | Promise<void>;\n\n private watcher: FSWatcher | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private pendingChanges: Map<string, FileEventType> = new Map();\n private isPaused = false;\n\n constructor(options: FileWatcherOptions) {\n this.root = options.root;\n this.extensions = options.extensions;\n this.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;\n this.onBatch = options.onBatch;\n }\n\n start(): Promise<void> {\n if (this.watcher) {\n return Promise.reject(new Error('Already watching. Call stop() first.'));\n }\n\n return new Promise((resolve, reject) => {\n let isReady = false;\n\n this.watcher = watch(this.root, {\n ignoreInitial: true,\n ignored: [...EXCLUDED_DIRS].map((dir) => `**/${dir}/**`),\n awaitWriteFinish: {\n stabilityThreshold: 100,\n },\n followSymlinks: false,\n });\n\n this.watcher\n .on('ready', () => {\n isReady = true;\n resolve();\n })\n .on('add', (path) => this.queueChange(path, 'add'))\n .on('change', (path) => this.queueChange(path, 'change'))\n .on('unlink', (path) => this.queueChange(path, 'unlink'))\n .on('error', (err) => {\n if ((err as NodeJS.ErrnoException).code === 'EMFILE') {\n console.error(\n 'File watcher hit file descriptor limit. ' +\n 'Try: ulimit -n 65536 or reduce watched files.'\n );\n }\n if (isReady) {\n // Error after ready - log and continue (graceful degradation)\n console.error('FileWatcher error:', err);\n } else {\n // Error during startup - reject the promise\n reject(err);\n }\n });\n });\n }\n\n stop(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n this.pendingChanges.clear();\n\n if (this.watcher) {\n this.watcher.close();\n this.watcher = null;\n }\n }\n\n isWatching(): boolean {\n return this.watcher !== null;\n }\n\n pause(): void {\n this.isPaused = true;\n }\n\n resume(): void {\n this.isPaused = false;\n }\n\n flush(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n\n if (this.pendingChanges.size === 0) {\n return;\n }\n\n const batch = new Map(this.pendingChanges);\n this.pendingChanges.clear();\n\n try {\n const result = this.onBatch(batch);\n // Handle async onBatch (returns Promise)\n if (result && typeof (result as Promise<void>).catch === 'function') {\n (result as Promise<void>).catch((err) => {\n console.error('FileWatcher onBatch callback threw an error:', err);\n });\n }\n } catch (err) {\n console.error('FileWatcher onBatch callback threw an error:', err);\n }\n }\n\n private queueChange(filePath: string, event: FileEventType): void {\n if (this.isPaused) return;\n\n const relativePath = relative(this.root, filePath);\n\n // Filter by extension using path.extname for correctness\n const ext = extname(filePath).toLowerCase();\n if (!ext || !this.extensions.has(ext)) {\n return;\n }\n\n // Filter: excluded directories\n const pathParts = relativePath.split('/');\n for (const part of pathParts) {\n if (EXCLUDED_DIRS.has(part)) {\n return;\n }\n }\n\n // Normalize ID (lowercase, forward slashes)\n const id = relativePath.toLowerCase().replace(/\\\\/g, '/');\n\n // Apply coalescing rules\n const existing = this.pendingChanges.get(id);\n\n if (existing) {\n if (existing === 'add' && event === 'change') {\n // add + change = add (keep as add)\n return;\n } else if (existing === 'add' && event === 'unlink') {\n // add + unlink = remove from queue\n this.pendingChanges.delete(id);\n // If queue is now empty, clear timer and return early (no wasted flush)\n if (this.pendingChanges.size === 0) {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n return;\n }\n } else if (existing === 'change' && event === 'unlink') {\n // change + unlink = unlink\n this.pendingChanges.set(id, 'unlink');\n } else if (existing === 'change' && event === 'add') {\n // change + add = add (delete was missed, treat as new file)\n this.pendingChanges.set(id, 'add');\n } else if (existing === 'unlink' && event === 'add') {\n // unlink + add = add (file deleted then re-created)\n this.pendingChanges.set(id, 'add');\n } else if (existing === 'unlink' && event === 'change') {\n // unlink + change = unlink (spurious change ignored)\n return;\n }\n // change + change = change (already set, no action needed)\n } else {\n this.pendingChanges.set(id, event);\n }\n\n // Reset debounce timer\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n }\n\n this.debounceTimer = setTimeout(() => {\n this.flush();\n }, this.debounceMs);\n }\n}\n","/**\n * Directories excluded from file scanning and watching.\n * Hidden directories (starting with .) and common non-content folders.\n */\nexport const EXCLUDED_DIRS: ReadonlySet<string> = new Set([\n '.roux',\n 'node_modules',\n '.git',\n '.obsidian',\n]);\n","/**\n * Pure functions for wiki-link resolution.\n * Extracted from DocStore to reduce file size and improve testability.\n */\n\nimport {\n hasFileExtension as hasFileExtensionImpl,\n normalizeLinkTarget,\n} from './normalize.js';\n\n/**\n * Check if a path has a file extension.\n * @deprecated Use hasFileExtension from './normalize.js' directly.\n */\nexport const hasFileExtension = hasFileExtensionImpl;\n\n/**\n * Normalize a wiki-link target to an ID.\n * @deprecated Use normalizeLinkTarget from './normalize.js' directly.\n */\nexport const normalizeWikiLink = normalizeLinkTarget;\n\n/**\n * Node shape for building filename index.\n * Uses stable nanoid-based IDs with title and optional sourceRef.\n */\ninterface IndexableNode {\n id: string;\n title: string;\n sourceRef?: { path?: string };\n}\n\n/**\n * Build an index mapping titles and filenames to node IDs.\n * Used for resolving wiki-links like [[My Note]] to their stable IDs.\n *\n * Resolution priority:\n * 1. Title (primary) - always indexed if non-empty\n * 2. Filename from sourceRef.path (fallback) - indexed if different from title\n */\nexport function buildFilenameIndex(\n nodes: Iterable<IndexableNode>\n): Map<string, string[]> {\n const index = new Map<string, string[]>();\n\n for (const node of nodes) {\n const path = node.sourceRef?.path ?? '';\n const titleKey = node.title.toLowerCase();\n\n // Warn if completely unindexable\n if (!titleKey && !path) {\n console.warn(\n `Node ${node.id} has no title or path — link resolution will fail`\n );\n }\n\n // Index by title (skip if empty)\n if (titleKey) {\n const existing = index.get(titleKey) ?? [];\n existing.push(node.id);\n index.set(titleKey, existing);\n }\n\n // Index by filename from path (if different from title)\n if (path) {\n const filename = path\n .split('/')\n .pop()\n ?.replace(/\\.[^.]+$/, '')\n .toLowerCase();\n if (filename && filename !== titleKey) {\n const existing = index.get(filename) ?? [];\n existing.push(node.id);\n index.set(filename, existing);\n }\n }\n }\n\n // Sort alphabetically for deterministic collision resolution\n for (const ids of index.values()) {\n ids.sort();\n }\n\n return index;\n}\n\n/**\n * Resolve an array of outgoing links to their full node IDs.\n * Pure function - returns resolved links without mutating anything.\n *\n * Resolution rules:\n * 1. If link already matches a valid node ID, keep it\n * 2. If link contains '/', keep it as-is (partial paths don't resolve)\n * 3. For bare filenames, look up in the filename index (after stripping .md)\n * 4. If no match found, try space/dash variant\n * 5. If still no match, keep original link\n */\nexport function resolveLinks(\n outgoingLinks: string[],\n filenameIndex: Map<string, string[]>,\n validNodeIds: Set<string>\n): string[] {\n return outgoingLinks.map((link) => {\n // If link already exists as a valid node ID, keep it\n if (validNodeIds.has(link)) {\n return link;\n }\n\n // Only resolve bare filenames (no path separators)\n // Partial paths like \"folder/target.md\" stay literal\n if (link.includes('/')) {\n return link;\n }\n\n // Strip .md extension for index lookup (titles are indexed without extension)\n const lookupKey = link.replace(/\\.md$/i, '').toLowerCase();\n\n // Try title/filename lookup\n const matches = filenameIndex.get(lookupKey);\n if (matches && matches.length > 0) {\n return matches[0]!;\n }\n\n // Fallback: try space/dash variant\n const variant = spaceDashVariant(lookupKey);\n if (variant) {\n const variantMatches = filenameIndex.get(variant);\n if (variantMatches && variantMatches.length > 0) {\n return variantMatches[0]!;\n }\n }\n\n return link;\n });\n}\n\n/**\n * Generate the space/dash variant of a filename.\n * Returns null if the filename has both or neither spaces and dashes.\n */\nexport function spaceDashVariant(filename: string): string | null {\n const hasSpace = filename.includes(' ');\n const hasDash = filename.includes('-');\n\n if (hasSpace && !hasDash) {\n return filename.replace(/ /g, '-');\n }\n if (hasDash && !hasSpace) {\n return filename.replace(/-/g, ' ');\n }\n return null;\n}\n","/**\n * File operations for DocStore\n *\n * Generic file I/O utilities extracted from DocStore for reuse\n * by FormatReader implementations.\n */\n\nimport { readFile, stat, readdir } from 'node:fs/promises';\nimport { join, resolve, extname } from 'node:path';\nimport { EXCLUDED_DIRS } from './constants.js';\n\n/**\n * Get file modification time in milliseconds\n */\nexport async function getFileMtime(filePath: string): Promise<number> {\n const stats = await stat(filePath);\n return stats.mtimeMs;\n}\n\n/**\n * Validate that an ID resolves within the source root.\n * Throws if path traversal is detected.\n */\nexport function validatePathWithinSource(sourceRoot: string, id: string): void {\n const resolvedPath = resolve(sourceRoot, id);\n const resolvedRoot = resolve(sourceRoot);\n\n if (!resolvedPath.startsWith(resolvedRoot + '/')) {\n throw new Error(`Path traversal detected: ${id} resolves outside source root`);\n }\n}\n\n/**\n * Recursively collect files matching given extensions.\n *\n * @param dir - Directory to search\n * @param extensions - Set of extensions to match (e.g., new Set(['.md', '.markdown']))\n * @returns Array of absolute file paths\n */\nexport async function collectFiles(\n dir: string,\n extensions: ReadonlySet<string>\n): Promise<string[]> {\n // Empty extension set = no files to collect\n if (extensions.size === 0) {\n return [];\n }\n\n const results: string[] = [];\n\n let entries;\n try {\n entries = await readdir(dir, { withFileTypes: true });\n } catch {\n // Directory doesn't exist\n return results;\n }\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n\n if (entry.isDirectory()) {\n // Skip excluded directories\n if (EXCLUDED_DIRS.has(entry.name)) {\n continue;\n }\n const nested = await collectFiles(fullPath, extensions);\n results.push(...nested);\n } else if (entry.isFile()) {\n const ext = extname(entry.name).toLowerCase();\n // Skip files with no extension (README, .gitignore, etc.)\n if (ext && extensions.has(ext)) {\n results.push(fullPath);\n }\n }\n }\n\n return results;\n}\n\n/**\n * Read file content as UTF-8 string\n */\nexport async function readFileContent(filePath: string): Promise<string> {\n return readFile(filePath, 'utf-8');\n}\n","import { nanoid } from 'nanoid';\n\n/** Pattern for valid nanoid: exactly 12 URL-safe characters */\nconst NANOID_PATTERN = /^[A-Za-z0-9_-]{12}$/;\n\n/**\n * Check if a string is a valid frontmatter ID (12-char nanoid).\n */\nexport const isValidId = (id: string): boolean => NANOID_PATTERN.test(id);\n\n/**\n * Generate a new frontmatter ID (12-char nanoid).\n */\nexport const generateId = (): string => nanoid(12);\n","/**\n * FormatReader plugin architecture\n *\n * Provides a registry for file format readers, enabling multi-format support\n * in DocStore while keeping format-specific logic isolated.\n */\n\nimport type { FormatReader, FileContext, ParseResult } from './types.js';\nimport { isValidId } from './id.js';\n\n/**\n * Registry for FormatReader implementations\n */\nexport class ReaderRegistry {\n private readers: Map<string, FormatReader> = new Map();\n\n /**\n * Register a reader for its declared extensions.\n * Throws if any extension is already registered (atomic - no partial registration).\n */\n register(reader: FormatReader): void {\n // Check for conflicts first (atomic)\n for (const ext of reader.extensions) {\n const normalizedExt = ext.toLowerCase();\n if (this.readers.has(normalizedExt)) {\n throw new Error(`Extension already registered: ${ext}`);\n }\n }\n\n // All clear - register all extensions\n for (const ext of reader.extensions) {\n const normalizedExt = ext.toLowerCase();\n this.readers.set(normalizedExt, reader);\n }\n }\n\n /**\n * Get reader for an extension, or null if none registered.\n * Case-insensitive.\n */\n getReader(extension: string): FormatReader | null {\n return this.readers.get(extension.toLowerCase()) ?? null;\n }\n\n /**\n * Get all registered extensions\n */\n getExtensions(): ReadonlySet<string> {\n return new Set(this.readers.keys());\n }\n\n /**\n * Check if an extension has a registered reader.\n * Case-insensitive.\n */\n hasReader(extension: string): boolean {\n return this.readers.has(extension.toLowerCase());\n }\n\n /**\n * Parse content using the appropriate reader for the file's extension.\n * Validates frontmatter ID and signals if writeback is needed.\n * Throws if no reader is registered for the extension.\n *\n * Note: Does NOT generate new IDs here - that happens in Phase 3's writeback.\n * Files without valid frontmatter IDs keep their path-based ID for now,\n * with needsIdWrite: true signaling that an ID should be generated and written.\n */\n parse(content: string, context: FileContext): ParseResult {\n const reader = this.getReader(context.extension);\n if (!reader) {\n throw new Error(`No reader registered for extension: ${context.extension}`);\n }\n const node = reader.parse(content, context);\n\n // Check if node has a valid stable frontmatter ID\n const needsIdWrite = !isValidId(node.id);\n\n return { node, needsIdWrite };\n }\n}\n","/**\n * MarkdownReader - FormatReader implementation for Markdown files\n *\n * Handles .md and .markdown files, extracting:\n * - YAML frontmatter (title, tags, properties)\n * - Wiki-links from content\n * - Node ID from file path\n */\n\nimport type { Node } from '../../../types/node.js';\nimport type { FormatReader, FileContext } from '../types.js';\nimport {\n parseMarkdown,\n extractWikiLinks,\n normalizeId,\n titleFromPath,\n} from '../parser.js';\nimport { normalizeWikiLink } from '../links.js';\n\nexport class MarkdownReader implements FormatReader {\n readonly extensions = ['.md', '.markdown'];\n\n parse(content: string, context: FileContext): Node {\n const parsed = parseMarkdown(content);\n\n // Use frontmatter ID if present, otherwise derive from path\n // (ReaderRegistry validates and generates if invalid)\n const id = parsed.id ?? normalizeId(context.relativePath);\n\n // Derive title from path if not in frontmatter\n const title = parsed.title ?? titleFromPath(id);\n\n // Extract and normalize wiki links\n const rawLinks = extractWikiLinks(parsed.content);\n const outgoingLinks = rawLinks.map((link) => normalizeWikiLink(link));\n\n return {\n id,\n title,\n content: parsed.content,\n tags: parsed.tags,\n outgoingLinks,\n properties: parsed.properties,\n sourceRef: {\n type: 'file',\n path: context.absolutePath,\n lastModified: context.mtime,\n },\n };\n }\n}\n","import { pipeline, type FeatureExtractionPipeline } from '@xenova/transformers';\nimport type { Embedding } from '../../types/provider.js';\n\nconst DEFAULT_MODEL = 'Xenova/all-MiniLM-L6-v2';\nconst DEFAULT_DIMENSIONS = 384;\n\nexport interface TransformersEmbeddingOptions {\n model?: string;\n dimensions?: number;\n id?: string;\n}\n\nexport class TransformersEmbedding implements Embedding {\n readonly id: string;\n private model: string;\n private dims: number;\n private pipe: FeatureExtractionPipeline | null = null;\n\n constructor(options: TransformersEmbeddingOptions = {}) {\n const {\n model = DEFAULT_MODEL,\n dimensions = DEFAULT_DIMENSIONS,\n id = 'transformers-embedding',\n } = options;\n this.id = id;\n this.model = model;\n this.dims = dimensions;\n }\n\n private async getPipeline(): Promise<FeatureExtractionPipeline> {\n if (!this.pipe) {\n this.pipe = await pipeline('feature-extraction', this.model);\n }\n return this.pipe;\n }\n\n async embed(text: string): Promise<number[]> {\n const pipe = await this.getPipeline();\n const output = await pipe(text, { pooling: 'mean', normalize: true });\n return Array.from(output.data as Float32Array);\n }\n\n async embedBatch(texts: string[]): Promise<number[][]> {\n if (texts.length === 0) {\n return [];\n }\n return Promise.all(texts.map((t) => this.embed(t)));\n }\n\n dimensions(): number {\n return this.dims;\n }\n\n modelId(): string {\n return this.model;\n }\n\n // Lifecycle hooks\n\n async onRegister(): Promise<void> {\n // No-op: pipeline is lazy-loaded on first embed() call\n }\n\n async onUnregister(): Promise<void> {\n // Release reference for GC\n // NOTE: @xenova/transformers may not truly unload from GPU memory\n this.pipe = null;\n }\n}\n","import type { Node, NodeUpdates } from './node.js';\nimport type { Direction, NeighborOptions } from './edge.js';\n\nexport type Metric = 'in_degree' | 'out_degree';\n\n// Batch operation types\nexport interface ListFilter {\n /** Filter by tag (case-insensitive) */\n tag?: string;\n /** Filter by path prefix (startsWith) */\n path?: string;\n}\n\nexport interface ListOptions {\n /** Default 100, max 1000 */\n limit?: number;\n /** Default 0 */\n offset?: number;\n}\n\nexport interface NodeSummary {\n id: string;\n title: string;\n}\n\nexport interface ListNodesResult {\n nodes: NodeSummary[];\n /** Total matching nodes (before limit/offset applied) */\n total: number;\n}\n\nexport type ResolveStrategy = 'exact' | 'fuzzy' | 'semantic';\n\nexport interface ResolveOptions {\n /** Filter candidates by tag */\n tag?: string;\n /** Filter candidates by path prefix */\n path?: string;\n /** 0-1, default 0.7, ignored for 'exact' */\n threshold?: number;\n /** Default 'fuzzy' */\n strategy?: ResolveStrategy;\n}\n\nexport interface ResolveResult {\n /** Original input */\n query: string;\n /** Matched node ID or null */\n match: string | null;\n /** 0-1, 0 if no match */\n score: number;\n}\n\nexport interface CentralityMetrics {\n inDegree: number;\n outDegree: number;\n}\n\nexport type TagMode = 'any' | 'all';\n\nexport interface VectorSearchResult {\n id: string;\n distance: number;\n}\n\n/** Link with resolved title for MCP responses. */\nexport interface LinkInfo {\n id: string;\n title: string;\n}\n\n// ============================================================================\n// Provider Base Types\n// ============================================================================\n\n/** Base fields all providers must implement. */\nexport interface ProviderBase {\n /** Unique identifier for this provider instance. Must be non-empty. */\n readonly id: string;\n}\n\n/**\n * Optional lifecycle hooks for providers.\n * - onRegister: Called after registration with GraphCore. Errors propagate to caller.\n * - onUnregister: Called before provider is replaced or GraphCore is destroyed. Best-effort, errors logged.\n */\nexport interface ProviderLifecycle {\n onRegister?(): Promise<void>;\n onUnregister?(): Promise<void>;\n}\n\n// ============================================================================\n// Provider Interfaces\n// ============================================================================\n\n/** Data persistence and graph operations. Required provider. */\nexport interface Store extends ProviderBase, ProviderLifecycle {\n // CRUD\n createNode(node: Node): Promise<void>;\n updateNode(id: string, updates: NodeUpdates): Promise<void>;\n deleteNode(id: string): Promise<void>;\n getNode(id: string): Promise<Node | null>;\n getNodes(ids: string[]): Promise<Node[]>;\n\n // Graph operations\n getNeighbors(id: string, options: NeighborOptions): Promise<Node[]>;\n findPath(source: string, target: string): Promise<string[] | null>;\n getHubs(metric: Metric, limit: number): Promise<Array<[string, number]>>;\n\n // Vector storage\n storeEmbedding(id: string, vector: number[], model: string): Promise<void>;\n searchByVector(\n vector: number[],\n limit: number\n ): Promise<VectorSearchResult[]>;\n\n // Search\n searchByTags(tags: string[], mode: TagMode, limit?: number): Promise<Node[]>;\n\n // Discovery\n getRandomNode(tags?: string[]): Promise<Node | null>;\n\n // Link resolution (for MCP response formatting)\n resolveTitles(ids: string[]): Promise<Map<string, string>>;\n\n // Batch operations\n listNodes(filter: ListFilter, options?: ListOptions): Promise<ListNodesResult>;\n resolveNodes(names: string[], options?: ResolveOptions): Promise<ResolveResult[]>;\n nodesExist(ids: string[]): Promise<Map<string, boolean>>;\n}\n\n/** Stateless vector generation. Storage handled by Store. */\nexport interface Embedding extends ProviderBase, ProviderLifecycle {\n embed(text: string): Promise<number[]>;\n embedBatch(texts: string[]): Promise<number[][]>;\n /** For storage allocation */\n dimensions(): number;\n modelId(): string;\n}\n\n/** Pluggable vector storage and similarity search. */\nexport interface VectorIndex {\n store(id: string, vector: number[], model: string): Promise<void>;\n search(vector: number[], limit: number): Promise<VectorSearchResult[]>;\n delete(id: string): Promise<void>;\n getModel(id: string): Promise<string | null>;\n hasEmbedding(id: string): boolean;\n}\n\nexport function isVectorIndex(value: unknown): value is VectorIndex {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n const obj = value as Record<string, unknown>;\n return (\n typeof obj.store === 'function' &&\n typeof obj.search === 'function' &&\n typeof obj.delete === 'function' &&\n typeof obj.getModel === 'function' &&\n typeof obj.hasEmbedding === 'function'\n );\n}\n\n/**\n * Runtime type guard for Store interface.\n * IMPORTANT: Update this function when Store interface changes.\n * Checks: id field (required, non-empty string) + 16 methods\n */\nexport function isStoreProvider(value: unknown): value is Store {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n const obj = value as Record<string, unknown>;\n return (\n typeof obj.id === 'string' &&\n obj.id.trim().length > 0 &&\n typeof obj.createNode === 'function' &&\n typeof obj.updateNode === 'function' &&\n typeof obj.deleteNode === 'function' &&\n typeof obj.getNode === 'function' &&\n typeof obj.getNodes === 'function' &&\n typeof obj.getNeighbors === 'function' &&\n typeof obj.findPath === 'function' &&\n typeof obj.getHubs === 'function' &&\n typeof obj.storeEmbedding === 'function' &&\n typeof obj.searchByVector === 'function' &&\n typeof obj.searchByTags === 'function' &&\n typeof obj.getRandomNode === 'function' &&\n typeof obj.resolveTitles === 'function' &&\n typeof obj.listNodes === 'function' &&\n typeof obj.resolveNodes === 'function' &&\n typeof obj.nodesExist === 'function'\n );\n}\n\n/**\n * Runtime type guard for Embedding interface.\n * IMPORTANT: Update this function when Embedding interface changes.\n * Current method count: 4 + id field\n */\nexport function isEmbeddingProvider(value: unknown): value is Embedding {\n if (value === null || typeof value !== 'object') {\n return false;\n }\n const obj = value as Record<string, unknown>;\n return (\n typeof obj.id === 'string' &&\n obj.id.trim().length > 0 &&\n typeof obj.embed === 'function' &&\n typeof obj.embedBatch === 'function' &&\n typeof obj.dimensions === 'function' &&\n typeof obj.modelId === 'function'\n );\n}\n\n// Re-export for convenience\nexport type { Direction, NeighborOptions };\n","import type { Node, NodeUpdates, NodeWithContext } from '../types/node.js';\nimport type {\n GraphCore,\n SearchOptions,\n} from '../types/graphcore.js';\nimport type {\n Store,\n Embedding,\n Metric,\n TagMode,\n NeighborOptions,\n ListFilter,\n ListOptions,\n ListNodesResult,\n ResolveOptions,\n ResolveResult,\n} from '../types/provider.js';\nimport {\n isStoreProvider,\n isEmbeddingProvider,\n} from '../types/provider.js';\nimport type { RouxConfig } from '../types/config.js';\nimport { DocStore } from '../providers/docstore/index.js';\nimport { TransformersEmbedding } from '../providers/embedding/transformers.js';\nimport { cosineSimilarity } from '../utils/math.js';\n\nexport class GraphCoreImpl implements GraphCore {\n private store: Store | null = null;\n private embedding: Embedding | null = null;\n\n async registerStore(provider: Store): Promise<void> {\n if (!provider) {\n throw new Error('Store provider is required');\n }\n if (!isStoreProvider(provider)) {\n throw new Error('Invalid Store provider: missing required methods or id');\n }\n\n // Unregister previous provider if exists\n if (this.store?.onUnregister) {\n try {\n await this.store.onUnregister();\n } catch (err) {\n console.warn('Error during store onUnregister:', err);\n }\n }\n\n // Set new provider (before calling onRegister so it can use the store)\n this.store = provider;\n\n // Call lifecycle hook if exists\n if (provider.onRegister) {\n try {\n await provider.onRegister();\n } catch (err) {\n // Rollback on failure\n this.store = null;\n throw err;\n }\n }\n }\n\n async registerEmbedding(provider: Embedding): Promise<void> {\n if (!provider) {\n throw new Error('Embedding provider is required');\n }\n if (!isEmbeddingProvider(provider)) {\n throw new Error('Invalid Embedding provider: missing required methods or id');\n }\n\n // Unregister previous provider if exists\n if (this.embedding?.onUnregister) {\n try {\n await this.embedding.onUnregister();\n } catch (err) {\n console.warn('Error during embedding onUnregister:', err);\n }\n }\n\n // Set new provider\n this.embedding = provider;\n\n // Call lifecycle hook if exists\n if (provider.onRegister) {\n try {\n await provider.onRegister();\n } catch (err) {\n // Rollback on failure\n this.embedding = null;\n throw err;\n }\n }\n }\n\n async destroy(): Promise<void> {\n // Unregister in reverse order of typical registration\n if (this.embedding?.onUnregister) {\n try {\n await this.embedding.onUnregister();\n } catch (err) {\n console.warn('Error during embedding onUnregister in destroy:', err);\n }\n }\n if (this.store?.onUnregister) {\n try {\n await this.store.onUnregister();\n } catch (err) {\n console.warn('Error during store onUnregister in destroy:', err);\n }\n }\n\n // Clear references\n this.embedding = null;\n this.store = null;\n }\n\n private requireStore(): Store {\n if (!this.store) {\n throw new Error('Store not registered');\n }\n return this.store;\n }\n\n private requireEmbedding(): Embedding {\n if (!this.embedding) {\n throw new Error('Embedding not registered');\n }\n return this.embedding;\n }\n\n async search(query: string, options?: SearchOptions): Promise<Node[]> {\n const store = this.requireStore();\n const embedding = this.requireEmbedding();\n\n const limit = options?.limit ?? 10;\n const vector = await embedding.embed(query);\n const results = await store.searchByVector(vector, limit);\n\n // Results are already sorted by distance ascending\n const ids = results.map((r) => r.id);\n return store.getNodes(ids);\n }\n\n async getNode(id: string, depth?: number): Promise<NodeWithContext | null> {\n const store = this.requireStore();\n const node = await store.getNode(id);\n\n if (!node) {\n return null;\n }\n\n if (!depth || depth === 0) {\n return node;\n }\n\n // Fetch neighbors for context\n const [incomingNeighbors, outgoingNeighbors] = await Promise.all([\n store.getNeighbors(id, { direction: 'in' }),\n store.getNeighbors(id, { direction: 'out' }),\n ]);\n\n // Deduplicate neighbors (same node could be both incoming and outgoing)\n const neighborMap = new Map<string, Node>();\n for (const n of [...incomingNeighbors, ...outgoingNeighbors]) {\n neighborMap.set(n.id, n);\n }\n\n const result: NodeWithContext = {\n ...node,\n neighbors: Array.from(neighborMap.values()),\n incomingCount: incomingNeighbors.length,\n outgoingCount: outgoingNeighbors.length,\n };\n\n return result;\n }\n\n async createNode(partial: Partial<Node>): Promise<Node> {\n const store = this.requireStore();\n\n if (!partial.id || partial.id.trim() === '') {\n throw new Error('Node id is required and cannot be empty');\n }\n if (!partial.title) {\n throw new Error('Node title is required');\n }\n\n const node: Node = {\n id: partial.id,\n title: partial.title,\n content: partial.content ?? '',\n tags: partial.tags ?? [],\n outgoingLinks: partial.outgoingLinks ?? [],\n properties: partial.properties ?? {},\n ...(partial.sourceRef && { sourceRef: partial.sourceRef }),\n };\n\n await store.createNode(node);\n return (await store.getNode(node.id)) ?? node;\n }\n\n async updateNode(id: string, updates: NodeUpdates): Promise<Node> {\n const store = this.requireStore();\n await store.updateNode(id, updates);\n const updated = await store.getNode(id);\n if (!updated) {\n throw new Error(`Node not found after update: ${id}`);\n }\n return updated;\n }\n\n async deleteNode(id: string): Promise<boolean> {\n const store = this.requireStore();\n try {\n await store.deleteNode(id);\n return true;\n } catch (err) {\n // Only swallow \"not found\" errors - propagate everything else\n if (err instanceof Error && /not found/i.test(err.message)) {\n return false;\n }\n throw err;\n }\n }\n\n async getNeighbors(id: string, options: NeighborOptions): Promise<Node[]> {\n const store = this.requireStore();\n return store.getNeighbors(id, options);\n }\n\n async findPath(source: string, target: string): Promise<string[] | null> {\n const store = this.requireStore();\n return store.findPath(source, target);\n }\n\n async getHubs(\n metric: Metric,\n limit: number\n ): Promise<Array<[string, number]>> {\n const store = this.requireStore();\n return store.getHubs(metric, limit);\n }\n\n async searchByTags(\n tags: string[],\n mode: TagMode,\n limit?: number\n ): Promise<Node[]> {\n const store = this.requireStore();\n return store.searchByTags(tags, mode, limit);\n }\n\n async getRandomNode(tags?: string[]): Promise<Node | null> {\n const store = this.requireStore();\n return store.getRandomNode(tags);\n }\n\n async listNodes(\n filter: ListFilter,\n options?: ListOptions\n ): Promise<ListNodesResult> {\n return this.requireStore().listNodes(filter, options);\n }\n\n async resolveNodes(\n names: string[],\n options?: ResolveOptions\n ): Promise<ResolveResult[]> {\n const store = this.requireStore();\n const strategy = options?.strategy ?? 'fuzzy';\n\n // Semantic strategy requires embedding provider\n if (strategy === 'semantic') {\n if (!this.embedding) {\n throw new Error('Semantic resolution requires Embedding');\n }\n\n // Build filter without undefined values\n const filter: ListFilter = {};\n if (options?.tag) filter.tag = options.tag;\n if (options?.path) filter.path = options.path;\n\n // Get candidates from store with filters\n const { nodes: candidates } = await store.listNodes(filter, { limit: 1000 });\n\n if (candidates.length === 0 || names.length === 0) {\n return names.map((query) => ({ query, match: null, score: 0 }));\n }\n\n const threshold = options?.threshold ?? 0.7;\n\n // Embed all queries in batch\n const queryVectors = await this.embedding.embedBatch(names);\n\n // Embed all candidate titles in batch\n const candidateTitles = candidates.map((c) => c.title);\n const candidateVectors = await this.embedding.embedBatch(candidateTitles);\n\n // Validate dimensions match\n if (queryVectors.length > 0 && candidateVectors.length > 0) {\n const queryDim = queryVectors[0]!.length;\n const candidateDim = candidateVectors[0]!.length;\n if (queryDim !== candidateDim) {\n throw new Error(\n `Embedding dimension mismatch: query=${queryDim}, candidate=${candidateDim}`\n );\n }\n }\n\n // For each query, find best matching candidate by cosine similarity\n return names.map((query, qIdx): ResolveResult => {\n const queryVector = queryVectors[qIdx]!;\n let bestScore = 0;\n let bestMatch: string | null = null;\n\n for (let cIdx = 0; cIdx < candidates.length; cIdx++) {\n const similarity = cosineSimilarity(queryVector, candidateVectors[cIdx]!);\n if (similarity > bestScore) {\n bestScore = similarity;\n bestMatch = candidates[cIdx]!.id;\n }\n }\n\n if (bestScore >= threshold) {\n return { query, match: bestMatch, score: bestScore };\n }\n return { query, match: null, score: 0 };\n });\n }\n\n // Exact and fuzzy delegate to store\n return store.resolveNodes(names, options);\n }\n\n static async fromConfig(config: RouxConfig): Promise<GraphCoreImpl> {\n if (!config.providers?.store) {\n throw new Error('Store configuration is required');\n }\n\n const core = new GraphCoreImpl();\n\n try {\n // Create store based on config\n if (config.providers.store.type === 'docstore') {\n const sourcePath = config.source?.path ?? '.';\n const cachePath = config.cache?.path ?? '.roux';\n const store = new DocStore({ sourceRoot: sourcePath, cacheDir: cachePath });\n await core.registerStore(store);\n } else {\n throw new Error(\n `Unsupported store provider type: ${config.providers.store.type}. Supported: docstore`\n );\n }\n\n // Create embedding provider (defaults to local transformers)\n const embeddingConfig = config.providers.embedding;\n if (!embeddingConfig || embeddingConfig.type === 'local') {\n const model = embeddingConfig?.model;\n const embedding = new TransformersEmbedding(model ? { model } : {});\n await core.registerEmbedding(embedding);\n } else {\n throw new Error(\n `Unsupported embedding provider type: ${embeddingConfig.type}. Supported: local`\n );\n }\n\n return core;\n } catch (err) {\n // Clean up any registered providers on failure\n await core.destroy();\n throw err;\n }\n }\n}\n","import { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n type CallToolResult,\n} from '@modelcontextprotocol/sdk/types.js';\n\nimport type { GraphCore } from '../types/graphcore.js';\nimport type { Store } from '../types/provider.js';\nimport { DEFAULT_NAMING, type NamingConventions } from '../types/config.js';\nimport { VERSION } from '../index.js';\nimport { McpError } from './types.js';\nimport { dispatchTool, getToolDefinitions, type HandlerContext } from './handlers/index.js';\n\nexport interface McpServerOptions {\n /** GraphCore instance for operations */\n core: GraphCore;\n /** Store for link resolution and direct store access */\n store: Store;\n /** Whether embedding provider is available (enables search tool) */\n hasEmbedding: boolean;\n /** Naming conventions for file creation */\n naming?: NamingConventions;\n}\n\n/** MCP Transport interface for server connection. */\nexport interface McpTransport {\n start?(): Promise<void>;\n close?(): Promise<void>;\n}\n\n/** Factory function to create a transport. Defaults to StdioServerTransport. */\nexport type TransportFactory = () => McpTransport;\n\n/** Response format for MCP tool calls. Extends SDK's CallToolResult. */\nexport type McpToolResponse = CallToolResult;\n\n/**\n * Format a successful tool result for MCP response.\n */\nexport function formatToolResponse(result: unknown): McpToolResponse {\n return {\n content: [{ type: 'text', text: JSON.stringify(result, null, 2) }],\n };\n}\n\n/**\n * Format an error for MCP response.\n * Handles McpError, generic Error, and non-Error thrown values.\n */\nexport function formatErrorResponse(error: unknown): McpToolResponse {\n if (error instanceof McpError) {\n return {\n content: [{ type: 'text', text: JSON.stringify(error.toResponse()) }],\n isError: true,\n };\n }\n const mcpError = new McpError(\n 'PROVIDER_ERROR',\n error instanceof Error ? error.message : 'Unknown error'\n );\n return {\n content: [{ type: 'text', text: JSON.stringify(mcpError.toResponse()) }],\n isError: true,\n };\n}\n\n/**\n * Execute a tool call and return formatted MCP response.\n * Handles dispatching to the appropriate handler and error formatting.\n */\nexport async function executeToolCall(\n ctx: HandlerContext,\n name: string,\n args: Record<string, unknown>\n): Promise<McpToolResponse> {\n try {\n const result = await dispatchTool(ctx, name, args);\n return formatToolResponse(result);\n } catch (error) {\n return formatErrorResponse(error);\n }\n}\n\nexport class McpServer {\n private server: Server;\n private ctx: HandlerContext;\n\n constructor(options: McpServerOptions) {\n this.ctx = {\n core: options.core,\n store: options.store,\n hasEmbedding: options.hasEmbedding,\n naming: options.naming ?? DEFAULT_NAMING,\n };\n\n this.server = new Server(\n { name: 'roux', version: VERSION },\n { capabilities: { tools: {} } }\n );\n\n this.setupHandlers();\n }\n\n private setupHandlers(): void {\n this.server.setRequestHandler(ListToolsRequestSchema, async () => ({\n tools: getToolDefinitions(this.ctx.hasEmbedding),\n }));\n\n this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n return executeToolCall(this.ctx, name, args ?? {});\n });\n }\n\n /**\n * Start the server with optional transport factory.\n * @param transportFactory Factory to create transport. Defaults to StdioServerTransport.\n */\n async start(transportFactory?: TransportFactory): Promise<void> {\n const transport = transportFactory\n ? transportFactory()\n : new StdioServerTransport();\n await this.server.connect(transport as Parameters<typeof this.server.connect>[0]);\n }\n\n async close(): Promise<void> {\n await this.server.close();\n }\n}\n\n/**\n * Create and start an MCP server.\n * @param options Server configuration\n * @param transportFactory Optional transport factory for testing\n */\nexport async function createMcpServer(\n options: McpServerOptions,\n transportFactory?: TransportFactory\n): Promise<McpServer> {\n const server = new McpServer(options);\n await server.start(transportFactory);\n return server;\n}\n","export interface SourceConfig {\n /** Relative to config file */\n path: string;\n include: string[];\n /** .roux/ always excluded (hardcoded) */\n exclude: string[];\n}\n\nexport interface CacheConfig {\n /** SQLite directory */\n path: string;\n}\n\nexport type ModelChangeBehavior = 'lazy' | 'eager';\n\nexport interface SystemConfig {\n onModelChange: ModelChangeBehavior;\n}\n\nexport type FilenameSeparator = 'space' | 'dash';\nexport type TitleCasing = 'title' | 'sentence' | 'as-is';\n\nexport interface NamingConventions {\n filename: FilenameSeparator;\n title: TitleCasing;\n}\n\nexport const DEFAULT_NAMING: NamingConventions = {\n filename: 'space',\n title: 'title',\n};\n\nexport interface DocStoreConfig {\n type: 'docstore';\n}\n\nexport interface LocalEmbeddingConfig {\n type: 'local';\n /** Default: Xenova/all-MiniLM-L6-v2 */\n model?: string;\n}\n\nexport interface OllamaEmbeddingConfig {\n type: 'ollama';\n model: string;\n endpoint?: string;\n timeout?: number;\n}\n\nexport interface OpenAIEmbeddingConfig {\n type: 'openai';\n model: string;\n timeout?: number;\n}\n\nexport type EmbeddingConfig =\n | LocalEmbeddingConfig\n | OllamaEmbeddingConfig\n | OpenAIEmbeddingConfig;\n\nexport interface OllamaLLMConfig {\n type: 'ollama';\n model: string;\n endpoint?: string;\n timeout?: number;\n}\n\nexport interface OpenAILLMConfig {\n type: 'openai';\n model: string;\n timeout?: number;\n}\n\nexport type LLMConfig = OllamaLLMConfig | OpenAILLMConfig;\n\nexport type StoreConfig = DocStoreConfig;\n\nexport interface ProvidersConfig {\n store: StoreConfig;\n embedding?: EmbeddingConfig;\n llm?: LLMConfig;\n}\n\nexport interface RouxConfig {\n source?: SourceConfig;\n cache?: CacheConfig;\n system?: SystemConfig;\n naming?: NamingConventions;\n providers: ProvidersConfig;\n}\n\nexport const DEFAULT_CONFIG: Required<\n Pick<RouxConfig, 'source' | 'cache' | 'system'>\n> & { providers: { store: DocStoreConfig } } = {\n source: {\n path: '.',\n include: ['*.md'],\n exclude: [],\n },\n cache: {\n path: '.roux/',\n },\n system: {\n onModelChange: 'lazy',\n },\n providers: {\n store: {\n type: 'docstore',\n },\n },\n};\n","// Roux - Graph Programming Interface\n\nexport const VERSION = '0.1.3';\n\n// Re-export all types\nexport * from './types/index.js';\n\n// Core\nexport { GraphCoreImpl } from './core/graphcore.js';\n\n// Providers\nexport { StoreProvider, type StoreProviderOptions } from './providers/store/index.js';\nexport { DocStore } from './providers/docstore/index.js';\nexport { TransformersEmbedding } from './providers/embedding/index.js';\nexport { SqliteVectorIndex } from './providers/vector/index.js';\n","import type { LinkInfo } from '../types/provider.js';\n\n/** Metadata-only response for browsing operations (search, get_neighbors). */\nexport interface NodeMetadataResponse {\n id: string;\n title: string;\n tags: string[];\n links: LinkInfo[];\n properties: Record<string, unknown>;\n}\n\n/** Full response including content (for get_node, create, update, etc.). */\nexport interface NodeResponse extends NodeMetadataResponse {\n content: string;\n}\n\n/** Extended response for get_node with depth > 0. */\nexport interface NodeWithContextResponse extends NodeResponse {\n incomingNeighbors: NodeResponse[];\n outgoingNeighbors: NodeResponse[];\n incomingCount: number;\n outgoingCount: number;\n}\n\n/** Search results include similarity score. Content optional based on include_content param. */\nexport interface SearchResultResponse extends NodeMetadataResponse {\n score: number;\n content?: string;\n}\n\n/** Hub results pair ID with metric value. */\nexport interface HubResponse {\n id: string;\n title: string;\n score: number;\n}\n\n/** Path results are ordered node IDs. */\nexport interface PathResponse {\n path: string[];\n length: number;\n}\n\n/** Delete operation result. */\nexport interface DeleteResponse {\n deleted: boolean;\n}\n\n/** Standard error response shape. */\nexport interface ErrorResponse {\n error: {\n code: ErrorCode;\n message: string;\n };\n}\n\n/** Known error codes. */\nexport type ErrorCode =\n | 'INVALID_PARAMS'\n | 'NODE_EXISTS'\n | 'NODE_NOT_FOUND'\n | 'LINK_INTEGRITY'\n | 'PROVIDER_ERROR';\n\n/** Custom error class for MCP operations. */\nexport class McpError extends Error {\n constructor(\n public readonly code: ErrorCode,\n message: string\n ) {\n super(message);\n this.name = 'McpError';\n }\n\n toResponse(): ErrorResponse {\n return {\n error: {\n code: this.code,\n message: this.message,\n },\n };\n }\n}\n","import type { HandlerContext } from './types.js';\nimport type { SearchResultResponse } from '../types.js';\nimport { McpError } from '../types.js';\nimport { coerceLimit } from '../validation.js';\nimport { nodesToSearchResults } from '../transforms.js';\n\nexport const schema = {\n type: 'object',\n properties: {\n query: {\n type: 'string',\n description: 'Natural language search query',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 10,\n description: 'Maximum results to return',\n },\n include_content: {\n type: 'boolean',\n default: false,\n description:\n 'Include node content in results. Default false returns metadata only (id, title, tags, properties, links). Set true to include truncated content.',\n },\n },\n required: ['query'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<SearchResultResponse[]> {\n if (!ctx.hasEmbedding) {\n throw new McpError('PROVIDER_ERROR', 'Search requires embedding provider');\n }\n\n const query = args.query;\n const limit = coerceLimit(args.limit, 10);\n const includeContent = args.include_content === true;\n\n if (typeof query !== 'string' || query.trim() === '') {\n throw new McpError('INVALID_PARAMS', 'query is required and must be a non-empty string');\n }\n\n const nodes = await ctx.core.search(query, { limit });\n\n const scores = new Map<string, number>();\n nodes.forEach((node, index) => {\n scores.set(node.id, Math.max(0, 1 - index * 0.05));\n });\n\n return nodesToSearchResults(nodes, scores, ctx.store, includeContent);\n}\n","import { McpError } from './types.js';\n\nexport function coerceInt(\n value: unknown,\n defaultValue: number,\n minValue: number,\n fieldName: string\n): number {\n if (value === undefined || value === null) {\n return defaultValue;\n }\n const num = Number(value);\n if (Number.isNaN(num)) {\n return defaultValue;\n }\n const floored = Math.floor(num);\n if (floored < minValue) {\n throw new McpError('INVALID_PARAMS', `${fieldName} must be at least ${minValue}`);\n }\n return floored;\n}\n\nexport function coerceLimit(value: unknown, defaultValue: number): number {\n return coerceInt(value, defaultValue, 1, 'limit');\n}\n\nexport function coerceOffset(value: unknown, defaultValue: number): number {\n return coerceInt(value, defaultValue, 0, 'offset');\n}\n\nexport function coerceDepth(value: unknown): number {\n if (value === undefined || value === null) {\n return 0;\n }\n const num = Number(value);\n if (Number.isNaN(num)) {\n return 0;\n }\n return num >= 1 ? 1 : 0;\n}\n\n/**\n * Validate that value is an array of strings.\n * Allows empty arrays — callers should check length if non-empty is required.\n */\nexport function validateStringArray(\n value: unknown,\n fieldName: string\n): string[] {\n if (!Array.isArray(value)) {\n throw new McpError('INVALID_PARAMS', `${fieldName} is required and must be an array`);\n }\n if (!value.every((item) => typeof item === 'string')) {\n throw new McpError('INVALID_PARAMS', `${fieldName} must contain only strings`);\n }\n return value;\n}\n\nexport function validateRequiredString(\n value: unknown,\n fieldName: string\n): string {\n if (value === undefined || value === null || typeof value !== 'string') {\n throw new McpError('INVALID_PARAMS', `${fieldName} is required and must be a string`);\n }\n return value;\n}\n\nexport function validateEnum<T extends string>(\n value: unknown,\n validValues: readonly T[],\n fieldName: string,\n defaultValue: T\n): T {\n if (value === undefined || value === null) {\n return defaultValue;\n }\n if (!validValues.includes(value as T)) {\n throw new McpError(\n 'INVALID_PARAMS',\n `${fieldName} must be one of: ${validValues.join(', ')}`\n );\n }\n return value as T;\n}\n\n/**\n * Validate optional tags array.\n * Returns undefined if not provided, validated array otherwise.\n */\nexport function validateOptionalTags(value: unknown): string[] | undefined {\n if (value === undefined) {\n return undefined;\n }\n if (!Array.isArray(value)) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n if (!value.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n return value;\n}\n\n/**\n * Validate required non-empty tags array.\n */\nexport function validateRequiredTags(value: unknown): string[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new McpError('INVALID_PARAMS', 'tags is required and must be a non-empty array');\n }\n if (!value.every((t) => typeof t === 'string')) {\n throw new McpError('INVALID_PARAMS', 'tags must contain only strings');\n }\n return value;\n}\n","/** Content truncation limits by context. */\nexport const TRUNCATION_LIMITS = {\n /** Primary node (get_node, single result) */\n primary: 10_000,\n /** List results (search, neighbors) */\n list: 500,\n /** Neighbor nodes in context */\n neighbor: 200,\n} as const;\n\nexport type TruncationContext = keyof typeof TRUNCATION_LIMITS;\n\nconst TRUNCATION_SUFFIX = '... [truncated]';\n\n/**\n * Truncate content to limit, appending suffix if truncated.\n * Returns original content if within limit.\n * Ensures valid UTF-16 output by not splitting surrogate pairs.\n */\nexport function truncateContent(\n content: string,\n context: TruncationContext\n): string {\n const limit = TRUNCATION_LIMITS[context];\n\n if (content.length <= limit) {\n return content;\n }\n\n // Account for suffix length when truncating, guard against negative\n let truncatedLength = Math.max(0, limit - TRUNCATION_SUFFIX.length);\n\n // Don't split surrogate pairs - if we'd end on a high surrogate, back up one\n if (truncatedLength > 0) {\n const lastCharCode = content.charCodeAt(truncatedLength - 1);\n // High surrogate range: 0xD800-0xDBFF\n if (lastCharCode >= 0xd800 && lastCharCode <= 0xdbff) {\n truncatedLength--;\n }\n }\n\n return content.slice(0, truncatedLength) + TRUNCATION_SUFFIX;\n}\n\n/**\n * Check if content was truncated (ends with truncation suffix).\n */\nexport function isTruncated(content: string): boolean {\n return content.endsWith(TRUNCATION_SUFFIX);\n}\n","import type { Node } from '../types/node.js';\nimport type { LinkInfo, Store } from '../types/provider.js';\nimport type {\n NodeResponse,\n NodeMetadataResponse,\n NodeWithContextResponse,\n SearchResultResponse,\n HubResponse,\n PathResponse,\n} from './types.js';\nimport { truncateContent, type TruncationContext } from './truncate.js';\n\n/** Maximum neighbors to include in NodeWithContextResponse. */\nexport const MAX_NEIGHBORS = 20;\n\n/** Maximum links to resolve titles for (prevents OOM on nodes with massive link counts). */\nexport const MAX_LINKS_TO_RESOLVE = 100;\n\n/**\n * Transform a Node to NodeResponse, resolving link titles.\n */\nexport async function nodeToResponse(\n node: Node,\n store: Store,\n truncation: TruncationContext\n): Promise<NodeResponse> {\n // Limit links to prevent OOM on nodes with massive outgoing link counts\n const linksToResolve = node.outgoingLinks.slice(0, MAX_LINKS_TO_RESOLVE);\n const titles = await store.resolveTitles(linksToResolve);\n\n const links: LinkInfo[] = linksToResolve.map((id) => ({\n id,\n title: titles.get(id) ?? id,\n }));\n\n return {\n id: node.id,\n title: node.title,\n content: truncateContent(node.content, truncation),\n tags: node.tags,\n links,\n properties: node.properties,\n };\n}\n\n/**\n * Transform multiple nodes to responses, resolving all link titles in batch.\n * When includeContent is false, returns NodeMetadataResponse[] (no content field).\n * When includeContent is true, returns NodeResponse[] (with truncated content).\n */\nexport async function nodesToResponses(\n nodes: Node[],\n store: Store,\n truncation: TruncationContext,\n includeContent: boolean\n): Promise<NodeResponse[] | NodeMetadataResponse[]> {\n // Collect unique link IDs, limiting per-node to prevent OOM\n const allLinkIds = new Set<string>();\n const nodeLinkLimits = new Map<string, string[]>();\n\n for (const node of nodes) {\n const limitedLinks = node.outgoingLinks.slice(0, MAX_LINKS_TO_RESOLVE);\n nodeLinkLimits.set(node.id, limitedLinks);\n for (const linkId of limitedLinks) {\n allLinkIds.add(linkId);\n }\n }\n\n // Batch resolve titles\n const titles = await store.resolveTitles(Array.from(allLinkIds));\n\n // Transform nodes using limited links\n return nodes.map((node) => {\n // Invariant: all node IDs are populated in the loop above\n const limitedLinks = nodeLinkLimits.get(node.id)!;\n const base: NodeMetadataResponse = {\n id: node.id,\n title: node.title,\n tags: node.tags,\n links: limitedLinks.map((id) => ({\n id,\n title: titles.get(id) ?? id,\n })),\n properties: node.properties,\n };\n\n if (includeContent) {\n return {\n ...base,\n content: truncateContent(node.content, truncation),\n } as NodeResponse;\n }\n\n return base;\n });\n}\n\n/**\n * Transform a Node with neighbor context to NodeWithContextResponse.\n */\nexport async function nodeToContextResponse(\n node: Node,\n incomingNeighbors: Node[],\n outgoingNeighbors: Node[],\n store: Store\n): Promise<NodeWithContextResponse> {\n // Get primary node response\n const primary = await nodeToResponse(node, store, 'primary');\n\n // Limit neighbors\n const limitedIncoming = incomingNeighbors.slice(0, MAX_NEIGHBORS);\n const limitedOutgoing = outgoingNeighbors.slice(0, MAX_NEIGHBORS);\n\n // Transform neighbors with neighbor truncation\n // Context responses always include content (truncated for neighbors)\n const [incomingResponses, outgoingResponses] = await Promise.all([\n nodesToResponses(limitedIncoming, store, 'neighbor', true) as Promise<NodeResponse[]>,\n nodesToResponses(limitedOutgoing, store, 'neighbor', true) as Promise<NodeResponse[]>,\n ]);\n\n return {\n ...primary,\n incomingNeighbors: incomingResponses,\n outgoingNeighbors: outgoingResponses,\n incomingCount: incomingNeighbors.length,\n outgoingCount: outgoingNeighbors.length,\n };\n}\n\n/**\n * Transform search results with scores.\n * When includeContent is false, content field is omitted.\n * When includeContent is true, content is truncated and included.\n */\nexport async function nodesToSearchResults(\n nodes: Node[],\n scores: Map<string, number>,\n store: Store,\n includeContent: boolean\n): Promise<SearchResultResponse[]> {\n const responses = await nodesToResponses(nodes, store, 'list', includeContent);\n\n return responses.map((response) => ({\n ...response,\n score: scores.get(response.id) ?? 0,\n }));\n}\n\n/**\n * Transform hub results (id, score pairs) to HubResponse[].\n */\nexport async function hubsToResponses(\n hubs: Array<[string, number]>,\n store: Store\n): Promise<HubResponse[]> {\n const ids = hubs.map(([id]) => id);\n const titles = await store.resolveTitles(ids);\n\n return hubs.map(([id, score]) => ({\n id,\n title: titles.get(id) ?? id,\n score,\n }));\n}\n\n/**\n * Transform a path (array of node IDs) to PathResponse.\n */\nexport function pathToResponse(path: string[]): PathResponse {\n return {\n path,\n length: path.length - 1,\n };\n}\n","import type { HandlerContext } from './types.js';\nimport type { NodeResponse, NodeWithContextResponse } from '../types.js';\nimport { coerceDepth, validateRequiredString } from '../validation.js';\nimport { nodeToResponse, nodeToContextResponse } from '../transforms.js';\n\nexport const schema = {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID (file path for DocStore). ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n depth: {\n type: 'integer',\n minimum: 0,\n maximum: 1,\n default: 0,\n description: '0 = node only, 1 = include neighbors',\n },\n },\n required: ['id'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse | NodeWithContextResponse | null> {\n const id = validateRequiredString(args.id, 'id');\n const depth = coerceDepth(args.depth);\n\n const node = await ctx.core.getNode(id, depth);\n if (!node) {\n return null;\n }\n\n if (depth === 0) {\n return nodeToResponse(node, ctx.store, 'primary');\n }\n\n const [incomingNeighbors, outgoingNeighbors] = await Promise.all([\n ctx.core.getNeighbors(id, { direction: 'in' }),\n ctx.core.getNeighbors(id, { direction: 'out' }),\n ]);\n\n return nodeToContextResponse(node, incomingNeighbors, outgoingNeighbors, ctx.store);\n}\n","import type { HandlerContext } from './types.js';\nimport type { NodeResponse, NodeMetadataResponse } from '../types.js';\nimport { coerceLimit, validateEnum, validateRequiredString } from '../validation.js';\nimport { nodesToResponses } from '../transforms.js';\n\nconst VALID_DIRECTIONS = ['in', 'out', 'both'] as const;\n\nexport const schema = {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Source node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n direction: {\n type: 'string',\n enum: ['in', 'out', 'both'],\n default: 'both',\n description: 'in = nodes linking here, out = nodes linked to, both = all',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 20,\n description: 'Maximum neighbors to return',\n },\n include_content: {\n type: 'boolean',\n default: false,\n description:\n 'Include node content in results. Default false returns metadata only (id, title, tags, properties, links). Set true to include truncated content.',\n },\n },\n required: ['id'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse[] | NodeMetadataResponse[]> {\n const id = validateRequiredString(args.id, 'id');\n const limit = coerceLimit(args.limit, 20);\n const includeContent = args.include_content === true;\n\n const direction = validateEnum(args.direction, VALID_DIRECTIONS, 'direction', 'both');\n\n const neighbors = await ctx.core.getNeighbors(id, { direction, limit });\n return nodesToResponses(neighbors, ctx.store, 'list', includeContent);\n}\n","import type { HandlerContext } from './types.js';\nimport type { PathResponse } from '../types.js';\nimport { validateRequiredString } from '../validation.js';\nimport { pathToResponse } from '../transforms.js';\n\nexport const schema = {\n type: 'object',\n properties: {\n source: {\n type: 'string',\n description:\n 'Start node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n target: {\n type: 'string',\n description:\n 'End node ID. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['source', 'target'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<PathResponse | null> {\n const source = validateRequiredString(args.source, 'source');\n const target = validateRequiredString(args.target, 'target');\n\n const path = await ctx.core.findPath(source, target);\n if (!path) {\n return null;\n }\n\n return pathToResponse(path);\n}\n","import type { HandlerContext } from './types.js';\nimport type { Metric } from '../../types/provider.js';\nimport type { HubResponse } from '../types.js';\nimport { coerceLimit, validateEnum } from '../validation.js';\nimport { hubsToResponses } from '../transforms.js';\n\nconst VALID_METRICS = ['in_degree', 'out_degree'] as const;\n\nexport const schema = {\n type: 'object',\n properties: {\n metric: {\n type: 'string',\n enum: ['in_degree', 'out_degree'],\n default: 'in_degree',\n description: 'Centrality metric',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 50,\n default: 10,\n description: 'Maximum results',\n },\n },\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<HubResponse[]> {\n const limit = coerceLimit(args.limit, 10);\n const metric = validateEnum(args.metric, VALID_METRICS, 'metric', 'in_degree');\n\n const hubs = await ctx.core.getHubs(metric as Metric, limit);\n return hubsToResponses(hubs, ctx.store);\n}\n","import type { HandlerContext } from './types.js';\nimport type { TagMode } from '../../types/provider.js';\nimport type { NodeResponse, NodeMetadataResponse } from '../types.js';\nimport { coerceLimit, validateEnum, validateRequiredTags } from '../validation.js';\nimport { nodesToResponses } from '../transforms.js';\n\nconst VALID_TAG_MODES = ['any', 'all'] as const;\n\nexport const schema = {\n type: 'object',\n properties: {\n tags: {\n type: 'array',\n items: { type: 'string' },\n minItems: 1,\n description: 'Tags to match',\n },\n mode: {\n type: 'string',\n enum: ['any', 'all'],\n default: 'any',\n description: 'any = OR matching, all = AND matching',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 100,\n default: 20,\n description: 'Maximum results',\n },\n include_content: {\n type: 'boolean',\n default: false,\n description:\n 'Include node content in results. Default false returns metadata only.',\n },\n },\n required: ['tags'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse[] | NodeMetadataResponse[]> {\n const tags = validateRequiredTags(args.tags);\n const limit = coerceLimit(args.limit, 20);\n const includeContent = args.include_content === true;\n\n const mode = validateEnum(args.mode, VALID_TAG_MODES, 'mode', 'any');\n\n const nodes = await ctx.core.searchByTags(tags, mode as TagMode, limit);\n return nodesToResponses(nodes, ctx.store, 'list', includeContent);\n}\n","import type { HandlerContext } from './types.js';\nimport type { NodeResponse } from '../types.js';\nimport { validateOptionalTags } from '../validation.js';\nimport { nodeToResponse } from '../transforms.js';\n\nexport const schema = {\n type: 'object',\n properties: {\n tags: {\n type: 'array',\n items: { type: 'string' },\n description: 'Optional: limit to nodes with these tags (any match)',\n },\n },\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse | null> {\n const tags = validateOptionalTags(args.tags);\n\n const node = await ctx.core.getRandomNode(tags);\n if (!node) {\n return null;\n }\n\n return nodeToResponse(node, ctx.store, 'primary');\n}\n","import type { HandlerContext } from './types.js';\nimport type { NodeResponse } from '../types.js';\nimport { McpError } from '../types.js';\nimport { validateRequiredString, validateOptionalTags } from '../validation.js';\nimport { DEFAULT_NAMING, type NamingConventions } from '../../types/config.js';\nimport { nodeToResponse } from '../transforms.js';\n\nexport const schema = {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Full path for new node (must end in .md). Will be lowercased (spaces and special characters preserved). Example: \"notes/My Note.md\" creates \"notes/my note.md\"',\n },\n title: {\n type: 'string',\n description:\n 'Optional display title. Defaults to filename without .md extension.',\n },\n content: {\n type: 'string',\n description: 'Full text content (markdown)',\n },\n tags: {\n type: 'array',\n items: { type: 'string' },\n default: [],\n description: 'Classification tags',\n },\n },\n required: ['id', 'content'],\n} as const;\n\n/**\n * Normalize a raw ID for node creation.\n * Applies filename separator convention (space↔dash), then lowercases.\n */\nexport function normalizeCreateId(\n rawId: string,\n naming: NamingConventions = DEFAULT_NAMING\n): string {\n let normalized = rawId.replace(/\\\\/g, '/').toLowerCase();\n\n if (naming.filename === 'space') {\n normalized = normalized.replace(/-/g, ' ');\n } else {\n normalized = normalized.replace(/ /g, '-');\n }\n\n return normalized;\n}\n\n/**\n * Derive display title from node ID.\n * Extracts filename without extension, applies naming conventions.\n * Returns 'Untitled' for empty or all-special-char filenames.\n */\nexport function deriveTitle(id: string, naming?: NamingConventions): string {\n const basename = id.split('/').pop() || '';\n const rawTitle = basename.replace(/\\.md$/i, '');\n\n if (!rawTitle || !/[a-zA-Z0-9]/.test(rawTitle)) {\n return 'Untitled';\n }\n\n if (!naming) {\n return rawTitle;\n }\n\n const spaced = naming.filename === 'dash'\n ? rawTitle.replace(/-/g, ' ')\n : rawTitle;\n\n switch (naming.title) {\n case 'title':\n return spaced.replace(/\\b\\w/g, (c) => c.toUpperCase());\n case 'sentence':\n return spaced.charAt(0).toUpperCase() + spaced.slice(1);\n case 'as-is':\n return rawTitle;\n }\n}\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse> {\n const idRaw = validateRequiredString(args.id, 'id');\n\n if (!idRaw.toLowerCase().endsWith('.md')) {\n throw new McpError('INVALID_PARAMS', 'id must end with .md extension');\n }\n\n const content = validateRequiredString(args.content, 'content');\n const titleRaw = args.title as string | undefined;\n const tags = validateOptionalTags(args.tags) ?? [];\n\n const id = normalizeCreateId(idRaw, ctx.naming);\n const title = titleRaw ?? deriveTitle(id, ctx.naming);\n\n const existing = await ctx.core.getNode(id);\n if (existing) {\n throw new McpError('NODE_EXISTS', `Node already exists: ${id}`);\n }\n\n const node = await ctx.core.createNode({\n id,\n title,\n content,\n tags,\n });\n\n return nodeToResponse(node, ctx.store, 'primary');\n}\n","import type { HandlerContext } from './types.js';\nimport type { NodeUpdates } from '../../types/node.js';\nimport type { NodeResponse } from '../types.js';\nimport { McpError } from '../types.js';\nimport { validateRequiredString, validateOptionalTags } from '../validation.js';\nimport { nodeToResponse } from '../transforms.js';\n\nexport const schema = {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID to update. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n title: {\n type: 'string',\n description: 'New title (renames file for DocStore)',\n },\n content: {\n type: 'string',\n description: 'New content (replaces entirely)',\n },\n tags: {\n type: 'array',\n items: { type: 'string' },\n description: 'New tags (replaces existing)',\n },\n },\n required: ['id'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodeResponse> {\n const id = validateRequiredString(args.id, 'id');\n const title = args.title as string | undefined;\n const content = args.content as string | undefined;\n const tags = validateOptionalTags(args.tags);\n\n if (title === undefined && content === undefined && tags === undefined) {\n throw new McpError(\n 'INVALID_PARAMS',\n 'At least one of title, content, or tags must be provided'\n );\n }\n\n const existing = await ctx.core.getNode(id);\n if (!existing) {\n throw new McpError('NODE_NOT_FOUND', `Node not found: ${id}`);\n }\n\n if (title !== undefined && title !== existing.title) {\n const incomingNeighbors = await ctx.core.getNeighbors(id, { direction: 'in' });\n if (incomingNeighbors.length > 0) {\n throw new McpError(\n 'LINK_INTEGRITY',\n `Cannot rename node with ${incomingNeighbors.length} incoming links`\n );\n }\n }\n\n const updates: NodeUpdates = {};\n if (title !== undefined) updates.title = title;\n if (content !== undefined) updates.content = content;\n if (tags !== undefined) updates.tags = tags;\n\n const updated = await ctx.core.updateNode(id, updates);\n return nodeToResponse(updated, ctx.store, 'primary');\n}\n","import type { HandlerContext } from './types.js';\nimport type { DeleteResponse } from '../types.js';\nimport { validateRequiredString } from '../validation.js';\n\nexport const schema = {\n type: 'object',\n properties: {\n id: {\n type: 'string',\n description:\n 'Node ID to delete. ID is normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['id'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<DeleteResponse> {\n const id = validateRequiredString(args.id, 'id');\n\n const deleted = await ctx.core.deleteNode(id);\n return { deleted };\n}\n","import type { HandlerContext } from './types.js';\nimport type { ListFilter, NodeSummary } from '../../types/provider.js';\nimport { coerceLimit, coerceOffset } from '../validation.js';\n\nexport interface ListNodesResponse {\n nodes: NodeSummary[];\n total: number;\n}\n\nexport const schema = {\n type: 'object',\n properties: {\n tag: {\n type: 'string',\n description:\n 'Filter by tag from the \"tags\" frontmatter array (case-insensitive). Does NOT search other frontmatter fields like \"type\" or \"category\".',\n },\n path: {\n type: 'string',\n description: 'Filter by path prefix (startsWith, case-insensitive)',\n },\n limit: {\n type: 'integer',\n minimum: 1,\n maximum: 1000,\n default: 100,\n description: 'Maximum results to return',\n },\n offset: {\n type: 'integer',\n minimum: 0,\n default: 0,\n description: 'Skip this many results (for pagination)',\n },\n },\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<ListNodesResponse> {\n const tag = args.tag as string | undefined;\n const path = args.path as string | undefined;\n const limit = coerceLimit(args.limit, 100);\n const offset = coerceOffset(args.offset, 0);\n\n const filter: ListFilter = {};\n if (tag) filter.tag = tag;\n if (path) filter.path = path;\n\n return ctx.core.listNodes(filter, { limit, offset });\n}\n","import type { HandlerContext } from './types.js';\nimport type { ResolveOptions, ResolveStrategy, ResolveResult } from '../../types/provider.js';\nimport { McpError } from '../types.js';\nimport { validateEnum, validateStringArray } from '../validation.js';\n\nconst VALID_STRATEGIES = ['exact', 'fuzzy', 'semantic'] as const;\n\nexport const schema = {\n type: 'object',\n properties: {\n names: {\n type: 'array',\n items: { type: 'string' },\n description: 'Names to resolve to existing nodes',\n },\n strategy: {\n type: 'string',\n enum: ['exact', 'fuzzy', 'semantic'],\n default: 'fuzzy',\n description:\n 'How to match names to nodes. \"exact\": case-insensitive title equality. \"fuzzy\": string similarity (Dice coefficient) — use for typos, misspellings, partial matches. \"semantic\": embedding cosine similarity — use for synonyms or related concepts (NOT typos). Misspellings embed poorly because they produce unrelated vectors.',\n },\n threshold: {\n type: 'number',\n minimum: 0,\n maximum: 1,\n default: 0.7,\n description:\n 'Minimum similarity score (0-1). Lower values match more loosely. For typo tolerance, use fuzzy with threshold 0.5-0.6. Ignored for exact strategy.',\n },\n tag: {\n type: 'string',\n description:\n 'Filter candidates by tag from \"tags\" frontmatter array (case-insensitive)',\n },\n path: {\n type: 'string',\n description: 'Filter candidates by path prefix (case-insensitive)',\n },\n },\n required: ['names'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<ResolveResult[]> {\n const names = validateStringArray(args.names, 'names');\n const threshold = args.threshold as number | undefined;\n const tag = args.tag as string | undefined;\n const path = args.path as string | undefined;\n\n const strategy = validateEnum(\n args.strategy,\n VALID_STRATEGIES,\n 'strategy',\n 'fuzzy'\n ) as ResolveStrategy;\n\n if (strategy === 'semantic' && !ctx.hasEmbedding) {\n throw new McpError('PROVIDER_ERROR', 'Semantic resolution requires embedding provider');\n }\n\n const options: ResolveOptions = { strategy };\n if (threshold !== undefined) options.threshold = threshold;\n if (tag) options.tag = tag;\n if (path) options.path = path;\n\n return ctx.core.resolveNodes(names, options);\n}\n","import type { HandlerContext } from './types.js';\nimport { validateStringArray } from '../validation.js';\n\nexport interface NodesExistResponse {\n [id: string]: boolean;\n}\n\nexport const schema = {\n type: 'object',\n properties: {\n ids: {\n type: 'array',\n items: { type: 'string' },\n description:\n 'Node IDs to check existence. IDs are normalized to lowercase (e.g., \"Recipes/Bulgogi.md\" becomes \"recipes/bulgogi.md\").',\n },\n },\n required: ['ids'],\n} as const;\n\nexport async function handler(\n ctx: HandlerContext,\n args: Record<string, unknown>\n): Promise<NodesExistResponse> {\n const ids = validateStringArray(args.ids, 'ids');\n\n const result = await ctx.store.nodesExist(ids);\n\n const response: NodesExistResponse = {};\n for (const [id, exists] of result) {\n response[id] = exists;\n }\n return response;\n}\n","import type { Tool } from '@modelcontextprotocol/sdk/types.js';\nimport type { HandlerContext } from './types.js';\nimport type { ResolveResult } from '../../types/provider.js';\nimport type {\n NodeResponse,\n NodeWithContextResponse,\n SearchResultResponse,\n HubResponse,\n PathResponse,\n DeleteResponse,\n} from '../types.js';\nimport { McpError } from '../types.js';\nimport type { ListNodesResponse } from './list_nodes.js';\nimport type { NodesExistResponse } from './nodes_exist.js';\n\nimport * as search from './search.js';\nimport * as getNode from './get_node.js';\nimport * as getNeighbors from './get_neighbors.js';\nimport * as findPath from './find_path.js';\nimport * as getHubs from './get_hubs.js';\nimport * as searchByTags from './search_by_tags.js';\nimport * as randomNode from './random_node.js';\nimport * as createNode from './create_node.js';\nimport * as updateNode from './update_node.js';\nimport * as deleteNode from './delete_node.js';\nimport * as listNodes from './list_nodes.js';\nimport * as resolveNodes from './resolve_nodes.js';\nimport * as nodesExist from './nodes_exist.js';\n\nexport type { HandlerContext } from './types.js';\nexport { normalizeCreateId, deriveTitle } from './create_node.js';\nexport { coerceInt } from '../validation.js';\nexport type { ListNodesResponse } from './list_nodes.js';\nexport type { NodesExistResponse } from './nodes_exist.js';\n\ntype Handler = {\n schema: object;\n handler: (ctx: HandlerContext, args: Record<string, unknown>) => Promise<unknown>;\n};\n\nconst handlers: Record<string, Handler> = {\n search,\n get_node: getNode,\n get_neighbors: getNeighbors,\n find_path: findPath,\n get_hubs: getHubs,\n search_by_tags: searchByTags,\n random_node: randomNode,\n create_node: createNode,\n update_node: updateNode,\n delete_node: deleteNode,\n list_nodes: listNodes,\n resolve_nodes: resolveNodes,\n nodes_exist: nodesExist,\n};\n\nconst TOOL_DESCRIPTIONS: Record<string, string> = {\n search: 'Semantic similarity search across all nodes',\n get_node: 'Retrieve a single node by ID with optional neighbor context',\n get_neighbors: 'Get nodes linked to or from a specific node',\n find_path: 'Find the shortest path between two nodes',\n get_hubs: 'Get the most central nodes by graph metric',\n search_by_tags: 'Filter nodes by tags (AND or OR matching)',\n random_node: 'Get a random node for discovery, optionally filtered by tags',\n create_node: 'Create a new node (writes file for DocStore)',\n update_node: 'Update an existing node. Title changes rejected if incoming links exist.',\n delete_node: 'Delete a node by ID',\n list_nodes: 'List nodes with optional filters and pagination. Tag filter searches the \"tags\" frontmatter array only. All IDs returned are lowercase.',\n resolve_nodes: 'Batch resolve names to existing node IDs. Strategy selection: \"exact\" for known titles, \"fuzzy\" for typos/misspellings (e.g., \"chikken\" -> \"chicken\"), \"semantic\" for synonyms/concepts (e.g., \"poultry leg meat\" -> \"chicken thigh\"). Semantic does NOT handle typos — misspellings produce garbage embeddings.',\n nodes_exist: 'Batch check if node IDs exist. IDs are normalized to lowercase before checking.',\n};\n\n/** Cast schema to Tool inputSchema type (readonly arrays → mutable) */\ntype InputSchema = Tool['inputSchema'];\nconst asSchema = (s: unknown): InputSchema => s as InputSchema;\n\nexport function getToolDefinitions(hasEmbedding: boolean): Tool[] {\n // Tool order: read operations first (get/find/search), then mutations (create/update/delete),\n // then batch utilities. Search is prepended only when embedding provider is available.\n const toolOrder = [\n 'get_node',\n 'get_neighbors',\n 'find_path',\n 'get_hubs',\n 'search_by_tags',\n 'random_node',\n 'create_node',\n 'update_node',\n 'delete_node',\n 'list_nodes',\n 'resolve_nodes',\n 'nodes_exist',\n ];\n\n const tools: Tool[] = toolOrder.map((name) => ({\n name,\n description: TOOL_DESCRIPTIONS[name]!,\n inputSchema: asSchema(handlers[name]!.schema),\n }));\n\n if (hasEmbedding) {\n tools.unshift({\n name: 'search',\n description: TOOL_DESCRIPTIONS.search!,\n inputSchema: asSchema(handlers.search!.schema),\n });\n }\n\n return tools;\n}\n\nexport type ToolResult =\n | NodeResponse\n | NodeWithContextResponse\n | SearchResultResponse[]\n | NodeResponse[]\n | HubResponse[]\n | PathResponse\n | DeleteResponse\n | ListNodesResponse\n | ResolveResult[]\n | NodesExistResponse\n | null;\n\nexport async function dispatchTool(\n ctx: HandlerContext,\n name: string,\n args: Record<string, unknown>\n): Promise<ToolResult> {\n const h = handlers[name];\n if (!h) {\n throw new McpError('INVALID_PARAMS', `Unknown tool: ${name}`);\n }\n return h.handler(ctx, args) as Promise<ToolResult>;\n}\n","import { access, writeFile, mkdir } from 'node:fs/promises';\nimport { join, dirname } from 'node:path';\nimport { Cache } from '../../providers/docstore/cache.js';\n\nexport interface VizOptions {\n output?: string | undefined;\n open?: boolean | undefined;\n}\n\nexport interface VizResult {\n outputPath: string;\n nodeCount: number;\n edgeCount: number;\n shouldOpen: boolean;\n}\n\ninterface GraphNode {\n id: string;\n title: string;\n inDegree: number;\n}\n\ninterface GraphEdge {\n source: string;\n target: string;\n}\n\nexport async function vizCommand(\n directory: string,\n options: VizOptions = {}\n): Promise<VizResult> {\n const configPath = join(directory, 'roux.yaml');\n\n // Check if initialized\n try {\n await access(configPath);\n } catch {\n throw new Error(`Directory not initialized. Run 'roux init' first.`);\n }\n\n const cacheDir = join(directory, '.roux');\n const outputPath = options.output ?? join(cacheDir, 'graph.html');\n\n const cache = new Cache(cacheDir);\n\n try {\n const nodes = cache.getAllNodes();\n const graphNodes: GraphNode[] = [];\n const graphEdges: GraphEdge[] = [];\n\n // Build set of existing node IDs for edge filtering\n const existingNodeIds = new Set(nodes.map((n) => n.id));\n\n // Build node list with degree info\n for (const node of nodes) {\n const centrality = cache.getCentrality(node.id);\n graphNodes.push({\n id: node.id,\n title: node.title,\n inDegree: centrality?.inDegree ?? 0,\n });\n\n // Build edges from outgoing links, filtering to existing targets only\n for (const target of node.outgoingLinks) {\n if (existingNodeIds.has(target)) {\n graphEdges.push({\n source: node.id,\n target,\n });\n }\n }\n }\n\n // Generate HTML\n const html = generateHtml(graphNodes, graphEdges);\n\n // Ensure output directory exists\n await mkdir(dirname(outputPath), { recursive: true });\n await writeFile(outputPath, html, 'utf-8');\n\n return {\n outputPath,\n nodeCount: graphNodes.length,\n edgeCount: graphEdges.length,\n shouldOpen: options.open ?? false,\n };\n } finally {\n cache.close();\n }\n}\n\nfunction generateHtml(nodes: GraphNode[], edges: GraphEdge[]): string {\n const nodesJson = JSON.stringify(nodes);\n const edgesJson = JSON.stringify(edges);\n\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Roux Graph Visualization</title>\n <script src=\"https://d3js.org/d3.v7.min.js\"></script>\n <style>\n * { margin: 0; padding: 0; box-sizing: border-box; }\n body { font-family: system-ui, sans-serif; background: #1a1a2e; overflow: hidden; }\n svg { display: block; width: 100vw; height: 100vh; }\n .node circle { cursor: pointer; }\n .node text { fill: #e0e0e0; font-size: 10px; pointer-events: none; }\n .link { stroke: #4a4a6a; stroke-opacity: 0.6; fill: none; }\n .tooltip {\n position: absolute;\n background: #16213e;\n color: #e0e0e0;\n padding: 8px 12px;\n border-radius: 4px;\n font-size: 12px;\n pointer-events: none;\n opacity: 0;\n transition: opacity 0.2s;\n border: 1px solid #4a4a6a;\n }\n </style>\n</head>\n<body>\n <div class=\"tooltip\" id=\"tooltip\"></div>\n <svg></svg>\n <script>\n const nodes = ${nodesJson};\n const links = ${edgesJson};\n\n const width = window.innerWidth;\n const height = window.innerHeight;\n\n const svg = d3.select(\"svg\")\n .attr(\"viewBox\", [0, 0, width, height]);\n\n const g = svg.append(\"g\");\n\n // Zoom behavior\n svg.call(d3.zoom()\n .scaleExtent([0.1, 4])\n .on(\"zoom\", (event) => g.attr(\"transform\", event.transform)));\n\n // Node size based on in-degree\n const maxDegree = Math.max(1, ...nodes.map(n => n.inDegree));\n const nodeRadius = d => 5 + (d.inDegree / maxDegree) * 15;\n\n // Force simulation\n const simulation = d3.forceSimulation(nodes)\n .force(\"link\", d3.forceLink(links).id(d => d.id).distance(100))\n .force(\"charge\", d3.forceManyBody().strength(-200))\n .force(\"center\", d3.forceCenter(width / 2, height / 2))\n .force(\"collision\", d3.forceCollide().radius(d => nodeRadius(d) + 5));\n\n // Draw links with arrows\n svg.append(\"defs\").append(\"marker\")\n .attr(\"id\", \"arrow\")\n .attr(\"viewBox\", \"0 -5 10 10\")\n .attr(\"refX\", 20)\n .attr(\"refY\", 0)\n .attr(\"markerWidth\", 6)\n .attr(\"markerHeight\", 6)\n .attr(\"orient\", \"auto\")\n .append(\"path\")\n .attr(\"fill\", \"#4a4a6a\")\n .attr(\"d\", \"M0,-5L10,0L0,5\");\n\n const link = g.append(\"g\")\n .selectAll(\"line\")\n .data(links)\n .join(\"line\")\n .attr(\"class\", \"link\")\n .attr(\"marker-end\", \"url(#arrow)\");\n\n // Draw nodes\n const node = g.append(\"g\")\n .selectAll(\".node\")\n .data(nodes)\n .join(\"g\")\n .attr(\"class\", \"node\")\n .call(d3.drag()\n .on(\"start\", dragstarted)\n .on(\"drag\", dragged)\n .on(\"end\", dragended));\n\n node.append(\"circle\")\n .attr(\"r\", nodeRadius)\n .attr(\"fill\", \"#0f4c75\")\n .attr(\"stroke\", \"#3282b8\")\n .attr(\"stroke-width\", 2);\n\n node.append(\"text\")\n .attr(\"dx\", d => nodeRadius(d) + 5)\n .attr(\"dy\", 4)\n .text(d => d.title.length > 20 ? d.title.slice(0, 17) + \"...\" : d.title);\n\n // HTML escape function for XSS prevention\n function escapeHtml(str) {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n }\n\n // Tooltip\n const tooltip = d3.select(\"#tooltip\");\n node\n .on(\"mouseover\", (event, d) => {\n tooltip\n .style(\"opacity\", 1)\n .html(\\`<strong>\\${escapeHtml(d.title)}</strong><br>ID: \\${escapeHtml(d.id)}<br>Incoming links: \\${d.inDegree}\\`);\n })\n .on(\"mousemove\", (event) => {\n tooltip\n .style(\"left\", (event.pageX + 10) + \"px\")\n .style(\"top\", (event.pageY - 10) + \"px\");\n })\n .on(\"mouseout\", () => tooltip.style(\"opacity\", 0));\n\n // Simulation tick\n simulation.on(\"tick\", () => {\n link\n .attr(\"x1\", d => d.source.x)\n .attr(\"y1\", d => d.source.y)\n .attr(\"x2\", d => d.target.x)\n .attr(\"y2\", d => d.target.y);\n\n node.attr(\"transform\", d => \\`translate(\\${d.x},\\${d.y})\\`);\n });\n\n function dragstarted(event, d) {\n if (!event.active) simulation.alphaTarget(0.3).restart();\n d.fx = d.x;\n d.fy = d.y;\n }\n\n function dragged(event, d) {\n d.fx = event.x;\n d.fy = event.y;\n }\n\n function dragended(event, d) {\n if (!event.active) simulation.alphaTarget(0);\n d.fx = null;\n d.fy = null;\n }\n </script>\n</body>\n</html>`;\n}\n"],"mappings":"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","names":["resolve","access","join","join","stringSimilarity","join","Database","join","Database","join","join","access","access","readFile","join","writeFile","mkdir","stat","mkdirSync","join","relative","extname","neighbors","resolve","readFile","join","extname","join","extname","readFile","mkdirSync","join","mkdir","writeFile","relative","extname","stat","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","handler","schema","schema","handler","watch","join","access","readFile","access","writeFile","mkdir","join","dirname","join","access","mkdir","dirname","writeFile","resolve"]}
|