gitnexus 1.6.11-rc.8 → 1.6.11

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 (531) hide show
  1. package/README.md +95 -26
  2. package/dist/_shared/graph/types.d.ts +81 -2
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -1
  5. package/dist/_shared/integrations/circuit-breaker.js.map +1 -1
  6. package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -1
  7. package/dist/_shared/integrations/resilient-fetch.js.map +1 -1
  8. package/dist/_shared/language-detection.d.ts.map +1 -1
  9. package/dist/_shared/language-detection.js +2 -0
  10. package/dist/_shared/language-detection.js.map +1 -1
  11. package/dist/_shared/languages.d.ts +2 -1
  12. package/dist/_shared/languages.d.ts.map +1 -1
  13. package/dist/_shared/languages.js +1 -0
  14. package/dist/_shared/languages.js.map +1 -1
  15. package/dist/_shared/lbug/schema-constants.d.ts +2 -2
  16. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  17. package/dist/_shared/lbug/schema-constants.js +4 -0
  18. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  19. package/dist/_shared/scope-resolution/evidence-weights.d.ts.map +1 -1
  20. package/dist/_shared/scope-resolution/finalize-algorithm.js +1 -0
  21. package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
  22. package/dist/_shared/scope-resolution/language-classification.d.ts +3 -0
  23. package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/language-classification.js +4 -0
  25. package/dist/_shared/scope-resolution/language-classification.js.map +1 -1
  26. package/dist/_shared/scope-resolution/reference-site.d.ts +10 -0
  27. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -1
  28. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -1
  29. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -1
  30. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  31. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  32. package/dist/_shared/scope-resolution/types.d.ts +15 -0
  33. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  34. package/dist/cli/ai-context.d.ts +4 -0
  35. package/dist/cli/ai-context.js +90 -14
  36. package/dist/cli/analyze-config.js +14 -0
  37. package/dist/cli/analyze-options.d.ts +14 -0
  38. package/dist/cli/analyze-watch.d.ts +20 -0
  39. package/dist/cli/analyze-watch.js +434 -0
  40. package/dist/cli/analyze.js +27 -9
  41. package/dist/cli/auto-sync.d.ts +1 -0
  42. package/dist/cli/auto-sync.js +107 -0
  43. package/dist/cli/command-banner.d.ts +15 -0
  44. package/dist/cli/command-banner.js +42 -0
  45. package/dist/cli/detect-changes-format.js +21 -0
  46. package/dist/cli/doctor.d.ts +7 -0
  47. package/dist/cli/doctor.js +14 -0
  48. package/dist/cli/group.js +7 -1
  49. package/dist/cli/help-i18n.js +4 -0
  50. package/dist/cli/i18n/en.d.ts +18 -3
  51. package/dist/cli/i18n/en.js +18 -3
  52. package/dist/cli/i18n/resources.d.ts +33 -3
  53. package/dist/cli/i18n/zh-CN.d.ts +15 -0
  54. package/dist/cli/i18n/zh-CN.js +18 -3
  55. package/dist/cli/index.js +34 -7
  56. package/dist/cli/mcp.d.ts +15 -0
  57. package/dist/cli/mcp.js +44 -0
  58. package/dist/cli/optional-grammars.d.ts +3 -2
  59. package/dist/cli/optional-grammars.js +19 -3
  60. package/dist/cli/setup.js +41 -15
  61. package/dist/cli/update-notice.d.ts +34 -0
  62. package/dist/cli/update-notice.js +91 -0
  63. package/dist/cli/update.d.ts +21 -0
  64. package/dist/cli/update.js +93 -0
  65. package/dist/cli/watch.d.ts +1 -20
  66. package/dist/cli/watch.js +5 -371
  67. package/dist/core/analysis-feature-registry.d.ts +2 -0
  68. package/dist/core/analysis-feature-registry.js +15 -0
  69. package/dist/core/analyzer-identity.js +21 -3
  70. package/dist/core/auto-sync/analysis-worker-launch.d.ts +30 -0
  71. package/dist/core/auto-sync/analysis-worker-launch.js +166 -0
  72. package/dist/core/auto-sync/config.d.ts +33 -0
  73. package/dist/core/auto-sync/config.js +320 -0
  74. package/dist/core/auto-sync/index.d.ts +6 -0
  75. package/dist/core/auto-sync/index.js +6 -0
  76. package/dist/core/auto-sync/path-security.d.ts +9 -0
  77. package/dist/core/auto-sync/path-security.js +251 -0
  78. package/dist/core/auto-sync/repo.d.ts +1 -0
  79. package/dist/core/auto-sync/repo.js +6 -0
  80. package/dist/core/auto-sync/runner.d.ts +44 -0
  81. package/dist/core/auto-sync/runner.js +428 -0
  82. package/dist/core/auto-sync/starter.d.ts +50 -0
  83. package/dist/core/auto-sync/starter.js +497 -0
  84. package/dist/core/auto-sync/state.d.ts +37 -0
  85. package/dist/core/auto-sync/state.js +119 -0
  86. package/dist/core/group/config-parser.js +8 -1
  87. package/dist/core/group/cross-impact.d.ts +3 -0
  88. package/dist/core/group/cross-impact.js +36 -4
  89. package/dist/core/group/extractors/http-patterns/java.js +22 -8
  90. package/dist/core/group/extractors/http-patterns/kotlin.js +86 -31
  91. package/dist/core/group/extractors/http-patterns/node.js +145 -8
  92. package/dist/core/group/extractors/http-patterns/python.js +11 -7
  93. package/dist/core/group/extractors/http-route-extractor.js +63 -17
  94. package/dist/core/group/extractors/java-workspace-extractor.js +244 -31
  95. package/dist/core/group/group-lock.js +5 -6
  96. package/dist/core/group/normalization.d.ts +38 -1
  97. package/dist/core/group/normalization.js +59 -3
  98. package/dist/core/group/service.d.ts +11 -0
  99. package/dist/core/group/service.js +13 -1
  100. package/dist/core/group/storage.js +2 -2
  101. package/dist/core/group/sync.d.ts +24 -1
  102. package/dist/core/group/sync.js +46 -8
  103. package/dist/core/group/types.d.ts +13 -0
  104. package/dist/core/incremental/derived-writeback.d.ts +36 -0
  105. package/dist/core/incremental/derived-writeback.js +68 -0
  106. package/dist/core/incremental/spring-config-drift.d.ts +12 -0
  107. package/dist/core/incremental/spring-config-drift.js +46 -0
  108. package/dist/core/incremental/subgraph-extract.d.ts +7 -4
  109. package/dist/core/incremental/subgraph-extract.js +30 -5
  110. package/dist/core/ingestion/asyncapi/document.d.ts +226 -0
  111. package/dist/core/ingestion/asyncapi/document.js +758 -0
  112. package/dist/core/ingestion/asyncapi/protocol.d.ts +79 -0
  113. package/dist/core/ingestion/asyncapi/protocol.js +203 -0
  114. package/dist/core/ingestion/call-extractors/configs/zig.d.ts +2 -0
  115. package/dist/core/ingestion/call-extractors/configs/zig.js +4 -0
  116. package/dist/core/ingestion/call-extractors/zig-static-gating.d.ts +100 -0
  117. package/dist/core/ingestion/call-extractors/zig-static-gating.js +319 -0
  118. package/dist/core/ingestion/call-processor.js +3 -1
  119. package/dist/core/ingestion/class-extractors/configs/zig.d.ts +2 -0
  120. package/dist/core/ingestion/class-extractors/configs/zig.js +32 -0
  121. package/dist/core/ingestion/class-extractors/generic.js +4 -1
  122. package/dist/core/ingestion/class-types.d.ts +6 -2
  123. package/dist/core/ingestion/destination-key.d.ts +59 -0
  124. package/dist/core/ingestion/destination-key.js +61 -0
  125. package/dist/core/ingestion/entry-point-scoring.d.ts +6 -2
  126. package/dist/core/ingestion/entry-point-scoring.js +8 -44
  127. package/dist/core/ingestion/export-detection.d.ts +37 -0
  128. package/dist/core/ingestion/export-detection.js +60 -0
  129. package/dist/core/ingestion/field-extractors/configs/zig.d.ts +8 -0
  130. package/dist/core/ingestion/field-extractors/configs/zig.js +49 -0
  131. package/dist/core/ingestion/field-extractors/generic.d.ts +1 -1
  132. package/dist/core/ingestion/field-extractors/generic.js +12 -1
  133. package/dist/core/ingestion/frameworks/spring/actuator-runtime.d.ts +26 -0
  134. package/dist/core/ingestion/frameworks/spring/actuator-runtime.js +843 -0
  135. package/dist/core/ingestion/frameworks/spring/analysis-features.d.ts +5 -0
  136. package/dist/core/ingestion/frameworks/spring/analysis-features.js +9 -0
  137. package/dist/core/ingestion/frameworks/spring/annotation-arguments.js +8 -1
  138. package/dist/core/ingestion/frameworks/spring/argument-facts.d.ts +80 -0
  139. package/dist/core/ingestion/frameworks/spring/argument-facts.js +85 -0
  140. package/dist/core/ingestion/frameworks/spring/config-bindings.d.ts +1 -0
  141. package/dist/core/ingestion/frameworks/spring/config-bindings.js +2 -1
  142. package/dist/core/ingestion/frameworks/spring/destinations.d.ts +539 -0
  143. package/dist/core/ingestion/frameworks/spring/destinations.js +840 -0
  144. package/dist/core/ingestion/frameworks/spring/dynamic-lookups.d.ts +22 -0
  145. package/dist/core/ingestion/frameworks/spring/dynamic-lookups.js +124 -0
  146. package/dist/core/ingestion/frameworks/spring/message-producers.d.ts +78 -0
  147. package/dist/core/ingestion/frameworks/spring/message-producers.js +90 -0
  148. package/dist/core/ingestion/frameworks/spring/non-http-handlers.d.ts +29 -0
  149. package/dist/core/ingestion/frameworks/spring/vendor-prefixes.d.ts +7 -0
  150. package/dist/core/ingestion/frameworks/spring/vendor-prefixes.js +22 -0
  151. package/dist/core/ingestion/import-resolvers/configs/zig.d.ts +13 -0
  152. package/dist/core/ingestion/import-resolvers/configs/zig.js +26 -0
  153. package/dist/core/ingestion/import-resolvers/zig.d.ts +34 -0
  154. package/dist/core/ingestion/import-resolvers/zig.js +193 -0
  155. package/dist/core/ingestion/language-config.d.ts +177 -0
  156. package/dist/core/ingestion/language-config.js +719 -0
  157. package/dist/core/ingestion/language-provider.d.ts +178 -1
  158. package/dist/core/ingestion/language-provider.js +22 -0
  159. package/dist/core/ingestion/languages/csharp/razor-view-components.d.ts +62 -0
  160. package/dist/core/ingestion/languages/csharp/razor-view-components.js +954 -0
  161. package/dist/core/ingestion/languages/csharp/resolution-config.d.ts +3 -0
  162. package/dist/core/ingestion/languages/csharp/resolution-config.js +6 -1
  163. package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -0
  164. package/dist/core/ingestion/languages/index.d.ts +1 -0
  165. package/dist/core/ingestion/languages/index.js +2 -0
  166. package/dist/core/ingestion/languages/java/analysis-features.d.ts +1 -1
  167. package/dist/core/ingestion/languages/java/analysis-features.js +14 -6
  168. package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +8 -0
  169. package/dist/core/ingestion/languages/java/capture-side-channel.js +34 -0
  170. package/dist/core/ingestion/languages/java/captures.js +22 -1
  171. package/dist/core/ingestion/languages/java/lombok-synthesizer.d.ts +42 -0
  172. package/dist/core/ingestion/languages/java/lombok-synthesizer.js +439 -0
  173. package/dist/core/ingestion/languages/java/scope-resolver.js +2 -0
  174. package/dist/core/ingestion/languages/java/spring-actuator.d.ts +2 -0
  175. package/dist/core/ingestion/languages/java/spring-actuator.js +62 -0
  176. package/dist/core/ingestion/languages/java/spring-di.d.ts +17 -1
  177. package/dist/core/ingestion/languages/java/spring-di.js +44 -2
  178. package/dist/core/ingestion/languages/java/spring-dynamic-lookup.d.ts +8 -0
  179. package/dist/core/ingestion/languages/java/spring-dynamic-lookup.js +62 -0
  180. package/dist/core/ingestion/languages/java/spring-message-producers.d.ts +20 -0
  181. package/dist/core/ingestion/languages/java/spring-message-producers.js +83 -0
  182. package/dist/core/ingestion/languages/java/spring-non-http-handlers.d.ts +11 -1
  183. package/dist/core/ingestion/languages/java/spring-non-http-handlers.js +13 -3
  184. package/dist/core/ingestion/languages/java.js +18 -8
  185. package/dist/core/ingestion/languages/jvm/accessor-synthesis.d.ts +99 -0
  186. package/dist/core/ingestion/languages/jvm/accessor-synthesis.js +173 -0
  187. package/dist/core/ingestion/languages/jvm/beanspec.d.ts +17 -0
  188. package/dist/core/ingestion/languages/jvm/beanspec.js +42 -0
  189. package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +15 -0
  190. package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +50 -0
  191. package/dist/core/ingestion/languages/kotlin/captures.js +24 -1
  192. package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.d.ts +26 -0
  193. package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.js +417 -0
  194. package/dist/core/ingestion/languages/kotlin/scope-resolver.js +4 -0
  195. package/dist/core/ingestion/languages/kotlin/spring-actuator.d.ts +4 -0
  196. package/dist/core/ingestion/languages/kotlin/spring-actuator.js +172 -0
  197. package/dist/core/ingestion/languages/kotlin/spring-config-bindings.d.ts +18 -0
  198. package/dist/core/ingestion/languages/kotlin/spring-config-bindings.js +411 -0
  199. package/dist/core/ingestion/languages/kotlin/spring-di.d.ts +51 -1
  200. package/dist/core/ingestion/languages/kotlin/spring-di.js +86 -6
  201. package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.d.ts +8 -0
  202. package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.js +77 -0
  203. package/dist/core/ingestion/languages/kotlin/spring-message-producers.d.ts +17 -0
  204. package/dist/core/ingestion/languages/kotlin/spring-message-producers.js +119 -0
  205. package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.d.ts +16 -0
  206. package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.js +77 -2
  207. package/dist/core/ingestion/languages/kotlin.js +24 -0
  208. package/dist/core/ingestion/languages/python.js +2 -0
  209. package/dist/core/ingestion/languages/rust/query.js +8 -8
  210. package/dist/core/ingestion/languages/zig/captures.d.ts +260 -0
  211. package/dist/core/ingestion/languages/zig/captures.js +2070 -0
  212. package/dist/core/ingestion/languages/zig/index.d.ts +4 -0
  213. package/dist/core/ingestion/languages/zig/index.js +4 -0
  214. package/dist/core/ingestion/languages/zig/interpret.d.ts +36 -0
  215. package/dist/core/ingestion/languages/zig/interpret.js +176 -0
  216. package/dist/core/ingestion/languages/zig/query.d.ts +3 -0
  217. package/dist/core/ingestion/languages/zig/query.js +426 -0
  218. package/dist/core/ingestion/languages/zig/range-binding.d.ts +37 -0
  219. package/dist/core/ingestion/languages/zig/range-binding.js +373 -0
  220. package/dist/core/ingestion/languages/zig/scope-resolver.d.ts +12 -0
  221. package/dist/core/ingestion/languages/zig/scope-resolver.js +58 -0
  222. package/dist/core/ingestion/languages/zig/simple-hooks.d.ts +25 -0
  223. package/dist/core/ingestion/languages/zig/simple-hooks.js +143 -0
  224. package/dist/core/ingestion/languages/zig.d.ts +32 -0
  225. package/dist/core/ingestion/languages/zig.js +130 -0
  226. package/dist/core/ingestion/method-extractors/configs/zig.d.ts +2 -0
  227. package/dist/core/ingestion/method-extractors/configs/zig.js +120 -0
  228. package/dist/core/ingestion/method-extractors/generic.js +16 -4
  229. package/dist/core/ingestion/method-types.d.ts +10 -4
  230. package/dist/core/ingestion/model/field-registry.d.ts +3 -3
  231. package/dist/core/ingestion/model/field-registry.js +11 -7
  232. package/dist/core/ingestion/model/method-registry.d.ts +3 -3
  233. package/dist/core/ingestion/model/method-registry.js +13 -8
  234. package/dist/core/ingestion/model/registration-table.js +3 -0
  235. package/dist/core/ingestion/model/type-registry.js +8 -4
  236. package/dist/core/ingestion/pipeline-phases/di.js +47 -14
  237. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  238. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  239. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +7 -1
  240. package/dist/core/ingestion/pipeline-phases/parse-impl.js +79 -91
  241. package/dist/core/ingestion/pipeline-phases/parse.d.ts +17 -0
  242. package/dist/core/ingestion/pipeline-phases/processes.d.ts +2 -1
  243. package/dist/core/ingestion/pipeline-phases/processes.js +39 -4
  244. package/dist/core/ingestion/pipeline-phases/routes.js +27 -18
  245. package/dist/core/ingestion/pipeline-phases/runner.d.ts +4 -1
  246. package/dist/core/ingestion/pipeline-phases/runner.js +34 -14
  247. package/dist/core/ingestion/pipeline-phases/scan.js +82 -1
  248. package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.d.ts +2 -0
  249. package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.js +8 -0
  250. package/dist/core/ingestion/pipeline-phases/spring-destinations.d.ts +123 -0
  251. package/dist/core/ingestion/pipeline-phases/spring-destinations.js +688 -0
  252. package/dist/core/ingestion/pipeline.d.ts +30 -1
  253. package/dist/core/ingestion/pipeline.js +52 -14
  254. package/dist/core/ingestion/resolve-references.js +5 -0
  255. package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +16 -0
  256. package/dist/core/ingestion/route-extractors/constant-resolver.js +10 -0
  257. package/dist/core/ingestion/route-extractors/java-const-resolver.d.ts +27 -1
  258. package/dist/core/ingestion/route-extractors/java-const-resolver.js +161 -13
  259. package/dist/core/ingestion/route-extractors/kotlin-const-resolver.d.ts +12 -18
  260. package/dist/core/ingestion/route-extractors/kotlin-const-resolver.js +214 -33
  261. package/dist/core/ingestion/route-extractors/kotlin-spring.d.ts +13 -0
  262. package/dist/core/ingestion/route-extractors/kotlin-spring.js +259 -0
  263. package/dist/core/ingestion/route-extractors/python-decorator-handler.d.ts +6 -0
  264. package/dist/core/ingestion/route-extractors/python-decorator-handler.js +16 -0
  265. package/dist/core/ingestion/route-extractors/spring-shared.d.ts +30 -1
  266. package/dist/core/ingestion/route-extractors/spring-shared.js +77 -9
  267. package/dist/core/ingestion/route-extractors/spring.js +9 -5
  268. package/dist/core/ingestion/scope-extractor.js +8 -0
  269. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +62 -0
  270. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +4 -0
  271. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +2 -0
  272. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +28 -0
  273. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +11 -2
  274. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +23 -2
  275. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +24 -3
  276. package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.d.ts +11 -1
  277. package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.js +26 -6
  278. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +11 -0
  279. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +29 -3
  280. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +8 -1
  281. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +35 -9
  282. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +1 -4
  283. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +277 -9
  284. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
  285. package/dist/core/ingestion/scope-resolution/pipeline/run.js +7 -3
  286. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +43 -9
  287. package/dist/core/ingestion/scope-resolution/scope/walkers.js +71 -10
  288. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -0
  289. package/dist/core/ingestion/tree-sitter-queries.js +161 -0
  290. package/dist/core/ingestion/type-extractors/zig.d.ts +2 -0
  291. package/dist/core/ingestion/type-extractors/zig.js +17 -0
  292. package/dist/core/ingestion/utils/ast-helpers.d.ts +40 -1
  293. package/dist/core/ingestion/utils/ast-helpers.js +124 -6
  294. package/dist/core/ingestion/utils/callable-flow-captures.d.ts +9 -0
  295. package/dist/core/ingestion/utils/callable-flow-captures.js +75 -14
  296. package/dist/core/ingestion/utils/test-file-path.d.ts +28 -0
  297. package/dist/core/ingestion/utils/test-file-path.js +87 -0
  298. package/dist/core/ingestion/variable-extractors/configs/zig.d.ts +2 -0
  299. package/dist/core/ingestion/variable-extractors/configs/zig.js +71 -0
  300. package/dist/core/ingestion/workers/parse-worker.js +85 -14
  301. package/dist/core/install-context.d.ts +4 -0
  302. package/dist/core/install-context.js +152 -0
  303. package/dist/core/lbug/csv-generator.d.ts +10 -1
  304. package/dist/core/lbug/csv-generator.js +92 -22
  305. package/dist/core/lbug/graph-emit-sink.d.ts +3 -2
  306. package/dist/core/lbug/graph-emit-sink.js +4 -2
  307. package/dist/core/lbug/lbug-adapter.d.ts +100 -5
  308. package/dist/core/lbug/lbug-adapter.js +236 -24
  309. package/dist/core/lbug/schema.d.ts +6 -5
  310. package/dist/core/lbug/schema.js +84 -22
  311. package/dist/core/net/url-guard.d.ts +6 -0
  312. package/dist/core/net/url-guard.js +147 -0
  313. package/dist/core/package-version.d.ts +5 -0
  314. package/dist/core/package-version.js +10 -0
  315. package/dist/core/run-analyze.d.ts +21 -4
  316. package/dist/core/run-analyze.js +426 -117
  317. package/dist/core/search/fts-indexes.d.ts +19 -1
  318. package/dist/core/search/fts-indexes.js +28 -1
  319. package/dist/core/tree-sitter/parser-loader.js +16 -0
  320. package/dist/core/update-cache.d.ts +39 -0
  321. package/dist/core/update-cache.js +141 -0
  322. package/dist/core/update-check.d.ts +31 -0
  323. package/dist/core/update-check.js +274 -0
  324. package/dist/mcp/local/local-backend.d.ts +27 -13
  325. package/dist/mcp/local/local-backend.js +148 -60
  326. package/dist/mcp/repository-policy.d.ts +5 -1
  327. package/dist/mcp/repository-policy.js +48 -4
  328. package/dist/mcp/resources.js +10 -1
  329. package/dist/mcp/server.js +8 -9
  330. package/dist/mcp/tools.js +32 -22
  331. package/dist/server/analyze-launch.d.ts +2 -0
  332. package/dist/server/analyze-launch.js +2 -0
  333. package/dist/server/analyze-worker-protocol.d.ts +6 -1
  334. package/dist/server/analyze-worker.d.ts +1 -0
  335. package/dist/server/analyze-worker.js +38 -20
  336. package/dist/server/api.d.ts +5 -1
  337. package/dist/server/api.js +105 -88
  338. package/dist/server/git-clone.d.ts +40 -16
  339. package/dist/server/git-clone.js +253 -199
  340. package/dist/server/grep-params.d.ts +18 -0
  341. package/dist/server/grep-params.js +83 -0
  342. package/dist/server/grep-scan.d.ts +23 -0
  343. package/dist/server/grep-scan.js +107 -0
  344. package/dist/server/grep-worker.d.ts +1 -0
  345. package/dist/server/grep-worker.js +12 -0
  346. package/dist/server/mcp-http.d.ts +8 -0
  347. package/dist/server/mcp-http.js +16 -1
  348. package/dist/server/update-controller.d.ts +29 -0
  349. package/dist/server/update-controller.js +82 -0
  350. package/dist/storage/file-lock.d.ts +15 -0
  351. package/dist/storage/file-lock.js +155 -0
  352. package/dist/storage/fs-atomic.d.ts +24 -0
  353. package/dist/storage/fs-atomic.js +86 -2
  354. package/dist/storage/global-dir.d.ts +2 -0
  355. package/dist/storage/global-dir.js +6 -0
  356. package/dist/storage/parse-cache.d.ts +45 -7
  357. package/dist/storage/parse-cache.js +189 -42
  358. package/dist/storage/parsedfile-store.d.ts +41 -60
  359. package/dist/storage/parsedfile-store.js +247 -171
  360. package/dist/storage/repo-manager.d.ts +10 -4
  361. package/dist/storage/repo-manager.js +35 -25
  362. package/dist/storage/repo-meta.d.ts +34 -0
  363. package/dist/storage/v8-sidecar.d.ts +48 -0
  364. package/dist/storage/v8-sidecar.js +347 -0
  365. package/dist/types/pipeline.d.ts +14 -0
  366. package/dist/utils/process-identity.d.ts +2 -0
  367. package/dist/utils/process-identity.js +35 -0
  368. package/package.json +10 -4
  369. package/scripts/assert-web-assets.mjs +35 -0
  370. package/scripts/build-web.js +72 -0
  371. package/scripts/build.js +31 -28
  372. package/scripts/cross-platform-tests.ts +5 -0
  373. package/skills/gitnexus-cli.md +5 -1
  374. package/web/assets/ProcessFlowModal-DAeX2BHW.js +24 -0
  375. package/web/assets/{abnfDiagram-QNN2D4NM-Cq2djWWe.js → abnfDiagram-PAR5IXJI-J3OdeHLl.js} +1 -1
  376. package/web/assets/{agent-BWAXPjC5.js → agent-BMRqXorA.js} +6 -4
  377. package/web/assets/architecture-7GRP2DOG-M7G3SWCO-G3sWyX6M.js +1 -0
  378. package/web/assets/architectureDiagram-2PNWDENW-ysKcRlGT.js +36 -0
  379. package/web/assets/blockDiagram-PUWGUBOA-oLXTa1lY.js +129 -0
  380. package/web/assets/c4Diagram-3NLNHZP5-D3iu4eC0.js +38 -0
  381. package/web/assets/{chunk-2SREHG4O-Dip42xOq.js → chunk-2NMSUPJH-B24w-wng.js} +1 -1
  382. package/web/assets/{chunk-VJFVEZTB-CX66F2ep.js → chunk-32MCUW2P-ByUyt0Sa.js} +3 -3
  383. package/web/assets/{chunk-4RFN2BYJ-DHHWuC-n.js → chunk-3NPNU5GP-DocZ07Sg.js} +1 -1
  384. package/web/assets/{chunk-XNMVGMAZ-Cg_LnPtj.js → chunk-3SMGFRGC-CAu4JRLv.js} +1 -1
  385. package/web/assets/{chunk-W44A43WB-BN8EQc_u.js → chunk-4CAJECRU-DRYMwG9z.js} +1 -1
  386. package/web/assets/chunk-4OADUZ5Q-CxrSvOwZ.js +10 -0
  387. package/web/assets/{chunk-DMV4VAQV-Bn6WXDSX.js → chunk-5IQCK5RY-CCMhl4so.js} +2 -2
  388. package/web/assets/{chunk-RRFMTAIC-CMN0wcYA.js → chunk-5MVFYWFU-Db9KvrYO.js} +1 -1
  389. package/web/assets/chunk-CLS4B6BI-Uvx5QNsu.js +168 -0
  390. package/web/assets/chunk-DZP67EKU-B3Mt380j.js +70 -0
  391. package/web/assets/{chunk-6L755F7B-CMhLBxi2.js → chunk-EICP7K5X-CqwE9hui.js} +1 -1
  392. package/web/assets/{chunk-BI6VK774-D0oaWk8V.js → chunk-GDY2UAB4-CtQQR0a8.js} +1 -1
  393. package/web/assets/chunk-H7VHZCWX-CE1nAmt6.js +1 -0
  394. package/web/assets/chunk-IJBDOHL6-BUKtY6oF.js +1 -0
  395. package/web/assets/{chunk-OIYT25JQ-CQ6KOZGX.js → chunk-JFFVMJIA-0fJenqia.js} +1 -1
  396. package/web/assets/{chunk-HNC4WDU7-DoU3KnA7.js → chunk-JZGDI5JK-BeTMSSkJ.js} +1 -1
  397. package/web/assets/{chunk-EQFTRU2I-1ix7TzW9.js → chunk-KD6SPIPN-Qp1Pflah.js} +1 -1
  398. package/web/assets/chunk-NF366YI6-CpSpya3n.js +231 -0
  399. package/web/assets/chunk-NUDPKBEF-CU1-BQIW.js +34 -0
  400. package/web/assets/{chunk-SATU7PGQ-B1zhgOvc.js → chunk-OJINMHTS-zOCQe4eI.js} +1 -1
  401. package/web/assets/{chunk-E5QJAATJ-dh-5CtIV.js → chunk-Q6NGPIAH-yyBomb63.js} +1 -1
  402. package/web/assets/chunk-QJSWEUOL-D_rf9gPD.js +128 -0
  403. package/web/assets/chunk-R7JDMUKV-CRoqKpD6.js +1 -0
  404. package/web/assets/{chunk-VE5CLXGZ-C1vB7wpY.js → chunk-RCXAM2GK-CUqrWQDV.js} +1 -1
  405. package/web/assets/{chunk-66DQ2XMT-CM0xjFAN.js → chunk-RT4MAWR3-zXy9-Lvt.js} +1 -1
  406. package/web/assets/{chunk-TZUXEDM2-B7p9AcTw.js → chunk-SFMINX4W-J2UrvMpD.js} +1 -1
  407. package/web/assets/{chunk-LORZLZWG-B57uGw2N.js → chunk-SLDQ463B-CHY6e_bQ.js} +2 -2
  408. package/web/assets/{chunk-FWYVLQTC-BgDItRzJ.js → chunk-TCEHX7R3-CXPDEVbm.js} +1 -1
  409. package/web/assets/{chunk-224SPVON-B8gxtXpy.js → chunk-VQC2EELJ-Dc8fIy4R.js} +1 -1
  410. package/web/assets/chunk-VRZKL3EH-DB3KixTQ.js +1 -0
  411. package/web/assets/{chunk-A7VWPJGB-B4O3oQdQ.js → chunk-VTHDQ7BV-b7FsPQZO.js} +1 -1
  412. package/web/assets/chunk-VY2OSTJR-Dkgc3Brg.js +2 -0
  413. package/web/assets/chunk-WUBWJARI-7FwILJ3k.js +2 -0
  414. package/web/assets/{chunk-D6VWDJW2-BxcBdZ9c.js → chunk-YBMQ5JGJ-BSnyWZmd.js} +1 -1
  415. package/web/assets/chunk-YDMLL4PJ-IHhMngTz.js +2 -0
  416. package/web/assets/chunk-Z6V44ARX-BjCEsF0Q.js +62 -0
  417. package/web/assets/chunk-ZJJ7VQJP-Dz089b-K.js +321 -0
  418. package/web/assets/chunk-ZNLK7HRR-B0iFpmW4.js +1 -0
  419. package/web/assets/chunk-ZQN7UHIL-Cy7KAjQt.js +1 -0
  420. package/web/assets/{chunk-L5GCOVLC-CE2w8Nm_.js → chunk-ZS6VBONO-DBjJO0w9.js} +2 -2
  421. package/web/assets/{chunk-NV3KIAZN-5yvcpMiu.js → chunk-ZUNWM646-DNRJyNPe.js} +1 -1
  422. package/web/assets/classDiagram-FGAMPOII-C5Ibyode.js +1 -0
  423. package/web/assets/classDiagram-v2-AI5RJEK4-C5Ibyode.js +1 -0
  424. package/web/assets/{cose-bilkent-RZA4XIPR-BHpK4Crp.js → cose-bilkent-Q72C2OJ3-DMeFXnK8.js} +1 -1
  425. package/web/assets/cynefin-OW5HDTMX-6OCTWOGG-vlqakYcO.js +1 -0
  426. package/web/assets/{cynefinDiagram-TR7LZUCG-C3Iar9lZ.js → cynefinDiagram-2OOWUY4M-BY6DZkTt.js} +1 -1
  427. package/web/assets/dagre-MPVFI544-DWO70fy_.js +4 -0
  428. package/web/assets/diagram-AE4NWXFW-f7JRVRUL.js +24 -0
  429. package/web/assets/{diagram-L7DVE3PN-GIRyVUtt.js → diagram-BUJID5UG-BXZv1rCS.js} +2 -2
  430. package/web/assets/{diagram-JCD7PQ7C-DC5-9FRr.js → diagram-C44CZ5NI-CRNfvjCq.js} +1 -1
  431. package/web/assets/diagram-EYOLEXXS-DZOjKQVE.js +30 -0
  432. package/web/assets/diagram-P7B6PWOJ-D3SQmw3_.js +3 -0
  433. package/web/assets/{ebnfDiagram-DRHRQJWO-D8_CI4ZF.js → ebnfDiagram-FTPZ3PV6-jKWpIDN2.js} +1 -1
  434. package/web/assets/erDiagram-H3ERR26V-BqJntEuJ.js +99 -0
  435. package/web/assets/eventmodeling-NTZA5JFV-WKSWQHFS-Bq4FqtBL.js +1 -0
  436. package/web/assets/flowDiagram-YHGXBVSY-Dnlk2bEu.js +1 -0
  437. package/web/assets/ganttDiagram-RWFTB7ED-Bl6Y1K5T.js +292 -0
  438. package/web/assets/gitGraph-4MIJSDKK-JH2HKYUO-B3yc4GkH.js +1 -0
  439. package/web/assets/gitGraphDiagram-MAR6GKOP-CkNvsrdw.js +106 -0
  440. package/web/assets/{index-CJDpyH-n.css → index-C2_aej58.css} +1 -1
  441. package/web/assets/{index-DDRie6_8.js → index-Dj3vQOHK.js} +51 -51
  442. package/web/assets/info-A6RAGUB7-3TX3OFWW-BM3N5_Dv.js +1 -0
  443. package/web/assets/infoDiagram-LV547AFH-COkFutAf.js +2 -0
  444. package/web/assets/{ishikawaDiagram-4JDSWWDM-DmqWtjkl.js → ishikawaDiagram-PUJQGNDS-Bb8FylSR.js} +2 -2
  445. package/web/assets/{journeyDiagram-QUS3FAKT-XAzVzrQW.js → journeyDiagram-EW5QJT4W-BzoHUBhM.js} +4 -4
  446. package/web/assets/{kanban-definition-D4L7SQKK-YurRnmYn.js → kanban-definition-JSQERAZB-Uy2Pi0yQ.js} +4 -4
  447. package/web/assets/katex-CZ4GXH2S-C0Fcou7U.js +257 -0
  448. package/web/assets/{mindmap-definition-66QP5TD6-ChrWWnil.js → mindmap-definition-VBJCLQLM-B-CnOnt_.js} +3 -3
  449. package/web/assets/packet-AYTQ26CC-MKII6P3Y-Bd52ZE3V.js +1 -0
  450. package/web/assets/{pegDiagram-YKIO5C6U-QGeQLEq2.js → pegDiagram-7WILOQTJ-D9sneKXg.js} +1 -1
  451. package/web/assets/pie-WAS4IAKB-6B4OPMJ5-DW5Xy69Z.js +1 -0
  452. package/web/assets/pieDiagram-EFA7CBSB-Dx2NQofL.js +39 -0
  453. package/web/assets/{quadrantDiagram-VO5QLCXA-B7098wSc.js → quadrantDiagram-WARVYA7P-DPKL9_0A.js} +3 -3
  454. package/web/assets/radar-RG4KPBEZ-UPTR3XUL-CkzibT2h.js +1 -0
  455. package/web/assets/railroad-74A4TZTK-MEFUPFMS-D9zF-mDN.js +1 -0
  456. package/web/assets/railroad-abnf-HS5TGJTU-R5YG2S5D-ruacjUbX.js +1 -0
  457. package/web/assets/railroad-ebnf-LZEXJU2U-G5QEX2L6-S1zc5OiO.js +1 -0
  458. package/web/assets/railroad-peg-WCYAUIDC-H5I2ZRY2-Cbcr0XzY.js +1 -0
  459. package/web/assets/{railroadDiagram-M77LTMSB-AXu4vxk_.js → railroadDiagram-5NIQVNLQ-DsweTo14.js} +1 -1
  460. package/web/assets/{requirementDiagram-RASO5HTO-B-l8InpK.js → requirementDiagram-SXREGOKZ-CXwdNSVR.js} +4 -4
  461. package/web/assets/{sankeyDiagram-F32LOJ6L-Kd7zjJgX.js → sankeyDiagram-VV74M2VP-CA6jtP9P.js} +3 -3
  462. package/web/assets/{sequenceDiagram-URATNSBD-BlcdY9DS.js → sequenceDiagram-52ZFIFDL-k_5rEbzm.js} +15 -15
  463. package/web/assets/src-Df5C1Nz4.js +1 -0
  464. package/web/assets/stateDiagram-3QAKPIVI-BejHTf31.js +1 -0
  465. package/web/assets/stateDiagram-v2-HN2P4RDZ-BrfoRuOc.js +1 -0
  466. package/web/assets/swimlanes-QLVP42QD-CQSVO5D6.js +1 -0
  467. package/web/assets/swimlanesDiagram-JKPDBFCN-DrWB9kcD.js +8 -0
  468. package/web/assets/{timeline-definition-WJXWRH4I-DUfp1R3E.js → timeline-definition-GS45XAXW-C0Ks8rPP.js} +4 -4
  469. package/web/assets/treeView-Q6P3EWNA-ZRJGANNQ-DmKOCo6L.js +1 -0
  470. package/web/assets/treemap-WGGIJYW6-K64VN5AS-DO3xY92U.js +1 -0
  471. package/web/assets/{vennDiagram-K3VCMQLI-Bj-FN-XS.js → vennDiagram-QIQU2ZRJ-rgPiS837.js} +3 -3
  472. package/web/assets/wardley-WFR3VGLG-D67OWCCH-DiDPBNft.js +1 -0
  473. package/web/assets/wardleyDiagram-U6YNM66N-DRVr7SPi.js +78 -0
  474. package/web/assets/{xychartDiagram-5VRUVLCW-CK_rbSWE.js → xychartDiagram-XJAZEDNM-COtlZzYb.js} +3 -3
  475. package/web/index.html +19 -17
  476. package/web/assets/ProcessFlowModal-Badh2Pbg.js +0 -24
  477. package/web/assets/architecture-TIHT7OUA-V6N4J3SY-fTePvLoe.js +0 -1
  478. package/web/assets/architectureDiagram-OT7VKSJ7-mKEXxkeF.js +0 -36
  479. package/web/assets/blockDiagram-54WLDVWM-Dg_hrRWE.js +0 -132
  480. package/web/assets/c4Diagram-X2LASM7V-CnhoT4aV.js +0 -10
  481. package/web/assets/chunk-2AEHWXPW-CV4r6-1I.js +0 -127
  482. package/web/assets/chunk-5VCL7Z4A-CQ-2dYWj.js +0 -1
  483. package/web/assets/chunk-6BELYETK-ChsYtfHW.js +0 -1
  484. package/web/assets/chunk-7CWYLC5S-DRgGkkpF.js +0 -2
  485. package/web/assets/chunk-7FYTHRHK-CsmYIswB.js +0 -32
  486. package/web/assets/chunk-KRXBNO2N-C-UNuKKf.js +0 -1
  487. package/web/assets/chunk-LAHSG3N2-Bz4hcn0N.js +0 -321
  488. package/web/assets/chunk-LIEV3EAG-pyKGssV3.js +0 -1
  489. package/web/assets/chunk-NLANEA3F-XpH_NPA5.js +0 -62
  490. package/web/assets/chunk-PE7DX7ZZ-CCvRVj43.js +0 -10
  491. package/web/assets/chunk-Q67WD55A-BWCH2SVz.js +0 -156
  492. package/web/assets/chunk-SZD42YQK-ijyKKVr1.js +0 -70
  493. package/web/assets/chunk-TFPGFQPA-Dj6a-0pC.js +0 -231
  494. package/web/assets/chunk-TGVD4F4B-BMc31Gj2.js +0 -2
  495. package/web/assets/chunk-UAT7B5JY-DW-A_fpI.js +0 -1
  496. package/web/assets/chunk-VY5UBI4V-yxPQAWxw.js +0 -1
  497. package/web/assets/chunk-WJBAP47W-BckNkuEN.js +0 -1
  498. package/web/assets/classDiagram-VPOSVPXH-BjOiXA_C.js +0 -1
  499. package/web/assets/classDiagram-v2-5VFNUXQA-BjOiXA_C.js +0 -1
  500. package/web/assets/cynefin-VYW2F7L2-2VAHYKR6-DAsF6GZv.js +0 -1
  501. package/web/assets/dagre-K64A6Z3X-CUwi79mA.js +0 -4
  502. package/web/assets/diagram-2TLGJV2N-C2F5Nfym.js +0 -30
  503. package/web/assets/diagram-KFBZAVDW-Dfmf_4ti.js +0 -3
  504. package/web/assets/diagram-YP3FGXHI-CyNFYtkd.js +0 -24
  505. package/web/assets/erDiagram-SZLJLQMB-CohQjznC.js +0 -85
  506. package/web/assets/eventmodeling-45OFAUF4-AOUJWSLX-BvTc6iQi.js +0 -1
  507. package/web/assets/flowDiagram-BWE6NHOH-B6RnEvD9.js +0 -1
  508. package/web/assets/ganttDiagram-2QTCJSNI-Cks_mEcF.js +0 -292
  509. package/web/assets/gitGraph-TEB2WS4Q-U5VWYIBC-Dy7CCSZ5.js +0 -1
  510. package/web/assets/gitGraphDiagram-VVZAVV5G-CG41cIVc.js +0 -106
  511. package/web/assets/info-DKCQHKI2-DX6KST54-BLJg1ikX.js +0 -1
  512. package/web/assets/infoDiagram-FY4PZKRE-BJ3vPVwW.js +0 -2
  513. package/web/assets/katex-C5OPUE3Q-Bnbavtmu.js +0 -257
  514. package/web/assets/packet-7NZHBO7P-5WG45QPS-KUxcuvaA.js +0 -1
  515. package/web/assets/pie-RZYD4A2V-2OCZ2VTD-DPs08kJY.js +0 -1
  516. package/web/assets/pieDiagram-YPY3TKG5-BuT3MYzm.js +0 -39
  517. package/web/assets/radar-I7S5WNFK-EPLSA35B-w5F0K3a7.js +0 -1
  518. package/web/assets/railroad-3IZDKUUU-PWFQV6Y6-Bu668yqY.js +0 -1
  519. package/web/assets/railroad-abnf-AHOZXSZD-JYYJX3BS-B6ghpETS.js +0 -1
  520. package/web/assets/railroad-ebnf-EBAXGLYW-RBZ4ONAP-D-XmHh3M.js +0 -1
  521. package/web/assets/railroad-peg-LSFZ7HO6-KHNPXGPG-mhkrhzEn.js +0 -1
  522. package/web/assets/src-C5sx_gMp.js +0 -1
  523. package/web/assets/stateDiagram-DODFXNG5-DTdHc-fF.js +0 -1
  524. package/web/assets/stateDiagram-v2-JNMM5JRA-DioNLDDo.js +0 -1
  525. package/web/assets/swimlanes-OSWOYLSK-CA0ofdwY.js +0 -2
  526. package/web/assets/swimlanesDiagram-RHGX2RDE-PkwUdckC.js +0 -8
  527. package/web/assets/treeView-QDETBFTQ-EHYTSQNO-C5lpBgPm.js +0 -1
  528. package/web/assets/treemap-6X3UGDF4-CCWCF7EK-BB0ZyO-B.js +0 -1
  529. package/web/assets/wardley-OPB4EBWU-6OWERDVT-CEA3Nq0j.js +0 -1
  530. package/web/assets/wardleyDiagram-XK6FUAWV-CVV0dHFr.js +0 -78
  531. /package/web/assets/{sizeCapture-5GORAGP4-BEqQzsxs.js → sizeCapture-FGA5AGYP-BEqQzsxs.js} +0 -0
@@ -6,7 +6,7 @@
6
6
  * core/group/ import (the established direction is core/group/ -> storage/,
7
7
  * e.g. core/group/service.ts already imports loadMeta from here).
8
8
  */
9
- import fsp from 'fs/promises';
9
+ import { closeSync, openSync, promises as fsp, renameSync, unlinkSync, writeSync } from 'node:fs';
10
10
  import { randomBytes } from 'crypto';
11
11
  const RETRY_CODES = new Set(['EBUSY', 'EPERM', 'EACCES']);
12
12
  /**
@@ -52,7 +52,7 @@ export async function retryRename(src, dst, attempts = 3) {
52
52
  * retry backoff (and, under a lock, make everyone else wait for it).
53
53
  */
54
54
  export async function writeFileAtomic(targetPath, data, attempts) {
55
- const tmpPath = `${targetPath}.tmp.${randomBytes(8).toString('hex')}`;
55
+ const tmpPath = tmpBeside(targetPath);
56
56
  const handle = await fsp.open(tmpPath, 'wx', 0o600);
57
57
  try {
58
58
  try {
@@ -68,3 +68,87 @@ export async function writeFileAtomic(targetPath, data, attempts) {
68
68
  throw err;
69
69
  }
70
70
  }
71
+ function tmpBeside(targetPath) {
72
+ return `${targetPath}.tmp.${randomBytes(8).toString('hex')}`;
73
+ }
74
+ /**
75
+ * Publish `src` at `dst` without ever writing through the inode currently
76
+ * named by `dst` (#3090). Fast path is `fs.link`. Any failure (EEXIST, EXDEV,
77
+ * EPERM, unsupported hardlinks) copies into a unique sibling and
78
+ * {@link retryRename}s over `dst`. POSIX rename of a file over an existing
79
+ * file unlinks the old name; it does not open or truncate that inode.
80
+ *
81
+ * Invariant: may replace the destination directory entry; must never modify
82
+ * the inode that entry currently references. There is no dest unlink and no
83
+ * in-place `copyFile(src, dst)`.
84
+ */
85
+ export async function linkOrCopyFile(src, dst) {
86
+ try {
87
+ await fsp.link(src, dst);
88
+ return;
89
+ }
90
+ catch {
91
+ /* any link failure → publish a new inode via tmp + rename */
92
+ }
93
+ const tmp = tmpBeside(dst);
94
+ try {
95
+ await fsp.copyFile(src, tmp);
96
+ await retryRename(tmp, dst);
97
+ }
98
+ catch (err) {
99
+ await fsp.unlink(tmp).catch(() => { });
100
+ throw err;
101
+ }
102
+ }
103
+ /**
104
+ * Binary sibling of {@link writeFileAtomic}. Same random tmp + `'wx'` + `0o600`
105
+ * + rename contract; used for optional V8 cache sidecars that cannot go through
106
+ * a UTF-8 `writeFile`.
107
+ */
108
+ export async function writeFileAtomicBytes(targetPath, data, attempts) {
109
+ const tmpPath = tmpBeside(targetPath);
110
+ const handle = await fsp.open(tmpPath, 'wx', 0o600);
111
+ try {
112
+ try {
113
+ await handle.writeFile(data);
114
+ }
115
+ finally {
116
+ await handle.close();
117
+ }
118
+ await retryRename(tmpPath, targetPath, attempts);
119
+ }
120
+ catch (err) {
121
+ await fsp.unlink(tmpPath).catch(() => { });
122
+ throw err;
123
+ }
124
+ }
125
+ /**
126
+ * Sync binary publish for parse workers. Same exclusive-tmp + mode contract as
127
+ * {@link writeFileAtomicBytes}; rename is not retried (the worker is not the
128
+ * Windows multi-reader case `retryRename` exists for).
129
+ */
130
+ export function writeFileAtomicBytesSync(targetPath, data) {
131
+ const tmpPath = tmpBeside(targetPath);
132
+ const fd = openSync(tmpPath, 'wx', 0o600);
133
+ try {
134
+ try {
135
+ let offset = 0;
136
+ while (offset < data.byteLength) {
137
+ offset += writeSync(fd, data, offset);
138
+ }
139
+ }
140
+ finally {
141
+ closeSync(fd);
142
+ }
143
+ renameSync(tmpPath, targetPath);
144
+ }
145
+ catch (err) {
146
+ try {
147
+ unlinkSync(tmpPath);
148
+ }
149
+ catch {
150
+ /* leftover tmp is unlinked on the next exclusive create */
151
+ }
152
+ throw err;
153
+ }
154
+ }
@@ -0,0 +1,2 @@
1
+ /** Get the path to the global GitNexus directory. */
2
+ export declare const getGlobalDir: () => string;
@@ -0,0 +1,6 @@
1
+ import os from 'node:os';
2
+ import path from 'node:path';
3
+ /** Get the path to the global GitNexus directory. */
4
+ export const getGlobalDir = () => {
5
+ return process.env.GITNEXUS_HOME || path.join(os.homedir(), '.gitnexus');
6
+ };
@@ -6,10 +6,12 @@
6
6
  * does is skip the tree-sitter worker dispatch when a chunk's contents
7
7
  * haven't changed since the last run.
8
8
  *
9
- * Granularity: chunk-level. The parse phase chunks files into ~20MB byte
10
- * budgets. The cache key is `sha256(joined(filePath:contentHash for each
11
- * file in the chunk, sorted))`. A change to a single file invalidates only
12
- * that file's chunk — typically 1 of ~50 chunks on a 1000-file repo.
9
+ * Granularity: chunk-level. Files are assigned to a stable
10
+ * `(language, hash(path) mod 128)` bucket, then packed to a 2 MiB (or
11
+ * operator) byte budget *inside* that bucket. Membership does not depend
12
+ * on worker count. The cache key is `sha256(joined(filePath:contentHash
13
+ * for each file in the chunk, sorted))`. A content edit invalidates only
14
+ * that file's pack; add/delete/rename only the affected bucket.
13
15
  *
14
16
  * Why not per-file:
15
17
  * - Workers process sub-batches and emit aggregated `ParseWorkerResult`s.
@@ -17,12 +19,43 @@
17
19
  * - Chunk-level invalidation gives a useful speedup floor (98% on a single
18
20
  * 1-of-50 invalidated chunk) without touching the worker.
19
21
  *
20
- * Survives `--force` because it's content-addressed: the same bytes always
21
- * produce the same key. `--force` only matters for the LadybugDB writeback;
22
- * the cache itself is always safe to reuse.
22
+ * `--force` still reuses content-addressed shards (it only rebuilds graph/FTS).
23
+ * `useParseCache: false` reparses every file, writes a staging generation, and
24
+ * publishes onto this cache only after a successful analysis.
23
25
  */
24
26
  import type { ParseWorkerResult } from '../core/ingestion/workers/parse-worker.js';
25
27
  export declare const PARSE_CACHE_VERSION: string;
28
+ /** Stable parse-cache bucket count (#3088). Changing this requires SCHEMA_BUMP. */
29
+ export declare const PARSE_CACHE_BUCKET_COUNT = 128;
30
+ /** Bucket id for cache membership: `sha256(path) mod N` without IEEE-754 truncation. */
31
+ export declare const parseCacheBucketId: (filePath: string) => number;
32
+ export type ParseCachePackFile = {
33
+ path: string;
34
+ size: number;
35
+ language: string;
36
+ };
37
+ /**
38
+ * Pack files into parse-cache chunks: group by (language, bucket id), sort
39
+ * paths inside the group, then cut at `byteBudget`. Bucket visit order is
40
+ * the lexicographic order of `${language}\\0${bucketId}` keys (deterministic,
41
+ * independent of scan order and worker count).
42
+ */
43
+ export declare const packParseCacheChunks: (files: readonly ParseCachePackFile[], byteBudget: number) => string[][];
44
+ /**
45
+ * Per-run staging root for `useParseCache: false`. Parse-cache shards and the
46
+ * ParsedFile stores write here so a crash cannot mix a new generation into the
47
+ * live `.gitnexus/parse-cache` / `parsedfile-cache` trees. `saveParseCache`
48
+ * publishes onto the live `storagePath` only after a successful analysis.
49
+ */
50
+ export declare const COLD_PARSE_REBUILD_DIRNAME = "parse-rebuild";
51
+ /** Deterministic staging path — tests only. Production uses {@link createColdParseRebuildDir}. */
52
+ export declare const getColdParseRebuildDir: (storagePath: string) => string;
53
+ /**
54
+ * Unique per analyze process so concurrent `--no-parse-cache` runs on
55
+ * different branch slots (shared `.gitnexus`, separate index locks) do not
56
+ * delete each other's staging tree.
57
+ */
58
+ export declare const createColdParseRebuildDir: (storagePath: string) => Promise<string>;
26
59
  /** Runtime view: keyed Map for fast lookup; mutated in place during a run. */
27
60
  export interface ParseCache {
28
61
  version: string;
@@ -39,6 +72,8 @@ export interface ParseCache {
39
72
  * When set, chunk payloads are loaded from / flushed to sharded files on
40
73
  * demand instead of retaining every chunk in `entries` for the whole run
41
74
  * (#1983 — Linux kernel OOM from duplicate in-memory cache + graph).
75
+ * May be a per-run staging directory (`getColdParseRebuildDir`) while the
76
+ * live index root is passed separately to `saveParseCache`.
42
77
  */
43
78
  storagePath?: string;
44
79
  /** Index of chunk hashes known to exist under `storagePath/parse-cache/`. */
@@ -87,6 +122,8 @@ export declare const mapReviver: (_key: string, value: unknown) => unknown;
87
122
  export declare const slimParseWorkerResultsForCache: (chunkResults: readonly ParseWorkerResult[]) => ParseWorkerResult[];
88
123
  /** Load one chunk shard. Does not retain it in `cache.entries`. */
89
124
  export declare const loadParseCacheChunk: (cache: ParseCache, chunkHash: string) => Promise<ParseWorkerResult[] | undefined>;
125
+ /** Drop the mkdir memo after the staging tree is wiped so the next persist recreates it. */
126
+ export declare const forgetCreatedParseCacheDir: (storagePath: string) => void;
90
127
  /**
91
128
  * Persist one chunk shard and avoid retaining it in RAM for the rest of the
92
129
  * run. Falls back to `cache.entries` when `storagePath` is unset (unit tests).
@@ -114,3 +151,4 @@ export declare const saveParseCache: (storagePath: string, cache: ParseCache) =>
114
151
  * don't keep their stale entries forever.
115
152
  */
116
153
  export declare const pruneCache: (cache: ParseCache, usedHashes: ReadonlySet<string>) => number;
154
+ export declare const emptyParseCache: (storagePath?: string) => ParseCache;
@@ -6,10 +6,12 @@
6
6
  * does is skip the tree-sitter worker dispatch when a chunk's contents
7
7
  * haven't changed since the last run.
8
8
  *
9
- * Granularity: chunk-level. The parse phase chunks files into ~20MB byte
10
- * budgets. The cache key is `sha256(joined(filePath:contentHash for each
11
- * file in the chunk, sorted))`. A change to a single file invalidates only
12
- * that file's chunk — typically 1 of ~50 chunks on a 1000-file repo.
9
+ * Granularity: chunk-level. Files are assigned to a stable
10
+ * `(language, hash(path) mod 128)` bucket, then packed to a 2 MiB (or
11
+ * operator) byte budget *inside* that bucket. Membership does not depend
12
+ * on worker count. The cache key is `sha256(joined(filePath:contentHash
13
+ * for each file in the chunk, sorted))`. A content edit invalidates only
14
+ * that file's pack; add/delete/rename only the affected bucket.
13
15
  *
14
16
  * Why not per-file:
15
17
  * - Workers process sub-batches and emit aggregated `ParseWorkerResult`s.
@@ -17,15 +19,17 @@
17
19
  * - Chunk-level invalidation gives a useful speedup floor (98% on a single
18
20
  * 1-of-50 invalidated chunk) without touching the worker.
19
21
  *
20
- * Survives `--force` because it's content-addressed: the same bytes always
21
- * produce the same key. `--force` only matters for the LadybugDB writeback;
22
- * the cache itself is always safe to reuse.
22
+ * `--force` still reuses content-addressed shards (it only rebuilds graph/FTS).
23
+ * `useParseCache: false` reparses every file, writes a staging generation, and
24
+ * publishes onto this cache only after a successful analysis.
23
25
  */
24
26
  import { createHash } from 'crypto';
25
27
  import { createRequire } from 'module';
26
28
  import fs from 'fs/promises';
27
29
  import path from 'path';
28
30
  import { fileURLToPath } from 'url';
31
+ import { compareCodeUnits } from '../lib/utils.js';
32
+ import { copyV8CacheIfPresent, tryLoadV8Cache, writeV8CacheFile } from './v8-sidecar.js';
29
33
  /**
30
34
  * Cache version composed of:
31
35
  * - A schema bump knob (`SCHEMA_BUMP`) for hand-controlled invalidation
@@ -627,6 +631,16 @@ import { fileURLToPath } from 'url';
627
631
  // therefore takes 79, the next free value above origin/main and every open PR
628
632
  // found by the contents-API scan at their exact head SHAs.
629
633
  //
634
+ // 79 -> 80 for #3088: parse-cache membership is `(language, sha256(path) mod
635
+ // 128)` then the byte budget *inside* that bucket. Worker count is no longer a
636
+ // membership input, so a warm v79 cache keyed sequential scan-order packs (and
637
+ // on multi-worker hosts, pool×2 MiB mega-chunks) must miss. Sidecar-era
638
+ // ParsedFile stores (#3086/#3087) share PARSE_CACHE_VERSION, so both stores
639
+ // invalidate in lockstep. origin/main at allocation is 79; open PRs that still
640
+ // touch gitnexus/src/storage/parse-cache.ts claim 78 (#3060), 71 (#2840), and
641
+ // 2 (#1616) — none claim 80. RE-CHECK AGAINST origin/main AND OPEN PRs
642
+ // IMMEDIATELY BEFORE MERGING.
643
+ //
630
644
  // WHY THIS IS STILL A HAND-PICKED NUMBER, when `SCHEMA_FINGERPRINT` next door
631
645
  // is a derived sha256 that cannot collide. The derivation exists and already
632
646
  // runs: `resolveAnalyzerRunnerIdentity` computes `build.digest` over the
@@ -645,7 +659,78 @@ import { fileURLToPath } from 'url';
645
659
  // `route-extractors/` and `workers/` module content — would close the missing-
646
660
  // bump axis without invalidating on unrelated churn, and is the real follow-up.
647
661
  // RE-CHECK AGAINST origin/main AND OPEN PRs IMMEDIATELY BEFORE MERGING.
648
- const SCHEMA_BUMP = 79;
662
+ // 80 -> 81: ParsedFile and parse-cache shards are one immutable `.v8` envelope
663
+ // each (no JSON/path/generation siblings). A v80 index still names `.json`
664
+ // keys and would skip workers while scope-resolution found nothing — the
665
+ // #1983 main-thread reparse. origin/main at allocation is 80.
666
+ // 81 -> 82: Java and Kotlin ParsedFile capture side channels now carry
667
+ // programmatic Spring lookup facts. A warm v81 cache has no such facts, so it
668
+ // would skip workers and silently omit the new INJECTS edges. origin/main at
669
+ // allocation is 81.
670
+ // 82 -> 83: Java Lombok @Data/@Getter/@Setter accessor synthesis emits
671
+ // synthetic Method nodes, HAS_METHOD edges, and matching scope captures into
672
+ // ParseWorkerResult / ParsedFile. A warm v82 cache replays pre-Lombok worker
673
+ // output and silently omits those callables. origin/main at allocation is 82.
674
+ // 83 -> 84: Kotlin val/var properties synthesize JVM get/set Method nodes
675
+ // (same provider hook as Java Lombok). A warm v83 cache omits those callables.
676
+ // origin/main at allocation is 83 (Java Lombok on this branch).
677
+ // 84 -> 85: JVM synthetic accessor captures now use the declaration
678
+ // qualified_name key consumed by scope extraction, and Kotlin accessor planning
679
+ // follows JvmAbi naming plus conservative @JvmName suppression. A warm v84
680
+ // cache can replay stale names and declaration metadata.
681
+ // 85 -> 86: Kotlin interface property accessors now record isAbstract on the
682
+ // synthetic Method. A warm v85 cache replays them as concrete.
683
+ // 86 -> 87: ModuleConstants gained wildcardImports (static-import-asterisk
684
+ // materialization) — a warm v86 cache has no wildcard bindings, so folding
685
+ // would skip them and drop wildcard-imported route constants. origin/main at
686
+ // allocation is 86 (#2885).
687
+ // 87 -> 88: Java ModuleConstants now preserves unfoldable declaration names
688
+ // across worker/cache replay so wildcard expansion cannot resurrect an imported
689
+ // member hidden by a local field. A warm v87 cache lacks that shadow metadata.
690
+ // 88 -> 89: the same side channels now carry Spring messaging facts — the
691
+ // arguments of non-HTTP handler annotations (`@KafkaListener(topics = ...)`)
692
+ // and a new `springMessageProducerFacts` list for template publishes
693
+ // (`KafkaTemplate.send`, `RabbitTemplate`/`JmsTemplate.convertAndSend`,
694
+ // `StreamBridge.send`). Both are parse-time worker output replayed verbatim
695
+ // from `ParsedFile.captureSideChannel`, so a warm v88 cache would skip workers
696
+ // and hand the annotation facts back with no `args` and the producer list
697
+ // empty. Measured on the fixture app: a warm all-cache-hit run
698
+ // (`usedWorkerPool=false`, `reparsedFileCount=0`) reproduces 6 Java and 7
699
+ // Kotlin producer facts purely from the store, which is exactly the state a
700
+ // pre-change cache would have served as zero. origin/main at allocation is 88.
701
+ // RE-CHECK AGAINST origin/main AND OPEN PRs IMMEDIATELY BEFORE MERGING — this
702
+ // entry was allocated 83 first, and five bumps landed upstream before it merged.
703
+ // 89 -> 90 (#2865): route decorator captures now carry `handlerName` in
704
+ // `decoratorRoutes`, which is what lets `resolveRouteHandlerSymbols` stamp
705
+ // `handlerSymbolId` and the routes phase emit the definition-level
706
+ // HANDLES_ROUTE edge. The field is minted in the parse WORKER and persisted
707
+ // verbatim, so a warm v89 cache replays handler-less decorator routes: every
708
+ // route loses its definition-level association on incremental analyze while
709
+ // every cold-run test passes — the inert-feature trap the entries above record.
710
+ // 89 is now taken by merged #3128. 90 is the next free value above origin/main
711
+ // (89) and above every in-flight claim found by scanning open PRs'
712
+ // parse-cache.ts at their exact head SHAs (highest other open claim was still
713
+ // ≤88). RE-CHECK AGAINST origin/main AND OPEN PRs IMMEDIATELY BEFORE MERGING.
714
+ // 90 -> 91 (#3130): Kotlin providers now emit Spring decoratorRoutes plus
715
+ // ModuleConstants shadow metadata. A warm v90 cache would replay unchanged
716
+ // Kotlin files with neither route candidates nor the constant declarations
717
+ // needed to fold them, leaving the new ingestion path silently inert.
718
+ // 91 -> 92 (#1432): the shared callable-flow reader (`callable-flow-captures.ts`)
719
+ // no longer names a callee by simple name for a MEMBER call and gates a
720
+ // field-stored-callable invoke on a visible member store — parse-time capture
721
+ // facts for Kotlin / C++ / C# / TypeScript member calls change (the
722
+ // scope-capture bench re-baselined all four), and Zig files are captured for
723
+ // the first time, with rules that changed within the PR (qualified struct
724
+ // literals, enum-variant field bindings, receiver tagging). A warm v91 cache
725
+ // replays the old facts verbatim, `--force` included: a reviewer re-testing a
726
+ // later head of this PR on an index built from an earlier one measured a
727
+ // byte-identical graph until `parse-cache/` and `parsedfile-cache/` were
728
+ // deleted by hand. 92 is the next free value above origin/main (91) at merge
729
+ // time. RE-CHECK AGAINST origin/main AND OPEN PRs IMMEDIATELY BEFORE MERGING.
730
+ // v93: Zig call captures inside a comptime-false branch carry
731
+ // `@reference.static-gated` (feat/zig-static-gated-edges); the site gains
732
+ // `staticGated` and the CALLS edge a BOOLEAN column.
733
+ const SCHEMA_BUMP = 93;
649
734
  const GITNEXUS_PKG_VERSION = (() => {
650
735
  try {
651
736
  // package.json sits at gitnexus/package.json — two levels up from
@@ -673,16 +758,74 @@ const GITNEXUS_PKG_VERSION = (() => {
673
758
  return '0.0.0-unknown';
674
759
  })();
675
760
  export const PARSE_CACHE_VERSION = `${SCHEMA_BUMP}+${GITNEXUS_PKG_VERSION}`;
761
+ /** SHA-256 hex of a string or buffer (paths for bucket ids, contents for cache keys). */
762
+ const sha256Hex = (input) => createHash('sha256')
763
+ .update(typeof input === 'string' ? Buffer.from(input) : input)
764
+ .digest('hex');
765
+ /** Stable parse-cache bucket count (#3088). Changing this requires SCHEMA_BUMP. */
766
+ export const PARSE_CACHE_BUCKET_COUNT = 128;
767
+ /** Bucket id for cache membership: `sha256(path) mod N` without IEEE-754 truncation. */
768
+ export const parseCacheBucketId = (filePath) => Number(BigInt(`0x${sha256Hex(filePath)}`) % BigInt(PARSE_CACHE_BUCKET_COUNT));
769
+ /**
770
+ * Pack files into parse-cache chunks: group by (language, bucket id), sort
771
+ * paths inside the group, then cut at `byteBudget`. Bucket visit order is
772
+ * the lexicographic order of `${language}\\0${bucketId}` keys (deterministic,
773
+ * independent of scan order and worker count).
774
+ */
775
+ export const packParseCacheChunks = (files, byteBudget) => {
776
+ const buckets = new Map();
777
+ for (const file of files) {
778
+ const key = `${file.language}\0${parseCacheBucketId(file.path)}`;
779
+ const list = buckets.get(key);
780
+ if (list)
781
+ list.push(file);
782
+ else
783
+ buckets.set(key, [file]);
784
+ }
785
+ const chunks = [];
786
+ for (const key of [...buckets.keys()].sort()) {
787
+ const group = buckets.get(key);
788
+ group.sort((a, b) => compareCodeUnits(a.path, b.path));
789
+ let current = [];
790
+ let bytes = 0;
791
+ for (const file of group) {
792
+ if (current.length > 0 && bytes + file.size > byteBudget) {
793
+ chunks.push(current);
794
+ current = [];
795
+ bytes = 0;
796
+ }
797
+ current.push(file.path);
798
+ bytes += file.size;
799
+ }
800
+ if (current.length > 0)
801
+ chunks.push(current);
802
+ }
803
+ return chunks;
804
+ };
676
805
  const LEGACY_CACHE_FILENAME = 'parse-cache.json';
677
806
  const CACHE_DIRNAME = 'parse-cache';
807
+ /**
808
+ * Per-run staging root for `useParseCache: false`. Parse-cache shards and the
809
+ * ParsedFile stores write here so a crash cannot mix a new generation into the
810
+ * live `.gitnexus/parse-cache` / `parsedfile-cache` trees. `saveParseCache`
811
+ * publishes onto the live `storagePath` only after a successful analysis.
812
+ */
813
+ export const COLD_PARSE_REBUILD_DIRNAME = 'parse-rebuild';
814
+ /** Deterministic staging path — tests only. Production uses {@link createColdParseRebuildDir}. */
815
+ export const getColdParseRebuildDir = (storagePath) => path.join(storagePath, COLD_PARSE_REBUILD_DIRNAME);
816
+ /**
817
+ * Unique per analyze process so concurrent `--no-parse-cache` runs on
818
+ * different branch slots (shared `.gitnexus`, separate index locks) do not
819
+ * delete each other's staging tree.
820
+ */
821
+ export const createColdParseRebuildDir = async (storagePath) => {
822
+ await fs.mkdir(storagePath, { recursive: true });
823
+ return fs.mkdtemp(path.join(storagePath, `${COLD_PARSE_REBUILD_DIRNAME}.`));
824
+ };
678
825
  const CACHE_INDEX_FILENAME = 'index.json';
679
826
  /** Keys on disk always come from `computeChunkHash` — 64-char lowercase hex. */
680
827
  const CHUNK_CACHE_KEY_HEX_RE = /^[a-f0-9]{64}$/;
681
828
  const isValidChunkCacheKey = (chunkHash) => CHUNK_CACHE_KEY_HEX_RE.test(chunkHash);
682
- /** SHA-256 hex of a single string or buffer. */
683
- const sha256Hex = (input) => createHash('sha256')
684
- .update(typeof input === 'string' ? Buffer.from(input) : input)
685
- .digest('hex');
686
829
  /** Stable hash of a single file's contents — used by callers to compose a chunk hash. */
687
830
  export const fileContentHash = (content) => sha256Hex(content);
688
831
  export const computeChunkHash = (entries, pdg = false) => {
@@ -759,7 +902,7 @@ export const mapReviver = (_key, value) => {
759
902
  const getLegacyCachePath = (storagePath) => path.join(storagePath, LEGACY_CACHE_FILENAME);
760
903
  const getCacheDirPath = (storagePath) => path.join(storagePath, CACHE_DIRNAME);
761
904
  const getCacheIndexPath = (storagePath) => path.join(getCacheDirPath(storagePath), CACHE_INDEX_FILENAME);
762
- const getCacheChunkPath = (storagePath, chunkHash) => path.join(getCacheDirPath(storagePath), `${chunkHash}.json`);
905
+ const getCacheChunkPath = (storagePath, chunkHash) => path.join(getCacheDirPath(storagePath), `${chunkHash}.v8`);
763
906
  /**
764
907
  * Drop fields that are not replayed by `mergeChunkResults` / parse-impl after
765
908
  * RING4-1 (#942). Shrinks on-disk shards and peak RSS during cold runs.
@@ -784,9 +927,12 @@ const readParseCacheChunkFromDisk = async (storagePath, chunkHash) => {
784
927
  if (!isValidChunkCacheKey(chunkHash))
785
928
  return undefined;
786
929
  try {
787
- const chunkRaw = await fs.readFile(getCacheChunkPath(storagePath, chunkHash), 'utf-8');
788
- const chunkData = JSON.parse(chunkRaw, mapReviver);
789
- return Array.isArray(chunkData) ? chunkData : undefined;
930
+ const chunkPath = getCacheChunkPath(storagePath, chunkHash);
931
+ const v8Hit = await tryLoadV8Cache(chunkPath);
932
+ if (v8Hit?.kind === 'hit' && Array.isArray(v8Hit.value)) {
933
+ return v8Hit.value;
934
+ }
935
+ return undefined;
790
936
  }
791
937
  catch {
792
938
  return undefined;
@@ -809,6 +955,10 @@ export const loadParseCacheChunk = async (cache, chunkHash) => {
809
955
  * process-scoped, so the Set stays bounded.
810
956
  */
811
957
  const createdCacheDirs = new Set();
958
+ /** Drop the mkdir memo after the staging tree is wiped so the next persist recreates it. */
959
+ export const forgetCreatedParseCacheDir = (storagePath) => {
960
+ createdCacheDirs.delete(getCacheDirPath(storagePath));
961
+ };
812
962
  /**
813
963
  * Persist one chunk shard and avoid retaining it in RAM for the rest of the
814
964
  * run. Falls back to `cache.entries` when `storagePath` is unset (unit tests).
@@ -821,19 +971,16 @@ export const persistParseCacheChunk = async (cache, chunkHash, chunkResults) =>
821
971
  await fs.mkdir(cacheDir, { recursive: true });
822
972
  createdCacheDirs.add(cacheDir);
823
973
  }
824
- const payload = JSON.stringify(slim, mapReplacer);
825
974
  const chunkPath = getCacheChunkPath(cache.storagePath, chunkHash);
826
- try {
827
- await fs.writeFile(chunkPath, payload, 'utf-8');
828
- }
829
- catch (error) {
830
- if (error.code !== 'ENOENT')
831
- throw error;
832
- // Long-lived analyze --watch processes can replace the sharded cache
833
- // directory after this process-local memo recorded it as created.
975
+ let ok = await writeV8CacheFile(chunkPath, slim);
976
+ if (!ok) {
834
977
  await fs.mkdir(cacheDir, { recursive: true });
835
978
  createdCacheDirs.add(cacheDir);
836
- await fs.writeFile(chunkPath, payload, 'utf-8');
979
+ ok = await writeV8CacheFile(chunkPath, slim);
980
+ }
981
+ if (!ok) {
982
+ cache.entries.set(chunkHash, slim);
983
+ return;
837
984
  }
838
985
  cache.onDiskKeys ??= new Set();
839
986
  cache.onDiskKeys.add(chunkHash);
@@ -930,28 +1077,27 @@ export const saveParseCache = async (storagePath, cache) => {
930
1077
  // index from what we persisted, not from the raw usedKeys snapshot.
931
1078
  const writtenKeys = [];
932
1079
  for (const chunkHash of keys) {
933
- const chunkPath = path.join(tmpDir, `${chunkHash}.json`);
1080
+ const chunkPath = path.join(tmpDir, `${chunkHash}.v8`);
934
1081
  const inMemory = cache.entries.get(chunkHash);
935
1082
  if (inMemory !== undefined) {
936
- let payload;
937
- try {
938
- payload = JSON.stringify(inMemory, mapReplacer);
1083
+ if (await writeV8CacheFile(chunkPath, inMemory)) {
1084
+ writtenKeys.push(chunkHash);
939
1085
  }
940
- catch {
941
- continue;
942
- }
943
- await fs.writeFile(chunkPath, payload, 'utf-8');
944
- writtenKeys.push(chunkHash);
945
1086
  continue;
946
1087
  }
947
- const existingPath = getCacheChunkPath(storagePath, chunkHash);
948
- try {
949
- await fs.copyFile(existingPath, chunkPath);
1088
+ // Cold rebuilds persist mid-run under `cache.storagePath` (staging). Prefer
1089
+ // that generation over a same-hash shard still sitting in the live dir so
1090
+ // we never publish a mixed old/new pair. Sibling-branch keys (#2106) that
1091
+ // this run did not rewrite still copy from the live path.
1092
+ const stagedPath = cache.storagePath !== undefined && cache.storagePath !== storagePath
1093
+ ? getCacheChunkPath(cache.storagePath, chunkHash)
1094
+ : undefined;
1095
+ const livePath = getCacheChunkPath(storagePath, chunkHash);
1096
+ const fromStaged = Boolean(stagedPath && cache.onDiskKeys?.has(chunkHash));
1097
+ const sourcePath = fromStaged && stagedPath ? stagedPath : livePath;
1098
+ if (await copyV8CacheIfPresent(sourcePath, chunkPath)) {
950
1099
  writtenKeys.push(chunkHash);
951
1100
  }
952
- catch {
953
- /* shard missing — skip; next run treats as cache miss */
954
- }
955
1101
  }
956
1102
  const index = {
957
1103
  version: cache.version,
@@ -989,10 +1135,11 @@ export const pruneCache = (cache, usedHashes) => {
989
1135
  }
990
1136
  return removed;
991
1137
  };
992
- const emptyCache = (storagePath) => ({
1138
+ export const emptyParseCache = (storagePath) => ({
993
1139
  version: PARSE_CACHE_VERSION,
994
1140
  entries: new Map(),
995
1141
  usedKeys: new Set(),
996
1142
  storagePath,
997
1143
  onDiskKeys: storagePath ? new Set() : undefined,
998
1144
  });
1145
+ const emptyCache = emptyParseCache;