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
@@ -24,11 +24,11 @@
24
24
  *
25
25
  * ## Shape
26
26
  *
27
- * `<storagePath>/parsedfile-store/<shardId>.json` — one shard per parse chunk,
28
- * a JSON array of `ParsedFile` serialized with the same `mapReplacer` the parse
29
- * cache uses (Scope.bindings / Scope.typeBindings are `Map`s). The store is
30
- * cleared at the start of each parse and after scope-resolution consumes it, so
31
- * it never lingers and never goes stale across runs.
27
+ * `<storagePath>/parsedfile-store/<shardId>.v8` — one shard per parse chunk,
28
+ * a V8 envelope of `ParsedFile[]` (Scope.bindings / Scope.typeBindings stay
29
+ * `Map`s). The store is cleared at the start of each parse and after
30
+ * scope-resolution consumes it, so it never lingers and never goes stale
31
+ * across runs.
32
32
  *
33
33
  * ## Durable sibling store (`parsedfile-cache/`, warm-cache coverage)
34
34
  *
@@ -41,19 +41,21 @@
41
41
  * that gap we ALSO write the worker's ParsedFiles to a second, CONTENT-ADDRESSED
42
42
  * store keyed by the parse chunk hash (`getDurableParsedFileDir`), which mirrors
43
43
  * the parse cache's lifecycle (persists across runs, pruned by `usedKeys`,
44
- * version-tied via `PARSE_CACHE_VERSION`). On a warm hit the chunk's durable
45
- * shards are byte-COPIED into the run-scoped store (no re-parse, no
46
- * re-serialize → byte-identical), so scope-resolution streams them exactly as
47
- * on a cold run. Content-addressing makes stale reuse impossible: a changed
48
- * file changes its chunk hash, which misses BOTH stores and re-dispatches.
44
+ * version-tied via `PARSE_CACHE_VERSION`). On a warm hit the chunk's immutable
45
+ * durable shards are hardlinked (or atomically copied) into the run store after
46
+ * their envelope metadata proves complete coverage. That pins a stable snapshot
47
+ * before workers are skipped, even when another branch refreshes the shared
48
+ * durable directory concurrently.
49
49
  */
50
- import { promises as fs, mkdirSync, writeFileSync } from 'node:fs';
50
+ import { promises as fs, mkdirSync } from 'node:fs';
51
51
  import path from 'node:path';
52
52
  import v8 from 'node:v8';
53
53
  import vm from 'node:vm';
54
54
  import { isValidReceiverChain } from '../core/ingestion/utils/receiver-chain-codec.js';
55
55
  import { logger } from '../core/logger.js';
56
- import { mapReplacer, mapReviver } from './parse-cache.js';
56
+ import { mapReviver } from './parse-cache.js';
57
+ import { linkOrCopyFile } from './fs-atomic.js';
58
+ import { inspectV8Cache, tryLoadV8Cache, writeV8CacheFile, writeV8CacheFileSync, } from './v8-sidecar.js';
57
59
  const STORE_DIRNAME = 'parsedfile-store';
58
60
  const DURABLE_DIRNAME = 'parsedfile-cache';
59
61
  const DURABLE_INDEX_FILENAME = 'index.json';
@@ -148,32 +150,28 @@ export const getParsedFileStoreDir = (storagePath) => path.join(storagePath, STO
148
150
  export const clearParsedFileStore = async (storagePath) => {
149
151
  await fs.rm(getParsedFileStoreDir(storagePath), { recursive: true, force: true });
150
152
  };
153
+ const isV8ShardName = (name) => name.endsWith('.v8') && !name.includes('.v8.');
154
+ const shardPath = (storagePath, shardId) => path.join(getParsedFileStoreDir(storagePath), `${shardId}.v8`);
155
+ const shardFilePaths = (parsedFiles) => parsedFiles.map((pf) => pf.filePath);
156
+ const LOAD_YIELD_EVERY_SHARDS = 128;
151
157
  /**
152
- * Single source of truth for a shard's bytes. Returns `null` for an empty
153
- * chunk (caller writes nothing). Both the async (`persistParsedFileChunk`) and
154
- * sync (`persistParsedFileShardSync`) writers go through this so the two paths
155
- * are guaranteed byte-identical — the shards must round-trip through the same
156
- * `mapReviver`, and matching bytes by having both authors type the same
157
- * `mapReplacer` call would be a coincidence, not a guarantee.
158
+ * Test seam for #3086. Production always calls {@link forceGc}; unit tests
159
+ * replace `run` to count cadence without requiring `--expose-gc`.
158
160
  */
159
- const serializeParsedFileShard = (parsedFiles) => {
160
- if (parsedFiles.length === 0)
161
- return null;
162
- return JSON.stringify(parsedFiles, mapReplacer);
161
+ export const parsedFileLoadGc = {
162
+ run: forceGc,
163
+ /** V8 envelope bytes visited between GCs (#3086). Tests may lower this. */
164
+ byteBudget: 128 * 1024 * 1024,
163
165
  };
164
- const shardPath = (storagePath, shardId) => path.join(getParsedFileStoreDir(storagePath), `${shardId}.json`);
165
166
  /**
166
- * Write one parse chunk's `ParsedFile[]` to the store as a single shard (async).
167
- * No-op for an empty chunk. `shardId` must be unique within a run. Used by the
168
- * main-thread no-store-disabled fallback and any non-worker writer; the worker
169
- * store path uses {@link persistParsedFileShardSync}.
167
+ * Write one parse chunk's `ParsedFile[]` to the store as a single `.v8` shard.
168
+ * No-op for an empty chunk. `shardId` must be unique within a run.
170
169
  */
171
170
  export const persistParsedFileChunk = async (storagePath, shardId, parsedFiles) => {
172
- const payload = serializeParsedFileShard(parsedFiles);
173
- if (payload === null)
174
- return;
171
+ if (parsedFiles.length === 0)
172
+ return true;
175
173
  await fs.mkdir(getParsedFileStoreDir(storagePath), { recursive: true });
176
- await fs.writeFile(shardPath(storagePath, shardId), payload, 'utf-8');
174
+ return writeV8CacheFile(shardPath(storagePath, shardId), parsedFiles, shardFilePaths(parsedFiles));
177
175
  };
178
176
  // Per-process set of store dirs we've already `mkdir`ed, so the sync worker
179
177
  // writer (called once per job, many times into the same dir) doesn't issue a
@@ -181,130 +179,106 @@ export const persistParsedFileChunk = async (storagePath, shardId, parsedFiles)
181
179
  const createdStoreDirs = new Set();
182
180
  /**
183
181
  * Synchronous shard writer for use INSIDE a parse worker (#1983 parallel
184
- * serialization). The worker is a dedicated thread, so a blocking write there
185
- * protects the main thread, and a sync write avoids threading `async`/`await`
186
- * through the synchronous per-file extract loop. Produces byte-identical shards
187
- * to {@link persistParsedFileChunk} via the shared {@link serializeParsedFileShard}.
188
- * No-op for an empty chunk. `shardId` must be globally unique for the run (the
189
- * worker uses `w<threadId>-<seq>`); a duplicate would silently overwrite.
182
+ * serialization). Returns false on write failure so the worker can keep
183
+ * ParsedFiles in the result instead of dropping them.
190
184
  */
191
185
  export const persistParsedFileShardSync = (storagePath, shardId, parsedFiles) => {
192
- const payload = serializeParsedFileShard(parsedFiles);
193
- if (payload === null)
194
- return;
186
+ if (parsedFiles.length === 0)
187
+ return true;
195
188
  const dir = getParsedFileStoreDir(storagePath);
196
189
  if (!createdStoreDirs.has(dir)) {
197
190
  mkdirSync(dir, { recursive: true });
198
191
  createdStoreDirs.add(dir);
199
192
  }
200
- writeFileSync(shardPath(storagePath, shardId), payload, 'utf-8');
193
+ return writeV8CacheFileSync(shardPath(storagePath, shardId), parsedFiles, shardFilePaths(parsedFiles));
201
194
  };
202
- /**
203
- * Stream the store and return the `ParsedFile`s whose `filePath` is in
204
- * `wantPaths`, keyed by path. Loads one shard at a time and retains only the
205
- * matching entries, so peak heap is bounded by (matched set) + (one shard)
206
- * rather than the whole store. Returns an empty map when the store is absent
207
- * (e.g. tests, or a run with no worker pool) — callers fall back to a fresh
208
- * extract for the missing files.
209
- */
210
- export const loadParsedFilesForPaths = async (storagePath, wantPaths) => {
211
- const out = new Map();
212
- if (wantPaths.size === 0)
213
- return out;
214
- const dir = getParsedFileStoreDir(storagePath);
215
- let shards;
195
+ const listV8Shards = async (dir) => {
216
196
  try {
217
- shards = (await fs.readdir(dir)).filter((f) => f.endsWith('.json'));
197
+ return (await fs.readdir(dir)).filter(isV8ShardName).map((name) => path.join(dir, name));
218
198
  }
219
199
  catch {
220
- return out; // store absent
200
+ return [];
221
201
  }
222
- // Shared interning pool for this load — deduplicates strings ACROSS shards
223
- // (one `int` / one repeated filePath for the whole language), which is where
224
- // most of the saving comes from. Dropped when this function returns.
202
+ };
203
+ export const loadParsedFilesForPaths = async (storagePath, wantPaths) => {
204
+ const out = new Map();
205
+ if (wantPaths.size === 0)
206
+ return out;
207
+ const shardPaths = await listV8Shards(getParsedFileStoreDir(storagePath));
225
208
  const pool = new Map();
226
209
  let droppedSites = 0;
227
210
  let filesWithDroppedSites = 0;
228
211
  let droppedChains = 0;
229
212
  let rejectedFiles = 0;
230
- for (let i = 0; i < shards.length; i++) {
231
- // Per-shard def pool: a SymbolDefinition's three serialized copies live within
232
- // a single shard (one ParsedFile), so the dedup is shard-local. A cross-shard
233
- // pool would retain defs of files NOT in `wantPaths` (loaded-but-discarded
234
- // shards), reintroducing the leak; per-shard drops them with the shard.
235
- const defPool = new Map();
236
- const reviver = makeInterningReviver(pool, defPool);
237
- let parsed;
238
- try {
239
- const raw = await fs.readFile(path.join(dir, shards[i]), 'utf-8');
240
- parsed = JSON.parse(raw, reviver);
213
+ let bytesSinceGc = 0;
214
+ let shardsSinceYield = 0;
215
+ const maybeYieldAndGc = async (forceByteGc) => {
216
+ if (forceByteGc) {
217
+ parsedFileLoadGc.run();
218
+ bytesSinceGc = 0;
219
+ shardsSinceYield = 0;
220
+ await new Promise((resolve) => setImmediate(resolve));
221
+ return;
241
222
  }
242
- catch {
243
- continue; // skip a corrupt shard; missing files fall back to fresh extract
223
+ shardsSinceYield++;
224
+ if (shardsSinceYield >= LOAD_YIELD_EVERY_SHARDS) {
225
+ shardsSinceYield = 0;
226
+ await new Promise((resolve) => setImmediate(resolve));
244
227
  }
245
- if (!Array.isArray(parsed))
228
+ };
229
+ for (const shardFull of shardPaths) {
230
+ const loaded = await tryLoadV8Cache(shardFull, pool, wantPaths);
231
+ if (loaded === undefined) {
232
+ await maybeYieldAndGc(false);
246
233
  continue;
247
- for (const pf of parsed) {
248
- if (!pf || typeof pf.filePath !== 'string' || !wantPaths.has(pf.filePath))
249
- continue;
250
- const flow = sanitizeCallableFlowSites(pf.callableFlowSites);
251
- if (flow === undefined) {
252
- // non-array garbage → distrust the file, re-extract
253
- rejectedFiles++;
254
- continue;
255
- }
256
- const chains = sanitizeReceiverChains(pf.referenceSites);
257
- if (chains === undefined) {
258
- rejectedFiles++;
259
- continue;
260
- }
261
- if (flow.dropped === 0 && chains.dropped === 0) {
262
- out.set(pf.filePath, pf);
263
- }
264
- else {
265
- droppedSites += flow.dropped;
266
- droppedChains += chains.dropped;
267
- filesWithDroppedSites++;
268
- out.set(pf.filePath, {
269
- ...pf,
270
- ...(flow.dropped === 0 ? {} : { callableFlowSites: flow.sites }),
271
- ...(chains.dropped === 0 ? {} : { referenceSites: chains.sites }),
272
- });
273
- }
274
234
  }
275
- // Every few shards, reclaim the transient pre-intern parse churn before it
276
- // piles up against the heap limit (~5 GB avoidable on the kernel), and
277
- // yield so the GC + any pending I/O can run.
278
- if ((i & 7) === 7) {
279
- forceGc();
280
- await new Promise((resolve) => setImmediate(resolve));
235
+ if (loaded.kind === 'skip') {
236
+ bytesSinceGc += loaded.bytes;
237
+ await maybeYieldAndGc(bytesSinceGc >= parsedFileLoadGc.byteBudget);
238
+ continue;
281
239
  }
240
+ bytesSinceGc += loaded.bytes;
241
+ const parsed = Array.isArray(loaded.value) ? loaded.value : undefined;
242
+ const crossedBudget = bytesSinceGc >= parsedFileLoadGc.byteBudget;
243
+ if (Array.isArray(parsed)) {
244
+ for (const pf of parsed) {
245
+ if (!pf || typeof pf.filePath !== 'string' || !wantPaths.has(pf.filePath))
246
+ continue;
247
+ const flow = sanitizeCallableFlowSites(pf.callableFlowSites);
248
+ if (flow === undefined) {
249
+ rejectedFiles++;
250
+ continue;
251
+ }
252
+ const chains = sanitizeReceiverChains(pf.referenceSites);
253
+ if (chains === undefined) {
254
+ rejectedFiles++;
255
+ continue;
256
+ }
257
+ if (flow.dropped === 0 && chains.dropped === 0) {
258
+ out.set(pf.filePath, pf);
259
+ }
260
+ else {
261
+ droppedSites += flow.dropped;
262
+ droppedChains += chains.dropped;
263
+ filesWithDroppedSites++;
264
+ out.set(pf.filePath, {
265
+ ...pf,
266
+ ...(flow.dropped === 0 ? {} : { callableFlowSites: flow.sites }),
267
+ ...(chains.dropped === 0 ? {} : { referenceSites: chains.sites }),
268
+ });
269
+ }
270
+ }
271
+ }
272
+ await maybeYieldAndGc(crossedBudget);
282
273
  }
283
274
  if (droppedSites > 0 || droppedChains > 0) {
284
- // Facts for the dropped sites are omitted this run (the file itself is
285
- // retained, so no re-extract happens) — surface it so a recurring drop
286
- // on every warm load is observable rather than silent (#2522 review).
287
275
  logger.warn({ droppedSites, droppedChains, files: filesWithDroppedSites }, 'parsedfile-store: dropped malformed/over-bound sites at load; files retained without those facts');
288
276
  }
289
277
  if (rejectedFiles > 0) {
290
- // The other half of the same defect. A rejected file silently falls back to
291
- // a fresh extract EVERY load, so a writer that keeps minting what this
292
- // reader keeps refusing is a permanent warm-cache miss that costs real time
293
- // and says nothing about why.
294
278
  logger.warn({ rejectedFiles }, 'parsedfile-store: rejected shard entries at load (untrusted shape); those files re-extract every run');
295
279
  }
296
280
  return out;
297
281
  };
298
- /**
299
- * Treat the durable ParsedFile store as an untrusted serialization boundary.
300
- * Sanitation is per-SITE, not per-file: one malformed or over-bound fact drops
301
- * only itself (counted, logged by the caller), so a legitimately pathological
302
- * source file cannot push its whole ParsedFile into a permanent, silent
303
- * warm-cache-miss reparse loop (#2522 review). Only a non-array field —
304
- * i.e. garbage that says the serialization itself is untrustworthy — rejects
305
- * the file, and `undefined` (never emitted / no facts) passes through.
306
- * Returns `undefined` for the reject-file case.
307
- */
308
282
  function sanitizeCallableFlowSites(value) {
309
283
  if (value === undefined)
310
284
  return { sites: undefined, dropped: 0 };
@@ -486,9 +460,6 @@ export const prepareDurableParsedFileChunk = async (durableDir, chunkHash) => {
486
460
  await fs.rm(dir, { recursive: true, force: true });
487
461
  await fs.mkdir(dir, { recursive: true });
488
462
  };
489
- // Per-process set of durable chunk subdirs already `mkdir`ed (mirrors
490
- // `createdStoreDirs`) so the worker doesn't `mkdirSync` on every shard.
491
- const createdDurableDirs = new Set();
492
463
  /**
493
464
  * Synchronous durable-shard writer for use INSIDE a parse worker, alongside
494
465
  * {@link persistParsedFileShardSync}. Writes the SAME bytes to a content-addressed
@@ -498,63 +469,85 @@ const createdDurableDirs = new Set();
498
469
  * uniqueness that makes the run-scoped `w<tid>-<seq>` name safe, prefixed by
499
470
  * content. No-op for an empty chunk.
500
471
  */
472
+ const createdDurableDirs = new Set();
501
473
  export const persistDurableParsedFileShardSync = (durableDir, chunkHash, threadId, shardSeq, parsedFiles) => {
502
- const payload = serializeParsedFileShard(parsedFiles);
503
- if (payload === null)
504
- return;
474
+ if (parsedFiles.length === 0)
475
+ return true;
505
476
  const dir = durableChunkDir(durableDir, chunkHash);
506
477
  if (!createdDurableDirs.has(dir)) {
507
478
  mkdirSync(dir, { recursive: true });
508
479
  createdDurableDirs.add(dir);
509
480
  }
510
- writeFileSync(path.join(dir, `${chunkHash}-w${threadId}-${shardSeq}.json`), payload, 'utf-8');
481
+ const dest = path.join(dir, `${chunkHash}-w${threadId}-${shardSeq}.v8`);
482
+ return writeV8CacheFileSync(dest, parsedFiles, shardFilePaths(parsedFiles));
511
483
  };
512
484
  /**
513
- * Restore a cached chunk's durable shards into the run-scoped store on a warm
514
- * hit. A verbatim byte copy (no parse, no re-serialize), so the restored
515
- * ParsedFiles are byte-identical to a cold run and `loadParsedFilesForPaths`
516
- * (which keys on `filePath`, not shard name) gives scope-resolution full
517
- * coverage. The durable shard names already carry the chunk hash, so they never
518
- * collide with the worker's run-scoped `w<tid>-<seq>` shards. Returns the number
519
- * of shards restored (0 ⇒ no durable coverage for this chunk; caller treats it
520
- * as a miss).
485
+ * Validate and snapshot one durable chunk into the run store. Every envelope
486
+ * must be runtime-compatible and integrity-valid, and together they must match
487
+ * the path coverage recorded when the durable index was published. Linking
488
+ * before returning pins the inodes against concurrent branch-cache rotation.
521
489
  */
522
- export const restoreDurableParsedFileShard = async (durableDir, runStoragePath, chunkHash) => {
523
- const src = durableChunkDir(durableDir, chunkHash);
524
- let shards;
490
+ export const durableChunkHasShards = async (runStoragePath, chunkHash, expectedPaths) => {
491
+ const sourceDir = durableChunkDir(getDurableParsedFileDir(runStoragePath), chunkHash);
492
+ const shards = await listV8Shards(sourceDir);
493
+ if (shards.length === 0 || expectedPaths.size === 0)
494
+ return false;
495
+ const runDir = getParsedFileStoreDir(runStoragePath);
525
496
  try {
526
- shards = (await fs.readdir(src)).filter((f) => f.endsWith('.json'));
497
+ await fs.mkdir(runDir, { recursive: true });
527
498
  }
528
499
  catch {
529
- return 0; // no durable shards for this chunk
500
+ return false;
530
501
  }
531
- if (shards.length === 0)
532
- return 0;
533
- const dst = getParsedFileStoreDir(runStoragePath);
534
- await fs.mkdir(dst, { recursive: true });
535
- for (const name of shards) {
536
- await fs.copyFile(path.join(src, name), path.join(dst, name));
502
+ const restored = [];
503
+ const covered = new Set();
504
+ const rollback = async () => {
505
+ await Promise.all(restored.map((filePath) => fs.rm(filePath, { force: true }).catch(() => { })));
506
+ return false;
507
+ };
508
+ for (const sourcePath of shards) {
509
+ const name = path.basename(sourcePath);
510
+ const destinationPath = path.join(runDir, name);
511
+ try {
512
+ await linkOrCopyFile(sourcePath, destinationPath);
513
+ restored.push(destinationPath);
514
+ }
515
+ catch {
516
+ return rollback();
517
+ }
518
+ const inspected = await inspectV8Cache(destinationPath);
519
+ if (!inspected)
520
+ return rollback();
521
+ for (const filePath of inspected.paths) {
522
+ if (!expectedPaths.has(filePath))
523
+ return rollback();
524
+ covered.add(filePath);
525
+ }
537
526
  }
538
- return shards.length;
527
+ if (covered.size !== expectedPaths.size)
528
+ return rollback();
529
+ return true;
539
530
  };
540
- /**
541
- * Read the durable index and return the set of chunk hashes it vouches for,
542
- * gated on `expectedVersion` (`PARSE_CACHE_VERSION`). A version mismatch or a
543
- * missing/corrupt index returns the empty set — the caller then treats every
544
- * chunk as a durable miss and re-dispatches workers (NEVER the main-thread
545
- * `extractParsedFile` fallback), which rewrites the durable store under the new
546
- * version. Mirrors `loadParseCache`'s version-invalidation contract.
547
- */
548
531
  export const loadDurableParsedFileIndex = async (durableDir, expectedVersion) => {
549
532
  try {
550
533
  const raw = await fs.readFile(path.join(durableDir, DURABLE_INDEX_FILENAME), 'utf-8');
551
534
  const idx = JSON.parse(raw);
552
- if (idx?.version !== expectedVersion || !Array.isArray(idx.keys))
553
- return new Set();
554
- return new Set(idx.keys);
535
+ if (!isRecord(idx) || idx.version !== expectedVersion || !isRecord(idx.entries)) {
536
+ return new Map();
537
+ }
538
+ const entries = new Map();
539
+ for (const [key, paths] of Object.entries(idx.entries)) {
540
+ if (!Array.isArray(paths) ||
541
+ paths.length === 0 ||
542
+ paths.some((filePath) => typeof filePath !== 'string')) {
543
+ return new Map();
544
+ }
545
+ entries.set(key, new Set(paths));
546
+ }
547
+ return entries;
555
548
  }
556
549
  catch {
557
- return new Set();
550
+ return new Map();
558
551
  }
559
552
  };
560
553
  /**
@@ -562,9 +555,9 @@ export const loadDurableParsedFileIndex = async (durableDir, expectedVersion) =>
562
555
  * be the parse cache's surviving on-disk keys (so the two stores stay coherent:
563
556
  * a chunk is "cached" iff BOTH its parse-cache shard and its durable shards
564
557
  * exist; a quarantined chunk — no parse-cache shard — drops its durable subdir
565
- * here and re-dispatches next run). Only subdirs with ≥1 shard are indexed
566
- * (mirrors `saveParseCache`'s written-keys discipline — never vouch for a chunk
567
- * hash with no backing shard). The index write is tmp+rename atomic.
558
+ * here and re-dispatches next run). Only chunks whose envelopes all validate
559
+ * are indexed, together with their exact persisted path coverage (never vouch
560
+ * for a missing/corrupt shard). The index write is tmp+rename atomic.
568
561
  */
569
562
  export const pruneAndSaveDurableParsedFileStore = async (durableDir, version, keepKeys) => {
570
563
  let entries;
@@ -574,17 +567,30 @@ export const pruneAndSaveDurableParsedFileStore = async (durableDir, version, ke
574
567
  catch {
575
568
  return; // nothing written this run
576
569
  }
577
- const survivors = [];
570
+ const survivors = {};
578
571
  for (const name of entries) {
579
572
  if (name === DURABLE_INDEX_FILENAME)
580
573
  continue;
581
574
  const full = path.join(durableDir, name);
582
575
  if (keepKeys.has(name)) {
583
576
  try {
584
- const shards = (await fs.readdir(full)).filter((f) => f.endsWith('.json'));
577
+ const shards = await listV8Shards(full);
585
578
  if (shards.length > 0) {
586
- survivors.push(name);
587
- continue;
579
+ const covered = new Set();
580
+ let valid = true;
581
+ for (const shard of shards) {
582
+ const inspected = await inspectV8Cache(shard);
583
+ if (!inspected) {
584
+ valid = false;
585
+ break;
586
+ }
587
+ for (const filePath of inspected.paths)
588
+ covered.add(filePath);
589
+ }
590
+ if (valid && covered.size > 0) {
591
+ survivors[name] = [...covered].sort();
592
+ continue;
593
+ }
588
594
  }
589
595
  }
590
596
  catch {
@@ -593,9 +599,79 @@ export const pruneAndSaveDurableParsedFileStore = async (durableDir, version, ke
593
599
  }
594
600
  await fs.rm(full, { recursive: true, force: true });
595
601
  }
596
- const idx = { version, keys: survivors };
602
+ const idx = { version, entries: survivors };
597
603
  const tmp = path.join(durableDir, `${DURABLE_INDEX_FILENAME}.tmp`);
598
604
  await fs.mkdir(durableDir, { recursive: true });
599
605
  await fs.writeFile(tmp, JSON.stringify(idx), 'utf-8');
600
606
  await fs.rename(tmp, path.join(durableDir, DURABLE_INDEX_FILENAME));
601
607
  };
608
+ /**
609
+ * Overlay this run's staged durable ParsedFile chunks onto the live store,
610
+ * then prune the live tree to `keepKeys`. Live chunks this run did not rewrite
611
+ * (other branches, unused hashes) stay until prune. No-op overlay when the
612
+ * staged dir is missing.
613
+ */
614
+ export const mergeStagedDurableParsedFileStore = async (liveStoragePath, stagedStoragePath, version, keepKeys) => {
615
+ const liveDir = getDurableParsedFileDir(liveStoragePath);
616
+ if (stagedStoragePath === liveStoragePath) {
617
+ await pruneAndSaveDurableParsedFileStore(liveDir, version, keepKeys);
618
+ return;
619
+ }
620
+ const stagedDir = getDurableParsedFileDir(stagedStoragePath);
621
+ await fs.mkdir(liveDir, { recursive: true });
622
+ let stagedEntries = [];
623
+ try {
624
+ stagedEntries = await fs.readdir(stagedDir);
625
+ }
626
+ catch {
627
+ await pruneAndSaveDurableParsedFileStore(liveDir, version, keepKeys);
628
+ return;
629
+ }
630
+ for (const name of stagedEntries) {
631
+ if (name === DURABLE_INDEX_FILENAME)
632
+ continue;
633
+ const from = path.join(stagedDir, name);
634
+ const to = path.join(liveDir, name);
635
+ await replaceDurableChunkDir(from, to);
636
+ }
637
+ await pruneAndSaveDurableParsedFileStore(liveDir, version, keepKeys);
638
+ };
639
+ /** Move `from` onto `to` without deleting `to` until the new tree is in place. */
640
+ const replaceDurableChunkDir = async (from, to) => {
641
+ try {
642
+ await fs.rename(from, to);
643
+ return;
644
+ }
645
+ catch {
646
+ /* dest exists, or the rename is cross-device */
647
+ }
648
+ const backup = `${to}.replacing`;
649
+ await fs.rm(backup, { recursive: true, force: true });
650
+ let backedUp = false;
651
+ try {
652
+ await fs.rename(to, backup);
653
+ backedUp = true;
654
+ }
655
+ catch {
656
+ /* dest was missing */
657
+ }
658
+ try {
659
+ try {
660
+ await fs.rename(from, to);
661
+ }
662
+ catch {
663
+ await fs.cp(from, to, { recursive: true });
664
+ await fs.rm(from, { recursive: true, force: true });
665
+ }
666
+ }
667
+ catch (err) {
668
+ if (backedUp) {
669
+ await fs.rm(to, { recursive: true, force: true }).catch(() => { });
670
+ await fs.rename(backup, to).catch(() => { });
671
+ }
672
+ throw err;
673
+ }
674
+ if (backedUp) {
675
+ await fs.rm(backup, { recursive: true, force: true });
676
+ }
677
+ };
@@ -19,6 +19,7 @@ export { branchSlug, resolveBranchPlacement };
19
19
  export type { BranchSummary };
20
20
  export { getStoragePath, INDEX_METADATA_FILE, isMissingFilesystemError, loadMeta };
21
21
  export type { AnalyzerRunnerIdentity, RepoMeta };
22
+ export { getGlobalDir } from './global-dir.js';
22
23
  /**
23
24
  * Normalise a repo path for registry comparison across platforms
24
25
  * (#664 review feedback from @evander-wang).
@@ -206,10 +207,6 @@ export declare function isReadOnlyFilesystemError(err: unknown): boolean;
206
207
  * Keep .gitnexus/ ignored. It contains local index state and caches.
207
208
  */
208
209
  export declare const ensureGitNexusIgnored: (repoPath: string) => Promise<void>;
209
- /**
210
- * Get the path to the global GitNexus directory
211
- */
212
- export declare const getGlobalDir: () => string;
213
210
  /**
214
211
  * Get the path to the global registry file
215
212
  */
@@ -475,6 +472,15 @@ export declare const assertSafeStoragePath: (entry: RegistryEntry) => void;
475
472
  * `GITNEXUS_HOME`.
476
473
  */
477
474
  export declare const resolveRegistryEntry: (entries: RegistryEntry[], target: string) => RegistryEntry;
475
+ /**
476
+ * Name-only registry match (the name tier of {@link resolveRegistryEntry},
477
+ * without path matching). Used by `group.yaml` member *values*, which are
478
+ * registry aliases, not filesystem paths.
479
+ *
480
+ * Zero matches → `undefined` (caller treats as missing). One match → that
481
+ * entry. Two or more → {@link RegistryAmbiguousTargetError}.
482
+ */
483
+ export declare const findRegistryEntryByName: (entries: RegistryEntry[], name: string) => RegistryEntry | undefined;
478
484
  /**
479
485
  * List all registered repos from the global registry.
480
486
  *