{"version":3,"file":"index-meta.d.ts","sourceRoot":"","sources":["../../../src/core/embsearch/index-meta.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAYH,MAAM,WAAW,QAAQ;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,MAAM,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,SAAS;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;CAChC;AAED,mEAAmE;AACnE,wBAAgB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI7D;AAED,sEAAsE;AACtE,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAS3E;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAiBtF;AAED,+EAA+E;AAC/E,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAMrE;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD","sourcesContent":["/**\n * Sidecar metadata for a semantic index store.\n *\n * The Rust store (manifest.json/ids.json/vectors.bin) only knows vector ids;\n * this sidecar (index-meta.json, written next to the store) tracks the source\n * side: per-file freshness (mtime+size, content hash recomputed only when they\n * change) and each file's chunk line-ranges so search hits can be rendered as\n * `path:start-end`. It also pins the chunker version and embedding model id —\n * a mismatch on either triggers a clean rebuild instead of an inconsistent\n * incremental update.\n */\n\nimport { createHash } from \"crypto\";\nimport { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from \"fs\";\n\nimport path, { join } from \"path\";\nimport { getAgentDir } from \"../../config.js\";\nimport { CHUNKER_VERSION } from \"./chunker.js\";\n\nconst META_FILE = \"index-meta.json\";\nconst META_FORMAT_VERSION = 1;\n\nexport interface FileMeta {\n\tmtimeMs: number;\n\tsize: number;\n\t/** SHA-256 of content, hex. Recomputed only when mtime/size differ. */\n\thash: string;\n\t/** Per-chunk 1-based inclusive [start, end] line ranges; index = chunk number. */\n\tchunks: Array<[number, number]>;\n}\n\nexport interface IndexMeta {\n\tformatVersion: number;\n\tchunkerVersion: number;\n\tmodelId: string;\n\t/** Absolute repo root this index was built from. */\n\trepoRoot: string;\n\t/** Last time this index was opened (ms). Enables later GC of dead stores. */\n\tlastUsedMs: number;\n\tfiles: Record<string, FileMeta>;\n}\n\n/** Directory holding the vector store + sidecar for `repoRoot`. */\nexport function getEmbsearchStoreDir(repoRoot: string): string {\n\tconst resolved = path.resolve(repoRoot);\n\tconst hash = createHash(\"sha256\").update(resolved).digest(\"hex\").slice(0, 16);\n\treturn join(getAgentDir(), \"embsearch\", hash);\n}\n\n/** Subdirectory the Rust store lives in (sidecar sits next to it). */\nexport function getVectorStoreDir(storeDir: string): string {\n\treturn join(storeDir, \"store\");\n}\n\nexport function emptyIndexMeta(repoRoot: string, modelId: string): IndexMeta {\n\treturn {\n\t\tformatVersion: META_FORMAT_VERSION,\n\t\tchunkerVersion: CHUNKER_VERSION,\n\t\tmodelId,\n\t\trepoRoot: path.resolve(repoRoot),\n\t\tlastUsedMs: Date.now(),\n\t\tfiles: {},\n\t};\n}\n\n/**\n * Load the sidecar. Returns undefined when absent, unreadable, or built with a\n * different format/chunker/model — callers treat all of those as \"rebuild\".\n */\nexport function loadIndexMeta(storeDir: string, modelId: string): IndexMeta | undefined {\n\tconst file = join(storeDir, META_FILE);\n\tif (!existsSync(file)) return undefined;\n\tlet meta: IndexMeta;\n\ttry {\n\t\tmeta = JSON.parse(readFileSync(file, \"utf-8\")) as IndexMeta;\n\t} catch {\n\t\treturn undefined;\n\t}\n\tif (\n\t\tmeta.formatVersion !== META_FORMAT_VERSION ||\n\t\tmeta.chunkerVersion !== CHUNKER_VERSION ||\n\t\tmeta.modelId !== modelId\n\t) {\n\t\treturn undefined;\n\t}\n\treturn meta;\n}\n\n/** Atomically persist the sidecar (temp + rename, matching the Rust store). */\nexport function saveIndexMeta(storeDir: string, meta: IndexMeta): void {\n\tmkdirSync(storeDir, { recursive: true });\n\tconst file = join(storeDir, META_FILE);\n\tconst tmp = `${file}.tmp`;\n\twriteFileSync(tmp, JSON.stringify(meta));\n\trenameSync(tmp, file);\n}\n\nexport function hashContent(content: string): string {\n\treturn createHash(\"sha256\").update(content).digest(\"hex\");\n}\n"]}