codevault 1.8.4 → 1.8.5

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.
Files changed (351) hide show
  1. package/README.md +3 -2
  2. package/dist/chunking/token-counter.d.ts.map +1 -1
  3. package/dist/chunking/token-counter.js +16 -10
  4. package/dist/chunking/token-counter.js.map +1 -1
  5. package/dist/cli/commands/index-cmd.d.ts.map +1 -1
  6. package/dist/cli/commands/index-cmd.js +108 -97
  7. package/dist/cli/commands/index-cmd.js.map +1 -1
  8. package/dist/cli/commands/interactive-config.d.ts.map +1 -1
  9. package/dist/cli/commands/interactive-config.js +40 -3
  10. package/dist/cli/commands/interactive-config.js.map +1 -1
  11. package/dist/cli/commands/search-cmd.d.ts.map +1 -1
  12. package/dist/cli/commands/search-cmd.js +11 -7
  13. package/dist/cli/commands/search-cmd.js.map +1 -1
  14. package/dist/cli/commands/search-with-code-cmd.d.ts.map +1 -1
  15. package/dist/cli/commands/search-with-code-cmd.js +3 -1
  16. package/dist/cli/commands/search-with-code-cmd.js.map +1 -1
  17. package/dist/cli/utils.d.ts +56 -0
  18. package/dist/cli/utils.d.ts.map +1 -0
  19. package/dist/cli/utils.js +98 -0
  20. package/dist/cli/utils.js.map +1 -0
  21. package/dist/config/constants.d.ts +4 -0
  22. package/dist/config/constants.d.ts.map +1 -1
  23. package/dist/config/constants.js +2 -0
  24. package/dist/config/constants.js.map +1 -1
  25. package/dist/context/packs.d.ts.map +1 -1
  26. package/dist/context/packs.js +3 -1
  27. package/dist/context/packs.js.map +1 -1
  28. package/dist/core/IndexerEngine.d.ts +2 -0
  29. package/dist/core/IndexerEngine.d.ts.map +1 -1
  30. package/dist/core/IndexerEngine.js +34 -26
  31. package/dist/core/IndexerEngine.js.map +1 -1
  32. package/dist/core/SearchService.d.ts +1 -0
  33. package/dist/core/SearchService.d.ts.map +1 -1
  34. package/dist/core/SearchService.js +18 -12
  35. package/dist/core/SearchService.js.map +1 -1
  36. package/dist/core/batch-indexer.d.ts.map +1 -1
  37. package/dist/core/batch-indexer.js +5 -13
  38. package/dist/core/batch-indexer.js.map +1 -1
  39. package/dist/core/indexing/IndexFinalizationStage.d.ts.map +1 -1
  40. package/dist/core/indexing/IndexFinalizationStage.js +21 -2
  41. package/dist/core/indexing/IndexFinalizationStage.js.map +1 -1
  42. package/dist/core/indexing/IndexState.d.ts +3 -8
  43. package/dist/core/indexing/IndexState.d.ts.map +1 -1
  44. package/dist/core/indexing/IndexState.js.map +1 -1
  45. package/dist/core/indexing/PersistManager.d.ts +1 -1
  46. package/dist/core/indexing/PersistManager.d.ts.map +1 -1
  47. package/dist/core/indexing/PersistManager.js +17 -17
  48. package/dist/core/indexing/PersistManager.js.map +1 -1
  49. package/dist/core/search/HybridFusion.d.ts +0 -1
  50. package/dist/core/search/HybridFusion.d.ts.map +1 -1
  51. package/dist/core/search/HybridFusion.js +2 -14
  52. package/dist/core/search/HybridFusion.js.map +1 -1
  53. package/dist/core/search/ResultMapper.d.ts +0 -1
  54. package/dist/core/search/ResultMapper.d.ts.map +1 -1
  55. package/dist/core/search/ResultMapper.js +2 -13
  56. package/dist/core/search/ResultMapper.js.map +1 -1
  57. package/dist/core/search/SearchContextManager.d.ts +3 -0
  58. package/dist/core/search/SearchContextManager.d.ts.map +1 -1
  59. package/dist/core/search/SearchContextManager.js +15 -2
  60. package/dist/core/search/SearchContextManager.js.map +1 -1
  61. package/dist/core/search.d.ts.map +1 -1
  62. package/dist/core/search.js +9 -4
  63. package/dist/core/search.js.map +1 -1
  64. package/dist/languages/rules.d.ts.map +1 -1
  65. package/dist/languages/rules.js +14 -5
  66. package/dist/languages/rules.js.map +1 -1
  67. package/dist/mcp/schemas.d.ts +2 -2
  68. package/dist/mcp-server.d.ts +1 -0
  69. package/dist/mcp-server.d.ts.map +1 -1
  70. package/dist/mcp-server.js +32 -21
  71. package/dist/mcp-server.js.map +1 -1
  72. package/dist/providers/base.d.ts +3 -2
  73. package/dist/providers/base.d.ts.map +1 -1
  74. package/dist/providers/base.js +3 -1
  75. package/dist/providers/base.js.map +1 -1
  76. package/dist/providers/chat-llm.d.ts +2 -2
  77. package/dist/providers/chat-llm.d.ts.map +1 -1
  78. package/dist/providers/chat-llm.js +4 -1
  79. package/dist/providers/chat-llm.js.map +1 -1
  80. package/dist/providers/openai.d.ts.map +1 -1
  81. package/dist/providers/openai.js +4 -1
  82. package/dist/providers/openai.js.map +1 -1
  83. package/dist/ranking/api-reranker.d.ts.map +1 -1
  84. package/dist/ranking/api-reranker.js +27 -8
  85. package/dist/ranking/api-reranker.js.map +1 -1
  86. package/dist/ranking/symbol-boost.d.ts.map +1 -1
  87. package/dist/ranking/symbol-boost.js +4 -11
  88. package/dist/ranking/symbol-boost.js.map +1 -1
  89. package/dist/search/bm25.d.ts +10 -0
  90. package/dist/search/bm25.d.ts.map +1 -1
  91. package/dist/search/bm25.js +16 -0
  92. package/dist/search/bm25.js.map +1 -1
  93. package/dist/storage/encrypted-chunks.d.ts +3 -0
  94. package/dist/storage/encrypted-chunks.d.ts.map +1 -1
  95. package/dist/storage/encrypted-chunks.js +108 -16
  96. package/dist/storage/encrypted-chunks.js.map +1 -1
  97. package/dist/synthesis/conversational-synthesizer.d.ts +2 -1
  98. package/dist/synthesis/conversational-synthesizer.d.ts.map +1 -1
  99. package/dist/synthesis/conversational-synthesizer.js +6 -1
  100. package/dist/synthesis/conversational-synthesizer.js.map +1 -1
  101. package/dist/synthesis/prompt-builder.d.ts.map +1 -1
  102. package/dist/synthesis/prompt-builder.js +40 -13
  103. package/dist/synthesis/prompt-builder.js.map +1 -1
  104. package/dist/synthesis/synthesizer.d.ts.map +1 -1
  105. package/dist/synthesis/synthesizer.js +14 -2
  106. package/dist/synthesis/synthesizer.js.map +1 -1
  107. package/dist/tests/api-reranker.test.d.ts +2 -0
  108. package/dist/tests/api-reranker.test.d.ts.map +1 -0
  109. package/dist/tests/api-reranker.test.js +575 -0
  110. package/dist/tests/api-reranker.test.js.map +1 -0
  111. package/dist/tests/bm25.test.d.ts +2 -0
  112. package/dist/tests/bm25.test.d.ts.map +1 -0
  113. package/dist/tests/bm25.test.js +340 -0
  114. package/dist/tests/bm25.test.js.map +1 -0
  115. package/dist/tests/chunking/file-grouper.test.d.ts +2 -0
  116. package/dist/tests/chunking/file-grouper.test.d.ts.map +1 -0
  117. package/dist/tests/chunking/file-grouper.test.js +495 -0
  118. package/dist/tests/chunking/file-grouper.test.js.map +1 -0
  119. package/dist/tests/chunking/semantic-chunker.test.d.ts +2 -0
  120. package/dist/tests/chunking/semantic-chunker.test.d.ts.map +1 -0
  121. package/dist/tests/chunking/semantic-chunker.test.js +509 -0
  122. package/dist/tests/chunking/semantic-chunker.test.js.map +1 -0
  123. package/dist/tests/chunking/token-counter.test.d.ts +2 -0
  124. package/dist/tests/chunking/token-counter.test.d.ts.map +1 -0
  125. package/dist/tests/chunking/token-counter.test.js +441 -0
  126. package/dist/tests/chunking/token-counter.test.js.map +1 -0
  127. package/dist/tests/cli/ask-cmd.test.d.ts +2 -0
  128. package/dist/tests/cli/ask-cmd.test.d.ts.map +1 -0
  129. package/dist/tests/cli/ask-cmd.test.js +152 -0
  130. package/dist/tests/cli/ask-cmd.test.js.map +1 -0
  131. package/dist/tests/cli/chat-cmd.test.d.ts +2 -0
  132. package/dist/tests/cli/chat-cmd.test.d.ts.map +1 -0
  133. package/dist/tests/cli/chat-cmd.test.js +118 -0
  134. package/dist/tests/cli/chat-cmd.test.js.map +1 -0
  135. package/dist/tests/cli/config-cmd.test.d.ts +2 -0
  136. package/dist/tests/cli/config-cmd.test.d.ts.map +1 -0
  137. package/dist/tests/cli/config-cmd.test.js +226 -0
  138. package/dist/tests/cli/config-cmd.test.js.map +1 -0
  139. package/dist/tests/cli/context.test.d.ts +2 -0
  140. package/dist/tests/cli/context.test.d.ts.map +1 -0
  141. package/dist/tests/cli/context.test.js +158 -0
  142. package/dist/tests/cli/context.test.js.map +1 -0
  143. package/dist/tests/cli/index-cmd.test.d.ts +2 -0
  144. package/dist/tests/cli/index-cmd.test.d.ts.map +1 -0
  145. package/dist/tests/cli/index-cmd.test.js +89 -0
  146. package/dist/tests/cli/index-cmd.test.js.map +1 -0
  147. package/dist/tests/cli/index.test.d.ts +2 -0
  148. package/dist/tests/cli/index.test.d.ts.map +1 -0
  149. package/dist/tests/cli/index.test.js +167 -0
  150. package/dist/tests/cli/index.test.js.map +1 -0
  151. package/dist/tests/cli/info-cmd.test.d.ts +2 -0
  152. package/dist/tests/cli/info-cmd.test.d.ts.map +1 -0
  153. package/dist/tests/cli/info-cmd.test.js +47 -0
  154. package/dist/tests/cli/info-cmd.test.js.map +1 -0
  155. package/dist/tests/cli/interactive-config.test.d.ts +2 -0
  156. package/dist/tests/cli/interactive-config.test.d.ts.map +1 -0
  157. package/dist/tests/cli/interactive-config.test.js +30 -0
  158. package/dist/tests/cli/interactive-config.test.js.map +1 -0
  159. package/dist/tests/cli/mcp-cmd.test.d.ts +2 -0
  160. package/dist/tests/cli/mcp-cmd.test.d.ts.map +1 -0
  161. package/dist/tests/cli/mcp-cmd.test.js +47 -0
  162. package/dist/tests/cli/mcp-cmd.test.js.map +1 -0
  163. package/dist/tests/cli/search-cmd.test.d.ts +2 -0
  164. package/dist/tests/cli/search-cmd.test.d.ts.map +1 -0
  165. package/dist/tests/cli/search-cmd.test.js +120 -0
  166. package/dist/tests/cli/search-cmd.test.js.map +1 -0
  167. package/dist/tests/cli/search-with-code-cmd.test.d.ts +2 -0
  168. package/dist/tests/cli/search-with-code-cmd.test.d.ts.map +1 -0
  169. package/dist/tests/cli/search-with-code-cmd.test.js +140 -0
  170. package/dist/tests/cli/search-with-code-cmd.test.js.map +1 -0
  171. package/dist/tests/cli/update-cmd.test.d.ts +2 -0
  172. package/dist/tests/cli/update-cmd.test.d.ts.map +1 -0
  173. package/dist/tests/cli/update-cmd.test.js +75 -0
  174. package/dist/tests/cli/update-cmd.test.js.map +1 -0
  175. package/dist/tests/cli/utils.test.d.ts +2 -0
  176. package/dist/tests/cli/utils.test.d.ts.map +1 -0
  177. package/dist/tests/cli/utils.test.js +119 -0
  178. package/dist/tests/cli/utils.test.js.map +1 -0
  179. package/dist/tests/cli/watch-cmd.test.d.ts +2 -0
  180. package/dist/tests/cli/watch-cmd.test.d.ts.map +1 -0
  181. package/dist/tests/cli/watch-cmd.test.js +84 -0
  182. package/dist/tests/cli/watch-cmd.test.js.map +1 -0
  183. package/dist/tests/cli-ui.test.d.ts +2 -0
  184. package/dist/tests/cli-ui.test.d.ts.map +1 -0
  185. package/dist/tests/cli-ui.test.js +608 -0
  186. package/dist/tests/cli-ui.test.js.map +1 -0
  187. package/dist/tests/codemap-io.test.d.ts +2 -0
  188. package/dist/tests/codemap-io.test.d.ts.map +1 -0
  189. package/dist/tests/codemap-io.test.js +992 -0
  190. package/dist/tests/codemap-io.test.js.map +1 -0
  191. package/dist/tests/config/apply-env.test.d.ts +2 -0
  192. package/dist/tests/config/apply-env.test.d.ts.map +1 -0
  193. package/dist/tests/config/apply-env.test.js +717 -0
  194. package/dist/tests/config/apply-env.test.js.map +1 -0
  195. package/dist/tests/config/constants.test.d.ts +2 -0
  196. package/dist/tests/config/constants.test.d.ts.map +1 -0
  197. package/dist/tests/config/constants.test.js +406 -0
  198. package/dist/tests/config/constants.test.js.map +1 -0
  199. package/dist/tests/config/loader.test.d.ts +2 -0
  200. package/dist/tests/config/loader.test.d.ts.map +1 -0
  201. package/dist/tests/config/loader.test.js +716 -0
  202. package/dist/tests/config/loader.test.js.map +1 -0
  203. package/dist/tests/config/resolver.test.d.ts +2 -0
  204. package/dist/tests/config/resolver.test.d.ts.map +1 -0
  205. package/dist/tests/config/resolver.test.js +402 -0
  206. package/dist/tests/config/resolver.test.js.map +1 -0
  207. package/dist/tests/config/types.test.d.ts +2 -0
  208. package/dist/tests/config/types.test.d.ts.map +1 -0
  209. package/dist/tests/config/types.test.js +460 -0
  210. package/dist/tests/config/types.test.js.map +1 -0
  211. package/dist/tests/context-packs.test.d.ts +2 -0
  212. package/dist/tests/context-packs.test.d.ts.map +1 -0
  213. package/dist/tests/context-packs.test.js +826 -0
  214. package/dist/tests/context-packs.test.js.map +1 -0
  215. package/dist/tests/conversational-synthesizer.test.d.ts +2 -0
  216. package/dist/tests/conversational-synthesizer.test.d.ts.map +1 -0
  217. package/dist/tests/conversational-synthesizer.test.js +595 -0
  218. package/dist/tests/conversational-synthesizer.test.js.map +1 -0
  219. package/dist/tests/database.test.d.ts +2 -0
  220. package/dist/tests/database.test.d.ts.map +1 -0
  221. package/dist/tests/database.test.js +965 -0
  222. package/dist/tests/database.test.js.map +1 -0
  223. package/dist/tests/encrypted-chunks.test.d.ts +2 -0
  224. package/dist/tests/encrypted-chunks.test.d.ts.map +1 -0
  225. package/dist/tests/encrypted-chunks.test.js +1470 -0
  226. package/dist/tests/encrypted-chunks.test.js.map +1 -0
  227. package/dist/tests/hybrid.test.d.ts +2 -0
  228. package/dist/tests/hybrid.test.d.ts.map +1 -0
  229. package/dist/tests/hybrid.test.js +456 -0
  230. package/dist/tests/hybrid.test.js.map +1 -0
  231. package/dist/tests/indexer/ChangeQueue.test.d.ts +12 -0
  232. package/dist/tests/indexer/ChangeQueue.test.d.ts.map +1 -0
  233. package/dist/tests/indexer/ChangeQueue.test.js +441 -0
  234. package/dist/tests/indexer/ChangeQueue.test.js.map +1 -0
  235. package/dist/tests/indexer/ProviderManager.test.d.ts +12 -0
  236. package/dist/tests/indexer/ProviderManager.test.d.ts.map +1 -0
  237. package/dist/tests/indexer/ProviderManager.test.js +290 -0
  238. package/dist/tests/indexer/ProviderManager.test.js.map +1 -0
  239. package/dist/tests/indexer/WatchService.test.d.ts +14 -0
  240. package/dist/tests/indexer/WatchService.test.d.ts.map +1 -0
  241. package/dist/tests/indexer/WatchService.test.js +667 -0
  242. package/dist/tests/indexer/WatchService.test.js.map +1 -0
  243. package/dist/tests/indexer/merkle.test.d.ts +11 -0
  244. package/dist/tests/indexer/merkle.test.d.ts.map +1 -0
  245. package/dist/tests/indexer/merkle.test.js +497 -0
  246. package/dist/tests/indexer/merkle.test.js.map +1 -0
  247. package/dist/tests/indexer/update.test.d.ts +10 -0
  248. package/dist/tests/indexer/update.test.d.ts.map +1 -0
  249. package/dist/tests/indexer/update.test.js +317 -0
  250. package/dist/tests/indexer/update.test.js.map +1 -0
  251. package/dist/tests/indexer/watch.test.d.ts +8 -0
  252. package/dist/tests/indexer/watch.test.d.ts.map +1 -0
  253. package/dist/tests/indexer/watch.test.js +95 -0
  254. package/dist/tests/indexer/watch.test.js.map +1 -0
  255. package/dist/tests/integration/index-search.integration.test.js +1 -0
  256. package/dist/tests/integration/index-search.integration.test.js.map +1 -1
  257. package/dist/tests/languages.test.d.ts +2 -0
  258. package/dist/tests/languages.test.d.ts.map +1 -0
  259. package/dist/tests/languages.test.js +575 -0
  260. package/dist/tests/languages.test.js.map +1 -0
  261. package/dist/tests/logger-redaction.test.d.ts +2 -0
  262. package/dist/tests/logger-redaction.test.d.ts.map +1 -0
  263. package/dist/tests/logger-redaction.test.js +48 -0
  264. package/dist/tests/logger-redaction.test.js.map +1 -0
  265. package/dist/tests/logger.test.d.ts +2 -0
  266. package/dist/tests/logger.test.d.ts.map +1 -0
  267. package/dist/tests/logger.test.js +468 -0
  268. package/dist/tests/logger.test.js.map +1 -0
  269. package/dist/tests/markdown-formatter.test.d.ts +2 -0
  270. package/dist/tests/markdown-formatter.test.d.ts.map +1 -0
  271. package/dist/tests/markdown-formatter.test.js +453 -0
  272. package/dist/tests/markdown-formatter.test.js.map +1 -0
  273. package/dist/tests/mcp/tools/use-context-pack.test.d.ts +7 -0
  274. package/dist/tests/mcp/tools/use-context-pack.test.d.ts.map +1 -0
  275. package/dist/tests/mcp/tools/use-context-pack.test.js +505 -0
  276. package/dist/tests/mcp/tools/use-context-pack.test.js.map +1 -0
  277. package/dist/tests/mutex.test.d.ts +2 -0
  278. package/dist/tests/mutex.test.d.ts.map +1 -0
  279. package/dist/tests/mutex.test.js +489 -0
  280. package/dist/tests/mutex.test.js.map +1 -0
  281. package/dist/tests/path-helpers.test.d.ts +2 -0
  282. package/dist/tests/path-helpers.test.d.ts.map +1 -0
  283. package/dist/tests/path-helpers.test.js +332 -0
  284. package/dist/tests/path-helpers.test.js.map +1 -0
  285. package/dist/tests/prompt-builder.test.d.ts +2 -0
  286. package/dist/tests/prompt-builder.test.d.ts.map +1 -0
  287. package/dist/tests/prompt-builder.test.js +417 -0
  288. package/dist/tests/prompt-builder.test.js.map +1 -0
  289. package/dist/tests/providers/base.test.d.ts +2 -0
  290. package/dist/tests/providers/base.test.d.ts.map +1 -0
  291. package/dist/tests/providers/base.test.js +299 -0
  292. package/dist/tests/providers/base.test.js.map +1 -0
  293. package/dist/tests/providers/chat-llm.test.d.ts +2 -0
  294. package/dist/tests/providers/chat-llm.test.d.ts.map +1 -0
  295. package/dist/tests/providers/chat-llm.test.js +435 -0
  296. package/dist/tests/providers/chat-llm.test.js.map +1 -0
  297. package/dist/tests/providers/index.test.d.ts +2 -0
  298. package/dist/tests/providers/index.test.d.ts.map +1 -0
  299. package/dist/tests/providers/index.test.js +204 -0
  300. package/dist/tests/providers/index.test.js.map +1 -0
  301. package/dist/tests/providers/mock.test.d.ts +2 -0
  302. package/dist/tests/providers/mock.test.d.ts.map +1 -0
  303. package/dist/tests/providers/mock.test.js +225 -0
  304. package/dist/tests/providers/mock.test.js.map +1 -0
  305. package/dist/tests/providers/openai.test.d.ts +2 -0
  306. package/dist/tests/providers/openai.test.d.ts.map +1 -0
  307. package/dist/tests/providers/openai.test.js +408 -0
  308. package/dist/tests/providers/openai.test.js.map +1 -0
  309. package/dist/tests/providers/token-counter.test.d.ts +2 -0
  310. package/dist/tests/providers/token-counter.test.d.ts.map +1 -0
  311. package/dist/tests/providers/token-counter.test.js +247 -0
  312. package/dist/tests/providers/token-counter.test.js.map +1 -0
  313. package/dist/tests/rate-limiter.test.js +392 -1
  314. package/dist/tests/rate-limiter.test.js.map +1 -1
  315. package/dist/tests/scope.test.d.ts +2 -0
  316. package/dist/tests/scope.test.d.ts.map +1 -0
  317. package/dist/tests/scope.test.js +529 -0
  318. package/dist/tests/scope.test.js.map +1 -0
  319. package/dist/tests/simple-lru.test.js +377 -0
  320. package/dist/tests/simple-lru.test.js.map +1 -1
  321. package/dist/tests/symbol-boost.test.js +730 -10
  322. package/dist/tests/symbol-boost.test.js.map +1 -1
  323. package/dist/tests/symbols-extract.test.d.ts +2 -0
  324. package/dist/tests/symbols-extract.test.d.ts.map +1 -0
  325. package/dist/tests/symbols-extract.test.js +536 -0
  326. package/dist/tests/symbols-extract.test.js.map +1 -0
  327. package/dist/tests/symbols-graph.test.d.ts +2 -0
  328. package/dist/tests/symbols-graph.test.d.ts.map +1 -0
  329. package/dist/tests/symbols-graph.test.js +656 -0
  330. package/dist/tests/symbols-graph.test.js.map +1 -0
  331. package/dist/tests/synthesizer.test.d.ts +2 -0
  332. package/dist/tests/synthesizer.test.d.ts.map +1 -0
  333. package/dist/tests/synthesizer.test.js +381 -0
  334. package/dist/tests/synthesizer.test.js.map +1 -0
  335. package/dist/types/context-pack.d.ts +3 -3
  336. package/dist/utils/logger.d.ts +5 -1
  337. package/dist/utils/logger.d.ts.map +1 -1
  338. package/dist/utils/logger.js +149 -4
  339. package/dist/utils/logger.js.map +1 -1
  340. package/dist/utils/mutex.d.ts +7 -2
  341. package/dist/utils/mutex.d.ts.map +1 -1
  342. package/dist/utils/mutex.js +31 -7
  343. package/dist/utils/mutex.js.map +1 -1
  344. package/dist/utils/path-helpers.d.ts.map +1 -1
  345. package/dist/utils/path-helpers.js +5 -2
  346. package/dist/utils/path-helpers.js.map +1 -1
  347. package/dist/utils/simple-lru.d.ts +6 -0
  348. package/dist/utils/simple-lru.d.ts.map +1 -1
  349. package/dist/utils/simple-lru.js +26 -0
  350. package/dist/utils/simple-lru.js.map +1 -1
  351. package/package.json +1 -1
@@ -0,0 +1,992 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import fs from 'fs';
4
+ import path from 'path';
5
+ import os from 'os';
6
+ import { resolveCodemapPath, readCodemap, writeCodemap, writeCodemapAsync, readCodemapAsync } from '../codemap/io.js';
7
+ import { normalizeChunkMetadata, normalizeCodemapRecord, DEFAULT_PATH_WEIGHT, DEFAULT_SUCCESS_RATE } from '../types/codemap.js';
8
+ import { attachSymbolGraphToCodemap } from '../symbols/graph.js';
9
+ // ============================================================================
10
+ // Test Utilities
11
+ // ============================================================================
12
+ function createTempDir() {
13
+ return fs.mkdtempSync(path.join(os.tmpdir(), 'codemap-test-'));
14
+ }
15
+ function cleanup(tempDir) {
16
+ if (fs.existsSync(tempDir)) {
17
+ fs.rmSync(tempDir, { recursive: true, force: true });
18
+ }
19
+ }
20
+ // ============================================================================
21
+ // resolveCodemapPath Tests
22
+ // ============================================================================
23
+ test('resolveCodemapPath returns default filename in current directory', () => {
24
+ const result = resolveCodemapPath();
25
+ assert.ok(result.endsWith('codevault.codemap.json'), 'should end with codevault.codemap.json');
26
+ assert.ok(path.isAbsolute(result), 'should return absolute path');
27
+ });
28
+ test('resolveCodemapPath resolves relative to provided basePath', () => {
29
+ const result = resolveCodemapPath('/some/project');
30
+ assert.equal(result, path.resolve('/some/project', 'codevault.codemap.json'));
31
+ });
32
+ test('resolveCodemapPath handles trailing slash in basePath', () => {
33
+ const result = resolveCodemapPath('/some/project/');
34
+ assert.ok(result.includes('codevault.codemap.json'));
35
+ });
36
+ test('resolveCodemapPath handles empty string basePath as current directory', () => {
37
+ const result = resolveCodemapPath('');
38
+ assert.ok(result.endsWith('codevault.codemap.json'));
39
+ });
40
+ // ============================================================================
41
+ // readCodemap Tests (Synchronous)
42
+ // ============================================================================
43
+ test('readCodemap returns empty object when file does not exist', () => {
44
+ const tempDir = createTempDir();
45
+ try {
46
+ const nonExistent = path.join(tempDir, 'nonexistent.json');
47
+ const result = readCodemap(nonExistent);
48
+ assert.deepEqual(result, {});
49
+ }
50
+ finally {
51
+ cleanup(tempDir);
52
+ }
53
+ });
54
+ test('readCodemap parses valid JSON file', () => {
55
+ const tempDir = createTempDir();
56
+ try {
57
+ const filePath = path.join(tempDir, 'codemap.json');
58
+ const testData = {
59
+ 'chunk1': {
60
+ file: 'src/example.ts',
61
+ sha: 'abc123',
62
+ symbol: 'testFunc',
63
+ lang: 'typescript',
64
+ symbol_neighbors: []
65
+ }
66
+ };
67
+ fs.writeFileSync(filePath, JSON.stringify(testData));
68
+ const result = readCodemap(filePath);
69
+ assert.equal(result.chunk1.file, 'src/example.ts');
70
+ assert.equal(result.chunk1.sha, 'abc123');
71
+ assert.equal(result.chunk1.symbol, 'testFunc');
72
+ }
73
+ finally {
74
+ cleanup(tempDir);
75
+ }
76
+ });
77
+ test('readCodemap returns empty object on malformed JSON', () => {
78
+ const tempDir = createTempDir();
79
+ try {
80
+ const filePath = path.join(tempDir, 'malformed.json');
81
+ fs.writeFileSync(filePath, '{ invalid json }');
82
+ const result = readCodemap(filePath);
83
+ assert.deepEqual(result, {});
84
+ }
85
+ finally {
86
+ cleanup(tempDir);
87
+ }
88
+ });
89
+ test('readCodemap normalizes data during read', () => {
90
+ const tempDir = createTempDir();
91
+ try {
92
+ const filePath = path.join(tempDir, 'normalize.json');
93
+ const rawData = {
94
+ 'chunk1': {
95
+ file: 'src/test.ts',
96
+ sha: 'xyz789',
97
+ path_weight: -5, // Should be clamped to 0
98
+ success_rate: 2.0, // Should be clamped to 1
99
+ variableCount: 'invalid', // Should default to 0
100
+ synonyms: ['valid', 123] // Non-string should be filtered
101
+ }
102
+ };
103
+ fs.writeFileSync(filePath, JSON.stringify(rawData));
104
+ const result = readCodemap(filePath);
105
+ assert.equal(result.chunk1.path_weight, 0);
106
+ assert.equal(result.chunk1.success_rate, 1);
107
+ assert.equal(result.chunk1.variableCount, 0);
108
+ assert.deepEqual(result.chunk1.synonyms, ['valid']);
109
+ }
110
+ finally {
111
+ cleanup(tempDir);
112
+ }
113
+ });
114
+ test('readCodemap uses default path when no argument provided', () => {
115
+ // This test verifies the function doesn't throw when called without args
116
+ // The file likely won't exist, so we expect empty object
117
+ const result = readCodemap();
118
+ assert.ok(typeof result === 'object');
119
+ });
120
+ // ============================================================================
121
+ // writeCodemap Tests (Synchronous)
122
+ // ============================================================================
123
+ test('writeCodemap creates file with normalized data', () => {
124
+ const tempDir = createTempDir();
125
+ try {
126
+ const filePath = path.join(tempDir, 'output.json');
127
+ const testData = {
128
+ 'chunk1': {
129
+ file: 'src/example.ts',
130
+ sha: 'abc123',
131
+ symbol: 'myFunc',
132
+ symbol_neighbors: []
133
+ }
134
+ };
135
+ const result = writeCodemap(filePath, testData);
136
+ assert.ok(fs.existsSync(filePath));
137
+ assert.equal(result.chunk1.file, 'src/example.ts');
138
+ assert.equal(result.chunk1.sha, 'abc123');
139
+ const written = JSON.parse(fs.readFileSync(filePath, 'utf8'));
140
+ assert.equal(written.chunk1.file, 'src/example.ts');
141
+ }
142
+ finally {
143
+ cleanup(tempDir);
144
+ }
145
+ });
146
+ test('writeCodemap creates nested directories if needed', () => {
147
+ const tempDir = createTempDir();
148
+ try {
149
+ const nestedPath = path.join(tempDir, 'deep', 'nested', 'dir', 'codemap.json');
150
+ const testData = {
151
+ 'chunk1': { file: 'test.ts', sha: 'hash1', symbol_neighbors: [] }
152
+ };
153
+ writeCodemap(nestedPath, testData);
154
+ assert.ok(fs.existsSync(nestedPath));
155
+ }
156
+ finally {
157
+ cleanup(tempDir);
158
+ }
159
+ });
160
+ test('writeCodemap overwrites existing file', () => {
161
+ const tempDir = createTempDir();
162
+ try {
163
+ const filePath = path.join(tempDir, 'overwrite.json');
164
+ fs.writeFileSync(filePath, '{"old": "data"}');
165
+ const newData = {
166
+ 'new_chunk': { file: 'new.ts', sha: 'newhash', symbol_neighbors: [] }
167
+ };
168
+ writeCodemap(filePath, newData);
169
+ const written = JSON.parse(fs.readFileSync(filePath, 'utf8'));
170
+ assert.ok('new_chunk' in written);
171
+ assert.ok(!('old' in written));
172
+ }
173
+ finally {
174
+ cleanup(tempDir);
175
+ }
176
+ });
177
+ test('writeCodemap handles null/undefined codemap gracefully', () => {
178
+ const tempDir = createTempDir();
179
+ try {
180
+ const filePath = path.join(tempDir, 'null.json');
181
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
182
+ const result = writeCodemap(filePath, null);
183
+ assert.deepEqual(result, {});
184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
+ const result2 = writeCodemap(filePath, undefined);
186
+ assert.deepEqual(result2, {});
187
+ }
188
+ finally {
189
+ cleanup(tempDir);
190
+ }
191
+ });
192
+ test('writeCodemap uses default path when undefined filePath provided', () => {
193
+ // Note: This will write to current directory, so we need to be careful
194
+ const testData = {
195
+ 'chunk1': { file: 'test.ts', sha: 'hash1', symbol_neighbors: [] }
196
+ };
197
+ // The function should not throw
198
+ const result = writeCodemap(undefined, testData);
199
+ assert.ok(typeof result === 'object');
200
+ // Cleanup the file created in current directory
201
+ const defaultPath = resolveCodemapPath('.');
202
+ if (fs.existsSync(defaultPath)) {
203
+ fs.unlinkSync(defaultPath);
204
+ }
205
+ });
206
+ // ============================================================================
207
+ // readCodemapAsync Tests
208
+ // ============================================================================
209
+ test('readCodemapAsync returns empty object when file does not exist', async () => {
210
+ const tempDir = createTempDir();
211
+ try {
212
+ const nonExistent = path.join(tempDir, 'nonexistent.json');
213
+ const result = await readCodemapAsync(nonExistent);
214
+ assert.deepEqual(result, {});
215
+ }
216
+ finally {
217
+ cleanup(tempDir);
218
+ }
219
+ });
220
+ test('readCodemapAsync parses valid JSON file', async () => {
221
+ const tempDir = createTempDir();
222
+ try {
223
+ const filePath = path.join(tempDir, 'async-read.json');
224
+ const testData = {
225
+ 'async_chunk': {
226
+ file: 'async.ts',
227
+ sha: 'asynchash',
228
+ symbol: 'asyncFunc',
229
+ symbol_neighbors: []
230
+ }
231
+ };
232
+ fs.writeFileSync(filePath, JSON.stringify(testData));
233
+ const result = await readCodemapAsync(filePath);
234
+ assert.equal(result.async_chunk.file, 'async.ts');
235
+ assert.equal(result.async_chunk.symbol, 'asyncFunc');
236
+ }
237
+ finally {
238
+ cleanup(tempDir);
239
+ }
240
+ });
241
+ test('readCodemapAsync returns empty object on malformed JSON', async () => {
242
+ const tempDir = createTempDir();
243
+ try {
244
+ const filePath = path.join(tempDir, 'async-malformed.json');
245
+ fs.writeFileSync(filePath, 'not valid json {{{');
246
+ const result = await readCodemapAsync(filePath);
247
+ assert.deepEqual(result, {});
248
+ }
249
+ finally {
250
+ cleanup(tempDir);
251
+ }
252
+ });
253
+ test('readCodemapAsync normalizes data during read', async () => {
254
+ const tempDir = createTempDir();
255
+ try {
256
+ const filePath = path.join(tempDir, 'async-normalize.json');
257
+ const rawData = {
258
+ 'chunk1': {
259
+ file: 'test.ts',
260
+ sha: 'hash',
261
+ success_rate: -0.5 // Should be clamped to 0
262
+ }
263
+ };
264
+ fs.writeFileSync(filePath, JSON.stringify(rawData));
265
+ const result = await readCodemapAsync(filePath);
266
+ assert.equal(result.chunk1.success_rate, 0);
267
+ }
268
+ finally {
269
+ cleanup(tempDir);
270
+ }
271
+ });
272
+ // ============================================================================
273
+ // writeCodemapAsync Tests
274
+ // ============================================================================
275
+ test('writeCodemapAsync creates file with normalized data', async () => {
276
+ const tempDir = createTempDir();
277
+ try {
278
+ const filePath = path.join(tempDir, 'async-write.json');
279
+ const testData = {
280
+ 'async_chunk': {
281
+ file: 'async.ts',
282
+ sha: 'asynchash',
283
+ symbol: 'myAsyncFunc',
284
+ symbol_neighbors: []
285
+ }
286
+ };
287
+ const result = await writeCodemapAsync(filePath, testData);
288
+ assert.ok(fs.existsSync(filePath));
289
+ assert.equal(result.async_chunk.file, 'async.ts');
290
+ }
291
+ finally {
292
+ cleanup(tempDir);
293
+ }
294
+ });
295
+ test('writeCodemapAsync creates nested directories', async () => {
296
+ const tempDir = createTempDir();
297
+ try {
298
+ const nestedPath = path.join(tempDir, 'async', 'nested', 'path', 'codemap.json');
299
+ const testData = {
300
+ 'chunk1': { file: 'test.ts', sha: 'hash1', symbol_neighbors: [] }
301
+ };
302
+ await writeCodemapAsync(nestedPath, testData);
303
+ assert.ok(fs.existsSync(nestedPath));
304
+ }
305
+ finally {
306
+ cleanup(tempDir);
307
+ }
308
+ });
309
+ test('writeCodemapAsync handles null codemap', async () => {
310
+ const tempDir = createTempDir();
311
+ try {
312
+ const filePath = path.join(tempDir, 'async-null.json');
313
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
314
+ const result = await writeCodemapAsync(filePath, null);
315
+ assert.deepEqual(result, {});
316
+ }
317
+ finally {
318
+ cleanup(tempDir);
319
+ }
320
+ });
321
+ // ============================================================================
322
+ // normalizeChunkMetadata Tests
323
+ // ============================================================================
324
+ test('normalizeChunkMetadata handles minimal valid input', () => {
325
+ const raw = {
326
+ file: 'test.ts',
327
+ sha: 'abc123'
328
+ };
329
+ const result = normalizeChunkMetadata(raw);
330
+ assert.equal(result.file, 'test.ts');
331
+ assert.equal(result.sha, 'abc123');
332
+ assert.equal(result.lang, 'unknown');
333
+ assert.equal(result.symbol, null);
334
+ assert.equal(result.path_weight, DEFAULT_PATH_WEIGHT);
335
+ assert.equal(result.success_rate, DEFAULT_SUCCESS_RATE);
336
+ assert.equal(result.encrypted, false);
337
+ assert.deepEqual(result.symbol_neighbors, []);
338
+ });
339
+ test('normalizeChunkMetadata handles empty/invalid file and sha', () => {
340
+ const raw = {
341
+ file: '',
342
+ sha: null
343
+ };
344
+ const result = normalizeChunkMetadata(raw);
345
+ assert.equal(result.file, 'unknown');
346
+ assert.equal(result.sha, 'unknown');
347
+ });
348
+ test('normalizeChunkMetadata clamps path_weight to non-negative', () => {
349
+ const result1 = normalizeChunkMetadata({ file: 'a', sha: 'b', path_weight: -10 });
350
+ assert.equal(result1.path_weight, 0);
351
+ const result2 = normalizeChunkMetadata({ file: 'a', sha: 'b', path_weight: 5.5 });
352
+ assert.equal(result2.path_weight, 5.5);
353
+ });
354
+ test('normalizeChunkMetadata clamps success_rate to 0-1 range', () => {
355
+ const result1 = normalizeChunkMetadata({ file: 'a', sha: 'b', success_rate: -0.5 });
356
+ assert.equal(result1.success_rate, 0);
357
+ const result2 = normalizeChunkMetadata({ file: 'a', sha: 'b', success_rate: 2.0 });
358
+ assert.equal(result2.success_rate, 1);
359
+ const result3 = normalizeChunkMetadata({ file: 'a', sha: 'b', success_rate: 0.75 });
360
+ assert.equal(result3.success_rate, 0.75);
361
+ });
362
+ test('normalizeChunkMetadata handles NaN success_rate', () => {
363
+ const result = normalizeChunkMetadata({ file: 'a', sha: 'b', success_rate: NaN });
364
+ assert.equal(result.success_rate, DEFAULT_SUCCESS_RATE);
365
+ });
366
+ test('normalizeChunkMetadata sanitizes variableCount', () => {
367
+ const result1 = normalizeChunkMetadata({ file: 'a', sha: 'b', variableCount: -5 });
368
+ assert.equal(result1.variableCount, 0);
369
+ const result2 = normalizeChunkMetadata({ file: 'a', sha: 'b', variableCount: 3.7 });
370
+ assert.equal(result2.variableCount, 4);
371
+ const result3 = normalizeChunkMetadata({ file: 'a', sha: 'b', variableCount: 'invalid' });
372
+ assert.equal(result3.variableCount, 0);
373
+ const result4 = normalizeChunkMetadata({ file: 'a', sha: 'b', variableCount: Infinity });
374
+ assert.equal(result4.variableCount, 0);
375
+ });
376
+ test('normalizeChunkMetadata sanitizes string arrays (synonyms, symbol_calls, etc.)', () => {
377
+ const raw = {
378
+ file: 'test.ts',
379
+ sha: 'hash',
380
+ synonyms: ['valid', 123, null, '', ' trimmed ', 'duplicate', 'duplicate'],
381
+ symbol_calls: ['funcA', 'funcB', 42],
382
+ symbol_parameters: ['param1', 'param2']
383
+ };
384
+ const result = normalizeChunkMetadata(raw);
385
+ assert.deepEqual(result.synonyms, ['valid', 'trimmed', 'duplicate']);
386
+ assert.deepEqual(result.symbol_calls, ['funcA', 'funcB']);
387
+ assert.deepEqual(result.symbol_parameters, ['param1', 'param2']);
388
+ });
389
+ test('normalizeChunkMetadata handles symbol_neighbors deduplication', () => {
390
+ const raw = {
391
+ file: 'test.ts',
392
+ sha: 'hash',
393
+ symbol_neighbors: ['neighbor1', 'neighbor1', 'neighbor2', ' neighbor3 ']
394
+ };
395
+ const result = normalizeChunkMetadata(raw);
396
+ assert.deepEqual(result.symbol_neighbors, ['neighbor1', 'neighbor2', 'neighbor3']);
397
+ });
398
+ test('normalizeChunkMetadata handles last_used_at date parsing', () => {
399
+ const validDate = '2024-01-15T10:30:00.000Z';
400
+ const result1 = normalizeChunkMetadata({ file: 'a', sha: 'b', last_used_at: validDate });
401
+ assert.equal(result1.last_used_at, validDate);
402
+ const result2 = normalizeChunkMetadata({ file: 'a', sha: 'b', last_used_at: 'invalid-date' });
403
+ assert.equal(result2.last_used_at, undefined);
404
+ const result3 = normalizeChunkMetadata({ file: 'a', sha: 'b', last_used_at: null });
405
+ assert.equal(result3.last_used_at, undefined);
406
+ });
407
+ test('normalizeChunkMetadata handles symbol_signature', () => {
408
+ const result1 = normalizeChunkMetadata({ file: 'a', sha: 'b', symbol_signature: 'myFunc(a, b)' });
409
+ assert.equal(result1.symbol_signature, 'myFunc(a, b)');
410
+ const result2 = normalizeChunkMetadata({ file: 'a', sha: 'b', symbol_signature: ' ' });
411
+ assert.equal(result2.symbol_signature, undefined);
412
+ const result3 = normalizeChunkMetadata({ file: 'a', sha: 'b', symbol_signature: 123 });
413
+ assert.equal(result3.symbol_signature, undefined);
414
+ });
415
+ test('normalizeChunkMetadata handles symbol_return', () => {
416
+ const result1 = normalizeChunkMetadata({ file: 'a', sha: 'b', symbol_return: 'Promise<void>' });
417
+ assert.equal(result1.symbol_return, 'Promise<void>');
418
+ const result2 = normalizeChunkMetadata({ file: 'a', sha: 'b', symbol_return: '' });
419
+ assert.equal(result2.symbol_return, undefined);
420
+ });
421
+ test('normalizeChunkMetadata preserves boolean fields', () => {
422
+ const raw = {
423
+ file: 'test.ts',
424
+ sha: 'hash',
425
+ hasCodevaultTags: true,
426
+ hasIntent: true,
427
+ hasDocumentation: true,
428
+ encrypted: true
429
+ };
430
+ const result = normalizeChunkMetadata(raw);
431
+ assert.equal(result.hasCodevaultTags, true);
432
+ assert.equal(result.hasIntent, true);
433
+ assert.equal(result.hasDocumentation, true);
434
+ assert.equal(result.encrypted, true);
435
+ });
436
+ test('normalizeChunkMetadata defaults boolean fields to false', () => {
437
+ const result = normalizeChunkMetadata({ file: 'a', sha: 'b' });
438
+ assert.equal(result.hasCodevaultTags, false);
439
+ assert.equal(result.hasIntent, false);
440
+ assert.equal(result.hasDocumentation, false);
441
+ assert.equal(result.encrypted, false);
442
+ });
443
+ test('normalizeChunkMetadata handles provider and dimensions', () => {
444
+ const result1 = normalizeChunkMetadata({
445
+ file: 'a',
446
+ sha: 'b',
447
+ provider: 'openai',
448
+ dimensions: 1536
449
+ });
450
+ assert.equal(result1.provider, 'openai');
451
+ assert.equal(result1.dimensions, 1536);
452
+ const result2 = normalizeChunkMetadata({
453
+ file: 'a',
454
+ sha: 'b',
455
+ provider: '',
456
+ dimensions: NaN
457
+ });
458
+ assert.equal(result2.provider, undefined);
459
+ assert.equal(result2.dimensions, undefined);
460
+ });
461
+ test('normalizeChunkMetadata preserves extra fields (passthrough)', () => {
462
+ const raw = {
463
+ file: 'test.ts',
464
+ sha: 'hash',
465
+ customField: 'customValue',
466
+ anotherCustom: 42
467
+ };
468
+ const result = normalizeChunkMetadata(raw);
469
+ assert.equal(result.customField, 'customValue');
470
+ assert.equal(result.anotherCustom, 42);
471
+ });
472
+ test('normalizeChunkMetadata merges with previous chunk data', () => {
473
+ const previous = {
474
+ file: 'old.ts',
475
+ sha: 'oldhash',
476
+ symbol: 'oldSymbol',
477
+ lang: 'typescript',
478
+ path_weight: 2.0,
479
+ success_rate: 0.8,
480
+ symbol_neighbors: ['neighbor1']
481
+ };
482
+ const incoming = {
483
+ sha: 'newhash',
484
+ symbol: 'newSymbol'
485
+ };
486
+ const result = normalizeChunkMetadata(incoming, previous);
487
+ assert.equal(result.file, 'old.ts'); // From previous
488
+ assert.equal(result.sha, 'newhash'); // Overwritten
489
+ assert.equal(result.symbol, 'newSymbol'); // Overwritten
490
+ assert.equal(result.lang, 'typescript'); // From previous
491
+ assert.equal(result.path_weight, 2.0); // From previous
492
+ });
493
+ test('normalizeChunkMetadata handles null/undefined input', () => {
494
+ const result1 = normalizeChunkMetadata(null);
495
+ assert.equal(result1.file, 'unknown');
496
+ assert.equal(result1.sha, 'unknown');
497
+ const result2 = normalizeChunkMetadata(undefined);
498
+ assert.equal(result2.file, 'unknown');
499
+ assert.equal(result2.sha, 'unknown');
500
+ });
501
+ // ============================================================================
502
+ // normalizeCodemapRecord Tests
503
+ // ============================================================================
504
+ test('normalizeCodemapRecord handles empty input', () => {
505
+ assert.deepEqual(normalizeCodemapRecord(null), {});
506
+ assert.deepEqual(normalizeCodemapRecord(undefined), {});
507
+ assert.deepEqual(normalizeCodemapRecord({}), {});
508
+ assert.deepEqual(normalizeCodemapRecord('string'), {});
509
+ assert.deepEqual(normalizeCodemapRecord(123), {});
510
+ });
511
+ test('normalizeCodemapRecord normalizes all chunks', () => {
512
+ const raw = {
513
+ 'chunk1': { file: 'a.ts', sha: 'hash1', path_weight: -1 },
514
+ 'chunk2': { file: 'b.ts', sha: 'hash2', success_rate: 5 }
515
+ };
516
+ const result = normalizeCodemapRecord(raw);
517
+ assert.equal(result.chunk1.path_weight, 0);
518
+ assert.equal(result.chunk2.success_rate, 1);
519
+ });
520
+ test('normalizeCodemapRecord sorts chunks alphabetically by key', () => {
521
+ const raw = {
522
+ 'z_chunk': { file: 'z.ts', sha: 'z' },
523
+ 'a_chunk': { file: 'a.ts', sha: 'a' },
524
+ 'm_chunk': { file: 'm.ts', sha: 'm' }
525
+ };
526
+ const result = normalizeCodemapRecord(raw);
527
+ const keys = Object.keys(result);
528
+ assert.deepEqual(keys, ['a_chunk', 'm_chunk', 'z_chunk']);
529
+ });
530
+ test('normalizeCodemapRecord filters out empty string keys', () => {
531
+ const raw = {
532
+ '': { file: 'empty.ts', sha: 'empty' },
533
+ 'valid': { file: 'valid.ts', sha: 'valid' }
534
+ };
535
+ const result = normalizeCodemapRecord(raw);
536
+ assert.ok(!('' in result));
537
+ assert.ok('valid' in result);
538
+ });
539
+ // ============================================================================
540
+ // attachSymbolGraphToCodemap Tests
541
+ // ============================================================================
542
+ test('attachSymbolGraphToCodemap handles null/undefined input', () => {
543
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
544
+ assert.equal(attachSymbolGraphToCodemap(null), null);
545
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
546
+ assert.equal(attachSymbolGraphToCodemap(undefined), undefined);
547
+ });
548
+ test('attachSymbolGraphToCodemap handles empty codemap', () => {
549
+ const result = attachSymbolGraphToCodemap({});
550
+ assert.deepEqual(result, {});
551
+ });
552
+ test('attachSymbolGraphToCodemap builds call targets from symbol_calls', () => {
553
+ const codemap = {
554
+ 'chunk1': {
555
+ file: 'src/main.ts',
556
+ sha: 'sha1',
557
+ symbol: 'main',
558
+ symbol_calls: ['helper'],
559
+ symbol_neighbors: []
560
+ },
561
+ 'chunk2': {
562
+ file: 'src/utils.ts',
563
+ sha: 'sha2',
564
+ symbol: 'helper',
565
+ symbol_calls: [],
566
+ symbol_neighbors: []
567
+ }
568
+ };
569
+ const result = attachSymbolGraphToCodemap(codemap);
570
+ // chunk1 calls helper (chunk2)
571
+ assert.deepEqual(result.chunk1.symbol_call_targets, ['sha2']);
572
+ // chunk2 is called by chunk1
573
+ assert.deepEqual(result.chunk2.symbol_callers, ['sha1']);
574
+ // Both should have neighbors
575
+ assert.ok(result.chunk1.symbol_neighbors?.includes('sha2'));
576
+ assert.ok(result.chunk2.symbol_neighbors?.includes('sha1'));
577
+ });
578
+ test('attachSymbolGraphToCodemap handles case-insensitive symbol matching', () => {
579
+ const codemap = {
580
+ 'chunk1': {
581
+ file: 'main.ts',
582
+ sha: 'sha1',
583
+ symbol: 'caller',
584
+ symbol_calls: ['ProcessData'], // Mixed case call
585
+ symbol_neighbors: []
586
+ },
587
+ 'chunk2': {
588
+ file: 'utils.ts',
589
+ sha: 'sha2',
590
+ symbol: 'processData', // camelCase definition
591
+ symbol_calls: [],
592
+ symbol_neighbors: []
593
+ }
594
+ };
595
+ const result = attachSymbolGraphToCodemap(codemap);
596
+ // Should match despite case difference
597
+ assert.deepEqual(result.chunk1.symbol_call_targets, ['sha2']);
598
+ });
599
+ test('attachSymbolGraphToCodemap prefers same-file candidates', () => {
600
+ const codemap = {
601
+ 'chunk1': {
602
+ file: 'src/main.ts',
603
+ sha: 'sha1',
604
+ symbol: 'caller',
605
+ symbol_calls: ['helper'],
606
+ symbol_neighbors: []
607
+ },
608
+ 'chunk2': {
609
+ file: 'src/main.ts', // Same file as caller
610
+ sha: 'sha2',
611
+ symbol: 'helper',
612
+ symbol_calls: [],
613
+ symbol_neighbors: []
614
+ },
615
+ 'chunk3': {
616
+ file: 'src/other.ts', // Different file
617
+ sha: 'sha3',
618
+ symbol: 'helper', // Same symbol name
619
+ symbol_calls: [],
620
+ symbol_neighbors: []
621
+ }
622
+ };
623
+ const result = attachSymbolGraphToCodemap(codemap);
624
+ // Should prefer chunk2 (same file) over chunk3
625
+ assert.deepEqual(result.chunk1.symbol_call_targets, ['sha2']);
626
+ });
627
+ test('attachSymbolGraphToCodemap ignores self-references', () => {
628
+ const codemap = {
629
+ 'chunk1': {
630
+ file: 'main.ts',
631
+ sha: 'sha1',
632
+ symbol: 'recursive',
633
+ symbol_calls: ['recursive'], // Calls itself
634
+ symbol_neighbors: []
635
+ }
636
+ };
637
+ const result = attachSymbolGraphToCodemap(codemap);
638
+ // Should not include self-reference
639
+ assert.deepEqual(result.chunk1.symbol_call_targets, []);
640
+ assert.deepEqual(result.chunk1.symbol_callers, []);
641
+ });
642
+ test('attachSymbolGraphToCodemap handles chunks without symbols', () => {
643
+ const codemap = {
644
+ 'chunk1': {
645
+ file: 'main.ts',
646
+ sha: 'sha1',
647
+ symbol: null,
648
+ symbol_calls: ['helper'],
649
+ symbol_neighbors: []
650
+ },
651
+ 'chunk2': {
652
+ file: 'utils.ts',
653
+ sha: 'sha2',
654
+ symbol: 'helper',
655
+ symbol_calls: [],
656
+ symbol_neighbors: []
657
+ }
658
+ };
659
+ const result = attachSymbolGraphToCodemap(codemap);
660
+ // chunk1 can still call chunk2
661
+ assert.deepEqual(result.chunk1.symbol_call_targets, ['sha2']);
662
+ });
663
+ test('attachSymbolGraphToCodemap handles missing symbol_calls array', () => {
664
+ const codemap = {
665
+ 'chunk1': {
666
+ file: 'main.ts',
667
+ sha: 'sha1',
668
+ symbol: 'main',
669
+ symbol_neighbors: []
670
+ // symbol_calls is missing
671
+ }
672
+ };
673
+ const result = attachSymbolGraphToCodemap(codemap);
674
+ assert.deepEqual(result.chunk1.symbol_call_targets, []);
675
+ assert.deepEqual(result.chunk1.symbol_callers, []);
676
+ });
677
+ test('attachSymbolGraphToCodemap handles complex call graph', () => {
678
+ const codemap = {
679
+ 'a': {
680
+ file: 'a.ts',
681
+ sha: 'sha_a',
682
+ symbol: 'funcA',
683
+ symbol_calls: ['funcB', 'funcC'],
684
+ symbol_neighbors: []
685
+ },
686
+ 'b': {
687
+ file: 'b.ts',
688
+ sha: 'sha_b',
689
+ symbol: 'funcB',
690
+ symbol_calls: ['funcC'],
691
+ symbol_neighbors: []
692
+ },
693
+ 'c': {
694
+ file: 'c.ts',
695
+ sha: 'sha_c',
696
+ symbol: 'funcC',
697
+ symbol_calls: [],
698
+ symbol_neighbors: []
699
+ }
700
+ };
701
+ const result = attachSymbolGraphToCodemap(codemap);
702
+ // A calls B and C
703
+ assert.ok(result.a.symbol_call_targets?.includes('sha_b'));
704
+ assert.ok(result.a.symbol_call_targets?.includes('sha_c'));
705
+ // B calls C
706
+ assert.deepEqual(result.b.symbol_call_targets, ['sha_c']);
707
+ // C is called by A and B
708
+ assert.ok(result.c.symbol_callers?.includes('sha_a'));
709
+ assert.ok(result.c.symbol_callers?.includes('sha_b'));
710
+ // Neighbors include both incoming and outgoing
711
+ assert.ok(result.c.symbol_neighbors?.includes('sha_a'));
712
+ assert.ok(result.c.symbol_neighbors?.includes('sha_b'));
713
+ });
714
+ test('attachSymbolGraphToCodemap ignores empty symbol calls', () => {
715
+ const codemap = {
716
+ 'chunk1': {
717
+ file: 'main.ts',
718
+ sha: 'sha1',
719
+ symbol: 'main',
720
+ symbol_calls: ['', ' ', 'validCall'],
721
+ symbol_neighbors: []
722
+ },
723
+ 'chunk2': {
724
+ file: 'utils.ts',
725
+ sha: 'sha2',
726
+ symbol: 'validCall',
727
+ symbol_calls: [],
728
+ symbol_neighbors: []
729
+ }
730
+ };
731
+ const result = attachSymbolGraphToCodemap(codemap);
732
+ // Should only resolve the valid call
733
+ assert.deepEqual(result.chunk1.symbol_call_targets, ['sha2']);
734
+ });
735
+ test('attachSymbolGraphToCodemap handles unresolved calls gracefully', () => {
736
+ const codemap = {
737
+ 'chunk1': {
738
+ file: 'main.ts',
739
+ sha: 'sha1',
740
+ symbol: 'main',
741
+ symbol_calls: ['nonExistentFunc', 'anotherMissing'],
742
+ symbol_neighbors: []
743
+ }
744
+ };
745
+ const result = attachSymbolGraphToCodemap(codemap);
746
+ // Unresolved calls should result in empty arrays
747
+ assert.deepEqual(result.chunk1.symbol_call_targets, []);
748
+ assert.deepEqual(result.chunk1.symbol_callers, []);
749
+ assert.deepEqual(result.chunk1.symbol_neighbors, []);
750
+ });
751
+ // ============================================================================
752
+ // Round-trip Tests (Serialization/Deserialization)
753
+ // ============================================================================
754
+ test('round-trip: write and read codemap preserves data', async () => {
755
+ const tempDir = createTempDir();
756
+ try {
757
+ const filePath = path.join(tempDir, 'roundtrip.json');
758
+ const original = {
759
+ 'chunk1': {
760
+ file: 'src/main.ts',
761
+ sha: 'abc123def456',
762
+ symbol: 'processData',
763
+ lang: 'typescript',
764
+ chunkType: 'function',
765
+ provider: 'openai',
766
+ dimensions: 1536,
767
+ hasCodevaultTags: true,
768
+ hasIntent: false,
769
+ hasDocumentation: true,
770
+ variableCount: 5,
771
+ synonyms: ['process', 'handle', 'transform'],
772
+ path_weight: 1.5,
773
+ success_rate: 0.85,
774
+ encrypted: false,
775
+ symbol_signature: 'processData(input: string): Promise<Result>',
776
+ symbol_parameters: ['input'],
777
+ symbol_return: 'Promise<Result>',
778
+ symbol_calls: ['validate', 'transform', 'save'],
779
+ symbol_call_targets: ['sha_validate', 'sha_transform', 'sha_save'],
780
+ symbol_callers: ['sha_main'],
781
+ symbol_neighbors: ['sha_validate', 'sha_transform', 'sha_save', 'sha_main']
782
+ }
783
+ };
784
+ writeCodemap(filePath, original);
785
+ const loaded = readCodemap(filePath);
786
+ assert.equal(loaded.chunk1.file, original.chunk1.file);
787
+ assert.equal(loaded.chunk1.sha, original.chunk1.sha);
788
+ assert.equal(loaded.chunk1.symbol, original.chunk1.symbol);
789
+ assert.equal(loaded.chunk1.lang, original.chunk1.lang);
790
+ assert.equal(loaded.chunk1.provider, original.chunk1.provider);
791
+ assert.equal(loaded.chunk1.dimensions, original.chunk1.dimensions);
792
+ assert.equal(loaded.chunk1.path_weight, original.chunk1.path_weight);
793
+ assert.equal(loaded.chunk1.success_rate, original.chunk1.success_rate);
794
+ assert.deepEqual(loaded.chunk1.synonyms, original.chunk1.synonyms);
795
+ assert.deepEqual(loaded.chunk1.symbol_parameters, original.chunk1.symbol_parameters);
796
+ assert.deepEqual(loaded.chunk1.symbol_calls, original.chunk1.symbol_calls);
797
+ }
798
+ finally {
799
+ cleanup(tempDir);
800
+ }
801
+ });
802
+ test('round-trip async: write and read codemap preserves data', async () => {
803
+ const tempDir = createTempDir();
804
+ try {
805
+ const filePath = path.join(tempDir, 'roundtrip-async.json');
806
+ const original = {
807
+ 'async_chunk': {
808
+ file: 'async.ts',
809
+ sha: 'asynchash123',
810
+ symbol: 'asyncFunc',
811
+ symbol_neighbors: ['neighbor1', 'neighbor2']
812
+ }
813
+ };
814
+ await writeCodemapAsync(filePath, original);
815
+ const loaded = await readCodemapAsync(filePath);
816
+ assert.equal(loaded.async_chunk.file, original.async_chunk.file);
817
+ assert.equal(loaded.async_chunk.sha, original.async_chunk.sha);
818
+ assert.deepEqual(loaded.async_chunk.symbol_neighbors, original.async_chunk.symbol_neighbors);
819
+ }
820
+ finally {
821
+ cleanup(tempDir);
822
+ }
823
+ });
824
+ // ============================================================================
825
+ // Cross-file Reference Tests
826
+ // ============================================================================
827
+ test('cross-file references: complete workflow', () => {
828
+ const codemap = {
829
+ 'user_service': {
830
+ file: 'src/services/user.ts',
831
+ sha: 'sha_user',
832
+ symbol: 'UserService',
833
+ symbol_calls: ['validateUser', 'saveToDatabase', 'sendNotification'],
834
+ symbol_neighbors: []
835
+ },
836
+ 'validator': {
837
+ file: 'src/utils/validator.ts',
838
+ sha: 'sha_validator',
839
+ symbol: 'validateUser',
840
+ symbol_calls: [],
841
+ symbol_neighbors: []
842
+ },
843
+ 'database': {
844
+ file: 'src/database/db.ts',
845
+ sha: 'sha_db',
846
+ symbol: 'saveToDatabase',
847
+ symbol_calls: ['connect', 'query'],
848
+ symbol_neighbors: []
849
+ },
850
+ 'notifier': {
851
+ file: 'src/services/notification.ts',
852
+ sha: 'sha_notifier',
853
+ symbol: 'sendNotification',
854
+ symbol_calls: ['validateUser'], // Also calls validator
855
+ symbol_neighbors: []
856
+ }
857
+ };
858
+ const result = attachSymbolGraphToCodemap(codemap);
859
+ // UserService should call all three
860
+ assert.ok(result.user_service.symbol_call_targets?.includes('sha_validator'));
861
+ assert.ok(result.user_service.symbol_call_targets?.includes('sha_db'));
862
+ assert.ok(result.user_service.symbol_call_targets?.includes('sha_notifier'));
863
+ // Validator is called by UserService and Notifier
864
+ assert.ok(result.validator.symbol_callers?.includes('sha_user'));
865
+ assert.ok(result.validator.symbol_callers?.includes('sha_notifier'));
866
+ // Validator's neighbors should include both callers
867
+ assert.ok(result.validator.symbol_neighbors?.includes('sha_user'));
868
+ assert.ok(result.validator.symbol_neighbors?.includes('sha_notifier'));
869
+ });
870
+ test('cross-file references: bidirectional relationships', () => {
871
+ const codemap = {
872
+ 'module_a': {
873
+ file: 'a.ts',
874
+ sha: 'sha_a',
875
+ symbol: 'moduleA',
876
+ symbol_calls: ['moduleB'],
877
+ symbol_neighbors: []
878
+ },
879
+ 'module_b': {
880
+ file: 'b.ts',
881
+ sha: 'sha_b',
882
+ symbol: 'moduleB',
883
+ symbol_calls: ['moduleA'], // Calls back to A
884
+ symbol_neighbors: []
885
+ }
886
+ };
887
+ const result = attachSymbolGraphToCodemap(codemap);
888
+ // A calls B
889
+ assert.deepEqual(result.module_a.symbol_call_targets, ['sha_b']);
890
+ // B calls A
891
+ assert.deepEqual(result.module_b.symbol_call_targets, ['sha_a']);
892
+ // A is called by B
893
+ assert.deepEqual(result.module_a.symbol_callers, ['sha_b']);
894
+ // B is called by A
895
+ assert.deepEqual(result.module_b.symbol_callers, ['sha_a']);
896
+ // Neighbors should be bidirectional
897
+ assert.deepEqual(result.module_a.symbol_neighbors, ['sha_b']);
898
+ assert.deepEqual(result.module_b.symbol_neighbors, ['sha_a']);
899
+ });
900
+ // ============================================================================
901
+ // Edge Cases and Error Handling
902
+ // ============================================================================
903
+ test('edge case: codemap with invalid entries is handled gracefully', () => {
904
+ const codemap = {
905
+ 'valid': {
906
+ file: 'valid.ts',
907
+ sha: 'valid_sha',
908
+ symbol: 'validFunc',
909
+ symbol_calls: [],
910
+ symbol_neighbors: []
911
+ },
912
+ 'invalid1': null,
913
+ 'invalid2': 'not an object',
914
+ 'invalid3': { noSha: true }
915
+ };
916
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
917
+ const result = attachSymbolGraphToCodemap(codemap);
918
+ // Valid entry should still work
919
+ assert.equal(result.valid.file, 'valid.ts');
920
+ });
921
+ test('edge case: very long symbol names', () => {
922
+ const longSymbol = 'a'.repeat(1000);
923
+ const codemap = {
924
+ 'chunk1': {
925
+ file: 'test.ts',
926
+ sha: 'sha1',
927
+ symbol: longSymbol,
928
+ symbol_calls: [longSymbol], // Self-reference should be ignored
929
+ symbol_neighbors: []
930
+ }
931
+ };
932
+ const result = attachSymbolGraphToCodemap(codemap);
933
+ assert.equal(result.chunk1.symbol, longSymbol);
934
+ assert.deepEqual(result.chunk1.symbol_call_targets, []);
935
+ });
936
+ test('edge case: unicode in symbol names', () => {
937
+ const codemap = {
938
+ 'chunk1': {
939
+ file: 'test.ts',
940
+ sha: 'sha1',
941
+ symbol: 'process_data',
942
+ symbol_calls: ['helper_func'],
943
+ symbol_neighbors: []
944
+ },
945
+ 'chunk2': {
946
+ file: 'test.ts',
947
+ sha: 'sha2',
948
+ symbol: 'helper_func',
949
+ symbol_calls: [],
950
+ symbol_neighbors: []
951
+ }
952
+ };
953
+ const result = attachSymbolGraphToCodemap(codemap);
954
+ assert.deepEqual(result.chunk1.symbol_call_targets, ['sha2']);
955
+ });
956
+ test('edge case: special characters in file paths', () => {
957
+ const result = normalizeChunkMetadata({
958
+ file: 'src/components/my-component (copy).tsx',
959
+ sha: 'hash123'
960
+ });
961
+ assert.equal(result.file, 'src/components/my-component (copy).tsx');
962
+ });
963
+ test('edge case: whitespace-only symbol', () => {
964
+ const result = normalizeChunkMetadata({
965
+ file: 'test.ts',
966
+ sha: 'hash',
967
+ symbol: ' '
968
+ });
969
+ assert.equal(result.symbol, null);
970
+ });
971
+ test('edge case: large codemap performance', () => {
972
+ const largeCodemap = {};
973
+ // Create 100 chunks
974
+ for (let i = 0; i < 100; i++) {
975
+ largeCodemap[`chunk_${i}`] = {
976
+ file: `src/file_${i}.ts`,
977
+ sha: `sha_${i}`,
978
+ symbol: `func_${i}`,
979
+ symbol_calls: i > 0 ? [`func_${i - 1}`] : [],
980
+ symbol_neighbors: []
981
+ };
982
+ }
983
+ const startTime = Date.now();
984
+ const result = attachSymbolGraphToCodemap(largeCodemap);
985
+ const duration = Date.now() - startTime;
986
+ // Should complete in reasonable time (less than 1 second)
987
+ assert.ok(duration < 1000, `Large codemap took ${duration}ms`);
988
+ // Verify chain is built correctly
989
+ assert.deepEqual(result.chunk_1.symbol_call_targets, ['sha_0']);
990
+ assert.deepEqual(result.chunk_99.symbol_call_targets, ['sha_98']);
991
+ });
992
+ //# sourceMappingURL=codemap-io.test.js.map