grepmind-core 0.1.0-alpha

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 (370) hide show
  1. package/LICENSE +190 -0
  2. package/dist/config/types.d.ts +174 -0
  3. package/dist/config/types.d.ts.map +1 -0
  4. package/dist/config/types.js +137 -0
  5. package/dist/config/types.js.map +1 -0
  6. package/dist/git.d.ts +98 -0
  7. package/dist/git.d.ts.map +1 -0
  8. package/dist/git.js +298 -0
  9. package/dist/git.js.map +1 -0
  10. package/dist/git.test.d.ts +7 -0
  11. package/dist/git.test.d.ts.map +1 -0
  12. package/dist/git.test.js +242 -0
  13. package/dist/git.test.js.map +1 -0
  14. package/dist/index.d.ts +44 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +67 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/indexer/branch.d.ts +121 -0
  19. package/dist/indexer/branch.d.ts.map +1 -0
  20. package/dist/indexer/branch.js +451 -0
  21. package/dist/indexer/branch.js.map +1 -0
  22. package/dist/indexer/chunker.d.ts +9 -0
  23. package/dist/indexer/chunker.d.ts.map +1 -0
  24. package/dist/indexer/chunker.js +70 -0
  25. package/dist/indexer/chunker.js.map +1 -0
  26. package/dist/indexer/chunker.test.d.ts +2 -0
  27. package/dist/indexer/chunker.test.d.ts.map +1 -0
  28. package/dist/indexer/chunker.test.js +180 -0
  29. package/dist/indexer/chunker.test.js.map +1 -0
  30. package/dist/indexer/code/branch.d.ts +155 -0
  31. package/dist/indexer/code/branch.d.ts.map +1 -0
  32. package/dist/indexer/code/branch.js +550 -0
  33. package/dist/indexer/code/branch.js.map +1 -0
  34. package/dist/indexer/code/branch.test.d.ts +7 -0
  35. package/dist/indexer/code/branch.test.d.ts.map +1 -0
  36. package/dist/indexer/code/branch.test.js +241 -0
  37. package/dist/indexer/code/branch.test.js.map +1 -0
  38. package/dist/indexer/code/chunker.d.ts +61 -0
  39. package/dist/indexer/code/chunker.d.ts.map +1 -0
  40. package/dist/indexer/code/chunker.js +311 -0
  41. package/dist/indexer/code/chunker.js.map +1 -0
  42. package/dist/indexer/code/chunker.test.d.ts +2 -0
  43. package/dist/indexer/code/chunker.test.d.ts.map +1 -0
  44. package/dist/indexer/code/chunker.test.js +552 -0
  45. package/dist/indexer/code/chunker.test.js.map +1 -0
  46. package/dist/indexer/code/fts.test.d.ts +2 -0
  47. package/dist/indexer/code/fts.test.d.ts.map +1 -0
  48. package/dist/indexer/code/fts.test.js +14 -0
  49. package/dist/indexer/code/fts.test.js.map +1 -0
  50. package/dist/indexer/code/graph/embedded.d.ts +11 -0
  51. package/dist/indexer/code/graph/embedded.d.ts.map +1 -0
  52. package/dist/indexer/code/graph/embedded.js +152 -0
  53. package/dist/indexer/code/graph/embedded.js.map +1 -0
  54. package/dist/indexer/code/graph/embedded.test.d.ts +2 -0
  55. package/dist/indexer/code/graph/embedded.test.d.ts.map +1 -0
  56. package/dist/indexer/code/graph/embedded.test.js +105 -0
  57. package/dist/indexer/code/graph/embedded.test.js.map +1 -0
  58. package/dist/indexer/code/graph/facts.d.ts +11 -0
  59. package/dist/indexer/code/graph/facts.d.ts.map +1 -0
  60. package/dist/indexer/code/graph/facts.js +456 -0
  61. package/dist/indexer/code/graph/facts.js.map +1 -0
  62. package/dist/indexer/code/graph/facts.test.d.ts +2 -0
  63. package/dist/indexer/code/graph/facts.test.d.ts.map +1 -0
  64. package/dist/indexer/code/graph/facts.test.js +181 -0
  65. package/dist/indexer/code/graph/facts.test.js.map +1 -0
  66. package/dist/indexer/code/graph/id.d.ts +14 -0
  67. package/dist/indexer/code/graph/id.d.ts.map +1 -0
  68. package/dist/indexer/code/graph/id.js +40 -0
  69. package/dist/indexer/code/graph/id.js.map +1 -0
  70. package/dist/indexer/code/graph/id.test.d.ts +2 -0
  71. package/dist/indexer/code/graph/id.test.d.ts.map +1 -0
  72. package/dist/indexer/code/graph/id.test.js +86 -0
  73. package/dist/indexer/code/graph/id.test.js.map +1 -0
  74. package/dist/indexer/code/graph/index.d.ts +133 -0
  75. package/dist/indexer/code/graph/index.d.ts.map +1 -0
  76. package/dist/indexer/code/graph/index.js +1876 -0
  77. package/dist/indexer/code/graph/index.js.map +1 -0
  78. package/dist/indexer/code/graph/index.test.d.ts +2 -0
  79. package/dist/indexer/code/graph/index.test.d.ts.map +1 -0
  80. package/dist/indexer/code/graph/index.test.js +210 -0
  81. package/dist/indexer/code/graph/index.test.js.map +1 -0
  82. package/dist/indexer/code/graph/queries.d.ts +22 -0
  83. package/dist/indexer/code/graph/queries.d.ts.map +1 -0
  84. package/dist/indexer/code/graph/queries.js +79 -0
  85. package/dist/indexer/code/graph/queries.js.map +1 -0
  86. package/dist/indexer/code/graph/queries.test.d.ts +2 -0
  87. package/dist/indexer/code/graph/queries.test.d.ts.map +1 -0
  88. package/dist/indexer/code/graph/queries.test.js +108 -0
  89. package/dist/indexer/code/graph/queries.test.js.map +1 -0
  90. package/dist/indexer/code/graph/resolver.d.ts +136 -0
  91. package/dist/indexer/code/graph/resolver.d.ts.map +1 -0
  92. package/dist/indexer/code/graph/resolver.js +839 -0
  93. package/dist/indexer/code/graph/resolver.js.map +1 -0
  94. package/dist/indexer/code/graph/resolver.test.d.ts +2 -0
  95. package/dist/indexer/code/graph/resolver.test.d.ts.map +1 -0
  96. package/dist/indexer/code/graph/resolver.test.js +482 -0
  97. package/dist/indexer/code/graph/resolver.test.js.map +1 -0
  98. package/dist/indexer/code/graph/semantic.d.ts +33 -0
  99. package/dist/indexer/code/graph/semantic.d.ts.map +1 -0
  100. package/dist/indexer/code/graph/semantic.js +279 -0
  101. package/dist/indexer/code/graph/semantic.js.map +1 -0
  102. package/dist/indexer/code/graph/semantic.test.d.ts +2 -0
  103. package/dist/indexer/code/graph/semantic.test.d.ts.map +1 -0
  104. package/dist/indexer/code/graph/semantic.test.js +127 -0
  105. package/dist/indexer/code/graph/semantic.test.js.map +1 -0
  106. package/dist/indexer/code/index.d.ts +404 -0
  107. package/dist/indexer/code/index.d.ts.map +1 -0
  108. package/dist/indexer/code/index.js +2070 -0
  109. package/dist/indexer/code/index.js.map +1 -0
  110. package/dist/indexer/code/languages/bash.d.ts +14 -0
  111. package/dist/indexer/code/languages/bash.d.ts.map +1 -0
  112. package/dist/indexer/code/languages/bash.js +125 -0
  113. package/dist/indexer/code/languages/bash.js.map +1 -0
  114. package/dist/indexer/code/languages/css.d.ts +16 -0
  115. package/dist/indexer/code/languages/css.d.ts.map +1 -0
  116. package/dist/indexer/code/languages/css.js +204 -0
  117. package/dist/indexer/code/languages/css.js.map +1 -0
  118. package/dist/indexer/code/languages/generic.d.ts +61 -0
  119. package/dist/indexer/code/languages/generic.d.ts.map +1 -0
  120. package/dist/indexer/code/languages/generic.js +150 -0
  121. package/dist/indexer/code/languages/generic.js.map +1 -0
  122. package/dist/indexer/code/languages/graphql.d.ts +13 -0
  123. package/dist/indexer/code/languages/graphql.d.ts.map +1 -0
  124. package/dist/indexer/code/languages/graphql.js +180 -0
  125. package/dist/indexer/code/languages/graphql.js.map +1 -0
  126. package/dist/indexer/code/languages/html.d.ts +16 -0
  127. package/dist/indexer/code/languages/html.d.ts.map +1 -0
  128. package/dist/indexer/code/languages/html.js +138 -0
  129. package/dist/indexer/code/languages/html.js.map +1 -0
  130. package/dist/indexer/code/languages/index.d.ts +9 -0
  131. package/dist/indexer/code/languages/index.d.ts.map +1 -0
  132. package/dist/indexer/code/languages/index.js +12 -0
  133. package/dist/indexer/code/languages/index.js.map +1 -0
  134. package/dist/indexer/code/languages/json.d.ts +12 -0
  135. package/dist/indexer/code/languages/json.d.ts.map +1 -0
  136. package/dist/indexer/code/languages/json.js +66 -0
  137. package/dist/indexer/code/languages/json.js.map +1 -0
  138. package/dist/indexer/code/languages/registry.d.ts +78 -0
  139. package/dist/indexer/code/languages/registry.d.ts.map +1 -0
  140. package/dist/indexer/code/languages/registry.js +72 -0
  141. package/dist/indexer/code/languages/registry.js.map +1 -0
  142. package/dist/indexer/code/languages/typescript.d.ts +39 -0
  143. package/dist/indexer/code/languages/typescript.d.ts.map +1 -0
  144. package/dist/indexer/code/languages/typescript.js +300 -0
  145. package/dist/indexer/code/languages/typescript.js.map +1 -0
  146. package/dist/indexer/code/languages/yaml.d.ts +13 -0
  147. package/dist/indexer/code/languages/yaml.d.ts.map +1 -0
  148. package/dist/indexer/code/languages/yaml.js +90 -0
  149. package/dist/indexer/code/languages/yaml.js.map +1 -0
  150. package/dist/indexer/code/parser.d.ts +26 -0
  151. package/dist/indexer/code/parser.d.ts.map +1 -0
  152. package/dist/indexer/code/parser.js +332 -0
  153. package/dist/indexer/code/parser.js.map +1 -0
  154. package/dist/indexer/code/retry.d.ts +58 -0
  155. package/dist/indexer/code/retry.d.ts.map +1 -0
  156. package/dist/indexer/code/retry.js +192 -0
  157. package/dist/indexer/code/retry.js.map +1 -0
  158. package/dist/indexer/code/tree/builder.d.ts +30 -0
  159. package/dist/indexer/code/tree/builder.d.ts.map +1 -0
  160. package/dist/indexer/code/tree/builder.js +132 -0
  161. package/dist/indexer/code/tree/builder.js.map +1 -0
  162. package/dist/indexer/code/tree/builder.test.d.ts +2 -0
  163. package/dist/indexer/code/tree/builder.test.d.ts.map +1 -0
  164. package/dist/indexer/code/tree/builder.test.js +31 -0
  165. package/dist/indexer/code/tree/builder.test.js.map +1 -0
  166. package/dist/indexer/code/tree/cache.d.ts +22 -0
  167. package/dist/indexer/code/tree/cache.d.ts.map +1 -0
  168. package/dist/indexer/code/tree/cache.js +85 -0
  169. package/dist/indexer/code/tree/cache.js.map +1 -0
  170. package/dist/indexer/code/tree/context.d.ts +32 -0
  171. package/dist/indexer/code/tree/context.d.ts.map +1 -0
  172. package/dist/indexer/code/tree/context.js +78 -0
  173. package/dist/indexer/code/tree/context.js.map +1 -0
  174. package/dist/indexer/code/tree/embedding.d.ts +9 -0
  175. package/dist/indexer/code/tree/embedding.d.ts.map +1 -0
  176. package/dist/indexer/code/tree/embedding.js +53 -0
  177. package/dist/indexer/code/tree/embedding.js.map +1 -0
  178. package/dist/indexer/code/tree/embedding.test.d.ts +2 -0
  179. package/dist/indexer/code/tree/embedding.test.d.ts.map +1 -0
  180. package/dist/indexer/code/tree/embedding.test.js +57 -0
  181. package/dist/indexer/code/tree/embedding.test.js.map +1 -0
  182. package/dist/indexer/code/tree/id.d.ts +3 -0
  183. package/dist/indexer/code/tree/id.d.ts.map +1 -0
  184. package/dist/indexer/code/tree/id.js +8 -0
  185. package/dist/indexer/code/tree/id.js.map +1 -0
  186. package/dist/indexer/code/tree/index.d.ts +113 -0
  187. package/dist/indexer/code/tree/index.d.ts.map +1 -0
  188. package/dist/indexer/code/tree/index.js +1146 -0
  189. package/dist/indexer/code/tree/index.js.map +1 -0
  190. package/dist/indexer/code/tree/rename.d.ts +13 -0
  191. package/dist/indexer/code/tree/rename.d.ts.map +1 -0
  192. package/dist/indexer/code/tree/rename.js +46 -0
  193. package/dist/indexer/code/tree/rename.js.map +1 -0
  194. package/dist/indexer/code/tree/repomap.d.ts +29 -0
  195. package/dist/indexer/code/tree/repomap.d.ts.map +1 -0
  196. package/dist/indexer/code/tree/repomap.js +95 -0
  197. package/dist/indexer/code/tree/repomap.js.map +1 -0
  198. package/dist/indexer/code/tree/repomap.test.d.ts +2 -0
  199. package/dist/indexer/code/tree/repomap.test.d.ts.map +1 -0
  200. package/dist/indexer/code/tree/repomap.test.js +93 -0
  201. package/dist/indexer/code/tree/repomap.test.js.map +1 -0
  202. package/dist/indexer/code/tree/stats.d.ts +26 -0
  203. package/dist/indexer/code/tree/stats.d.ts.map +1 -0
  204. package/dist/indexer/code/tree/stats.js +49 -0
  205. package/dist/indexer/code/tree/stats.js.map +1 -0
  206. package/dist/indexer/code/tree/types.d.ts +186 -0
  207. package/dist/indexer/code/tree/types.d.ts.map +1 -0
  208. package/dist/indexer/code/tree/types.js +10 -0
  209. package/dist/indexer/code/tree/types.js.map +1 -0
  210. package/dist/indexer/code/wal.d.ts +144 -0
  211. package/dist/indexer/code/wal.d.ts.map +1 -0
  212. package/dist/indexer/code/wal.js +283 -0
  213. package/dist/indexer/code/wal.js.map +1 -0
  214. package/dist/indexer/embeddings.d.ts +113 -0
  215. package/dist/indexer/embeddings.d.ts.map +1 -0
  216. package/dist/indexer/embeddings.js +477 -0
  217. package/dist/indexer/embeddings.js.map +1 -0
  218. package/dist/indexer/git-sync.d.ts +117 -0
  219. package/dist/indexer/git-sync.d.ts.map +1 -0
  220. package/dist/indexer/git-sync.js +398 -0
  221. package/dist/indexer/git-sync.js.map +1 -0
  222. package/dist/indexer/index.d.ts +175 -0
  223. package/dist/indexer/index.d.ts.map +1 -0
  224. package/dist/indexer/index.js +1096 -0
  225. package/dist/indexer/index.js.map +1 -0
  226. package/dist/indexer/mocks/mock-reranker.d.ts +12 -0
  227. package/dist/indexer/mocks/mock-reranker.d.ts.map +1 -0
  228. package/dist/indexer/mocks/mock-reranker.js +26 -0
  229. package/dist/indexer/mocks/mock-reranker.js.map +1 -0
  230. package/dist/indexer/parser.d.ts +8 -0
  231. package/dist/indexer/parser.d.ts.map +1 -0
  232. package/dist/indexer/parser.js +44 -0
  233. package/dist/indexer/parser.js.map +1 -0
  234. package/dist/indexer/parser.test.d.ts +2 -0
  235. package/dist/indexer/parser.test.d.ts.map +1 -0
  236. package/dist/indexer/parser.test.js +197 -0
  237. package/dist/indexer/parser.test.js.map +1 -0
  238. package/dist/indexer/reranking.d.ts +71 -0
  239. package/dist/indexer/reranking.d.ts.map +1 -0
  240. package/dist/indexer/reranking.integration.test.d.ts +2 -0
  241. package/dist/indexer/reranking.integration.test.d.ts.map +1 -0
  242. package/dist/indexer/reranking.integration.test.js +104 -0
  243. package/dist/indexer/reranking.integration.test.js.map +1 -0
  244. package/dist/indexer/reranking.js +256 -0
  245. package/dist/indexer/reranking.js.map +1 -0
  246. package/dist/indexer/reranking.test.d.ts +2 -0
  247. package/dist/indexer/reranking.test.d.ts.map +1 -0
  248. package/dist/indexer/reranking.test.js +130 -0
  249. package/dist/indexer/reranking.test.js.map +1 -0
  250. package/dist/indexer/wal/file-storage.d.ts +60 -0
  251. package/dist/indexer/wal/file-storage.d.ts.map +1 -0
  252. package/dist/indexer/wal/file-storage.js +277 -0
  253. package/dist/indexer/wal/file-storage.js.map +1 -0
  254. package/dist/indexer/wal/file-storage.test.d.ts +8 -0
  255. package/dist/indexer/wal/file-storage.test.d.ts.map +1 -0
  256. package/dist/indexer/wal/file-storage.test.js +444 -0
  257. package/dist/indexer/wal/file-storage.test.js.map +1 -0
  258. package/dist/indexer/wal/index.d.ts +41 -0
  259. package/dist/indexer/wal/index.d.ts.map +1 -0
  260. package/dist/indexer/wal/index.js +61 -0
  261. package/dist/indexer/wal/index.js.map +1 -0
  262. package/dist/indexer/wal/integration.test.d.ts +11 -0
  263. package/dist/indexer/wal/integration.test.d.ts.map +1 -0
  264. package/dist/indexer/wal/integration.test.js +378 -0
  265. package/dist/indexer/wal/integration.test.js.map +1 -0
  266. package/dist/indexer/wal/lancedb-storage.d.ts +72 -0
  267. package/dist/indexer/wal/lancedb-storage.d.ts.map +1 -0
  268. package/dist/indexer/wal/lancedb-storage.js +462 -0
  269. package/dist/indexer/wal/lancedb-storage.js.map +1 -0
  270. package/dist/indexer/wal/lancedb-storage.test.d.ts +8 -0
  271. package/dist/indexer/wal/lancedb-storage.test.d.ts.map +1 -0
  272. package/dist/indexer/wal/lancedb-storage.test.js +415 -0
  273. package/dist/indexer/wal/lancedb-storage.test.js.map +1 -0
  274. package/dist/indexer/wal/sync-wal.d.ts +144 -0
  275. package/dist/indexer/wal/sync-wal.d.ts.map +1 -0
  276. package/dist/indexer/wal/sync-wal.js +863 -0
  277. package/dist/indexer/wal/sync-wal.js.map +1 -0
  278. package/dist/indexer/wal/sync-wal.test.d.ts +8 -0
  279. package/dist/indexer/wal/sync-wal.test.d.ts.map +1 -0
  280. package/dist/indexer/wal/sync-wal.test.js +752 -0
  281. package/dist/indexer/wal/sync-wal.test.js.map +1 -0
  282. package/dist/indexer/wal/types.d.ts +167 -0
  283. package/dist/indexer/wal/types.d.ts.map +1 -0
  284. package/dist/indexer/wal/types.js +12 -0
  285. package/dist/indexer/wal/types.js.map +1 -0
  286. package/dist/indexer/watcher.d.ts +36 -0
  287. package/dist/indexer/watcher.d.ts.map +1 -0
  288. package/dist/indexer/watcher.js +110 -0
  289. package/dist/indexer/watcher.js.map +1 -0
  290. package/dist/search/explore.d.ts +62 -0
  291. package/dist/search/explore.d.ts.map +1 -0
  292. package/dist/search/explore.js +111 -0
  293. package/dist/search/explore.js.map +1 -0
  294. package/dist/search/fts.d.ts +23 -0
  295. package/dist/search/fts.d.ts.map +1 -0
  296. package/dist/search/fts.js +64 -0
  297. package/dist/search/fts.js.map +1 -0
  298. package/dist/search/fts.test.d.ts +2 -0
  299. package/dist/search/fts.test.d.ts.map +1 -0
  300. package/dist/search/fts.test.js +27 -0
  301. package/dist/search/fts.test.js.map +1 -0
  302. package/dist/search/grep.d.ts +75 -0
  303. package/dist/search/grep.d.ts.map +1 -0
  304. package/dist/search/grep.js +96 -0
  305. package/dist/search/grep.js.map +1 -0
  306. package/dist/search/grep.test.d.ts +2 -0
  307. package/dist/search/grep.test.d.ts.map +1 -0
  308. package/dist/search/grep.test.js +178 -0
  309. package/dist/search/grep.test.js.map +1 -0
  310. package/dist/search/hybrid-grep.d.ts +43 -0
  311. package/dist/search/hybrid-grep.d.ts.map +1 -0
  312. package/dist/search/hybrid-grep.js +130 -0
  313. package/dist/search/hybrid-grep.js.map +1 -0
  314. package/dist/search/hybrid-grep.test.d.ts +2 -0
  315. package/dist/search/hybrid-grep.test.d.ts.map +1 -0
  316. package/dist/search/hybrid-grep.test.js +133 -0
  317. package/dist/search/hybrid-grep.test.js.map +1 -0
  318. package/dist/search/rg-executor.d.ts +63 -0
  319. package/dist/search/rg-executor.d.ts.map +1 -0
  320. package/dist/search/rg-executor.js +146 -0
  321. package/dist/search/rg-executor.js.map +1 -0
  322. package/dist/search/rg-executor.test.d.ts +2 -0
  323. package/dist/search/rg-executor.test.d.ts.map +1 -0
  324. package/dist/search/rg-executor.test.js +104 -0
  325. package/dist/search/rg-executor.test.js.map +1 -0
  326. package/dist/search/rg-parser/extractor.d.ts +14 -0
  327. package/dist/search/rg-parser/extractor.d.ts.map +1 -0
  328. package/dist/search/rg-parser/extractor.js +82 -0
  329. package/dist/search/rg-parser/extractor.js.map +1 -0
  330. package/dist/search/rg-parser/extractor.test.d.ts +2 -0
  331. package/dist/search/rg-parser/extractor.test.d.ts.map +1 -0
  332. package/dist/search/rg-parser/extractor.test.js +35 -0
  333. package/dist/search/rg-parser/extractor.test.js.map +1 -0
  334. package/dist/search/rg-parser/fts-builder.d.ts +7 -0
  335. package/dist/search/rg-parser/fts-builder.d.ts.map +1 -0
  336. package/dist/search/rg-parser/fts-builder.js +18 -0
  337. package/dist/search/rg-parser/fts-builder.js.map +1 -0
  338. package/dist/search/rg-parser/fts-builder.test.d.ts +2 -0
  339. package/dist/search/rg-parser/fts-builder.test.d.ts.map +1 -0
  340. package/dist/search/rg-parser/fts-builder.test.js +26 -0
  341. package/dist/search/rg-parser/fts-builder.test.js.map +1 -0
  342. package/dist/search/rg-parser/index.d.ts +36 -0
  343. package/dist/search/rg-parser/index.d.ts.map +1 -0
  344. package/dist/search/rg-parser/index.js +83 -0
  345. package/dist/search/rg-parser/index.js.map +1 -0
  346. package/dist/search/rg-parser/index.test.d.ts +2 -0
  347. package/dist/search/rg-parser/index.test.d.ts.map +1 -0
  348. package/dist/search/rg-parser/index.test.js +34 -0
  349. package/dist/search/rg-parser/index.test.js.map +1 -0
  350. package/dist/search/rg-parser/strategy.d.ts +14 -0
  351. package/dist/search/rg-parser/strategy.d.ts.map +1 -0
  352. package/dist/search/rg-parser/strategy.js +31 -0
  353. package/dist/search/rg-parser/strategy.js.map +1 -0
  354. package/dist/search/rg-parser/strategy.test.d.ts +2 -0
  355. package/dist/search/rg-parser/strategy.test.d.ts.map +1 -0
  356. package/dist/search/rg-parser/strategy.test.js +29 -0
  357. package/dist/search/rg-parser/strategy.test.js.map +1 -0
  358. package/dist/types.d.ts +345 -0
  359. package/dist/types.d.ts.map +1 -0
  360. package/dist/types.js +7 -0
  361. package/dist/types.js.map +1 -0
  362. package/dist/utils/vault.d.ts +84 -0
  363. package/dist/utils/vault.d.ts.map +1 -0
  364. package/dist/utils/vault.js +138 -0
  365. package/dist/utils/vault.js.map +1 -0
  366. package/dist/utils/vault.test.d.ts +2 -0
  367. package/dist/utils/vault.test.d.ts.map +1 -0
  368. package/dist/utils/vault.test.js +153 -0
  369. package/dist/utils/vault.test.js.map +1 -0
  370. package/package.json +69 -0
@@ -0,0 +1,863 @@
1
+ /**
2
+ * SyncWal - Unified Write-Ahead Log for Sync Tasks
3
+ *
4
+ * PostgreSQL-inspired WAL with:
5
+ * - LSN (Log Sequence Number) for recovery positioning
6
+ * - Checkpoint + truncate for bounded WAL size
7
+ * - Offset-based recovery (no paths stored)
8
+ * - Global lock with heartbeat for stale detection
9
+ * - Cancellation support
10
+ */
11
+ import { randomUUID } from 'node:crypto';
12
+ import { FileWalStorage } from './file-storage.js';
13
+ import { DEFAULT_SYNC_WAL_CONFIG } from './types.js';
14
+ // ═══════════════════════════════════════════════════════════════════════════
15
+ // SYNC WAL CLASS
16
+ // ═══════════════════════════════════════════════════════════════════════════
17
+ export class SyncWal {
18
+ storage;
19
+ config;
20
+ // Current state
21
+ currentLsn = 0;
22
+ currentTaskId = null;
23
+ currentTaskType = null;
24
+ totalFiles = 0;
25
+ manifestHash = null;
26
+ startedAt = 0;
27
+ phase = 'analyzing';
28
+ currentFile = null;
29
+ // Memtable (in-memory state between checkpoints)
30
+ memtable = null;
31
+ // Checkpoint tracking
32
+ filesSinceCheckpoint = 0;
33
+ checkpointProcessedCount = 0;
34
+ lastCheckpointLsn = 0;
35
+ // Heartbeat
36
+ heartbeatInterval;
37
+ // Cancellation and pause
38
+ cancelRequested = new Set();
39
+ pauseRequested = new Set();
40
+ isPaused = false;
41
+ // Event callback
42
+ onProgressCallback;
43
+ onEmbeddingProgressCallback;
44
+ onSubPhaseProgressCallback;
45
+ constructor(storage, config) {
46
+ this.storage = storage;
47
+ this.config = { ...DEFAULT_SYNC_WAL_CONFIG, ...config };
48
+ }
49
+ // ═══════════════════════════════════════════════════════════════════════════
50
+ // INITIALIZATION
51
+ // ═══════════════════════════════════════════════════════════════════════════
52
+ /**
53
+ * Initialize SyncWal - call on server start
54
+ * Handles stale lock detection and recovery
55
+ */
56
+ async initialize() {
57
+ // Check for stale locks
58
+ const lockStatus = await this.isLocked();
59
+ if (lockStatus.locked && lockStatus.stale && lockStatus.lock) {
60
+ console.warn('Detected stale lock, clearing...');
61
+ await this.forceClearStaleLock();
62
+ }
63
+ // Load manifest to restore LSN
64
+ const manifest = await this.storage.getManifest();
65
+ if (manifest) {
66
+ this.currentLsn = manifest.walLsn;
67
+ this.lastCheckpointLsn = manifest.lastCheckpointLsn;
68
+ }
69
+ // Check for recovery
70
+ return this.recover();
71
+ }
72
+ // ═══════════════════════════════════════════════════════════════════════════
73
+ // WRITE PATH (hot path — optimized for speed)
74
+ // ═══════════════════════════════════════════════════════════════════════════
75
+ /**
76
+ * Start a new sync task
77
+ */
78
+ async startTask(type, totalFiles, manifestHash) {
79
+ const taskId = randomUUID();
80
+ const now = Date.now();
81
+ // Acquire lock
82
+ const lock = {
83
+ id: taskId,
84
+ type,
85
+ pid: process.pid,
86
+ startedAt: now,
87
+ heartbeat: now,
88
+ };
89
+ const acquired = await this.acquireLock(lock);
90
+ if (!acquired) {
91
+ throw new Error(`Cannot start ${type} sync: another sync is already running`);
92
+ }
93
+ try {
94
+ // Initialize state
95
+ this.currentTaskId = taskId;
96
+ this.currentTaskType = type;
97
+ this.totalFiles = totalFiles;
98
+ this.manifestHash = manifestHash || null;
99
+ this.startedAt = now;
100
+ this.phase = 'indexing';
101
+ this.currentFile = null;
102
+ this.filesSinceCheckpoint = 0;
103
+ this.checkpointProcessedCount = 0;
104
+ // Initialize memtable
105
+ this.memtable = {
106
+ taskId,
107
+ processedCount: 0,
108
+ failedPaths: new Map(),
109
+ stats: { added: 0, updated: 0, deleted: 0 },
110
+ lastLsn: this.currentLsn,
111
+ };
112
+ // Append task:start to WAL
113
+ const entry = {
114
+ lsn: this.currentLsn++,
115
+ ts: now,
116
+ op: 'task:start',
117
+ taskId,
118
+ data: {
119
+ type,
120
+ totalFiles,
121
+ manifestHash,
122
+ },
123
+ };
124
+ await this.storage.appendEntry(entry);
125
+ // Create/update manifest
126
+ const task = {
127
+ id: taskId,
128
+ type,
129
+ status: 'running',
130
+ startedAt: now,
131
+ updatedAt: now,
132
+ phase: 'indexing',
133
+ totalFiles,
134
+ processedFiles: 0,
135
+ added: 0,
136
+ updated: 0,
137
+ deleted: 0,
138
+ failedFiles: [],
139
+ manifestHash,
140
+ lastCheckpointLsn: this.lastCheckpointLsn,
141
+ };
142
+ const manifest = {
143
+ version: 1,
144
+ current: task,
145
+ lastCompleted: (await this.storage.getManifest())?.lastCompleted || null,
146
+ walLsn: this.currentLsn,
147
+ lastCheckpointLsn: this.lastCheckpointLsn,
148
+ };
149
+ await this.storage.writeManifest(manifest);
150
+ // Start heartbeat
151
+ this.startHeartbeat();
152
+ // Emit progress
153
+ this.emitProgress();
154
+ return taskId;
155
+ }
156
+ catch (err) {
157
+ // Release lock on any error after acquisition
158
+ await this.storage.releaseLock(taskId);
159
+ this.currentTaskId = null;
160
+ this.currentTaskType = null;
161
+ this.memtable = null;
162
+ throw err;
163
+ }
164
+ }
165
+ /**
166
+ * Mark a file as successfully processed
167
+ */
168
+ async fileProcessed(taskId, path, chunks, action = 'updated') {
169
+ if (taskId !== this.currentTaskId || !this.memtable) {
170
+ throw new Error('Task not active');
171
+ }
172
+ // Update current file
173
+ this.currentFile = path;
174
+ // 1. Append to WAL (O(1) write)
175
+ await this.storage.appendEntry({
176
+ lsn: this.currentLsn++,
177
+ ts: Date.now(),
178
+ op: 'file:done',
179
+ taskId,
180
+ data: { path, chunks },
181
+ });
182
+ // 2. Update memtable (O(1))
183
+ this.memtable.processedCount++;
184
+ this.memtable.lastLsn = this.currentLsn;
185
+ if (action === 'added') {
186
+ this.memtable.stats.added++;
187
+ }
188
+ else {
189
+ this.memtable.stats.updated++;
190
+ }
191
+ this.filesSinceCheckpoint++;
192
+ // 3. Maybe checkpoint (amortized O(1))
193
+ if (this.filesSinceCheckpoint >= this.config.checkpointInterval) {
194
+ await this.checkpoint();
195
+ }
196
+ // 4. Emit progress (every 10 files or on checkpoint)
197
+ if (this.filesSinceCheckpoint % 10 === 0) {
198
+ this.emitProgress();
199
+ }
200
+ }
201
+ /**
202
+ * Mark a file as failed
203
+ */
204
+ async fileFailed(taskId, path, error, retries) {
205
+ if (taskId !== this.currentTaskId || !this.memtable) {
206
+ throw new Error('Task not active');
207
+ }
208
+ // Append to WAL
209
+ await this.storage.appendEntry({
210
+ lsn: this.currentLsn++,
211
+ ts: Date.now(),
212
+ op: 'file:fail',
213
+ taskId,
214
+ data: { path, error, retries },
215
+ });
216
+ // Update memtable
217
+ this.memtable.failedPaths.set(path, { error, retries });
218
+ this.memtable.processedCount++;
219
+ this.memtable.lastLsn = this.currentLsn;
220
+ this.filesSinceCheckpoint++;
221
+ // Maybe checkpoint
222
+ if (this.filesSinceCheckpoint >= this.config.checkpointInterval) {
223
+ await this.checkpoint();
224
+ }
225
+ }
226
+ /**
227
+ * Mark file as deleted
228
+ */
229
+ async fileDeleted(taskId, path) {
230
+ if (taskId !== this.currentTaskId || !this.memtable) {
231
+ throw new Error('Task not active');
232
+ }
233
+ // Append to WAL
234
+ await this.storage.appendEntry({
235
+ lsn: this.currentLsn++,
236
+ ts: Date.now(),
237
+ op: 'file:done',
238
+ taskId,
239
+ data: { path, chunks: 0 },
240
+ });
241
+ // Update memtable
242
+ this.memtable.stats.deleted++;
243
+ this.memtable.processedCount++;
244
+ this.memtable.lastLsn = this.currentLsn;
245
+ this.filesSinceCheckpoint++;
246
+ // Maybe checkpoint
247
+ if (this.filesSinceCheckpoint >= this.config.checkpointInterval) {
248
+ await this.checkpoint();
249
+ }
250
+ }
251
+ /**
252
+ * Complete the sync task
253
+ */
254
+ async completeTask(taskId) {
255
+ console.log(`[SyncWal.completeTask] completing task ${taskId}`);
256
+ if (taskId !== this.currentTaskId || !this.memtable) {
257
+ throw new Error('Task not active');
258
+ }
259
+ const now = Date.now();
260
+ // Get cumulative stats
261
+ const stats = this.getCumulativeStats();
262
+ // Append task:complete to WAL
263
+ await this.storage.appendEntry({
264
+ lsn: this.currentLsn++,
265
+ ts: now,
266
+ op: 'task:complete',
267
+ taskId,
268
+ data: { stats },
269
+ });
270
+ // Update manifest
271
+ const summary = {
272
+ id: taskId,
273
+ type: this.currentTaskType,
274
+ status: 'completed',
275
+ startedAt: this.startedAt,
276
+ completedAt: now,
277
+ totalFiles: this.totalFiles,
278
+ stats: {
279
+ added: stats.added,
280
+ updated: stats.updated,
281
+ deleted: stats.deleted,
282
+ },
283
+ failedCount: stats.failed.length,
284
+ };
285
+ const manifest = {
286
+ version: 1,
287
+ current: null,
288
+ lastCompleted: summary,
289
+ walLsn: this.currentLsn,
290
+ lastCheckpointLsn: this.lastCheckpointLsn,
291
+ };
292
+ await this.storage.writeManifest(manifest);
293
+ // Clear checkpoint (task is done)
294
+ await this.storage.writeCheckpoint({
295
+ version: 1,
296
+ lsn: this.currentLsn,
297
+ createdAt: now,
298
+ task: {
299
+ id: taskId,
300
+ type: this.currentTaskType,
301
+ status: 'completed',
302
+ startedAt: this.startedAt,
303
+ totalFiles: this.totalFiles,
304
+ processedCount: this.checkpointProcessedCount + this.memtable.processedCount,
305
+ manifestHash: this.manifestHash || undefined,
306
+ },
307
+ stats,
308
+ });
309
+ console.log(`[SyncWal.completeTask] checkpoint written with status=completed for task ${taskId}`);
310
+ // Update phase for final progress emit
311
+ this.phase = 'done';
312
+ this.emitProgress();
313
+ // Cleanup
314
+ await this.cleanup();
315
+ }
316
+ /**
317
+ * Fail the sync task
318
+ */
319
+ async failTask(taskId, error) {
320
+ if (taskId !== this.currentTaskId) {
321
+ throw new Error('Task not active');
322
+ }
323
+ const now = Date.now();
324
+ const stats = this.getCumulativeStats();
325
+ // Append task:fail to WAL
326
+ await this.storage.appendEntry({
327
+ lsn: this.currentLsn++,
328
+ ts: now,
329
+ op: 'task:fail',
330
+ taskId,
331
+ data: { stats, error },
332
+ });
333
+ // Update manifest
334
+ const summary = {
335
+ id: taskId,
336
+ type: this.currentTaskType,
337
+ status: 'failed',
338
+ startedAt: this.startedAt,
339
+ completedAt: now,
340
+ totalFiles: this.totalFiles,
341
+ stats: {
342
+ added: stats.added,
343
+ updated: stats.updated,
344
+ deleted: stats.deleted,
345
+ },
346
+ failedCount: stats.failed.length,
347
+ };
348
+ const manifest = {
349
+ version: 1,
350
+ current: null,
351
+ lastCompleted: summary,
352
+ walLsn: this.currentLsn,
353
+ lastCheckpointLsn: this.lastCheckpointLsn,
354
+ };
355
+ await this.storage.writeManifest(manifest);
356
+ // Cleanup
357
+ await this.cleanup();
358
+ }
359
+ /**
360
+ * Cancel the sync task
361
+ */
362
+ async cancelTask(taskId) {
363
+ if (taskId !== this.currentTaskId) {
364
+ throw new Error('Task not active');
365
+ }
366
+ const now = Date.now();
367
+ const stats = this.getCumulativeStats();
368
+ // Append task:cancel to WAL
369
+ await this.storage.appendEntry({
370
+ lsn: this.currentLsn++,
371
+ ts: now,
372
+ op: 'task:cancel',
373
+ taskId,
374
+ });
375
+ // Update manifest
376
+ const summary = {
377
+ id: taskId,
378
+ type: this.currentTaskType,
379
+ status: 'cancelled',
380
+ startedAt: this.startedAt,
381
+ completedAt: now,
382
+ totalFiles: this.totalFiles,
383
+ stats: {
384
+ added: stats.added,
385
+ updated: stats.updated,
386
+ deleted: stats.deleted,
387
+ },
388
+ failedCount: stats.failed.length,
389
+ };
390
+ const manifest = {
391
+ version: 1,
392
+ current: null,
393
+ lastCompleted: summary,
394
+ walLsn: this.currentLsn,
395
+ lastCheckpointLsn: this.lastCheckpointLsn,
396
+ };
397
+ await this.storage.writeManifest(manifest);
398
+ // Cleanup
399
+ await this.cleanup();
400
+ }
401
+ /**
402
+ * Set task phase
403
+ */
404
+ setPhase(phase) {
405
+ this.phase = phase;
406
+ this.emitProgress();
407
+ }
408
+ // ═══════════════════════════════════════════════════════════════════════════
409
+ // CHECKPOINT (PostgreSQL-style)
410
+ // ═══════════════════════════════════════════════════════════════════════════
411
+ async checkpoint() {
412
+ if (!this.memtable || !this.currentTaskId)
413
+ return;
414
+ const checkpointLsn = this.currentLsn;
415
+ const totalProcessed = this.checkpointProcessedCount + this.memtable.processedCount;
416
+ const now = Date.now();
417
+ // 1. Write checkpoint marker to WAL
418
+ await this.storage.appendEntry({
419
+ lsn: checkpointLsn,
420
+ ts: now,
421
+ op: 'checkpoint',
422
+ taskId: this.currentTaskId,
423
+ data: { processedCount: totalProcessed },
424
+ });
425
+ this.currentLsn++;
426
+ // 2. Get cumulative stats
427
+ const stats = this.getCumulativeStats();
428
+ // 3. Write checkpoint file
429
+ await this.storage.writeCheckpoint({
430
+ version: 1,
431
+ lsn: checkpointLsn,
432
+ createdAt: now,
433
+ task: {
434
+ id: this.currentTaskId,
435
+ type: this.currentTaskType,
436
+ status: 'running',
437
+ startedAt: this.startedAt,
438
+ totalFiles: this.totalFiles,
439
+ processedCount: totalProcessed,
440
+ manifestHash: this.manifestHash || undefined,
441
+ },
442
+ stats,
443
+ });
444
+ // 4. Truncate WAL before checkpoint LSN
445
+ await this.storage.truncateBeforeLsn(checkpointLsn);
446
+ // 5. Update manifest
447
+ const manifest = await this.storage.getManifest();
448
+ if (manifest) {
449
+ manifest.lastCheckpointLsn = checkpointLsn;
450
+ manifest.walLsn = this.currentLsn;
451
+ if (manifest.current) {
452
+ manifest.current.processedFiles = totalProcessed;
453
+ manifest.current.updatedAt = now;
454
+ manifest.current.lastCheckpointLsn = checkpointLsn;
455
+ }
456
+ await this.storage.writeManifest(manifest);
457
+ }
458
+ // 6. Update checkpoint baseline and clear memtable counts
459
+ this.checkpointProcessedCount = totalProcessed;
460
+ this.lastCheckpointLsn = checkpointLsn;
461
+ this.memtable.processedCount = 0;
462
+ this.filesSinceCheckpoint = 0;
463
+ // 7. Update heartbeat
464
+ await this.storage.updateHeartbeat(this.currentTaskId);
465
+ // 8. Emit progress
466
+ this.emitProgress();
467
+ }
468
+ // ═══════════════════════════════════════════════════════════════════════════
469
+ // RECOVERY (checkpoint + WAL replay)
470
+ // ═══════════════════════════════════════════════════════════════════════════
471
+ /**
472
+ * Check for incomplete task and return recovery plan
473
+ */
474
+ async recover() {
475
+ // 1. Load checkpoint (O(1), small file ~1KB)
476
+ const checkpoint = await this.storage.getCheckpoint();
477
+ // Debug logging
478
+ if (checkpoint) {
479
+ console.log(`[SyncWal.recover] checkpoint found: taskId=${checkpoint.task.id}, status=${checkpoint.task.status}, type=${checkpoint.task.type}`);
480
+ }
481
+ else {
482
+ console.log('[SyncWal.recover] no checkpoint found');
483
+ }
484
+ if (!checkpoint || checkpoint.task.status !== 'running') {
485
+ return null;
486
+ }
487
+ // 2. Read WAL tail (only entries after last checkpoint)
488
+ const walEntries = await this.storage.getEntriesFromLsn(checkpoint.lsn);
489
+ // 3. Count file:done entries in WAL tail
490
+ let walProcessedCount = 0;
491
+ for (const entry of walEntries) {
492
+ if (entry.op === 'file:done') {
493
+ walProcessedCount++;
494
+ }
495
+ // Update LSN from WAL
496
+ if (entry.lsn >= this.currentLsn) {
497
+ this.currentLsn = entry.lsn + 1;
498
+ }
499
+ }
500
+ // 4. Total skip count = checkpoint.processedCount + WAL tail count
501
+ const skipCount = checkpoint.task.processedCount + walProcessedCount;
502
+ // 5. Return recovery plan
503
+ return {
504
+ taskId: checkpoint.task.id,
505
+ type: checkpoint.task.type,
506
+ totalFiles: checkpoint.task.totalFiles,
507
+ skipCount,
508
+ manifestHash: checkpoint.task.manifestHash || '',
509
+ stats: checkpoint.stats,
510
+ };
511
+ }
512
+ /**
513
+ * Resume a task from recovery plan
514
+ */
515
+ async resumeTask(plan) {
516
+ const now = Date.now();
517
+ // Restore state from recovery plan
518
+ this.currentTaskId = plan.taskId;
519
+ this.currentTaskType = plan.type;
520
+ this.totalFiles = plan.totalFiles;
521
+ this.manifestHash = plan.manifestHash || null;
522
+ this.checkpointProcessedCount = plan.skipCount;
523
+ this.phase = 'indexing';
524
+ // Re-acquire lock
525
+ const lock = {
526
+ id: plan.taskId,
527
+ type: plan.type,
528
+ pid: process.pid,
529
+ startedAt: now,
530
+ heartbeat: now,
531
+ };
532
+ // Force acquire (we know there's no valid lock from recovery check)
533
+ if (this.storage instanceof FileWalStorage) {
534
+ await this.storage.forceClearLock();
535
+ }
536
+ const acquired = await this.storage.acquireLock(lock);
537
+ if (!acquired) {
538
+ throw new Error('Failed to re-acquire lock for recovery');
539
+ }
540
+ // Initialize memtable with restored stats
541
+ this.memtable = {
542
+ taskId: plan.taskId,
543
+ processedCount: 0,
544
+ failedPaths: new Map(plan.stats.failed.map((f) => [f.path, { error: f.error, retries: f.retries }])),
545
+ stats: {
546
+ added: plan.stats.added,
547
+ updated: plan.stats.updated,
548
+ deleted: plan.stats.deleted,
549
+ },
550
+ lastLsn: this.currentLsn,
551
+ };
552
+ // Update manifest
553
+ const manifest = await this.storage.getManifest();
554
+ if (manifest && manifest.current) {
555
+ manifest.current.updatedAt = now;
556
+ await this.storage.writeManifest(manifest);
557
+ }
558
+ // Start heartbeat
559
+ this.startHeartbeat();
560
+ }
561
+ // ═══════════════════════════════════════════════════════════════════════════
562
+ // STATE QUERIES
563
+ // ═══════════════════════════════════════════════════════════════════════════
564
+ async getManifest() {
565
+ return this.storage.getManifest();
566
+ }
567
+ async getCurrentTask() {
568
+ const manifest = await this.storage.getManifest();
569
+ return manifest?.current || null;
570
+ }
571
+ async getLastCompleted() {
572
+ const manifest = await this.storage.getManifest();
573
+ return manifest?.lastCompleted || null;
574
+ }
575
+ getProgress() {
576
+ if (!this.currentTaskId || !this.currentTaskType) {
577
+ return null;
578
+ }
579
+ const stats = this.getCumulativeStats();
580
+ const processedFiles = this.checkpointProcessedCount + (this.memtable?.processedCount || 0);
581
+ const now = Date.now();
582
+ const elapsedMs = now - this.startedAt;
583
+ // Estimate remaining time
584
+ let estimatedRemainingMs;
585
+ if (processedFiles > 0) {
586
+ const msPerFile = elapsedMs / processedFiles;
587
+ const remainingFiles = this.totalFiles - processedFiles;
588
+ estimatedRemainingMs = Math.round(msPerFile * remainingFiles);
589
+ }
590
+ return {
591
+ taskId: this.currentTaskId,
592
+ type: this.currentTaskType,
593
+ status: this.isPaused ? 'paused' : 'running',
594
+ phase: this.phase,
595
+ paused: this.isPaused,
596
+ totalFiles: this.totalFiles,
597
+ processedFiles,
598
+ currentFile: this.currentFile || undefined,
599
+ added: stats.added,
600
+ updated: stats.updated,
601
+ deleted: stats.deleted,
602
+ failedCount: stats.failed.length,
603
+ startedAt: this.startedAt,
604
+ elapsedMs,
605
+ estimatedRemainingMs,
606
+ };
607
+ }
608
+ // ═══════════════════════════════════════════════════════════════════════════
609
+ // LOCK MANAGEMENT
610
+ // ═══════════════════════════════════════════════════════════════════════════
611
+ async acquireLock(lock) {
612
+ // First try to acquire directly
613
+ const acquired = await this.storage.acquireLock(lock);
614
+ if (acquired)
615
+ return true;
616
+ // Check if existing lock is stale
617
+ const status = await this.isLocked();
618
+ if (status.locked && status.stale && status.lock) {
619
+ // Atomic compare-and-clear: only clear if lock unchanged
620
+ if (this.storage instanceof FileWalStorage) {
621
+ const cleared = await this.storage.compareAndClearLock(status.lock);
622
+ if (cleared) {
623
+ // Retry acquire
624
+ return this.storage.acquireLock(lock);
625
+ }
626
+ }
627
+ }
628
+ return false;
629
+ }
630
+ async releaseLock() {
631
+ if (this.currentTaskId) {
632
+ await this.storage.releaseLock(this.currentTaskId);
633
+ }
634
+ }
635
+ async isLocked() {
636
+ const lock = await this.storage.getLock();
637
+ if (!lock) {
638
+ return { locked: false, stale: false };
639
+ }
640
+ // Check if stale
641
+ const now = Date.now();
642
+ const heartbeatAge = now - lock.heartbeat;
643
+ const isHeartbeatStale = heartbeatAge > this.config.staleLockTimeout;
644
+ const isPidDead = !FileWalStorage.isPidRunning(lock.pid);
645
+ const stale = isHeartbeatStale || isPidDead;
646
+ return { locked: true, stale, lock };
647
+ }
648
+ async forceClearStaleLock() {
649
+ const status = await this.isLocked();
650
+ if (!status.locked) {
651
+ return true; // No lock to clear
652
+ }
653
+ if (!status.stale) {
654
+ return false; // Lock is not stale, cannot force clear
655
+ }
656
+ if (this.storage instanceof FileWalStorage) {
657
+ await this.storage.forceClearLock();
658
+ return true;
659
+ }
660
+ return false;
661
+ }
662
+ /**
663
+ * Clear all WAL state (lock, checkpoint, WAL entries, manifest).
664
+ * Use this to reset sync state when stuck in recovery loop.
665
+ * WARNING: This will lose any recovery information for incomplete syncs.
666
+ */
667
+ async clear() {
668
+ // Stop any running task first
669
+ this.stopHeartbeat();
670
+ await this.releaseLock();
671
+ // Clear all storage
672
+ await this.storage.clear();
673
+ // Reset in-memory state
674
+ this.currentTaskId = null;
675
+ this.currentTaskType = null;
676
+ this.memtable = null;
677
+ this.totalFiles = 0;
678
+ this.manifestHash = null;
679
+ this.filesSinceCheckpoint = 0;
680
+ this.checkpointProcessedCount = 0;
681
+ this.currentFile = null;
682
+ this.phase = 'analyzing';
683
+ this.currentLsn = 0;
684
+ this.lastCheckpointLsn = 0;
685
+ this.startedAt = 0;
686
+ this.cancelRequested.clear();
687
+ this.pauseRequested.clear();
688
+ this.isPaused = false;
689
+ }
690
+ // ═══════════════════════════════════════════════════════════════════════════
691
+ // HEARTBEAT
692
+ // ═══════════════════════════════════════════════════════════════════════════
693
+ startHeartbeat() {
694
+ this.stopHeartbeat();
695
+ this.heartbeatInterval = setInterval(async () => {
696
+ if (this.currentTaskId) {
697
+ try {
698
+ await this.storage.updateHeartbeat(this.currentTaskId);
699
+ }
700
+ catch (err) {
701
+ console.warn('Heartbeat update failed:', err);
702
+ }
703
+ }
704
+ }, this.config.heartbeatInterval);
705
+ }
706
+ stopHeartbeat() {
707
+ if (this.heartbeatInterval) {
708
+ clearInterval(this.heartbeatInterval);
709
+ this.heartbeatInterval = undefined;
710
+ }
711
+ }
712
+ // ═══════════════════════════════════════════════════════════════════════════
713
+ // CANCELLATION
714
+ // ═══════════════════════════════════════════════════════════════════════════
715
+ async requestCancel(taskId) {
716
+ this.cancelRequested.add(taskId);
717
+ // If paused, also unpause so cancellation can proceed
718
+ this.pauseRequested.delete(taskId);
719
+ this.isPaused = false;
720
+ }
721
+ isCancelled(taskId) {
722
+ return this.cancelRequested.has(taskId);
723
+ }
724
+ // ═══════════════════════════════════════════════════════════════════════════
725
+ // PAUSE / RESUME
726
+ // ═══════════════════════════════════════════════════════════════════════════
727
+ /**
728
+ * Request pause for the current sync task
729
+ */
730
+ requestPause(taskId) {
731
+ if (taskId === this.currentTaskId) {
732
+ this.pauseRequested.add(taskId);
733
+ this.isPaused = true;
734
+ this.emitProgress();
735
+ }
736
+ }
737
+ /**
738
+ * Resume a paused sync task
739
+ */
740
+ requestResume(taskId) {
741
+ if (taskId === this.currentTaskId) {
742
+ this.pauseRequested.delete(taskId);
743
+ this.isPaused = false;
744
+ this.emitProgress();
745
+ }
746
+ }
747
+ /**
748
+ * Check if task should pause (call from indexer loop)
749
+ */
750
+ shouldPause(taskId) {
751
+ return this.pauseRequested.has(taskId);
752
+ }
753
+ /**
754
+ * Wait while paused (call from indexer loop)
755
+ * Returns true if resumed, false if cancelled during pause
756
+ */
757
+ async waitWhilePaused(taskId) {
758
+ while (this.pauseRequested.has(taskId)) {
759
+ if (this.cancelRequested.has(taskId)) {
760
+ return false; // Cancelled during pause
761
+ }
762
+ await new Promise((resolve) => setTimeout(resolve, 100));
763
+ }
764
+ return true; // Resumed
765
+ }
766
+ /**
767
+ * Check if currently paused
768
+ */
769
+ isPausedNow() {
770
+ return this.isPaused;
771
+ }
772
+ // ═══════════════════════════════════════════════════════════════════════════
773
+ // PROGRESS CALLBACK
774
+ // ═══════════════════════════════════════════════════════════════════════════
775
+ onProgress(callback) {
776
+ this.onProgressCallback = callback;
777
+ }
778
+ onEmbeddingProgress(callback) {
779
+ this.onEmbeddingProgressCallback = callback;
780
+ }
781
+ /**
782
+ * Get callback for embedding progress (to pass to embedding provider)
783
+ */
784
+ getEmbeddingProgressCallback() {
785
+ return this.onEmbeddingProgressCallback;
786
+ }
787
+ onSubPhaseProgress(callback) {
788
+ this.onSubPhaseProgressCallback = callback;
789
+ }
790
+ /**
791
+ * Emit sub-phase progress (for graph/tree building phases)
792
+ */
793
+ emitSubPhaseProgress(current, total, subPhase) {
794
+ this.onSubPhaseProgressCallback?.(current, total, subPhase);
795
+ }
796
+ emitProgress() {
797
+ const progress = this.getProgress();
798
+ if (progress && this.onProgressCallback) {
799
+ this.onProgressCallback(progress);
800
+ }
801
+ }
802
+ // ═══════════════════════════════════════════════════════════════════════════
803
+ // GRACEFUL SHUTDOWN
804
+ // ═══════════════════════════════════════════════════════════════════════════
805
+ async shutdown() {
806
+ this.stopHeartbeat();
807
+ if (this.currentTaskId) {
808
+ // Final checkpoint before shutdown
809
+ await this.checkpoint().catch(() => { });
810
+ }
811
+ await this.releaseLock();
812
+ }
813
+ // ═══════════════════════════════════════════════════════════════════════════
814
+ // HELPERS
815
+ // ═══════════════════════════════════════════════════════════════════════════
816
+ getCumulativeStats() {
817
+ const failed = [];
818
+ if (this.memtable) {
819
+ for (const [path, info] of this.memtable.failedPaths) {
820
+ failed.push({ path, error: info.error, retries: info.retries });
821
+ }
822
+ }
823
+ return {
824
+ added: this.memtable?.stats.added || 0,
825
+ updated: this.memtable?.stats.updated || 0,
826
+ deleted: this.memtable?.stats.deleted || 0,
827
+ failed,
828
+ };
829
+ }
830
+ async cleanup() {
831
+ this.stopHeartbeat();
832
+ await this.releaseLock();
833
+ // Clear cancel/pause requests before resetting taskId
834
+ if (this.currentTaskId) {
835
+ this.cancelRequested.delete(this.currentTaskId);
836
+ this.pauseRequested.delete(this.currentTaskId);
837
+ }
838
+ this.isPaused = false;
839
+ // Reset state
840
+ this.currentTaskId = null;
841
+ this.currentTaskType = null;
842
+ this.memtable = null;
843
+ this.totalFiles = 0;
844
+ this.manifestHash = null;
845
+ this.filesSinceCheckpoint = 0;
846
+ this.checkpointProcessedCount = 0;
847
+ this.currentFile = null;
848
+ this.phase = 'analyzing';
849
+ }
850
+ /**
851
+ * Get current task ID (for external use)
852
+ */
853
+ getCurrentTaskId() {
854
+ return this.currentTaskId;
855
+ }
856
+ /**
857
+ * Check if sync is currently running
858
+ */
859
+ isRunning() {
860
+ return this.currentTaskId !== null;
861
+ }
862
+ }
863
+ //# sourceMappingURL=sync-wal.js.map