@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/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../node_modules/string-similarity/src/index.js","../src/types/node.ts","../src/types/provider.ts","../src/types/config.ts","../src/providers/docstore/index.ts","../src/providers/docstore/cache.ts","../src/providers/vector/sqlite.ts","../src/providers/docstore/parser.ts","../src/graph/builder.ts","../src/graph/operations.ts","../src/providers/embedding/transformers.ts","../src/core/graphcore.ts","../src/index.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","export interface SourceRef {\n type: 'file' | 'api' | 'manual';\n path?: string;\n lastModified?: Date;\n}\n\n/** The canonical data model. All modules speak Node. */\nexport interface Node {\n /** Store-specific format */\n id: string;\n title: string;\n content: string;\n tags: string[];\n /** By id */\n outgoingLinks: string[];\n properties: Record<string, unknown>;\n sourceRef?: SourceRef;\n}\n\nexport interface NodeWithContext extends Node {\n /** Populated when depth > 0 */\n neighbors?: Node[];\n incomingCount?: number;\n outgoingCount?: number;\n}\n\nexport function isNode(value: unknown): value is Node {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n return (\n typeof obj['id'] === 'string' &&\n typeof obj['title'] === 'string' &&\n typeof obj['content'] === 'string' &&\n Array.isArray(obj['tags']) &&\n obj['tags'].every((t) => typeof t === 'string') &&\n Array.isArray(obj['outgoingLinks']) &&\n obj['outgoingLinks'].every((l) => typeof l === 'string') &&\n typeof obj['properties'] === 'object' &&\n obj['properties'] !== null\n );\n}\n\nexport function isSourceRef(value: unknown): value is SourceRef {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n const validTypes = ['file', 'api', 'manual'];\n return (\n typeof obj['type'] === 'string' &&\n validTypes.includes(obj['type']) &&\n (obj['path'] === undefined || typeof obj['path'] === 'string') &&\n (obj['lastModified'] === undefined || obj['lastModified'] instanceof Date)\n );\n}\n","import type { Node } from './node.js';\nimport type { Direction, NeighborOptions } from './edge.js';\n\nexport type Metric = 'pagerank' | '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/** Data persistence and graph operations. Required provider. */\nexport interface StoreProvider {\n // CRUD\n createNode(node: Node): Promise<void>;\n updateNode(id: string, updates: Partial<Node>): 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): 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 StoreProvider. */\nexport interface EmbeddingProvider {\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 VectorProvider {\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 isVectorProvider(value: unknown): value is VectorProvider {\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// Re-export for convenience\nexport type { Direction, NeighborOptions };\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 /** Embedding regeneration strategy */\n onModelChange: ModelChangeBehavior;\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 /** Defaults to local */\n embedding?: EmbeddingConfig;\n llm?: LLMConfig;\n}\n\nexport interface RouxConfig {\n source?: SourceConfig;\n cache?: CacheConfig;\n system?: SystemConfig;\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","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 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 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","// Roux - Graph Programming Interface\n\nexport const VERSION = '0.1.0';\n\n// Re-export all types\nexport * from './types/index.js';\n\n// Core\nexport { GraphCoreImpl } from './core/graphcore.js';\n\n// Providers\nexport { DocStore } from './providers/docstore/index.js';\nexport { TransformersEmbeddingProvider } from './providers/embedding/index.js';\nexport { SqliteVectorProvider } from './providers/vector/index.js';\n"],"mappings":"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","names":["join","stringSimilarity","Database","join","join","resolve"]}
|
|
1
|
+
{"version":3,"sources":["../node_modules/string-similarity/src/index.js","../src/types/node.ts","../src/types/provider.ts","../src/types/guards.ts","../src/types/config.ts","../src/providers/docstore/index.ts","../src/graph/builder.ts","../src/graph/traversal.ts","../src/utils/heap.ts","../src/graph/analysis.ts","../src/graph/manager.ts","../src/providers/store/resolve.ts","../src/providers/store/index.ts","../src/providers/docstore/cache.ts","../src/providers/docstore/cache/centrality.ts","../src/providers/vector/sqlite.ts","../src/utils/math.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/core/graphcore.ts","../src/index.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","export interface SourceRef {\n type: 'file' | 'api' | 'manual';\n path?: string;\n lastModified?: Date;\n}\n\n/** The canonical data model. All modules speak Node. */\nexport interface Node {\n /** Store-specific format */\n id: string;\n title: string;\n content: string;\n tags: string[];\n /** By id */\n outgoingLinks: string[];\n properties: Record<string, unknown>;\n sourceRef?: SourceRef;\n}\n\n/** Caller-settable fields for updateNode. No id (immutable), no outgoingLinks (derived from content). */\nexport interface NodeUpdates {\n title?: string;\n content?: string;\n tags?: string[];\n properties?: Record<string, unknown>;\n}\n\nexport interface NodeWithContext extends Node {\n /** Populated when depth > 0 */\n neighbors?: Node[];\n incomingCount?: number;\n outgoingCount?: number;\n}\n\nexport function isNode(value: unknown): value is Node {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n\n // Validate required fields\n if (\n typeof obj['id'] !== 'string' ||\n typeof obj['title'] !== 'string' ||\n typeof obj['content'] !== 'string'\n ) {\n return false;\n }\n\n // Validate tags array\n if (\n !Array.isArray(obj['tags']) ||\n !obj['tags'].every((t) => typeof t === 'string')\n ) {\n return false;\n }\n\n // Validate outgoingLinks array\n if (\n !Array.isArray(obj['outgoingLinks']) ||\n !obj['outgoingLinks'].every((l) => typeof l === 'string')\n ) {\n return false;\n }\n\n // Validate properties is a plain object (not null, not array)\n if (\n typeof obj['properties'] !== 'object' ||\n obj['properties'] === null ||\n Array.isArray(obj['properties'])\n ) {\n return false;\n }\n\n // Validate sourceRef if present\n if (obj['sourceRef'] !== undefined && !isSourceRef(obj['sourceRef'])) {\n return false;\n }\n\n return true;\n}\n\nexport function isSourceRef(value: unknown): value is SourceRef {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n const obj = value as Record<string, unknown>;\n const validTypes = ['file', 'api', 'manual'];\n\n if (\n typeof obj['type'] !== 'string' ||\n !validTypes.includes(obj['type'])\n ) {\n return false;\n }\n\n if (obj['path'] !== undefined && typeof obj['path'] !== 'string') {\n return false;\n }\n\n if (obj['lastModified'] !== undefined) {\n if (!(obj['lastModified'] instanceof Date)) {\n return false;\n }\n // Reject Invalid Date objects\n if (isNaN(obj['lastModified'].getTime())) {\n return false;\n }\n }\n\n return true;\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","export type PropertyType =\n | 'string'\n | 'number'\n | 'boolean'\n | 'function'\n | 'object'\n | 'array';\n\nexport interface PropertySchema {\n type: PropertyType;\n optional?: boolean;\n /** For 'string' type, require non-empty */\n nonEmpty?: boolean;\n}\n\nexport type Schema = Record<string, PropertySchema>;\n\n/**\n * Create a type guard function from a schema definition.\n *\n * @example\n * const isUser = createGuard<User>({\n * id: { type: 'string', nonEmpty: true },\n * name: { type: 'string' },\n * age: { type: 'number', optional: true },\n * });\n */\nexport function createGuard<T>(schema: Schema): (value: unknown) => value is T {\n return (value: unknown): value is T => {\n if (value === null || typeof value !== 'object') return false;\n const obj = value as Record<string, unknown>;\n\n for (const [key, spec] of Object.entries(schema)) {\n const val = obj[key];\n\n if (spec.optional && val === undefined) continue;\n\n if (spec.type === 'array') {\n if (!Array.isArray(val)) return false;\n } else if (spec.type === 'object') {\n if (typeof val !== 'object' || val === null) return false;\n } else {\n if (typeof val !== spec.type) return false;\n }\n\n if (spec.nonEmpty && spec.type === 'string' && (val as string).length === 0) {\n return false;\n }\n }\n\n return true;\n };\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","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","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","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 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 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 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 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 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 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, 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","// 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"],"mappings":"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","names":["stat","mkdirSync","join","relative","extname","neighbors","stringSimilarity","Database","join","Database","join","resolve","join","extname","join","extname","mkdirSync","join","relative","extname","stat"]}
|