cgraphx 2.0.1 → 2.0.3

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 (701) hide show
  1. package/dist/.claude-template/FLOW-MAP.md +7 -5
  2. package/dist/.claude-template/commands/end.md +1 -1
  3. package/dist/.claude-template/skills/cgraphx-guide/SKILL.md +2 -1
  4. package/dist/.claude-template/skills/cgraphx-guide/how-to-use.html +4 -2
  5. package/dist/.claude-template/skills/cgraphx-guide/how-to-use.md +5 -2
  6. package/dist/.claude-template/skills/iboc-check/SKILL.md +116 -0
  7. package/dist/.claude-template/skills/iboc-check/filler-prompt.md +75 -0
  8. package/dist/.claude-template/skills/implementation/SKILL.md +7 -0
  9. package/dist/.claude-template/skills/init-project-guides/SKILL.md +1 -1
  10. package/dist/.claude-template/skills/run-api-test/references/db-verification.md +2 -2
  11. package/dist/.claude-template/skills/subagent-implement/SKILL.md +3 -2
  12. package/dist/.claude-template/skills/write-api/assets/template-request.bru +3 -3
  13. package/dist/.claude-template/skills/write-api/references/bru-format.md +6 -6
  14. package/dist/.claude-template/skills/write-delivery-guide/SKILL.md +97 -0
  15. package/dist/.claude-template/skills/write-delivery-guide/template-change-type.md +120 -0
  16. package/dist/.claude-template/skills/write-delivery-guide/template-multi-role-flow.md +148 -0
  17. package/dist/.claude-template/skills/write-unit-test-code/SKILL.md +23 -6
  18. package/dist/core/code/engine/cli/analyze-config.js +320 -0
  19. package/dist/core/code/engine/cli/analyze.js +1086 -0
  20. package/dist/core/code/engine/cli/cli-message.js +83 -0
  21. package/dist/core/code/engine/cli/detect-changes-format.js +58 -0
  22. package/dist/core/code/engine/cli/embedding-dims.js +43 -0
  23. package/dist/core/code/engine/cli/format-elapsed.js +12 -0
  24. package/dist/core/code/engine/cli/help-i18n.js +144 -0
  25. package/dist/core/code/engine/cli/i18n/en.js +110 -0
  26. package/dist/core/code/engine/cli/i18n/index.js +47 -0
  27. package/dist/core/code/engine/cli/i18n/resources.js +9 -0
  28. package/dist/core/code/engine/cli/i18n/zh-CN.js +110 -0
  29. package/dist/core/code/engine/cli/lazy-action.js +67 -0
  30. package/dist/core/code/engine/cli/optional-grammars.js +136 -0
  31. package/dist/core/code/engine/cli/resolve-invocation.js +76 -0
  32. package/dist/core/code/engine/cli/status.js +156 -0
  33. package/dist/core/code/engine/cli/tool.js +384 -0
  34. package/dist/core/code/engine/config/ignore-service.js +511 -0
  35. package/dist/core/code/engine/config/supported-languages.js +17 -0
  36. package/dist/core/code/engine/core/analysis-features.js +64 -0
  37. package/dist/core/code/engine/core/analyzer-identity.js +2171 -0
  38. package/dist/core/code/engine/core/git-staleness.js +180 -0
  39. package/dist/core/code/engine/core/graph/graph.js +180 -0
  40. package/dist/core/code/engine/core/graph/import-cycles.js +106 -0
  41. package/dist/core/code/engine/core/graph/types.js +2 -0
  42. package/dist/core/code/engine/core/index-freshness.js +12 -0
  43. package/dist/core/code/engine/core/ingestion/binding-accumulator.js +341 -0
  44. package/dist/core/code/engine/core/ingestion/call-extractors/configs/c-cpp.js +168 -0
  45. package/dist/core/code/engine/core/ingestion/call-extractors/configs/csharp.js +9 -0
  46. package/dist/core/code/engine/core/ingestion/call-extractors/configs/dart.js +8 -0
  47. package/dist/core/code/engine/core/ingestion/call-extractors/configs/go.js +8 -0
  48. package/dist/core/code/engine/core/ingestion/call-extractors/configs/jvm.js +54 -0
  49. package/dist/core/code/engine/core/ingestion/call-extractors/configs/php.js +8 -0
  50. package/dist/core/code/engine/core/ingestion/call-extractors/configs/python.js +8 -0
  51. package/dist/core/code/engine/core/ingestion/call-extractors/configs/ruby.js +8 -0
  52. package/dist/core/code/engine/core/ingestion/call-extractors/configs/rust.js +8 -0
  53. package/dist/core/code/engine/core/ingestion/call-extractors/configs/swift.js +8 -0
  54. package/dist/core/code/engine/core/ingestion/call-extractors/configs/typescript-javascript.js +11 -0
  55. package/dist/core/code/engine/core/ingestion/call-extractors/generic.js +62 -0
  56. package/dist/core/code/engine/core/ingestion/call-processor.js +503 -0
  57. package/dist/core/code/engine/core/ingestion/call-routing.js +98 -0
  58. package/dist/core/code/engine/core/ingestion/call-types.js +3 -0
  59. package/dist/core/code/engine/core/ingestion/cfg/callee-cell-format.js +45 -0
  60. package/dist/core/code/engine/core/ingestion/cfg/cfg-builder.js +202 -0
  61. package/dist/core/code/engine/core/ingestion/cfg/collect.js +81 -0
  62. package/dist/core/code/engine/core/ingestion/cfg/control-dependence.js +185 -0
  63. package/dist/core/code/engine/core/ingestion/cfg/control-flow-context.js +130 -0
  64. package/dist/core/code/engine/core/ingestion/cfg/emit.js +646 -0
  65. package/dist/core/code/engine/core/ingestion/cfg/post-dominators.js +182 -0
  66. package/dist/core/code/engine/core/ingestion/cfg/reaching-def-reason-codec.js +139 -0
  67. package/dist/core/code/engine/core/ingestion/cfg/reaching-defs-graph.js +322 -0
  68. package/dist/core/code/engine/core/ingestion/cfg/reaching-defs.js +792 -0
  69. package/dist/core/code/engine/core/ingestion/cfg/synthetic-escape.js +305 -0
  70. package/dist/core/code/engine/core/ingestion/cfg/traversal-result.js +6 -0
  71. package/dist/core/code/engine/core/ingestion/cfg/types.js +14 -0
  72. package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp-harvest.js +545 -0
  73. package/dist/core/code/engine/core/ingestion/cfg/visitors/c-cpp.js +590 -0
  74. package/dist/core/code/engine/core/ingestion/cfg/visitors/call-site-harvest.js +356 -0
  75. package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp-harvest.js +593 -0
  76. package/dist/core/code/engine/core/ingestion/cfg/visitors/csharp.js +871 -0
  77. package/dist/core/code/engine/core/ingestion/cfg/visitors/dart-harvest.js +874 -0
  78. package/dist/core/code/engine/core/ingestion/cfg/visitors/dart.js +840 -0
  79. package/dist/core/code/engine/core/ingestion/cfg/visitors/go-harvest.js +625 -0
  80. package/dist/core/code/engine/core/ingestion/cfg/visitors/go.js +642 -0
  81. package/dist/core/code/engine/core/ingestion/cfg/visitors/java-harvest.js +517 -0
  82. package/dist/core/code/engine/core/ingestion/cfg/visitors/java.js +816 -0
  83. package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin-harvest.js +723 -0
  84. package/dist/core/code/engine/core/ingestion/cfg/visitors/kotlin.js +813 -0
  85. package/dist/core/code/engine/core/ingestion/cfg/visitors/php-harvest.js +630 -0
  86. package/dist/core/code/engine/core/ingestion/cfg/visitors/php.js +725 -0
  87. package/dist/core/code/engine/core/ingestion/cfg/visitors/python-harvest.js +776 -0
  88. package/dist/core/code/engine/core/ingestion/cfg/visitors/python.js +562 -0
  89. package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby-harvest.js +591 -0
  90. package/dist/core/code/engine/core/ingestion/cfg/visitors/ruby.js +760 -0
  91. package/dist/core/code/engine/core/ingestion/cfg/visitors/rust-harvest.js +877 -0
  92. package/dist/core/code/engine/core/ingestion/cfg/visitors/rust.js +562 -0
  93. package/dist/core/code/engine/core/ingestion/cfg/visitors/scope-tree-harvest.js +120 -0
  94. package/dist/core/code/engine/core/ingestion/cfg/visitors/swift-harvest.js +683 -0
  95. package/dist/core/code/engine/core/ingestion/cfg/visitors/swift.js +791 -0
  96. package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript-harvest.js +1060 -0
  97. package/dist/core/code/engine/core/ingestion/cfg/visitors/typescript.js +587 -0
  98. package/dist/core/code/engine/core/ingestion/class-extractors/configs/c-cpp.js +77 -0
  99. package/dist/core/code/engine/core/ingestion/class-extractors/configs/csharp.js +24 -0
  100. package/dist/core/code/engine/core/ingestion/class-extractors/configs/dart.js +10 -0
  101. package/dist/core/code/engine/core/ingestion/class-extractors/configs/go.js +28 -0
  102. package/dist/core/code/engine/core/ingestion/class-extractors/configs/jvm.js +67 -0
  103. package/dist/core/code/engine/core/ingestion/class-extractors/configs/php.js +10 -0
  104. package/dist/core/code/engine/core/ingestion/class-extractors/configs/python.js +10 -0
  105. package/dist/core/code/engine/core/ingestion/class-extractors/configs/ruby.js +13 -0
  106. package/dist/core/code/engine/core/ingestion/class-extractors/configs/rust.js +10 -0
  107. package/dist/core/code/engine/core/ingestion/class-extractors/configs/swift.js +21 -0
  108. package/dist/core/code/engine/core/ingestion/class-extractors/configs/typescript-javascript.js +31 -0
  109. package/dist/core/code/engine/core/ingestion/class-extractors/generic.js +144 -0
  110. package/dist/core/code/engine/core/ingestion/class-types.js +2 -0
  111. package/dist/core/code/engine/core/ingestion/cluster-enricher.js +174 -0
  112. package/dist/core/code/engine/core/ingestion/community-processor.js +604 -0
  113. package/dist/core/code/engine/core/ingestion/constants.js +26 -0
  114. package/dist/core/code/engine/core/ingestion/cpp-ue-preprocessor.js +263 -0
  115. package/dist/core/code/engine/core/ingestion/csharp-namespace-gate.js +133 -0
  116. package/dist/core/code/engine/core/ingestion/di-extractors/index.js +38 -0
  117. package/dist/core/code/engine/core/ingestion/di-extractors/spring.js +310 -0
  118. package/dist/core/code/engine/core/ingestion/emit-references.js +244 -0
  119. package/dist/core/code/engine/core/ingestion/entry-point-scoring.js +201 -0
  120. package/dist/core/code/engine/core/ingestion/export-detection.js +244 -0
  121. package/dist/core/code/engine/core/ingestion/field-extractor.js +29 -0
  122. package/dist/core/code/engine/core/ingestion/field-extractors/configs/c-cpp.js +107 -0
  123. package/dist/core/code/engine/core/ingestion/field-extractors/configs/csharp.js +124 -0
  124. package/dist/core/code/engine/core/ingestion/field-extractors/configs/dart.js +99 -0
  125. package/dist/core/code/engine/core/ingestion/field-extractors/configs/go.js +102 -0
  126. package/dist/core/code/engine/core/ingestion/field-extractors/configs/helpers.js +198 -0
  127. package/dist/core/code/engine/core/ingestion/field-extractors/configs/jvm.js +172 -0
  128. package/dist/core/code/engine/core/ingestion/field-extractors/configs/php.js +67 -0
  129. package/dist/core/code/engine/core/ingestion/field-extractors/configs/python.js +94 -0
  130. package/dist/core/code/engine/core/ingestion/field-extractors/configs/ruby.js +79 -0
  131. package/dist/core/code/engine/core/ingestion/field-extractors/configs/rust.js +55 -0
  132. package/dist/core/code/engine/core/ingestion/field-extractors/configs/swift.js +93 -0
  133. package/dist/core/code/engine/core/ingestion/field-extractors/configs/typescript-javascript.js +59 -0
  134. package/dist/core/code/engine/core/ingestion/field-extractors/generic.js +147 -0
  135. package/dist/core/code/engine/core/ingestion/field-extractors/typescript.js +266 -0
  136. package/dist/core/code/engine/core/ingestion/field-types.js +3 -0
  137. package/dist/core/code/engine/core/ingestion/filesystem-walker.js +136 -0
  138. package/dist/core/code/engine/core/ingestion/finalize-orchestrator.js +159 -0
  139. package/dist/core/code/engine/core/ingestion/framework-detection.js +432 -0
  140. package/dist/core/code/engine/core/ingestion/frameworks/spring/analysis-features.js +38 -0
  141. package/dist/core/code/engine/core/ingestion/frameworks/spring/annotation-arguments.js +234 -0
  142. package/dist/core/code/engine/core/ingestion/frameworks/spring/aop-candidates.js +88 -0
  143. package/dist/core/code/engine/core/ingestion/frameworks/spring/aop.js +487 -0
  144. package/dist/core/code/engine/core/ingestion/frameworks/spring/auto-configuration.js +21 -0
  145. package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-candidates.js +189 -0
  146. package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-catalog.js +32 -0
  147. package/dist/core/code/engine/core/ingestion/frameworks/spring/bean-factories.js +73 -0
  148. package/dist/core/code/engine/core/ingestion/frameworks/spring/conditionals.js +323 -0
  149. package/dist/core/code/engine/core/ingestion/frameworks/spring/config-bindings.js +119 -0
  150. package/dist/core/code/engine/core/ingestion/frameworks/spring/di-metadata.js +385 -0
  151. package/dist/core/code/engine/core/ingestion/frameworks/spring/resource-injection.js +96 -0
  152. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/c-cpp.js +17 -0
  153. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/csharp.js +46 -0
  154. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/dart.js +59 -0
  155. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/go.js +30 -0
  156. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/jvm.js +73 -0
  157. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/php.js +19 -0
  158. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/python.js +45 -0
  159. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/ruby.js +20 -0
  160. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/rust.js +58 -0
  161. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/swift.js +94 -0
  162. package/dist/core/code/engine/core/ingestion/import-resolvers/configs/typescript-javascript.js +26 -0
  163. package/dist/core/code/engine/core/ingestion/import-resolvers/csharp.js +128 -0
  164. package/dist/core/code/engine/core/ingestion/import-resolvers/go.js +50 -0
  165. package/dist/core/code/engine/core/ingestion/import-resolvers/jvm.js +112 -0
  166. package/dist/core/code/engine/core/ingestion/import-resolvers/php.js +80 -0
  167. package/dist/core/code/engine/core/ingestion/import-resolvers/python.js +75 -0
  168. package/dist/core/code/engine/core/ingestion/import-resolvers/resolver-factory.js +36 -0
  169. package/dist/core/code/engine/core/ingestion/import-resolvers/ruby.js +20 -0
  170. package/dist/core/code/engine/core/ingestion/import-resolvers/rust.js +79 -0
  171. package/dist/core/code/engine/core/ingestion/import-resolvers/standard.js +180 -0
  172. package/dist/core/code/engine/core/ingestion/import-resolvers/types.js +7 -0
  173. package/dist/core/code/engine/core/ingestion/import-resolvers/utils.js +153 -0
  174. package/dist/core/code/engine/core/ingestion/import-target-adapter.js +99 -0
  175. package/dist/core/code/engine/core/ingestion/language-config.js +391 -0
  176. package/dist/core/code/engine/core/ingestion/language-provider.js +25 -0
  177. package/dist/core/code/engine/core/ingestion/languages/c/arity-metadata.js +98 -0
  178. package/dist/core/code/engine/core/ingestion/languages/c/arity.js +21 -0
  179. package/dist/core/code/engine/core/ingestion/languages/c/capture-side-channel.js +69 -0
  180. package/dist/core/code/engine/core/ingestion/languages/c/captures.js +189 -0
  181. package/dist/core/code/engine/core/ingestion/languages/c/header-scan.js +58 -0
  182. package/dist/core/code/engine/core/ingestion/languages/c/import-decomposer.js +68 -0
  183. package/dist/core/code/engine/core/ingestion/languages/c/import-target.js +103 -0
  184. package/dist/core/code/engine/core/ingestion/languages/c/index.js +33 -0
  185. package/dist/core/code/engine/core/ingestion/languages/c/interpret.js +53 -0
  186. package/dist/core/code/engine/core/ingestion/languages/c/merge-bindings.js +26 -0
  187. package/dist/core/code/engine/core/ingestion/languages/c/query.js +210 -0
  188. package/dist/core/code/engine/core/ingestion/languages/c/scope-resolver.js +112 -0
  189. package/dist/core/code/engine/core/ingestion/languages/c/simple-hooks.js +24 -0
  190. package/dist/core/code/engine/core/ingestion/languages/c/static-linkage.js +109 -0
  191. package/dist/core/code/engine/core/ingestion/languages/c-cpp.js +506 -0
  192. package/dist/core/code/engine/core/ingestion/languages/cpp/adl.js +804 -0
  193. package/dist/core/code/engine/core/ingestion/languages/cpp/arity-metadata.js +258 -0
  194. package/dist/core/code/engine/core/ingestion/languages/cpp/arity.js +37 -0
  195. package/dist/core/code/engine/core/ingestion/languages/cpp/capture-side-channel.js +89 -0
  196. package/dist/core/code/engine/core/ingestion/languages/cpp/captures.js +1975 -0
  197. package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-extractor.js +311 -0
  198. package/dist/core/code/engine/core/ingestion/languages/cpp/constraint-filter.js +210 -0
  199. package/dist/core/code/engine/core/ingestion/languages/cpp/conversion-rank.js +163 -0
  200. package/dist/core/code/engine/core/ingestion/languages/cpp/file-local-linkage.js +327 -0
  201. package/dist/core/code/engine/core/ingestion/languages/cpp/header-scan.js +53 -0
  202. package/dist/core/code/engine/core/ingestion/languages/cpp/import-decomposer.js +134 -0
  203. package/dist/core/code/engine/core/ingestion/languages/cpp/import-target.js +16 -0
  204. package/dist/core/code/engine/core/ingestion/languages/cpp/index.js +33 -0
  205. package/dist/core/code/engine/core/ingestion/languages/cpp/inline-namespaces.js +379 -0
  206. package/dist/core/code/engine/core/ingestion/languages/cpp/interpret.js +239 -0
  207. package/dist/core/code/engine/core/ingestion/languages/cpp/member-lookup.js +470 -0
  208. package/dist/core/code/engine/core/ingestion/languages/cpp/merge-bindings.js +32 -0
  209. package/dist/core/code/engine/core/ingestion/languages/cpp/query.js +763 -0
  210. package/dist/core/code/engine/core/ingestion/languages/cpp/range-bindings.js +230 -0
  211. package/dist/core/code/engine/core/ingestion/languages/cpp/scope-resolver.js +354 -0
  212. package/dist/core/code/engine/core/ingestion/languages/cpp/simple-hooks.js +67 -0
  213. package/dist/core/code/engine/core/ingestion/languages/cpp/two-phase-lookup.js +348 -0
  214. package/dist/core/code/engine/core/ingestion/languages/cpp/type-classifier.js +56 -0
  215. package/dist/core/code/engine/core/ingestion/languages/cpp/user-defined-conversions.js +128 -0
  216. package/dist/core/code/engine/core/ingestion/languages/csharp/accessor-unwrap.js +67 -0
  217. package/dist/core/code/engine/core/ingestion/languages/csharp/arity-metadata.js +49 -0
  218. package/dist/core/code/engine/core/ingestion/languages/csharp/arity.js +40 -0
  219. package/dist/core/code/engine/core/ingestion/languages/csharp/cache-stats.js +32 -0
  220. package/dist/core/code/engine/core/ingestion/languages/csharp/captures.js +557 -0
  221. package/dist/core/code/engine/core/ingestion/languages/csharp/import-decomposer.js +96 -0
  222. package/dist/core/code/engine/core/ingestion/languages/csharp/import-target.js +176 -0
  223. package/dist/core/code/engine/core/ingestion/languages/csharp/index.js +95 -0
  224. package/dist/core/code/engine/core/ingestion/languages/csharp/interpret.js +150 -0
  225. package/dist/core/code/engine/core/ingestion/languages/csharp/merge-bindings.js +58 -0
  226. package/dist/core/code/engine/core/ingestion/languages/csharp/namespace-siblings.js +708 -0
  227. package/dist/core/code/engine/core/ingestion/languages/csharp/qualified-type-names.js +62 -0
  228. package/dist/core/code/engine/core/ingestion/languages/csharp/query.js +578 -0
  229. package/dist/core/code/engine/core/ingestion/languages/csharp/receiver-binding.js +142 -0
  230. package/dist/core/code/engine/core/ingestion/languages/csharp/resolution-config.js +18 -0
  231. package/dist/core/code/engine/core/ingestion/languages/csharp/scope-resolver.js +84 -0
  232. package/dist/core/code/engine/core/ingestion/languages/csharp/simple-hooks.js +81 -0
  233. package/dist/core/code/engine/core/ingestion/languages/csharp.js +204 -0
  234. package/dist/core/code/engine/core/ingestion/languages/dart/arity-metadata.js +38 -0
  235. package/dist/core/code/engine/core/ingestion/languages/dart/arity.js +34 -0
  236. package/dist/core/code/engine/core/ingestion/languages/dart/built-ins.js +37 -0
  237. package/dist/core/code/engine/core/ingestion/languages/dart/cache-stats.js +30 -0
  238. package/dist/core/code/engine/core/ingestion/languages/dart/captures.js +1096 -0
  239. package/dist/core/code/engine/core/ingestion/languages/dart/expand-wildcards.js +34 -0
  240. package/dist/core/code/engine/core/ingestion/languages/dart/extension-type-preprocess.js +33 -0
  241. package/dist/core/code/engine/core/ingestion/languages/dart/import-target.js +68 -0
  242. package/dist/core/code/engine/core/ingestion/languages/dart/index.js +45 -0
  243. package/dist/core/code/engine/core/ingestion/languages/dart/interpret.js +101 -0
  244. package/dist/core/code/engine/core/ingestion/languages/dart/merge-bindings.js +42 -0
  245. package/dist/core/code/engine/core/ingestion/languages/dart/query.js +246 -0
  246. package/dist/core/code/engine/core/ingestion/languages/dart/receiver-binding.js +90 -0
  247. package/dist/core/code/engine/core/ingestion/languages/dart/scope-resolver.js +197 -0
  248. package/dist/core/code/engine/core/ingestion/languages/dart/signature-bindings.js +54 -0
  249. package/dist/core/code/engine/core/ingestion/languages/dart/simple-hooks.js +61 -0
  250. package/dist/core/code/engine/core/ingestion/languages/dart.js +138 -0
  251. package/dist/core/code/engine/core/ingestion/languages/go/arity-metadata.js +71 -0
  252. package/dist/core/code/engine/core/ingestion/languages/go/arity.js +17 -0
  253. package/dist/core/code/engine/core/ingestion/languages/go/cache-stats.js +21 -0
  254. package/dist/core/code/engine/core/ingestion/languages/go/captures.js +492 -0
  255. package/dist/core/code/engine/core/ingestion/languages/go/expand-wildcards.js +97 -0
  256. package/dist/core/code/engine/core/ingestion/languages/go/generic-type-parameters.js +146 -0
  257. package/dist/core/code/engine/core/ingestion/languages/go/import-decomposer.js +47 -0
  258. package/dist/core/code/engine/core/ingestion/languages/go/import-target.js +70 -0
  259. package/dist/core/code/engine/core/ingestion/languages/go/index.js +39 -0
  260. package/dist/core/code/engine/core/ingestion/languages/go/interface-impls.js +955 -0
  261. package/dist/core/code/engine/core/ingestion/languages/go/interpret.js +177 -0
  262. package/dist/core/code/engine/core/ingestion/languages/go/merge-bindings.js +21 -0
  263. package/dist/core/code/engine/core/ingestion/languages/go/method-owners.js +131 -0
  264. package/dist/core/code/engine/core/ingestion/languages/go/namespace-mirror.js +56 -0
  265. package/dist/core/code/engine/core/ingestion/languages/go/package-clause.js +79 -0
  266. package/dist/core/code/engine/core/ingestion/languages/go/package-siblings.js +83 -0
  267. package/dist/core/code/engine/core/ingestion/languages/go/query.js +298 -0
  268. package/dist/core/code/engine/core/ingestion/languages/go/range-binding.js +127 -0
  269. package/dist/core/code/engine/core/ingestion/languages/go/receiver-binding.js +24 -0
  270. package/dist/core/code/engine/core/ingestion/languages/go/scope-resolver.js +75 -0
  271. package/dist/core/code/engine/core/ingestion/languages/go/simple-hooks.js +31 -0
  272. package/dist/core/code/engine/core/ingestion/languages/go/type-binding.js +279 -0
  273. package/dist/core/code/engine/core/ingestion/languages/go.js +160 -0
  274. package/dist/core/code/engine/core/ingestion/languages/index.js +66 -0
  275. package/dist/core/code/engine/core/ingestion/languages/java/analysis-features.js +16 -0
  276. package/dist/core/code/engine/core/ingestion/languages/java/arity-metadata.js +43 -0
  277. package/dist/core/code/engine/core/ingestion/languages/java/arity.js +27 -0
  278. package/dist/core/code/engine/core/ingestion/languages/java/cache-stats.js +32 -0
  279. package/dist/core/code/engine/core/ingestion/languages/java/capture-side-channel.js +123 -0
  280. package/dist/core/code/engine/core/ingestion/languages/java/captures.js +791 -0
  281. package/dist/core/code/engine/core/ingestion/languages/java/import-decomposer.js +88 -0
  282. package/dist/core/code/engine/core/ingestion/languages/java/import-target.js +103 -0
  283. package/dist/core/code/engine/core/ingestion/languages/java/index.js +43 -0
  284. package/dist/core/code/engine/core/ingestion/languages/java/interpret.js +146 -0
  285. package/dist/core/code/engine/core/ingestion/languages/java/merge-bindings.js +43 -0
  286. package/dist/core/code/engine/core/ingestion/languages/java/package-facts.js +16 -0
  287. package/dist/core/code/engine/core/ingestion/languages/java/package-siblings.js +11 -0
  288. package/dist/core/code/engine/core/ingestion/languages/java/query.js +319 -0
  289. package/dist/core/code/engine/core/ingestion/languages/java/receiver-binding.js +98 -0
  290. package/dist/core/code/engine/core/ingestion/languages/java/scope-resolver.js +213 -0
  291. package/dist/core/code/engine/core/ingestion/languages/java/simple-hooks.js +39 -0
  292. package/dist/core/code/engine/core/ingestion/languages/java/spring-aop.js +53 -0
  293. package/dist/core/code/engine/core/ingestion/languages/java/spring-bean-metadata.js +11 -0
  294. package/dist/core/code/engine/core/ingestion/languages/java/spring-conditionals.js +52 -0
  295. package/dist/core/code/engine/core/ingestion/languages/java/spring-config-bindings.js +222 -0
  296. package/dist/core/code/engine/core/ingestion/languages/java/spring-di.js +165 -0
  297. package/dist/core/code/engine/core/ingestion/languages/java.js +192 -0
  298. package/dist/core/code/engine/core/ingestion/languages/javascript/arity.js +15 -0
  299. package/dist/core/code/engine/core/ingestion/languages/javascript/captures.js +1122 -0
  300. package/dist/core/code/engine/core/ingestion/languages/javascript/import-target.js +56 -0
  301. package/dist/core/code/engine/core/ingestion/languages/javascript/index.js +109 -0
  302. package/dist/core/code/engine/core/ingestion/languages/javascript/interpret.js +45 -0
  303. package/dist/core/code/engine/core/ingestion/languages/javascript/merge-bindings.js +21 -0
  304. package/dist/core/code/engine/core/ingestion/languages/javascript/query.js +659 -0
  305. package/dist/core/code/engine/core/ingestion/languages/javascript/scope-resolver.js +78 -0
  306. package/dist/core/code/engine/core/ingestion/languages/javascript/simple-hooks.js +44 -0
  307. package/dist/core/code/engine/core/ingestion/languages/jvm/package-facts.js +46 -0
  308. package/dist/core/code/engine/core/ingestion/languages/jvm/package-siblings.js +200 -0
  309. package/dist/core/code/engine/core/ingestion/languages/kotlin/arity-metadata.js +23 -0
  310. package/dist/core/code/engine/core/ingestion/languages/kotlin/arity.js +18 -0
  311. package/dist/core/code/engine/core/ingestion/languages/kotlin/cache-stats.js +21 -0
  312. package/dist/core/code/engine/core/ingestion/languages/kotlin/capture-side-channel.js +160 -0
  313. package/dist/core/code/engine/core/ingestion/languages/kotlin/captures.js +1262 -0
  314. package/dist/core/code/engine/core/ingestion/languages/kotlin/companion-scopes.js +72 -0
  315. package/dist/core/code/engine/core/ingestion/languages/kotlin/import-decomposer.js +40 -0
  316. package/dist/core/code/engine/core/ingestion/languages/kotlin/import-target.js +135 -0
  317. package/dist/core/code/engine/core/ingestion/languages/kotlin/index.js +26 -0
  318. package/dist/core/code/engine/core/ingestion/languages/kotlin/interpret.js +75 -0
  319. package/dist/core/code/engine/core/ingestion/languages/kotlin/merge-bindings.js +28 -0
  320. package/dist/core/code/engine/core/ingestion/languages/kotlin/owners.js +134 -0
  321. package/dist/core/code/engine/core/ingestion/languages/kotlin/package-facts.js +16 -0
  322. package/dist/core/code/engine/core/ingestion/languages/kotlin/package-siblings.js +11 -0
  323. package/dist/core/code/engine/core/ingestion/languages/kotlin/query.js +243 -0
  324. package/dist/core/code/engine/core/ingestion/languages/kotlin/receiver-binding.js +103 -0
  325. package/dist/core/code/engine/core/ingestion/languages/kotlin/scope-resolver.js +207 -0
  326. package/dist/core/code/engine/core/ingestion/languages/kotlin/simple-hooks.js +42 -0
  327. package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-aop.js +68 -0
  328. package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-bean-metadata.js +11 -0
  329. package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-conditionals.js +53 -0
  330. package/dist/core/code/engine/core/ingestion/languages/kotlin/spring-di.js +304 -0
  331. package/dist/core/code/engine/core/ingestion/languages/kotlin.js +189 -0
  332. package/dist/core/code/engine/core/ingestion/languages/php/arity-metadata.js +66 -0
  333. package/dist/core/code/engine/core/ingestion/languages/php/arity.js +43 -0
  334. package/dist/core/code/engine/core/ingestion/languages/php/cache-stats.js +32 -0
  335. package/dist/core/code/engine/core/ingestion/languages/php/captures.js +1166 -0
  336. package/dist/core/code/engine/core/ingestion/languages/php/import-decomposer.js +238 -0
  337. package/dist/core/code/engine/core/ingestion/languages/php/import-target.js +213 -0
  338. package/dist/core/code/engine/core/ingestion/languages/php/index.js +85 -0
  339. package/dist/core/code/engine/core/ingestion/languages/php/interpret.js +256 -0
  340. package/dist/core/code/engine/core/ingestion/languages/php/merge-bindings.js +50 -0
  341. package/dist/core/code/engine/core/ingestion/languages/php/namespace-siblings.js +353 -0
  342. package/dist/core/code/engine/core/ingestion/languages/php/query.js +391 -0
  343. package/dist/core/code/engine/core/ingestion/languages/php/receiver-binding.js +135 -0
  344. package/dist/core/code/engine/core/ingestion/languages/php/scope-resolver.js +361 -0
  345. package/dist/core/code/engine/core/ingestion/languages/php/simple-hooks.js +116 -0
  346. package/dist/core/code/engine/core/ingestion/languages/php.js +302 -0
  347. package/dist/core/code/engine/core/ingestion/languages/python/arity-metadata.js +49 -0
  348. package/dist/core/code/engine/core/ingestion/languages/python/arity.js +41 -0
  349. package/dist/core/code/engine/core/ingestion/languages/python/cache-stats.js +34 -0
  350. package/dist/core/code/engine/core/ingestion/languages/python/captures.js +299 -0
  351. package/dist/core/code/engine/core/ingestion/languages/python/depends-references.js +68 -0
  352. package/dist/core/code/engine/core/ingestion/languages/python/import-decomposer.js +115 -0
  353. package/dist/core/code/engine/core/ingestion/languages/python/import-target.js +440 -0
  354. package/dist/core/code/engine/core/ingestion/languages/python/index-stats.js +30 -0
  355. package/dist/core/code/engine/core/ingestion/languages/python/index.js +96 -0
  356. package/dist/core/code/engine/core/ingestion/languages/python/interpret.js +430 -0
  357. package/dist/core/code/engine/core/ingestion/languages/python/merge-bindings.js +47 -0
  358. package/dist/core/code/engine/core/ingestion/languages/python/query.js +323 -0
  359. package/dist/core/code/engine/core/ingestion/languages/python/receiver-binding.js +310 -0
  360. package/dist/core/code/engine/core/ingestion/languages/python/scope-resolver.js +80 -0
  361. package/dist/core/code/engine/core/ingestion/languages/python/simple-hooks.js +53 -0
  362. package/dist/core/code/engine/core/ingestion/languages/python.js +140 -0
  363. package/dist/core/code/engine/core/ingestion/languages/ruby/arity.js +41 -0
  364. package/dist/core/code/engine/core/ingestion/languages/ruby/cache-stats.js +21 -0
  365. package/dist/core/code/engine/core/ingestion/languages/ruby/captures.js +864 -0
  366. package/dist/core/code/engine/core/ingestion/languages/ruby/import-target.js +88 -0
  367. package/dist/core/code/engine/core/ingestion/languages/ruby/index.js +29 -0
  368. package/dist/core/code/engine/core/ingestion/languages/ruby/interpret.js +115 -0
  369. package/dist/core/code/engine/core/ingestion/languages/ruby/merge-bindings.js +21 -0
  370. package/dist/core/code/engine/core/ingestion/languages/ruby/query.js +349 -0
  371. package/dist/core/code/engine/core/ingestion/languages/ruby/receiver-binding.js +70 -0
  372. package/dist/core/code/engine/core/ingestion/languages/ruby/scope-resolver.js +263 -0
  373. package/dist/core/code/engine/core/ingestion/languages/ruby/simple-hooks.js +68 -0
  374. package/dist/core/code/engine/core/ingestion/languages/ruby.js +215 -0
  375. package/dist/core/code/engine/core/ingestion/languages/rust/arity.js +16 -0
  376. package/dist/core/code/engine/core/ingestion/languages/rust/cache-stats.js +21 -0
  377. package/dist/core/code/engine/core/ingestion/languages/rust/captures.js +304 -0
  378. package/dist/core/code/engine/core/ingestion/languages/rust/import-decomposer.js +167 -0
  379. package/dist/core/code/engine/core/ingestion/languages/rust/import-target.js +108 -0
  380. package/dist/core/code/engine/core/ingestion/languages/rust/index.js +29 -0
  381. package/dist/core/code/engine/core/ingestion/languages/rust/interpret.js +201 -0
  382. package/dist/core/code/engine/core/ingestion/languages/rust/merge-bindings.js +21 -0
  383. package/dist/core/code/engine/core/ingestion/languages/rust/method-owners.js +76 -0
  384. package/dist/core/code/engine/core/ingestion/languages/rust/module-path.js +222 -0
  385. package/dist/core/code/engine/core/ingestion/languages/rust/qualified-call.js +482 -0
  386. package/dist/core/code/engine/core/ingestion/languages/rust/query.js +280 -0
  387. package/dist/core/code/engine/core/ingestion/languages/rust/range-binding.js +687 -0
  388. package/dist/core/code/engine/core/ingestion/languages/rust/receiver-binding.js +148 -0
  389. package/dist/core/code/engine/core/ingestion/languages/rust/scope-resolver.js +151 -0
  390. package/dist/core/code/engine/core/ingestion/languages/rust/simple-hooks.js +32 -0
  391. package/dist/core/code/engine/core/ingestion/languages/rust.js +183 -0
  392. package/dist/core/code/engine/core/ingestion/languages/swift/arity-metadata.js +44 -0
  393. package/dist/core/code/engine/core/ingestion/languages/swift/arity.js +45 -0
  394. package/dist/core/code/engine/core/ingestion/languages/swift/base-type.js +30 -0
  395. package/dist/core/code/engine/core/ingestion/languages/swift/cache-stats.js +32 -0
  396. package/dist/core/code/engine/core/ingestion/languages/swift/captures.js +594 -0
  397. package/dist/core/code/engine/core/ingestion/languages/swift/conditional-directive-preprocess.js +256 -0
  398. package/dist/core/code/engine/core/ingestion/languages/swift/implicit-imports.js +60 -0
  399. package/dist/core/code/engine/core/ingestion/languages/swift/import-decomposer.js +87 -0
  400. package/dist/core/code/engine/core/ingestion/languages/swift/import-target.js +84 -0
  401. package/dist/core/code/engine/core/ingestion/languages/swift/index.js +56 -0
  402. package/dist/core/code/engine/core/ingestion/languages/swift/interpret.js +93 -0
  403. package/dist/core/code/engine/core/ingestion/languages/swift/merge-bindings.js +51 -0
  404. package/dist/core/code/engine/core/ingestion/languages/swift/query.js +226 -0
  405. package/dist/core/code/engine/core/ingestion/languages/swift/receiver-binding.js +169 -0
  406. package/dist/core/code/engine/core/ingestion/languages/swift/scope-resolver.js +192 -0
  407. package/dist/core/code/engine/core/ingestion/languages/swift/sibling-type-bindings.js +68 -0
  408. package/dist/core/code/engine/core/ingestion/languages/swift/signature-bindings.js +69 -0
  409. package/dist/core/code/engine/core/ingestion/languages/swift/simple-hooks.js +65 -0
  410. package/dist/core/code/engine/core/ingestion/languages/swift/target-grouping.js +97 -0
  411. package/dist/core/code/engine/core/ingestion/languages/swift/target-siblings.js +74 -0
  412. package/dist/core/code/engine/core/ingestion/languages/swift.js +246 -0
  413. package/dist/core/code/engine/core/ingestion/languages/typescript/arity-metadata.js +106 -0
  414. package/dist/core/code/engine/core/ingestion/languages/typescript/arity.js +57 -0
  415. package/dist/core/code/engine/core/ingestion/languages/typescript/array-callback.js +58 -0
  416. package/dist/core/code/engine/core/ingestion/languages/typescript/cache-stats.js +34 -0
  417. package/dist/core/code/engine/core/ingestion/languages/typescript/captures.js +956 -0
  418. package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-export-assignment.js +535 -0
  419. package/dist/core/code/engine/core/ingestion/languages/typescript/cjs-module-exports.js +196 -0
  420. package/dist/core/code/engine/core/ingestion/languages/typescript/import-decomposer.js +374 -0
  421. package/dist/core/code/engine/core/ingestion/languages/typescript/import-target.js +65 -0
  422. package/dist/core/code/engine/core/ingestion/languages/typescript/index.js +108 -0
  423. package/dist/core/code/engine/core/ingestion/languages/typescript/interpret.js +344 -0
  424. package/dist/core/code/engine/core/ingestion/languages/typescript/merge-bindings.js +161 -0
  425. package/dist/core/code/engine/core/ingestion/languages/typescript/nuxt-auto-imports.js +325 -0
  426. package/dist/core/code/engine/core/ingestion/languages/typescript/query.js +1328 -0
  427. package/dist/core/code/engine/core/ingestion/languages/typescript/receiver-binding.js +201 -0
  428. package/dist/core/code/engine/core/ingestion/languages/typescript/scope-resolver.js +293 -0
  429. package/dist/core/code/engine/core/ingestion/languages/typescript/simple-hooks.js +139 -0
  430. package/dist/core/code/engine/core/ingestion/languages/typescript.js +455 -0
  431. package/dist/core/code/engine/core/ingestion/languages/vue/captures.js +70 -0
  432. package/dist/core/code/engine/core/ingestion/languages/vue/import-target.js +61 -0
  433. package/dist/core/code/engine/core/ingestion/languages/vue/index.js +55 -0
  434. package/dist/core/code/engine/core/ingestion/languages/vue/scope-resolver.js +295 -0
  435. package/dist/core/code/engine/core/ingestion/languages/vue.js +96 -0
  436. package/dist/core/code/engine/core/ingestion/local-symbol-pruner.js +68 -0
  437. package/dist/core/code/engine/core/ingestion/method-extractors/configs/c-cpp.js +387 -0
  438. package/dist/core/code/engine/core/ingestion/method-extractors/configs/csharp.js +290 -0
  439. package/dist/core/code/engine/core/ingestion/method-extractors/configs/dart.js +392 -0
  440. package/dist/core/code/engine/core/ingestion/method-extractors/configs/go.js +179 -0
  441. package/dist/core/code/engine/core/ingestion/method-extractors/configs/jvm.js +350 -0
  442. package/dist/core/code/engine/core/ingestion/method-extractors/configs/php.js +306 -0
  443. package/dist/core/code/engine/core/ingestion/method-extractors/configs/python.js +312 -0
  444. package/dist/core/code/engine/core/ingestion/method-extractors/configs/ruby.js +289 -0
  445. package/dist/core/code/engine/core/ingestion/method-extractors/configs/rust.js +198 -0
  446. package/dist/core/code/engine/core/ingestion/method-extractors/configs/swift.js +286 -0
  447. package/dist/core/code/engine/core/ingestion/method-extractors/configs/typescript-javascript.js +341 -0
  448. package/dist/core/code/engine/core/ingestion/method-extractors/generic.js +209 -0
  449. package/dist/core/code/engine/core/ingestion/method-types.js +3 -0
  450. package/dist/core/code/engine/core/ingestion/model/field-registry.js +41 -0
  451. package/dist/core/code/engine/core/ingestion/model/index.js +52 -0
  452. package/dist/core/code/engine/core/ingestion/model/method-registry.js +138 -0
  453. package/dist/core/code/engine/core/ingestion/model/owned-members-lookup.js +46 -0
  454. package/dist/core/code/engine/core/ingestion/model/registration-table.js +234 -0
  455. package/dist/core/code/engine/core/ingestion/model/resolve.js +183 -0
  456. package/dist/core/code/engine/core/ingestion/model/scope-resolution-indexes.js +43 -0
  457. package/dist/core/code/engine/core/ingestion/model/semantic-model.js +179 -0
  458. package/dist/core/code/engine/core/ingestion/model/symbol-table.js +216 -0
  459. package/dist/core/code/engine/core/ingestion/model/type-registry.js +84 -0
  460. package/dist/core/code/engine/core/ingestion/mro-processor.js +709 -0
  461. package/dist/core/code/engine/core/ingestion/parsing-processor.js +312 -0
  462. package/dist/core/code/engine/core/ingestion/pipeline-phases/communities.js +69 -0
  463. package/dist/core/code/engine/core/ingestion/pipeline-phases/cross-file.js +71 -0
  464. package/dist/core/code/engine/core/ingestion/pipeline-phases/di.js +338 -0
  465. package/dist/core/code/engine/core/ingestion/pipeline-phases/http-api-calls.js +312 -0
  466. package/dist/core/code/engine/core/ingestion/pipeline-phases/index.js +54 -0
  467. package/dist/core/code/engine/core/ingestion/pipeline-phases/mro.js +40 -0
  468. package/dist/core/code/engine/core/ingestion/pipeline-phases/orm.js +78 -0
  469. package/dist/core/code/engine/core/ingestion/pipeline-phases/parse-impl.js +1286 -0
  470. package/dist/core/code/engine/core/ingestion/pipeline-phases/parse.js +41 -0
  471. package/dist/core/code/engine/core/ingestion/pipeline-phases/processes.js +193 -0
  472. package/dist/core/code/engine/core/ingestion/pipeline-phases/prune-local-symbols.js +29 -0
  473. package/dist/core/code/engine/core/ingestion/pipeline-phases/registry.js +52 -0
  474. package/dist/core/code/engine/core/ingestion/pipeline-phases/routes.js +409 -0
  475. package/dist/core/code/engine/core/ingestion/pipeline-phases/rpc-edges.js +301 -0
  476. package/dist/core/code/engine/core/ingestion/pipeline-phases/runner.js +207 -0
  477. package/dist/core/code/engine/core/ingestion/pipeline-phases/scan.js +49 -0
  478. package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-aop.js +442 -0
  479. package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-auto-configuration.js +264 -0
  480. package/dist/core/code/engine/core/ingestion/pipeline-phases/spring-config.js +440 -0
  481. package/dist/core/code/engine/core/ingestion/pipeline-phases/structure.js +38 -0
  482. package/dist/core/code/engine/core/ingestion/pipeline-phases/tools.js +89 -0
  483. package/dist/core/code/engine/core/ingestion/pipeline-phases/types.js +40 -0
  484. package/dist/core/code/engine/core/ingestion/pipeline.js +152 -0
  485. package/dist/core/code/engine/core/ingestion/process-processor.js +325 -0
  486. package/dist/core/code/engine/core/ingestion/resolve-references.js +205 -0
  487. package/dist/core/code/engine/core/ingestion/route-extractors/constant-resolver.js +135 -0
  488. package/dist/core/code/engine/core/ingestion/route-extractors/django-root-discovery.js +221 -0
  489. package/dist/core/code/engine/core/ingestion/route-extractors/django.js +428 -0
  490. package/dist/core/code/engine/core/ingestion/route-extractors/expo.js +39 -0
  491. package/dist/core/code/engine/core/ingestion/route-extractors/fastapi-router-bindings.js +264 -0
  492. package/dist/core/code/engine/core/ingestion/route-extractors/laravel.js +501 -0
  493. package/dist/core/code/engine/core/ingestion/route-extractors/middleware.js +175 -0
  494. package/dist/core/code/engine/core/ingestion/route-extractors/nextjs.js +81 -0
  495. package/dist/core/code/engine/core/ingestion/route-extractors/php.js +25 -0
  496. package/dist/core/code/engine/core/ingestion/route-extractors/python-const-resolver.js +307 -0
  497. package/dist/core/code/engine/core/ingestion/route-extractors/response-shapes.js +299 -0
  498. package/dist/core/code/engine/core/ingestion/route-extractors/route-path.js +71 -0
  499. package/dist/core/code/engine/core/ingestion/route-extractors/spring-shared.js +310 -0
  500. package/dist/core/code/engine/core/ingestion/route-extractors/spring.js +441 -0
  501. package/dist/core/code/engine/core/ingestion/scope-extractor-bridge.js +60 -0
  502. package/dist/core/code/engine/core/ingestion/scope-extractor.js +1373 -0
  503. package/dist/core/code/engine/core/ingestion/scope-resolution/contract/scope-resolver.js +282 -0
  504. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +72 -0
  505. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/edges.js +194 -0
  506. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/ids.js +472 -0
  507. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/imports-to-edges.js +49 -0
  508. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/method-dispatch.js +43 -0
  509. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +274 -0
  510. package/dist/core/code/engine/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +93 -0
  511. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/callable-value-flow.js +1240 -0
  512. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/compound-receiver.js +1174 -0
  513. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/free-call-fallback.js +873 -0
  514. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/imported-return-types.js +226 -0
  515. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/mro.js +107 -0
  516. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/overload-narrowing.js +441 -0
  517. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/property-dispatch.js +122 -0
  518. package/dist/core/code/engine/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +1720 -0
  519. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/phase.js +395 -0
  520. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +208 -0
  521. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/registry.js +49 -0
  522. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/run.js +632 -0
  523. package/dist/core/code/engine/core/ingestion/scope-resolution/pipeline/validate-bindings-immutability.js +112 -0
  524. package/dist/core/code/engine/core/ingestion/scope-resolution/resolution-outcome.js +41 -0
  525. package/dist/core/code/engine/core/ingestion/scope-resolution/scope/namespace-targets.js +81 -0
  526. package/dist/core/code/engine/core/ingestion/scope-resolution/scope/walkers.js +1835 -0
  527. package/dist/core/code/engine/core/ingestion/scope-resolution/unresolved-receivers.js +242 -0
  528. package/dist/core/code/engine/core/ingestion/scope-resolution/utils/definition-id.js +22 -0
  529. package/dist/core/code/engine/core/ingestion/scope-resolution/workspace-index.js +151 -0
  530. package/dist/core/code/engine/core/ingestion/structure-processor.js +40 -0
  531. package/dist/core/code/engine/core/ingestion/tree-sitter-queries.js +2244 -0
  532. package/dist/core/code/engine/core/ingestion/ts-js-hoc-utils.js +115 -0
  533. package/dist/core/code/engine/core/ingestion/type-env.js +1136 -0
  534. package/dist/core/code/engine/core/ingestion/type-extractors/c-cpp.js +555 -0
  535. package/dist/core/code/engine/core/ingestion/type-extractors/csharp.js +570 -0
  536. package/dist/core/code/engine/core/ingestion/type-extractors/dart.js +372 -0
  537. package/dist/core/code/engine/core/ingestion/type-extractors/go.js +508 -0
  538. package/dist/core/code/engine/core/ingestion/type-extractors/jvm.js +875 -0
  539. package/dist/core/code/engine/core/ingestion/type-extractors/php.js +537 -0
  540. package/dist/core/code/engine/core/ingestion/type-extractors/python.js +477 -0
  541. package/dist/core/code/engine/core/ingestion/type-extractors/ruby.js +380 -0
  542. package/dist/core/code/engine/core/ingestion/type-extractors/rust.js +502 -0
  543. package/dist/core/code/engine/core/ingestion/type-extractors/shared.js +843 -0
  544. package/dist/core/code/engine/core/ingestion/type-extractors/swift.js +490 -0
  545. package/dist/core/code/engine/core/ingestion/type-extractors/types.js +2 -0
  546. package/dist/core/code/engine/core/ingestion/type-extractors/typescript.js +690 -0
  547. package/dist/core/code/engine/core/ingestion/utils/ast-helpers.js +1693 -0
  548. package/dist/core/code/engine/core/ingestion/utils/call-analysis.js +779 -0
  549. package/dist/core/code/engine/core/ingestion/utils/callable-flow-captures.js +932 -0
  550. package/dist/core/code/engine/core/ingestion/utils/callable-labels.js +49 -0
  551. package/dist/core/code/engine/core/ingestion/utils/deferred-resolution-profile.js +151 -0
  552. package/dist/core/code/engine/core/ingestion/utils/effective-ram.js +67 -0
  553. package/dist/core/code/engine/core/ingestion/utils/env.js +60 -0
  554. package/dist/core/code/engine/core/ingestion/utils/event-loop.js +9 -0
  555. package/dist/core/code/engine/core/ingestion/utils/graph-sort.js +103 -0
  556. package/dist/core/code/engine/core/ingestion/utils/heap-probe.js +45 -0
  557. package/dist/core/code/engine/core/ingestion/utils/heritage-marker.js +47 -0
  558. package/dist/core/code/engine/core/ingestion/utils/line-base.js +24 -0
  559. package/dist/core/code/engine/core/ingestion/utils/max-file-size.js +59 -0
  560. package/dist/core/code/engine/core/ingestion/utils/method-props.js +198 -0
  561. package/dist/core/code/engine/core/ingestion/utils/qualified-name.js +73 -0
  562. package/dist/core/code/engine/core/ingestion/utils/receiver-chain-captures.js +60 -0
  563. package/dist/core/code/engine/core/ingestion/utils/receiver-chain-codec.js +188 -0
  564. package/dist/core/code/engine/core/ingestion/utils/scope-tree-walk.js +36 -0
  565. package/dist/core/code/engine/core/ingestion/utils/symbol-labels.js +48 -0
  566. package/dist/core/code/engine/core/ingestion/utils/template-arguments.js +187 -0
  567. package/dist/core/code/engine/core/ingestion/utils/type-parameters.js +209 -0
  568. package/dist/core/code/engine/core/ingestion/utils/verbose.js +6 -0
  569. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/c-cpp.js +133 -0
  570. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/csharp.js +66 -0
  571. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/dart.js +111 -0
  572. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/go.js +153 -0
  573. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/jvm.js +145 -0
  574. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/php.js +61 -0
  575. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/python.js +104 -0
  576. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/ruby.js +55 -0
  577. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/rust.js +79 -0
  578. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/swift.js +91 -0
  579. package/dist/core/code/engine/core/ingestion/variable-extractors/configs/typescript-javascript.js +86 -0
  580. package/dist/core/code/engine/core/ingestion/variable-extractors/generic.js +111 -0
  581. package/dist/core/code/engine/core/ingestion/variable-types.js +3 -0
  582. package/dist/core/code/engine/core/ingestion/vue-sfc-extractor.js +544 -0
  583. package/dist/core/code/engine/core/ingestion/workers/callable-id.js +122 -0
  584. package/dist/core/code/engine/core/ingestion/workers/clone-safety.js +470 -0
  585. package/dist/core/code/engine/core/ingestion/workers/parse-worker.js +2456 -0
  586. package/dist/core/code/engine/core/ingestion/workers/post-result.js +90 -0
  587. package/dist/core/code/engine/core/ingestion/workers/quarantine.js +41 -0
  588. package/dist/core/code/engine/core/ingestion/workers/result-merge.js +59 -0
  589. package/dist/core/code/engine/core/ingestion/workers/worker-pool.js +1725 -0
  590. package/dist/core/code/engine/core/ingestion/workspace-config.js +139 -0
  591. package/dist/core/code/engine/core/lbug/conn-lock.js +72 -0
  592. package/dist/core/code/engine/core/lbug/csv-generator.js +710 -0
  593. package/dist/core/code/engine/core/lbug/cypher-escape.js +24 -0
  594. package/dist/core/code/engine/core/lbug/extension-load-error.js +339 -0
  595. package/dist/core/code/engine/core/lbug/extension-loader.js +261 -0
  596. package/dist/core/code/engine/core/lbug/graph-emit-sink.js +584 -0
  597. package/dist/core/code/engine/core/lbug/lbug-adapter.js +2703 -0
  598. package/dist/core/code/engine/core/lbug/lbug-config.js +1029 -0
  599. package/dist/core/code/engine/core/lbug/native-check.js +435 -0
  600. package/dist/core/code/engine/core/lbug/pool-adapter.js +973 -0
  601. package/dist/core/code/engine/core/lbug/query-params.js +25 -0
  602. package/dist/core/code/engine/core/lbug/query-result-utils.js +31 -0
  603. package/dist/core/code/engine/core/lbug/rel-pair-routing.js +373 -0
  604. package/dist/core/code/engine/core/lbug/schema.js +771 -0
  605. package/dist/core/code/engine/core/lbug/shutdown-helpers.js +40 -0
  606. package/dist/core/code/engine/core/lbug/sidecar-recovery.js +716 -0
  607. package/dist/core/code/engine/core/lbug/stdio-capture.js +49 -0
  608. package/dist/core/code/engine/core/lbug/sync-csv-writer.js +115 -0
  609. package/dist/core/code/engine/core/lbug/wal-checkpoint-driver.js +215 -0
  610. package/dist/core/code/engine/core/lbug/wal-driver-state.js +28 -0
  611. package/dist/core/code/engine/core/logger.js +339 -0
  612. package/dist/core/code/engine/core/platform/capabilities.js +91 -0
  613. package/dist/core/code/engine/core/run-analyze.js +1098 -0
  614. package/dist/core/code/engine/core/tree-sitter/parser-loader.js +281 -0
  615. package/dist/core/code/engine/core/tree-sitter/safe-parse.js +258 -0
  616. package/dist/core/code/engine/core/tree-sitter/vendored-grammars.js +64 -0
  617. package/dist/core/code/engine/lib/utils.js +121 -0
  618. package/dist/core/code/engine/mcp/core/lbug-adapter.js +27 -0
  619. package/dist/core/code/engine/mcp/local/aop-metadata.js +230 -0
  620. package/dist/core/code/engine/mcp/local/bean-metadata.js +49 -0
  621. package/dist/core/code/engine/mcp/local/limits.js +15 -0
  622. package/dist/core/code/engine/mcp/local/line-display.js +6 -0
  623. package/dist/core/code/engine/mcp/local/local-backend.js +4142 -0
  624. package/dist/core/code/engine/storage/branch-index.js +72 -0
  625. package/dist/core/code/engine/storage/file-hash.js +95 -0
  626. package/dist/core/code/engine/storage/fs-atomic.js +34 -0
  627. package/dist/core/code/engine/storage/git.js +555 -0
  628. package/dist/core/code/engine/storage/index-lock.js +664 -0
  629. package/dist/core/code/engine/storage/parse-cache.js +650 -0
  630. package/dist/core/code/engine/storage/parsedfile-store.js +620 -0
  631. package/dist/core/code/engine/storage/repo-manager.js +1061 -0
  632. package/dist/core/code/engine/storage/scope-index-store.js +247 -0
  633. package/dist/core/code/engine/types/pipeline.js +2 -0
  634. package/dist/core/code/scripts/install-duckdb-extension.mjs +125 -0
  635. package/dist/core/code/scripts/resolve-analyze-cmd.cjs +346 -0
  636. package/dist/core/code/shared/graph/types.js +8 -0
  637. package/dist/core/code/shared/index.js +105 -0
  638. package/dist/core/code/shared/integrations/circuit-breaker.js +242 -0
  639. package/dist/core/code/shared/integrations/resilient-fetch.js +224 -0
  640. package/dist/core/code/shared/integrations/retry.js +70 -0
  641. package/dist/core/code/shared/integrations/understand-quickly.js +145 -0
  642. package/dist/core/code/shared/language-detection.js +162 -0
  643. package/dist/core/code/shared/languages.js +27 -0
  644. package/dist/core/code/shared/lbug/schema-constants.js +98 -0
  645. package/dist/core/code/shared/mro-strategy.js +2 -0
  646. package/dist/core/code/shared/pipeline.js +5 -0
  647. package/dist/core/code/shared/scope-resolution/callable-flow-site.js +11 -0
  648. package/dist/core/code/shared/scope-resolution/def-index.js +53 -0
  649. package/dist/core/code/shared/scope-resolution/evidence-weights.js +87 -0
  650. package/dist/core/code/shared/scope-resolution/finalize-algorithm.js +807 -0
  651. package/dist/core/code/shared/scope-resolution/language-classification.js +46 -0
  652. package/dist/core/code/shared/scope-resolution/method-dispatch-index.js +100 -0
  653. package/dist/core/code/shared/scope-resolution/module-scope-index.js +59 -0
  654. package/dist/core/code/shared/scope-resolution/origin-priority.js +23 -0
  655. package/dist/core/code/shared/scope-resolution/parsed-file.js +54 -0
  656. package/dist/core/code/shared/scope-resolution/position-index.js +136 -0
  657. package/dist/core/code/shared/scope-resolution/qualified-name-index.js +77 -0
  658. package/dist/core/code/shared/scope-resolution/reference-site.js +24 -0
  659. package/dist/core/code/shared/scope-resolution/registries/class-registry.js +32 -0
  660. package/dist/core/code/shared/scope-resolution/registries/context.js +52 -0
  661. package/dist/core/code/shared/scope-resolution/registries/evidence.js +152 -0
  662. package/dist/core/code/shared/scope-resolution/registries/field-registry.js +33 -0
  663. package/dist/core/code/shared/scope-resolution/registries/lookup-core.js +392 -0
  664. package/dist/core/code/shared/scope-resolution/registries/lookup-qualified.js +58 -0
  665. package/dist/core/code/shared/scope-resolution/registries/macro-registry.js +34 -0
  666. package/dist/core/code/shared/scope-resolution/registries/method-registry.js +34 -0
  667. package/dist/core/code/shared/scope-resolution/registries/tie-breaks.js +63 -0
  668. package/dist/core/code/shared/scope-resolution/resolve-type-ref.js +128 -0
  669. package/dist/core/code/shared/scope-resolution/scope-id.js +49 -0
  670. package/dist/core/code/shared/scope-resolution/scope-tree.js +225 -0
  671. package/dist/core/code/shared/scope-resolution/symbol-definition.js +12 -0
  672. package/dist/core/code/shared/scope-resolution/types.js +25 -0
  673. package/dist/core/code/shared/test-helpers.js +17 -0
  674. package/dist/core/code/vendor/leiden/index.cjs +355 -0
  675. package/dist/core/code/vendor/leiden/utils.cjs +419 -0
  676. package/dist/core/features/scanner.d.ts +2 -0
  677. package/dist/core/features/scanner.d.ts.map +1 -1
  678. package/dist/core/features/scanner.js +27 -6
  679. package/dist/core/features/scanner.js.map +1 -1
  680. package/dist/core/timeline/cli.d.ts.map +1 -1
  681. package/dist/core/timeline/cli.js +13 -6
  682. package/dist/core/timeline/cli.js.map +1 -1
  683. package/dist/core/timeline/debris.d.ts +20 -0
  684. package/dist/core/timeline/debris.d.ts.map +1 -0
  685. package/dist/core/timeline/debris.js +124 -0
  686. package/dist/core/timeline/debris.js.map +1 -0
  687. package/dist/core/timeline/hook-runner.d.ts.map +1 -1
  688. package/dist/core/timeline/hook-runner.js +3 -1
  689. package/dist/core/timeline/hook-runner.js.map +1 -1
  690. package/dist/core/timeline/hooks.d.ts.map +1 -1
  691. package/dist/core/timeline/hooks.js +13 -5
  692. package/dist/core/timeline/hooks.js.map +1 -1
  693. package/dist/core/timeline/installer.d.ts +2 -0
  694. package/dist/core/timeline/installer.d.ts.map +1 -1
  695. package/dist/core/timeline/installer.js +12 -0
  696. package/dist/core/timeline/installer.js.map +1 -1
  697. package/dist/core/timeline/project-root.d.ts +21 -0
  698. package/dist/core/timeline/project-root.d.ts.map +1 -0
  699. package/dist/core/timeline/project-root.js +83 -0
  700. package/dist/core/timeline/project-root.js.map +1 -0
  701. package/package.json +1 -1
@@ -0,0 +1,2171 @@
1
+ "use strict";
2
+ /**
3
+ * Reproducible analyzer identity stamped into RepoMeta after a successful run.
4
+ *
5
+ * Schema v4 uses length-prefixed canonical frames. Build files and runtime
6
+ * artifacts contribute SHA-256 payload digests, so a validated stat inventory
7
+ * can safely reuse those expensive per-file digests across short-lived CLI and
8
+ * server-worker processes. The cache is only an optimization: malformed,
9
+ * mismatched, or missing entries are rehashed, and every identity calculation
10
+ * performs a final return-boundary inventory before returning.
11
+ *
12
+ * `invokedArtifact` remains in the receipt for diagnostics, but is deliberately
13
+ * excluded from semantic freshness. The CLI and the server analyze worker are
14
+ * different entry files inside the same build tree; alternating between them
15
+ * must not make an otherwise identical index stale.
16
+ */
17
+ var __importDefault = (this && this.__importDefault) || function (mod) {
18
+ return (mod && mod.__esModule) ? mod : { "default": mod };
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ exports._hasNodeModulesSegmentForTests = exports._isInsideForTests = exports._hashAnalyzerIdentityFramesForTests = exports.ANALYZER_RUNNER_IDENTITY_SCHEMA_VERSION = void 0;
22
+ exports.normalizeAnalyzerRootPath = normalizeAnalyzerRootPath;
23
+ exports._clearAnalyzerIdentityProcessCacheForTests = _clearAnalyzerIdentityProcessCacheForTests;
24
+ exports.normalizeAnalyzerRunnerIdentityForComparison = normalizeAnalyzerRunnerIdentityForComparison;
25
+ exports.resolveAnalyzerRunnerIdentity = resolveAnalyzerRunnerIdentity;
26
+ exports.analyzerRunnerIdentitiesEqual = analyzerRunnerIdentitiesEqual;
27
+ exports.captureAnalyzerIdentityBeforeLoad = captureAnalyzerIdentityBeforeLoad;
28
+ exports.finalizeAnalyzerRunnerIdentity = finalizeAnalyzerRunnerIdentity;
29
+ const node_fs_1 = require("node:fs");
30
+ const node_crypto_1 = require("node:crypto");
31
+ const node_child_process_1 = require("node:child_process");
32
+ const node_util_1 = require("node:util");
33
+ const node_os_1 = __importDefault(require("node:os"));
34
+ const node_path_1 = __importDefault(require("node:path"));
35
+ const node_url_1 = require("node:url");
36
+ exports.ANALYZER_RUNNER_IDENTITY_SCHEMA_VERSION = 4;
37
+ const BUILD_CANONICALIZATION = 'cgraph-analyzer-build-v2';
38
+ const DEPENDENCY_RUNTIME_CANONICALIZATION = 'cgraph-analyzer-dependency-runtime-v4';
39
+ const IDENTITY_CACHE_SCHEMA_VERSION = 6;
40
+ const MAX_CACHE_ENTRIES = 100_000;
41
+ const MAX_CACHE_FILE_BYTES = 64 * 1024 * 1024;
42
+ const HASH_BUFFER_BYTES = 256 * 1024;
43
+ const SHA256_PATTERN = /^sha256:[a-f0-9]{64}$/;
44
+ const DEFAULT_TRAVERSAL_LIMITS = {
45
+ buildEntries: 100_000,
46
+ buildDepth: 128,
47
+ buildBytes: 512 * 1024 * 1024,
48
+ runtimePackages: 10_000,
49
+ runtimeEdges: 100_000,
50
+ runtimeEntries: 250_000,
51
+ runtimeDepth: 64,
52
+ runtimePayloads: 100_000,
53
+ runtimeBytes: 2 * 1024 * 1024 * 1024,
54
+ resolutionAncestors: 256,
55
+ };
56
+ function toBuffer(value) {
57
+ return Buffer.isBuffer(value) ? value : Buffer.from(value);
58
+ }
59
+ /** Update a hash with one unambiguous, length-prefixed canonical record. */
60
+ function updateCanonicalFrame(hash, fields) {
61
+ const fieldCount = Buffer.allocUnsafe(4);
62
+ fieldCount.writeUInt32BE(fields.length);
63
+ hash.update(fieldCount);
64
+ for (const field of fields) {
65
+ const bytes = toBuffer(field);
66
+ const length = Buffer.allocUnsafe(8);
67
+ length.writeBigUInt64BE(BigInt(bytes.length));
68
+ hash.update(length);
69
+ hash.update(bytes);
70
+ }
71
+ }
72
+ function hashCanonicalFrames(frames) {
73
+ const hash = (0, node_crypto_1.createHash)('sha256');
74
+ for (const frame of frames)
75
+ updateCanonicalFrame(hash, frame);
76
+ return `sha256:${hash.digest('hex')}`;
77
+ }
78
+ /** @internal Regression seam for adversarial canonical-framing tests. */
79
+ exports._hashAnalyzerIdentityFramesForTests = hashCanonicalFrames;
80
+ function sha256(payload) {
81
+ return `sha256:${(0, node_crypto_1.createHash)('sha256').update(payload).digest('hex')}`;
82
+ }
83
+ function digestBytes(digest) {
84
+ if (!SHA256_PATTERN.test(digest))
85
+ throw new Error(`Invalid SHA-256 digest: ${digest}`);
86
+ return Buffer.from(digest.slice('sha256:'.length), 'hex');
87
+ }
88
+ function statState(stat) {
89
+ const bigintStat = stat;
90
+ return {
91
+ dev: String(bigintStat.dev),
92
+ ino: String(bigintStat.ino),
93
+ mode: String(bigintStat.mode),
94
+ nlink: String(bigintStat.nlink),
95
+ size: String(bigintStat.size),
96
+ mtimeNs: String(bigintStat.mtimeNs),
97
+ ctimeNs: String(bigintStat.ctimeNs),
98
+ };
99
+ }
100
+ function resolveTraversalLimits(options) {
101
+ const overrides = options.traversalLimits ?? {};
102
+ const resolved = { ...DEFAULT_TRAVERSAL_LIMITS };
103
+ for (const key of Object.keys(DEFAULT_TRAVERSAL_LIMITS)) {
104
+ const value = overrides[key];
105
+ if (value === undefined)
106
+ continue;
107
+ if (!Number.isSafeInteger(value) || value < 1) {
108
+ throw new Error(`Analyzer identity traversal limit ${key} must be a positive safe integer`);
109
+ }
110
+ resolved[key] = Math.min(value, DEFAULT_TRAVERSAL_LIMITS[key]);
111
+ }
112
+ return resolved;
113
+ }
114
+ function stateSize(state, label) {
115
+ const value = BigInt(state.size);
116
+ if (value < 0n || value > BigInt(Number.MAX_SAFE_INTEGER)) {
117
+ throw new Error(`Analyzer identity input has an unsupported size: ${label}`);
118
+ }
119
+ return Number(value);
120
+ }
121
+ function detectLibcVariant() {
122
+ if (process.platform !== 'linux')
123
+ return 'not-applicable';
124
+ try {
125
+ const report = process.report?.getReport();
126
+ const glibc = report?.header?.glibcVersionRuntime;
127
+ if (typeof glibc === 'string' && glibc.length > 0)
128
+ return `glibc:${glibc}`;
129
+ if (Array.isArray(report?.sharedObjects)) {
130
+ const musl = report.sharedObjects.find((entry) => typeof entry === 'string' && /(?:^|[/\\])(?:ld-)?musl[^/\\]*\.so/i.test(entry));
131
+ if (musl)
132
+ return `musl:${node_path_1.default.basename(musl)}`;
133
+ }
134
+ }
135
+ catch {
136
+ // Runtime reporting is optional on some embedded Node builds. Unknown is
137
+ // still a distinct, fail-closed variant rather than being conflated with
138
+ // glibc or a known musl loader.
139
+ }
140
+ return 'linux-libc:unknown';
141
+ }
142
+ const LIBC_VARIANT = detectLibcVariant();
143
+ function resolveRuntimeVariant() {
144
+ return {
145
+ // Normalized like build.rootPath (#2668): executablePath is a compared
146
+ // identity field (only invokedArtifact is stripped in the comparison), and
147
+ // process.execPath carries the same Windows drive-letter case ambiguity —
148
+ // so leaving it un-normalized would reintroduce the false-stale via runtime.
149
+ executablePath: normalizeAnalyzerRootPath(resolveExistingPath(process.execPath), process.platform),
150
+ nodeVersion: process.version,
151
+ platform: process.platform,
152
+ architecture: process.arch,
153
+ endianness: node_os_1.default.endianness(),
154
+ modulesAbi: process.versions.modules ?? 'unknown',
155
+ napiAbi: process.versions.napi ?? 'unknown',
156
+ libc: LIBC_VARIANT,
157
+ };
158
+ }
159
+ function snapshotReadableFile(candidate) {
160
+ const link = (0, node_fs_1.lstatSync)(candidate, { bigint: true });
161
+ const target = (0, node_fs_1.statSync)(candidate, { bigint: true });
162
+ if (!target.isFile())
163
+ throw new Error(`Analyzer identity input is not a file: ${candidate}`);
164
+ return {
165
+ link: statState(link),
166
+ target: statState(target),
167
+ ...(link.isSymbolicLink() ? { symlinkTarget: (0, node_fs_1.readlinkSync)(candidate) } : {}),
168
+ };
169
+ }
170
+ /**
171
+ * Snapshot a symbolic link without resolving it. Unlike
172
+ * {@link snapshotReadableFile} this never stats the target, so it is total over
173
+ * linked directories, dangling links, and links to device nodes — the inputs
174
+ * that make the readable-file snapshot throw.
175
+ */
176
+ function snapshotSymlinkArtifact(candidate) {
177
+ const link = (0, node_fs_1.lstatSync)(candidate, { bigint: true });
178
+ if (!link.isSymbolicLink()) {
179
+ throw new Error(`Analyzer identity input is not a symbolic link: ${candidate}`);
180
+ }
181
+ return { link: statState(link), symlinkTarget: (0, node_fs_1.readlinkSync)(candidate) };
182
+ }
183
+ function snapshotRuntimeArtifact(artifact) {
184
+ return artifact.kind === 'unfollowed-symlink'
185
+ ? snapshotSymlinkArtifact(artifact.absolutePath)
186
+ : snapshotReadableFile(artifact.absolutePath);
187
+ }
188
+ function snapshotDirectory(candidate) {
189
+ const stat = (0, node_fs_1.lstatSync)(candidate, { bigint: true });
190
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
191
+ throw new Error(`Analyzer identity input is not a directory: ${candidate}`);
192
+ }
193
+ return statState(stat);
194
+ }
195
+ function readDirectory(candidate, options) {
196
+ options.onCacheMissWork?.({ kind: 'directory-walk', path: candidate });
197
+ return (0, node_fs_1.readdirSync)(candidate, { withFileTypes: true });
198
+ }
199
+ function directoryEntriesDigestFrom(entriesInput) {
200
+ const entries = entriesInput
201
+ .map((entry) => ({
202
+ name: entry.name,
203
+ kind: entry.isDirectory()
204
+ ? 'directory'
205
+ : entry.isFile()
206
+ ? 'file'
207
+ : entry.isSymbolicLink()
208
+ ? 'symlink'
209
+ : 'other',
210
+ }))
211
+ .sort((a, b) => compareBytes(a.name, b.name));
212
+ const hash = (0, node_crypto_1.createHash)('sha256');
213
+ updateCanonicalFrame(hash, ['directory-entries-v1']);
214
+ for (const entry of entries)
215
+ updateCanonicalFrame(hash, [entry.name, entry.kind]);
216
+ return `sha256:${hash.digest('hex')}`;
217
+ }
218
+ function directoryEntriesDigest(candidate) {
219
+ return directoryEntriesDigestFrom((0, node_fs_1.readdirSync)(candidate, { withFileTypes: true }));
220
+ }
221
+ function snapshotDirectoryInventory(candidate) {
222
+ for (let attempt = 0; attempt < 2; attempt += 1) {
223
+ const before = snapshotDirectory(candidate);
224
+ const entriesDigest = directoryEntriesDigest(candidate);
225
+ const after = snapshotDirectory(candidate);
226
+ if ((0, node_util_1.isDeepStrictEqual)(before, after))
227
+ return { state: after, entriesDigest };
228
+ }
229
+ throw new Error(`Analyzer identity directory changed while it was read: ${candidate}`);
230
+ }
231
+ function readStableFile(candidate) {
232
+ for (let attempt = 0; attempt < 2; attempt += 1) {
233
+ const before = snapshotReadableFile(candidate);
234
+ const bytes = (0, node_fs_1.readFileSync)(candidate);
235
+ const after = snapshotReadableFile(candidate);
236
+ if ((0, node_util_1.isDeepStrictEqual)(before, after))
237
+ return { bytes, state: after };
238
+ }
239
+ throw new Error(`Analyzer identity input changed while it was being read: ${candidate}`);
240
+ }
241
+ function readStableFileWithinBudget(candidate, budget, maxBytes) {
242
+ const before = snapshotReadableFile(candidate);
243
+ const bytes = stateSize(before.target, candidate);
244
+ if (budget.bytes + bytes > maxBytes) {
245
+ throw new Error(`Analyzer runtime scan exceeded ${maxBytes} bytes: ${candidate}`);
246
+ }
247
+ const stable = readStableFile(candidate);
248
+ budget.bytes += stable.bytes.length;
249
+ return stable;
250
+ }
251
+ /** Hash a stable file through a fixed-size buffer instead of materializing it. */
252
+ function hashStableFile(candidate) {
253
+ for (let attempt = 0; attempt < 2; attempt += 1) {
254
+ const before = snapshotReadableFile(candidate);
255
+ const expectedBytes = stateSize(before.target, candidate);
256
+ let descriptor = null;
257
+ try {
258
+ descriptor = (0, node_fs_1.openSync)(candidate, node_fs_1.constants.O_RDONLY);
259
+ const openedBefore = statState((0, node_fs_1.fstatSync)(descriptor, { bigint: true }));
260
+ if (!(0, node_util_1.isDeepStrictEqual)(openedBefore, before.target))
261
+ continue;
262
+ const hash = (0, node_crypto_1.createHash)('sha256');
263
+ const buffer = Buffer.allocUnsafe(HASH_BUFFER_BYTES);
264
+ let bytes = 0;
265
+ while (true) {
266
+ const read = (0, node_fs_1.readSync)(descriptor, buffer, 0, buffer.length, null);
267
+ if (read === 0)
268
+ break;
269
+ hash.update(buffer.subarray(0, read));
270
+ bytes += read;
271
+ if (bytes > expectedBytes)
272
+ break;
273
+ }
274
+ const openedAfter = statState((0, node_fs_1.fstatSync)(descriptor, { bigint: true }));
275
+ (0, node_fs_1.closeSync)(descriptor);
276
+ descriptor = null;
277
+ const after = snapshotReadableFile(candidate);
278
+ if (bytes === expectedBytes &&
279
+ (0, node_util_1.isDeepStrictEqual)(openedBefore, openedAfter) &&
280
+ (0, node_util_1.isDeepStrictEqual)(before, after)) {
281
+ return { digest: `sha256:${hash.digest('hex')}`, state: after, bytes };
282
+ }
283
+ }
284
+ finally {
285
+ if (descriptor !== null)
286
+ (0, node_fs_1.closeSync)(descriptor);
287
+ }
288
+ }
289
+ throw new Error(`Analyzer identity input changed while it was being hashed: ${candidate}`);
290
+ }
291
+ function resolveExistingPath(candidate) {
292
+ return node_fs_1.realpathSync.native(node_path_1.default.resolve(candidate));
293
+ }
294
+ /**
295
+ * Case-stabilize a path's Windows drive letter so two processes that observed
296
+ * the same directory under different drive-letter casing (`c:\…` vs `C:\…`)
297
+ * produce byte-identical analyzer-identity path fields (#2668).
298
+ *
299
+ * `realpathSync.native` canonicalizes 8.3 short names and symlinks but does not
300
+ * guarantee the drive-letter case it returns — it can preserve whatever casing
301
+ * the caller's path carried, and `import.meta.url` casing depends on how each
302
+ * entry process (CLI shim vs `npx`/npm wrapper vs server worker) was launched.
303
+ * When `analyze` stamps `build.rootPath` under one casing and `status`
304
+ * recomputes it under another, `analyzerRunnerIdentitiesEqual` deep-compares
305
+ * unequal and `status` reports a freshly-analyzed, untouched repo as stale.
306
+ * Uppercasing the drive letter (drive letters are case-insensitive; uppercase
307
+ * is the conventional form) collapses that variance. POSIX paths are returned
308
+ * unchanged. `platform` is explicit so the transform is unit-testable off
309
+ * Windows.
310
+ *
311
+ * The optional `\\?\` extended-length prefix is preserved and the drive letter
312
+ * after it is still normalized; UNC paths (`\\server\share`, `\\?\UNC\...`)
313
+ * have no drive letter and are left untouched.
314
+ *
315
+ * That optional group is defensive, not a case `realpathSync.native` produces:
316
+ * libuv's `fs__realpath_handle` strips `\\?\` (and rewrites `\\?\UNC\` back to
317
+ * `\\`) before returning, so the prefix can only reach here from caller-supplied
318
+ * input, which `path.resolve` preserves (#2667).
319
+ *
320
+ * Preserving it is load-bearing. The roots this normalizes are not just compared —
321
+ * they are READ FROM: `resolveBuildRoot` joins `package.json` onto `packageRoot`,
322
+ * `collectBuildEntries` walks `buildRoot`, and the lockfile lookup walks
323
+ * `packageRoot`'s ancestors. Node does not re-add `\\?\` for over-MAX_PATH paths,
324
+ * so stripping here would break analyzer-identity resolution on a deep checkout
325
+ * exactly as it would at any other filesystem boundary. (These fields are also
326
+ * compared between an `analyze` and a later `status` run, so a shape change would
327
+ * additionally risk the #2668 false-stale class — but the filesystem reads are the
328
+ * reason that matters.)
329
+ *
330
+ * Registry-style path COMPARISON is a different domain, never opens what it
331
+ * canonicalizes, and does normalize the prefix away: see
332
+ * `stripWindowsLongPathPrefix` in `src/lib/utils.ts` and its use in
333
+ * `canonicalizePath`.
334
+ */
335
+ function normalizeAnalyzerRootPath(p, platform) {
336
+ if (platform !== 'win32')
337
+ return p;
338
+ return p.replace(/^(\\\\\?\\)?([a-z]):/, (_match, prefix, drive) => `${prefix ?? ''}${drive.toUpperCase()}:`);
339
+ }
340
+ function isFile(candidate) {
341
+ try {
342
+ return (0, node_fs_1.statSync)(candidate).isFile();
343
+ }
344
+ catch {
345
+ return false;
346
+ }
347
+ }
348
+ function readManifest(manifestPath, options, budget, limits) {
349
+ options.onCacheMissWork?.({ kind: 'manifest-read', path: manifestPath });
350
+ const { bytes, state } = readStableFileWithinBudget(manifestPath, budget, limits.runtimeBytes);
351
+ return { bytes, state, manifest: JSON.parse(bytes.toString('utf8')) };
352
+ }
353
+ function manifestLabel(manifest) {
354
+ const name = typeof manifest.name === 'string' ? manifest.name : '<unnamed>';
355
+ const version = typeof manifest.version === 'string' ? manifest.version : '<unversioned>';
356
+ return `${name}@${version}`;
357
+ }
358
+ /**
359
+ * Whether `candidate` is `parent` itself or lives beneath it.
360
+ *
361
+ * The absolute-result rejection is load-bearing on Windows: `path.relative`
362
+ * cannot express a relative path between two different drives, so it returns the
363
+ * absolute target instead — `path.win32.relative('C:\\parent', 'D:\\other')` is
364
+ * `'D:\\other'`. That string does not start with `..`, so the `..` checks alone
365
+ * would report an unrelated drive as *inside* the parent. This mirrors the
366
+ * containment guards elsewhere in the repo (`server/api.ts`,
367
+ * `server/git-clone.ts`, `group/extractors/fs-utils.ts`), which all pair the
368
+ * `..` check with `path.isAbsolute`.
369
+ *
370
+ * `pathApi` is injectable so the win32 semantics are unit-testable from a POSIX
371
+ * runner; production callers always use the platform-bound `path`.
372
+ */
373
+ function isInside(parent, candidate, pathApi = node_path_1.default) {
374
+ const relative = pathApi.relative(parent, candidate);
375
+ if (pathApi.isAbsolute(relative))
376
+ return false;
377
+ return relative === '' || (!relative.startsWith(`..${pathApi.sep}`) && relative !== '..');
378
+ }
379
+ /** Test seam for {@link isInside} (see `_hashAnalyzerIdentityFramesForTests`). */
380
+ exports._isInsideForTests = isInside;
381
+ function resolveBuildRoot(analyzerModulePath) {
382
+ let cursor = node_path_1.default.dirname(analyzerModulePath);
383
+ while (true) {
384
+ const base = node_path_1.default.basename(cursor);
385
+ if (base === 'src' || base === 'dist') {
386
+ const packageRoot = node_path_1.default.dirname(cursor);
387
+ const packageJson = node_path_1.default.join(packageRoot, 'package.json');
388
+ if ((0, node_fs_1.lstatSync)(packageJson).isFile()) {
389
+ // Normalize the drive-letter case at this single upstream source so
390
+ // every derived identity path field — build.rootPath, identityCacheKey,
391
+ // and (via collectDependencyInputs) dependencyRuntime.manifestPath /
392
+ // lockfilePath — inherits a case-stable root and analyze-stamp equals
393
+ // status-recompute regardless of launch-path casing (#2668).
394
+ // Migration: a Windows index stamped before this fix carries the old,
395
+ // un-normalized casing, so the first post-upgrade `status` sees one
396
+ // spurious "stale" flip — self-healing on the next `analyze`, which
397
+ // re-stamps the normalized (idempotent) form.
398
+ return {
399
+ packageRoot: normalizeAnalyzerRootPath(packageRoot, process.platform),
400
+ buildRoot: normalizeAnalyzerRootPath(cursor, process.platform),
401
+ kind: base === 'src' ? 'source' : 'distribution',
402
+ };
403
+ }
404
+ }
405
+ const parent = node_path_1.default.dirname(cursor);
406
+ if (parent === cursor)
407
+ break;
408
+ cursor = parent;
409
+ }
410
+ throw new Error(`Cannot resolve GitNexus package root from analyzer module: ${analyzerModulePath}`);
411
+ }
412
+ function compareBytes(a, b) {
413
+ return Buffer.compare(Buffer.from(a), Buffer.from(b));
414
+ }
415
+ function collectBuildEntries(buildRoot, options, limits) {
416
+ const entries = [];
417
+ const pending = [
418
+ { absoluteDir: buildRoot, depth: 0 },
419
+ ];
420
+ let scannedEntries = 0;
421
+ let scannedBytes = 0;
422
+ while (pending.length > 0) {
423
+ const next = pending.pop();
424
+ if (!next)
425
+ break;
426
+ const { absoluteDir, depth } = next;
427
+ const directoryEntries = readDirectory(absoluteDir, options);
428
+ scannedEntries += directoryEntries.length;
429
+ if (scannedEntries > limits.buildEntries) {
430
+ throw new Error(`Analyzer build scan exceeded ${limits.buildEntries} entries: ${buildRoot}`);
431
+ }
432
+ for (const entry of directoryEntries) {
433
+ const absolutePath = node_path_1.default.join(absoluteDir, entry.name);
434
+ const relativePath = node_path_1.default.relative(buildRoot, absolutePath).split(node_path_1.default.sep).join('/');
435
+ const link = (0, node_fs_1.lstatSync)(absolutePath, { bigint: true });
436
+ if (link.isDirectory()) {
437
+ entries.push({
438
+ absolutePath,
439
+ relativePath,
440
+ kind: 'directory',
441
+ state: statState(link),
442
+ });
443
+ if (depth >= limits.buildDepth) {
444
+ throw new Error(`Analyzer build scan exceeded depth ${limits.buildDepth}: ${absolutePath}`);
445
+ }
446
+ pending.push({ absoluteDir: absolutePath, depth: depth + 1 });
447
+ }
448
+ else if (link.isFile()) {
449
+ const state = statState(link);
450
+ scannedBytes += stateSize(state, absolutePath);
451
+ if (scannedBytes > limits.buildBytes) {
452
+ throw new Error(`Analyzer build scan exceeded ${limits.buildBytes} bytes: ${buildRoot}`);
453
+ }
454
+ entries.push({ absolutePath, relativePath, kind: 'file', state });
455
+ }
456
+ else if (link.isSymbolicLink()) {
457
+ entries.push({ absolutePath, relativePath, kind: 'symlink', state: statState(link) });
458
+ }
459
+ else {
460
+ throw new Error(`Unsupported analyzer build entry: ${absolutePath}`);
461
+ }
462
+ }
463
+ }
464
+ entries.sort((a, b) => compareBytes(a.relativePath, b.relativePath));
465
+ return entries;
466
+ }
467
+ function buildSnapshot(entries) {
468
+ return entries.map(({ relativePath, kind, state }) => ({ relativePath, kind, state }));
469
+ }
470
+ function buildCacheKey(entry) {
471
+ return JSON.stringify([entry.kind, entry.relativePath]);
472
+ }
473
+ function hashBuildTree(buildRoot, cache, options, limits) {
474
+ const entries = collectBuildEntries(buildRoot, options, limits);
475
+ const cachedEntries = new Map((cache?.buildEntries ?? []).map((entry) => [buildCacheKey(entry), entry]));
476
+ const nextEntries = [];
477
+ const hash = (0, node_crypto_1.createHash)('sha256');
478
+ updateCanonicalFrame(hash, ['domain', BUILD_CANONICALIZATION]);
479
+ for (const entry of entries) {
480
+ const cached = cachedEntries.get(buildCacheKey(entry));
481
+ let digest;
482
+ if (entry.kind !== 'directory' &&
483
+ cached?.digest &&
484
+ SHA256_PATTERN.test(cached.digest) &&
485
+ (0, node_util_1.isDeepStrictEqual)(cached.state, entry.state)) {
486
+ digest = cached.digest;
487
+ }
488
+ else if (entry.kind === 'file') {
489
+ const stable = hashStableFile(entry.absolutePath);
490
+ entry.state = stable.state.link;
491
+ digest = stable.digest;
492
+ options.onHashedInput?.({
493
+ kind: 'build',
494
+ path: entry.absolutePath,
495
+ bytes: stable.bytes,
496
+ });
497
+ }
498
+ else if (entry.kind === 'symlink') {
499
+ // Imported files and directories are resolved through symlinks by Node.
500
+ // Hashing only link text would let bytes outside buildRoot change without
501
+ // changing the receipt. Reject them instead of inventing an incomplete
502
+ // recursive trust boundary (target containment, cycles, and TOCTOU).
503
+ throw new Error(`Analyzer build symbolic links are not supported; materialize the build tree: ${entry.absolutePath}`);
504
+ }
505
+ updateCanonicalFrame(hash, [
506
+ 'build-entry',
507
+ entry.relativePath,
508
+ entry.kind,
509
+ digest ? digestBytes(digest) : Buffer.alloc(0),
510
+ ]);
511
+ nextEntries.push({
512
+ relativePath: entry.relativePath,
513
+ kind: entry.kind,
514
+ state: entry.state,
515
+ ...(digest ? { digest } : {}),
516
+ });
517
+ }
518
+ const directoryGuards = [
519
+ { relativePath: '', state: snapshotDirectory(buildRoot), entriesDigest: '' },
520
+ ...nextEntries
521
+ .filter((entry) => entry.kind === 'directory')
522
+ .map((entry) => ({
523
+ relativePath: entry.relativePath,
524
+ state: entry.state,
525
+ entriesDigest: '',
526
+ })),
527
+ ].map((guard) => {
528
+ const absolutePath = guard.relativePath
529
+ ? node_path_1.default.join(buildRoot, ...guard.relativePath.split('/'))
530
+ : buildRoot;
531
+ const inventory = snapshotDirectoryInventory(absolutePath);
532
+ return {
533
+ relativePath: guard.relativePath,
534
+ state: inventory.state,
535
+ entriesDigest: inventory.entriesDigest,
536
+ };
537
+ });
538
+ return {
539
+ digest: `sha256:${hash.digest('hex')}`,
540
+ entries: nextEntries,
541
+ snapshot: buildSnapshot(entries),
542
+ rootState: directoryGuards[0].state,
543
+ directoryGuards,
544
+ };
545
+ }
546
+ function recordDirectoryGuard(guards, candidate) {
547
+ if (guards.has(candidate))
548
+ return true;
549
+ try {
550
+ guards.set(candidate, snapshotDirectoryInventory(candidate));
551
+ return true;
552
+ }
553
+ catch {
554
+ return false;
555
+ }
556
+ }
557
+ function snapshotDependencyPathGuard(candidate) {
558
+ try {
559
+ const stat = (0, node_fs_1.lstatSync)(candidate, { bigint: true });
560
+ const type = stat.isDirectory()
561
+ ? 'directory'
562
+ : stat.isFile()
563
+ ? 'file'
564
+ : stat.isSymbolicLink()
565
+ ? 'symlink'
566
+ : 'other';
567
+ return {
568
+ type,
569
+ state: statState(stat),
570
+ ...(type === 'symlink' ? { symlinkTarget: (0, node_fs_1.readlinkSync)(candidate) } : {}),
571
+ };
572
+ }
573
+ catch {
574
+ return null;
575
+ }
576
+ }
577
+ function recordDependencyPathGuard(guards, candidate) {
578
+ if (guards.has(candidate))
579
+ return guards.get(candidate) ?? null;
580
+ const result = snapshotDependencyPathGuard(candidate);
581
+ guards.set(candidate, result);
582
+ return result;
583
+ }
584
+ function findNearestPackageLock(packageRoot, pathGuards, limits) {
585
+ let cursor = packageRoot;
586
+ let ancestors = 0;
587
+ while (true) {
588
+ ancestors += 1;
589
+ if (ancestors > limits.resolutionAncestors) {
590
+ throw new Error(`Analyzer package-lock lookup exceeded ${limits.resolutionAncestors} ancestors: ${packageRoot}`);
591
+ }
592
+ const candidate = node_path_1.default.join(cursor, 'package-lock.json');
593
+ recordDependencyPathGuard(pathGuards, candidate);
594
+ // Preserve the link path so ReadableFileState guards both the link and its
595
+ // resolved target. Realpathing here would miss a later retarget while the
596
+ // old target remained unchanged.
597
+ try {
598
+ const link = (0, node_fs_1.lstatSync)(candidate);
599
+ if (link.isFile())
600
+ return node_path_1.default.resolve(candidate);
601
+ if (link.isSymbolicLink()) {
602
+ try {
603
+ const target = (0, node_fs_1.statSync)(candidate);
604
+ if (!target.isFile()) {
605
+ throw new Error(`Analyzer package lock symbolic link does not resolve to a file: ${candidate}`);
606
+ }
607
+ }
608
+ catch {
609
+ throw new Error(`Analyzer package lock symbolic link does not resolve to a file: ${candidate}`);
610
+ }
611
+ return node_path_1.default.resolve(candidate);
612
+ }
613
+ throw new Error(`Analyzer package lock is not a regular file: ${candidate}`);
614
+ }
615
+ catch (error) {
616
+ if (error.code !== 'ENOENT')
617
+ throw error;
618
+ }
619
+ const parent = node_path_1.default.dirname(cursor);
620
+ if (parent === cursor)
621
+ return null;
622
+ cursor = parent;
623
+ }
624
+ }
625
+ function runtimePackageLocator(packageRoot, runtimeRoot) {
626
+ if (runtimeRoot === packageRoot)
627
+ return 'root:.';
628
+ const relative = node_path_1.default.relative(packageRoot, runtimeRoot).split(node_path_1.default.sep).join('/');
629
+ return `relative:${relative}`;
630
+ }
631
+ /** Protocols that name a checkout-local package instead of a registry tarball. */
632
+ const LOCAL_LINK_PROTOCOL_PATTERN = /^(?:file|link|workspace|portal):/;
633
+ /** npm's bare local-path shorthands: `./x`, `../x`, `/x`, `~/x`, `C:\x`. */
634
+ const LOCAL_LINK_PATH_PATTERN = /^(?:\.\.?[/\\]|~[/\\]|[/\\]|[A-Za-z]:)/;
635
+ function isLocallyLinkedSpecifier(specifier) {
636
+ if (typeof specifier !== 'string')
637
+ return false;
638
+ const value = specifier.trim();
639
+ return LOCAL_LINK_PROTOCOL_PATTERN.test(value) || LOCAL_LINK_PATH_PATTERN.test(value);
640
+ }
641
+ /**
642
+ * Whether a REALPATH'd package root lives inside some installed dependency
643
+ * tree. Used as the resolved-location half of "is this dependency a checkout
644
+ * this repository owns?" (see {@link undeclaredLocalDevDependencyNames}).
645
+ *
646
+ * The input must already be realpath'd: `resolveDependencyPackageRoot` returns
647
+ * `realpathSync.native`, so a package reached through a link out of
648
+ * `node_modules` reports its checkout location and a package that merely lives
649
+ * in `node_modules` reports a path that still carries the segment.
650
+ *
651
+ * `pathApi` is injectable so the Windows separator handling is unit-testable
652
+ * from a POSIX runner, exactly as {@link isInside} does. The separator sets
653
+ * differ deliberately: `\` is a legal filename character on POSIX, so only
654
+ * win32 may treat it as a boundary.
655
+ */
656
+ function hasNodeModulesSegment(candidate, pathApi = node_path_1.default) {
657
+ const segments = pathApi.sep === '\\' ? candidate.split(/[\\/]+/) : candidate.split('/');
658
+ return segments.includes('node_modules');
659
+ }
660
+ /** Test seam for {@link hasNodeModulesSegment} (see {@link _isInsideForTests}). */
661
+ exports._hasNodeModulesSegmentForTests = hasNodeModulesSegment;
662
+ /**
663
+ * How many dev dependencies may be admitted by RESOLVED LOCATION alone before
664
+ * the whole resolved-location channel is treated as untrustworthy and disabled.
665
+ *
666
+ * "Realpath carries no `node_modules` segment" is a proxy for "checkout-local",
667
+ * and a layout that materializes packages outside `node_modules` — pnpm with a
668
+ * relocated `virtual-store-dir`, a custom linker — makes every dev dependency
669
+ * pass it. Folding an entire dev tree into the receipt is not a graceful
670
+ * degradation: `runtimePackages`/`runtimeEntries`/`runtimeBytes` THROW, so a
671
+ * mis-fired proxy on a legitimate install would abort analyze outright.
672
+ *
673
+ * The bound is therefore on ADMISSIONS, and overflow admits NONE of them rather
674
+ * than an arbitrary prefix. A prefix would not bound the failure — the abort
675
+ * comes from the transitive payload of whichever trees get folded in — and it
676
+ * would make the receipt depend on an arbitrary slice of a sorted name list.
677
+ * Dropping the channel wholesale falls back to the specifier-only receipt,
678
+ * which is the behaviour that ships today and is known not to abort, and leaves
679
+ * the declared-intent half in {@link dependencyNames} untouched.
680
+ *
681
+ * Four is measured, not guessed. Monorepo and workspace links are declared
682
+ * (`file:`/`link:`/`workspace:`) and travel the uncapped declared half, so this
683
+ * channel only ever carries UNDECLARED `npm link <pkg>` — a manual, per-package
684
+ * developer action, in practice one or two packages. A mis-fire admits the
685
+ * entire dev-only set instead: 13 names in this repository's own install, tens
686
+ * in a typical application. The cap sits an order of magnitude below the
687
+ * mis-fire population and comfortably above realistic link counts.
688
+ */
689
+ const MAX_UNDECLARED_LOCAL_DEV_DEPENDENCIES = 4;
690
+ /**
691
+ * Dependency names whose resolved packages can contribute analyzer semantics.
692
+ *
693
+ * The three runtime sections are enumerated wholesale. `devDependencies` are
694
+ * deliberately not: a registry dev tool (vitest, eslint, typescript) is never
695
+ * loaded by the analyzer, and folding the dev tree into the receipt would churn
696
+ * `dependencyRuntime.digest` — and force a full re-analysis — on every unrelated
697
+ * devDependency bump.
698
+ *
699
+ * Locally linked dev dependencies are the exception. A `file:`/`link:`/
700
+ * `workspace:` sibling is part of this checkout and ships code the analyzer
701
+ * imports at runtime: GitNexus links `cgraph-shared`, whose schema constants
702
+ * feed `RELATION_SCHEMA`/`NODE_SCHEMA_QUERIES`. In `kind: 'source'` runs that
703
+ * sibling sits outside `buildRoot`, so leaving it out let a semantic change
704
+ * there alter analyzer behaviour while moving neither `build.digest` nor
705
+ * `dependencyRuntime.digest` — DDL-affecting edits were still caught by the
706
+ * schema fingerprint, semantics-only edits by nothing.
707
+ *
708
+ * An unresolvable link (a published install, where the sibling checkout does not
709
+ * exist) still contributes its `<missing>` edge, so the linked package appearing
710
+ * or disappearing remains a receipt change rather than a silent one. That is why
711
+ * the specifier check cannot be replaced by resolution: resolution returns
712
+ * `null` for an absent linked checkout exactly as it does for an uninstalled
713
+ * registry dev tool, and the two must not be conflated.
714
+ *
715
+ * This function is the DECLARED-INTENT half and is enumerated for every package
716
+ * in the dependency BFS, so it must stay a pure function of the manifest. The
717
+ * RESOLVED-LOCATION half — `npm link <pkg>`, which leaves the specifier a
718
+ * registry range — lives in {@link undeclaredLocalDevDependencyNames} and is
719
+ * applied to the root package only.
720
+ */
721
+ function dependencyNames(manifest) {
722
+ const names = new Set();
723
+ for (const section of [
724
+ manifest.dependencies,
725
+ manifest.optionalDependencies,
726
+ manifest.peerDependencies,
727
+ ]) {
728
+ if (!section || typeof section !== 'object')
729
+ continue;
730
+ for (const name of Object.keys(section))
731
+ names.add(name);
732
+ }
733
+ const development = manifest.devDependencies;
734
+ if (development && typeof development === 'object') {
735
+ for (const [name, specifier] of Object.entries(development)) {
736
+ if (isLocallyLinkedSpecifier(specifier))
737
+ names.add(name);
738
+ }
739
+ }
740
+ return [...names].sort(compareBytes);
741
+ }
742
+ function resolveDependencyPackageRoot(fromRoot, packageName, pathGuards, limits) {
743
+ let cursor = fromRoot;
744
+ const segments = packageName.split('/');
745
+ let ancestors = 0;
746
+ while (true) {
747
+ ancestors += 1;
748
+ if (ancestors > limits.resolutionAncestors) {
749
+ throw new Error(`Analyzer dependency resolution exceeded ${limits.resolutionAncestors} ancestors: ${packageName}`);
750
+ }
751
+ const nodeModulesRoot = node_path_1.default.join(cursor, 'node_modules');
752
+ recordDependencyPathGuard(pathGuards, nodeModulesRoot);
753
+ let candidateParent = nodeModulesRoot;
754
+ for (const segment of segments) {
755
+ candidateParent = node_path_1.default.join(candidateParent, segment);
756
+ // Guard every lexical hop, not only the final manifest. Package
757
+ // managers commonly expose packages through symlinks; a retarget can
758
+ // otherwise preserve a hard-linked manifest's stat identity while
759
+ // changing the runtime payload tree selected by Node.
760
+ recordDependencyPathGuard(pathGuards, candidateParent);
761
+ }
762
+ const manifestPath = node_path_1.default.join(candidateParent, 'package.json');
763
+ recordDependencyPathGuard(pathGuards, manifestPath);
764
+ if (isFile(manifestPath))
765
+ return resolveExistingPath(node_path_1.default.dirname(manifestPath));
766
+ const parent = node_path_1.default.dirname(cursor);
767
+ if (parent === cursor)
768
+ return null;
769
+ cursor = parent;
770
+ }
771
+ }
772
+ /**
773
+ * Dev dependencies that are locally linked by INSTALLED LOCATION rather than by
774
+ * declared specifier — the `npm link <pkg>` shape, where the manifest still
775
+ * carries a registry range while `node_modules/<pkg>` is a symlink into a
776
+ * working checkout. {@link isLocallyLinkedSpecifier} is blind to those, yet the
777
+ * linked code is exactly as load-bearing for analyzer semantics as a declared
778
+ * `file:` sibling, so a semantic-only edit there would move neither digest.
779
+ *
780
+ * The resolver already knows: {@link resolveDependencyPackageRoot} returns a
781
+ * realpath, so a linked package reports a root outside every `node_modules`
782
+ * tree while an ordinary installed package cannot.
783
+ *
784
+ * Two properties are load-bearing and must not be relaxed:
785
+ *
786
+ * 1. ROOT ONLY. {@link dependencyNames} runs for every package in the BFS, and
787
+ * published tarballs keep their `devDependencies`, so probing dev-only names
788
+ * everywhere costs 1998 resolutions rather than the ~13 this manifest
789
+ * declares — measured on this install, with 0 true positives. Persisted
790
+ * `dependencyPathGuards` grow 2220 → 11049, and every guard is re-probed on
791
+ * each warm validation, so the cost is recurring and on the `status` path.
792
+ * Root-only costs 13 resolutions and ~29 guards.
793
+ * 2. An unresolvable name is NEVER admitted. `null` here means "uninstalled
794
+ * registry dev tool" far more often than "broken link", and admitting it
795
+ * would emit a `<missing>` edge for every dev tool absent from a published
796
+ * install. Declared links keep that edge through {@link dependencyNames};
797
+ * undeclared ones have no declaration to honour.
798
+ *
799
+ * The admission count is bounded by {@link MAX_UNDECLARED_LOCAL_DEV_DEPENDENCIES}.
800
+ */
801
+ function undeclaredLocalDevDependencyNames(rootPackage, pathGuards, limits) {
802
+ const development = rootPackage.manifest.devDependencies;
803
+ if (!development || typeof development !== 'object')
804
+ return [];
805
+ const admitted = [];
806
+ for (const [name, specifier] of Object.entries(development)) {
807
+ // Already carried by the declared half; resolving again would only add
808
+ // guards. Its `<missing>` edge is that half's responsibility.
809
+ if (isLocallyLinkedSpecifier(specifier))
810
+ continue;
811
+ const resolved = resolveDependencyPackageRoot(rootPackage.root, name, pathGuards, limits);
812
+ if (resolved !== null && !hasNodeModulesSegment(resolved))
813
+ admitted.push(name);
814
+ }
815
+ return admitted.length <= MAX_UNDECLARED_LOCAL_DEV_DEPENDENCIES ? admitted : [];
816
+ }
817
+ function collectRuntimePackages(packageRoot, directoryGuards, pathGuards, options, budget, limits) {
818
+ const rootManifestPath = node_path_1.default.join(packageRoot, 'package.json');
819
+ recordDirectoryGuard(directoryGuards, packageRoot);
820
+ const rootRead = readManifest(rootManifestPath, options, budget, limits);
821
+ const rootPackage = {
822
+ root: packageRoot,
823
+ locator: runtimePackageLocator(packageRoot, packageRoot),
824
+ manifestPath: rootManifestPath,
825
+ manifestBytes: rootRead.bytes,
826
+ manifestState: rootRead.state,
827
+ manifest: rootRead.manifest,
828
+ label: manifestLabel(rootRead.manifest),
829
+ };
830
+ const queue = [rootPackage];
831
+ const packages = new Map([[packageRoot, rootPackage]]);
832
+ const edges = [];
833
+ budget.packages = 1;
834
+ for (let index = 0; index < queue.length; index += 1) {
835
+ const parent = queue[index];
836
+ // The declared half is enumerated for every package; the resolved-location
837
+ // half is scoped to the root package, where the 1998-resolution /
838
+ // 8829-extra-guard blow-up documented on
839
+ // `undeclaredLocalDevDependencyNames` cannot occur. Dropping this scope is
840
+ // the expensive regression, so it is pinned by a guard-count test.
841
+ const dependencies = parent.root === packageRoot
842
+ ? [
843
+ ...new Set([
844
+ ...dependencyNames(parent.manifest),
845
+ ...undeclaredLocalDevDependencyNames(parent, pathGuards, limits),
846
+ ]),
847
+ ].sort(compareBytes)
848
+ : dependencyNames(parent.manifest);
849
+ for (const dependencyName of dependencies) {
850
+ budget.edges += 1;
851
+ if (budget.edges > limits.runtimeEdges) {
852
+ throw new Error(`Analyzer dependency graph exceeded ${limits.runtimeEdges} edges: ${packageRoot}`);
853
+ }
854
+ const childRoot = resolveDependencyPackageRoot(parent.root, dependencyName, pathGuards, limits);
855
+ if (!childRoot) {
856
+ edges.push({
857
+ parentLocator: parent.locator,
858
+ parentLabel: parent.label,
859
+ dependencyName,
860
+ childLocator: '<missing>',
861
+ childLabel: '<missing>',
862
+ });
863
+ continue;
864
+ }
865
+ let child = packages.get(childRoot);
866
+ if (!child) {
867
+ budget.packages += 1;
868
+ if (budget.packages > limits.runtimePackages) {
869
+ throw new Error(`Analyzer dependency graph exceeded ${limits.runtimePackages} packages: ${packageRoot}`);
870
+ }
871
+ const manifestPath = node_path_1.default.join(childRoot, 'package.json');
872
+ recordDirectoryGuard(directoryGuards, childRoot);
873
+ const read = readManifest(manifestPath, options, budget, limits);
874
+ child = {
875
+ root: childRoot,
876
+ locator: runtimePackageLocator(packageRoot, childRoot),
877
+ manifestPath,
878
+ manifestBytes: read.bytes,
879
+ manifestState: read.state,
880
+ manifest: read.manifest,
881
+ label: manifestLabel(read.manifest),
882
+ };
883
+ packages.set(childRoot, child);
884
+ queue.push(child);
885
+ }
886
+ edges.push({
887
+ parentLocator: parent.locator,
888
+ parentLabel: parent.label,
889
+ dependencyName,
890
+ childLocator: child.locator,
891
+ childLabel: child.label,
892
+ });
893
+ }
894
+ }
895
+ return { packages: [...packages.values()], edges };
896
+ }
897
+ const PRUNED_RUNTIME_DIRECTORIES = new Set(['node_modules', '.git', '.hg', '.svn']);
898
+ function shouldHashRuntimePayload(relativePath) {
899
+ const lower = relativePath.toLowerCase();
900
+ // Each package manifest is already hashed as a separately framed dependency
901
+ // input. Avoid counting/reading it twice while still hashing every other
902
+ // package payload: JavaScript modules, JSON data, native/Wasm binaries,
903
+ // extensionless exports, and files loaded explicitly through fs APIs. File
904
+ // and directory names are not authoritative platform-selection metadata:
905
+ // loaders may select or read a payload whose name mentions another target.
906
+ return lower !== 'package.json';
907
+ }
908
+ function collectArtifacts(root, canonicalPrefix, directoryGuards, options, budget, limits) {
909
+ const artifacts = [];
910
+ const pending = [{ absoluteDir: root, depth: 0 }];
911
+ while (pending.length > 0) {
912
+ const next = pending.pop();
913
+ if (!next)
914
+ break;
915
+ const { absoluteDir, depth } = next;
916
+ if (!recordDirectoryGuard(directoryGuards, absoluteDir)) {
917
+ throw new Error(`Analyzer runtime payload directory is unavailable: ${absoluteDir}`);
918
+ }
919
+ const entries = readDirectory(absoluteDir, options);
920
+ budget.entries += entries.length;
921
+ if (budget.entries > limits.runtimeEntries) {
922
+ throw new Error(`Analyzer runtime payload scan exceeded ${limits.runtimeEntries} entries: ${root}`);
923
+ }
924
+ for (const entry of entries) {
925
+ const absolutePath = node_path_1.default.join(absoluteDir, entry.name);
926
+ const relativePath = node_path_1.default.relative(root, absolutePath).split(node_path_1.default.sep).join('/');
927
+ const stat = (0, node_fs_1.lstatSync)(absolutePath);
928
+ // Nested dependencies are collected from their manifests as separate
929
+ // packages. Only prune those separately traversed trees and VCS
930
+ // metadata; generic cache/model directories can contain loadable code,
931
+ // native addons, Wasm modules, or data consumed by the runtime.
932
+ //
933
+ // Pruning is decided by NAME alone. These four names never carry analyzer
934
+ // payload in any form: `node_modules` is traversed separately through
935
+ // `resolveDependencyPackageRoot` (which follows links and guards each
936
+ // hop), and a `.git`/`.hg`/`.svn` entry is VCS metadata whether it is a
937
+ // directory, a symbolic link into a shared store, or — inside a submodule
938
+ // or linked worktree checkout — a regular file holding a gitdir pointer.
939
+ // Hashing that pointer would make analyzer identity depend on where the
940
+ // checkout happens to live, which is a false-stale source, not a
941
+ // semantic input.
942
+ if (PRUNED_RUNTIME_DIRECTORIES.has(entry.name))
943
+ continue;
944
+ if (stat.isDirectory()) {
945
+ if (depth >= limits.runtimeDepth) {
946
+ throw new Error(`Analyzer runtime payload scan exceeded depth ${limits.runtimeDepth}: ${absolutePath}`);
947
+ }
948
+ pending.push({ absoluteDir: absolutePath, depth: depth + 1 });
949
+ }
950
+ else if (stat.isSymbolicLink() && !isFile(absolutePath)) {
951
+ // A symbolic link that does not resolve to a regular file must never
952
+ // reach the payload branch below: `snapshotReadableFile` stats the
953
+ // target, and a directory (or a dangling link) makes it throw, aborting
954
+ // the entire analyze. Workspace-linked checkouts made this reachable
955
+ // for every name, not just the pruned four — `dist -> build`, a
956
+ // vendored-grammar link, anything a sibling checkout ships.
957
+ //
958
+ // Such links are RECORDED by their link text rather than followed.
959
+ // Following them would (a) recurse without cycle protection — this
960
+ // traversal has none, so `self -> .` would ride the depth limit, which
961
+ // THROWS, trading one hard abort for another; (b) re-scan trees already
962
+ // reached by their real path, inflating the entry/byte budgets that
963
+ // also throw; and (c) need a whole containment/TOCTOU trust boundary
964
+ // for targets outside the package. Recording the text is cycle-free,
965
+ // costs one `readlink`, and still moves the receipt when the link is
966
+ // retargeted. The trade-off is that a link's target contributes no
967
+ // content of its own: when it points outside the package, only the
968
+ // link text is covered. Links that DO resolve to a regular file keep
969
+ // their content digest below, unchanged.
970
+ if (shouldHashRuntimePayload(relativePath)) {
971
+ budget.artifacts += 1;
972
+ if (budget.artifacts > limits.runtimePayloads) {
973
+ throw new Error(`Analyzer runtime payload scan exceeded ${limits.runtimePayloads} payloads: ${root}`);
974
+ }
975
+ artifacts.push({
976
+ absolutePath,
977
+ canonicalPath: `${canonicalPrefix}/${relativePath}`,
978
+ kind: 'unfollowed-symlink',
979
+ });
980
+ }
981
+ }
982
+ else if ((stat.isFile() || stat.isSymbolicLink()) &&
983
+ shouldHashRuntimePayload(relativePath)) {
984
+ const readableState = snapshotReadableFile(absolutePath);
985
+ const payloadBytes = stateSize(readableState.target, absolutePath);
986
+ budget.artifacts += 1;
987
+ if (budget.artifacts > limits.runtimePayloads) {
988
+ throw new Error(`Analyzer runtime payload scan exceeded ${limits.runtimePayloads} payloads: ${root}`);
989
+ }
990
+ if (budget.bytes + payloadBytes > limits.runtimeBytes) {
991
+ throw new Error(`Analyzer runtime scan exceeded ${limits.runtimeBytes} bytes: ${absolutePath}`);
992
+ }
993
+ budget.bytes += payloadBytes;
994
+ artifacts.push({
995
+ absolutePath,
996
+ canonicalPath: `${canonicalPrefix}/${relativePath}`,
997
+ kind: stat.isSymbolicLink() ? 'symlink' : 'file',
998
+ });
999
+ }
1000
+ else if (!stat.isFile() && !stat.isSymbolicLink()) {
1001
+ throw new Error(`Unsupported analyzer runtime payload entry: ${absolutePath}`);
1002
+ }
1003
+ }
1004
+ }
1005
+ return artifacts;
1006
+ }
1007
+ function collectVendoredGrammarInputs(packageRoot, directoryGuards, options, budget, limits) {
1008
+ const vendorRoot = node_path_1.default.join(packageRoot, 'vendor');
1009
+ if (!(0, node_fs_1.existsSync)(vendorRoot) || !(0, node_fs_1.lstatSync)(vendorRoot).isDirectory()) {
1010
+ recordDirectoryGuard(directoryGuards, packageRoot);
1011
+ return { manifests: [], artifacts: [] };
1012
+ }
1013
+ const manifests = [];
1014
+ const artifacts = [];
1015
+ if (!recordDirectoryGuard(directoryGuards, vendorRoot)) {
1016
+ throw new Error(`Analyzer vendored runtime directory is unavailable: ${vendorRoot}`);
1017
+ }
1018
+ const vendorEntries = readDirectory(vendorRoot, options);
1019
+ budget.entries += vendorEntries.length;
1020
+ if (budget.entries > limits.runtimeEntries) {
1021
+ throw new Error(`Analyzer runtime payload scan exceeded ${limits.runtimeEntries} entries: ${vendorRoot}`);
1022
+ }
1023
+ for (const entry of vendorEntries) {
1024
+ if (!entry.isDirectory() || !entry.name.startsWith('tree-sitter-'))
1025
+ continue;
1026
+ const grammarRoot = node_path_1.default.join(vendorRoot, entry.name);
1027
+ const manifestPath = node_path_1.default.join(grammarRoot, 'package.json');
1028
+ if (isFile(manifestPath)) {
1029
+ options.onCacheMissWork?.({ kind: 'manifest-read', path: manifestPath });
1030
+ const read = readStableFileWithinBudget(manifestPath, budget, limits.runtimeBytes);
1031
+ manifests.push({
1032
+ canonicalPath: `vendor:${entry.name}/package.json`,
1033
+ absolutePath: manifestPath,
1034
+ bytes: read.bytes,
1035
+ state: read.state,
1036
+ });
1037
+ }
1038
+ artifacts.push(...collectArtifacts(grammarRoot, `vendor:${entry.name}`, directoryGuards, options, budget, limits));
1039
+ }
1040
+ return { manifests, artifacts };
1041
+ }
1042
+ function collectDependencyInputs(packageRoot, options, limits) {
1043
+ const directoryGuards = new Map();
1044
+ const pathGuards = new Map();
1045
+ const artifactScanBudget = {
1046
+ entries: 0,
1047
+ artifacts: 0,
1048
+ bytes: 0,
1049
+ packages: 0,
1050
+ edges: 0,
1051
+ };
1052
+ const manifestPath = resolveExistingPath(node_path_1.default.join(packageRoot, 'package.json'));
1053
+ const lockfilePath = findNearestPackageLock(packageRoot, pathGuards, limits);
1054
+ if (lockfilePath)
1055
+ options.onCacheMissWork?.({ kind: 'manifest-read', path: lockfilePath });
1056
+ const lockfile = lockfilePath
1057
+ ? readStableFileWithinBudget(lockfilePath, artifactScanBudget, limits.runtimeBytes)
1058
+ : null;
1059
+ const { packages, edges } = collectRuntimePackages(packageRoot, directoryGuards, pathGuards, options, artifactScanBudget, limits);
1060
+ const vendored = collectVendoredGrammarInputs(packageRoot, directoryGuards, options, artifactScanBudget, limits);
1061
+ // The root build and vendored grammars are covered separately. Every
1062
+ // resolved external runtime package is scanned, regardless of package name:
1063
+ // native loaders are not constrained to a permanent allowlist (for example,
1064
+ // Transformers resolves Sharp's @img platform packages).
1065
+ const artifacts = packages
1066
+ .slice(1)
1067
+ .flatMap((runtimePackage) => collectArtifacts(runtimePackage.root, `package:${runtimePackage.locator}`, directoryGuards, options, artifactScanBudget, limits));
1068
+ artifacts.push(...vendored.artifacts);
1069
+ artifacts.sort((a, b) => compareBytes(`${a.canonicalPath}\u0000${a.absolutePath}`, `${b.canonicalPath}\u0000${b.absolutePath}`));
1070
+ return {
1071
+ manifestPath,
1072
+ lockfilePath,
1073
+ lockfileBytes: lockfile?.bytes ?? null,
1074
+ lockfileState: lockfile?.state ?? null,
1075
+ packages,
1076
+ edges,
1077
+ vendoredManifests: vendored.manifests,
1078
+ artifacts,
1079
+ directoryGuards,
1080
+ pathGuards,
1081
+ };
1082
+ }
1083
+ function dependencySnapshot(inputs) {
1084
+ const edgeKey = (edge) => JSON.stringify([
1085
+ edge.parentLocator,
1086
+ edge.parentLabel,
1087
+ edge.dependencyName,
1088
+ edge.childLocator,
1089
+ edge.childLabel,
1090
+ ]);
1091
+ return {
1092
+ manifestPath: inputs.manifestPath,
1093
+ lockfilePath: inputs.lockfilePath,
1094
+ lockfileDigest: inputs.lockfileBytes ? sha256(inputs.lockfileBytes) : null,
1095
+ lockfileState: inputs.lockfileState,
1096
+ packages: inputs.packages
1097
+ .map((runtimePackage) => ({
1098
+ root: runtimePackage.root,
1099
+ locator: runtimePackage.locator,
1100
+ manifestPath: runtimePackage.manifestPath,
1101
+ manifestDigest: sha256(runtimePackage.manifestBytes),
1102
+ manifestState: runtimePackage.manifestState,
1103
+ label: runtimePackage.label,
1104
+ }))
1105
+ .sort((a, b) => compareBytes(a.locator, b.locator)),
1106
+ edges: inputs.edges.map(edgeKey).sort(compareBytes),
1107
+ vendoredManifests: inputs.vendoredManifests
1108
+ .map((entry) => ({
1109
+ canonicalPath: entry.canonicalPath,
1110
+ absolutePath: entry.absolutePath,
1111
+ digest: sha256(entry.bytes),
1112
+ state: entry.state,
1113
+ }))
1114
+ .sort((a, b) => compareBytes(a.canonicalPath, b.canonicalPath)),
1115
+ artifacts: inputs.artifacts.map((artifact) => ({
1116
+ absolutePath: artifact.absolutePath,
1117
+ canonicalPath: artifact.canonicalPath,
1118
+ kind: artifact.kind,
1119
+ state: snapshotRuntimeArtifact(artifact),
1120
+ })),
1121
+ directories: [...inputs.directoryGuards.entries()]
1122
+ .map(([absolutePath, guard]) => ({ absolutePath, ...guard }))
1123
+ .sort((a, b) => compareBytes(a.absolutePath, b.absolutePath)),
1124
+ paths: [...inputs.pathGuards.entries()]
1125
+ .map(([absolutePath, result]) => ({ absolutePath, result }))
1126
+ .sort((a, b) => compareBytes(a.absolutePath, b.absolutePath)),
1127
+ };
1128
+ }
1129
+ function artifactCacheKey(artifact) {
1130
+ return JSON.stringify([artifact.kind, artifact.canonicalPath, artifact.absolutePath]);
1131
+ }
1132
+ function hashRuntimeArtifact(artifact, cache, options) {
1133
+ if (artifact.kind === 'unfollowed-symlink') {
1134
+ // The link text is the entire payload, so there is no file read for a
1135
+ // cached digest to amortize: recompute it and stay independent of the
1136
+ // cache's freshness. The distinct frame label keeps a link recording from
1137
+ // ever colliding with a content digest.
1138
+ const state = snapshotSymlinkArtifact(artifact.absolutePath);
1139
+ return {
1140
+ ...artifact,
1141
+ state,
1142
+ digest: hashCanonicalFrames([
1143
+ ['runtime-payload-link-v1', artifact.kind, state.symlinkTarget],
1144
+ ]),
1145
+ };
1146
+ }
1147
+ const before = snapshotReadableFile(artifact.absolutePath);
1148
+ if (cache &&
1149
+ cache.kind !== 'unfollowed-symlink' &&
1150
+ SHA256_PATTERN.test(cache.digest) &&
1151
+ (0, node_util_1.isDeepStrictEqual)(cache.state, before)) {
1152
+ return { ...artifact, state: before, digest: cache.digest };
1153
+ }
1154
+ const stable = hashStableFile(artifact.absolutePath);
1155
+ const digest = hashCanonicalFrames([
1156
+ [
1157
+ 'runtime-payload-content-v1',
1158
+ artifact.kind,
1159
+ stable.state.symlinkTarget ?? '',
1160
+ digestBytes(stable.digest),
1161
+ ],
1162
+ ]);
1163
+ options.onHashedInput?.({
1164
+ kind: 'runtime-artifact',
1165
+ path: artifact.absolutePath,
1166
+ bytes: stable.bytes,
1167
+ });
1168
+ return { ...artifact, state: stable.state, digest };
1169
+ }
1170
+ function compareEdges(a, b) {
1171
+ return compareBytes(JSON.stringify([
1172
+ a.parentLocator,
1173
+ a.parentLabel,
1174
+ a.dependencyName,
1175
+ a.childLocator,
1176
+ a.childLabel,
1177
+ ]), JSON.stringify([
1178
+ b.parentLocator,
1179
+ b.parentLabel,
1180
+ b.dependencyName,
1181
+ b.childLocator,
1182
+ b.childLabel,
1183
+ ]));
1184
+ }
1185
+ function hashDependencyRuntime(inputs, cache, options, runtimeVariant) {
1186
+ const cachedArtifacts = new Map((cache?.artifactEntries ?? []).map((entry) => [artifactCacheKey(entry), entry]));
1187
+ const nextArtifacts = [];
1188
+ const hash = (0, node_crypto_1.createHash)('sha256');
1189
+ updateCanonicalFrame(hash, ['domain', DEPENDENCY_RUNTIME_CANONICALIZATION]);
1190
+ updateCanonicalFrame(hash, [
1191
+ 'runtime-variant',
1192
+ runtimeVariant.nodeVersion,
1193
+ runtimeVariant.platform,
1194
+ runtimeVariant.architecture,
1195
+ runtimeVariant.endianness,
1196
+ runtimeVariant.modulesAbi,
1197
+ runtimeVariant.napiAbi,
1198
+ runtimeVariant.libc,
1199
+ ]);
1200
+ updateCanonicalFrame(hash, [
1201
+ 'lockfile',
1202
+ inputs.lockfilePath ? 'present' : 'absent',
1203
+ inputs.lockfileBytes ?? Buffer.alloc(0),
1204
+ ]);
1205
+ const packageEntries = inputs.packages
1206
+ .map((runtimePackage) => ({
1207
+ canonicalPath: `package:${runtimePackage.locator}/package.json`,
1208
+ bytes: runtimePackage.manifestBytes,
1209
+ }))
1210
+ .concat(inputs.vendoredManifests.map(({ canonicalPath, bytes }) => ({ canonicalPath, bytes })))
1211
+ .sort((a, b) => compareBytes(a.canonicalPath, b.canonicalPath));
1212
+ for (const entry of packageEntries) {
1213
+ updateCanonicalFrame(hash, ['package-manifest', entry.canonicalPath, entry.bytes]);
1214
+ }
1215
+ for (const edge of [...inputs.edges].sort(compareEdges)) {
1216
+ updateCanonicalFrame(hash, [
1217
+ 'dependency-edge',
1218
+ edge.parentLocator,
1219
+ edge.parentLabel,
1220
+ edge.dependencyName,
1221
+ edge.childLocator,
1222
+ edge.childLabel,
1223
+ ]);
1224
+ }
1225
+ for (const artifact of inputs.artifacts) {
1226
+ const hashed = hashRuntimeArtifact(artifact, cachedArtifacts.get(artifactCacheKey(artifact)), options);
1227
+ updateCanonicalFrame(hash, [
1228
+ 'runtime-artifact',
1229
+ artifact.canonicalPath,
1230
+ artifact.kind,
1231
+ digestBytes(hashed.digest),
1232
+ ]);
1233
+ nextArtifacts.push(hashed);
1234
+ }
1235
+ return {
1236
+ identity: {
1237
+ manifestPath: inputs.manifestPath,
1238
+ lockfilePath: inputs.lockfilePath,
1239
+ canonicalization: DEPENDENCY_RUNTIME_CANONICALIZATION,
1240
+ packageCount: inputs.packages.length,
1241
+ artifactCount: inputs.artifacts.length,
1242
+ digest: `sha256:${hash.digest('hex')}`,
1243
+ },
1244
+ entries: nextArtifacts,
1245
+ };
1246
+ }
1247
+ function isStatState(value) {
1248
+ if (typeof value !== 'object' || value === null)
1249
+ return false;
1250
+ const record = value;
1251
+ return ['dev', 'ino', 'mode', 'nlink', 'size', 'mtimeNs', 'ctimeNs'].every((key) => typeof record[key] === 'string');
1252
+ }
1253
+ function isReadableFileState(value) {
1254
+ if (typeof value !== 'object' || value === null)
1255
+ return false;
1256
+ const record = value;
1257
+ return (isStatState(record.link) &&
1258
+ isStatState(record.target) &&
1259
+ (record.symlinkTarget === undefined || typeof record.symlinkTarget === 'string'));
1260
+ }
1261
+ function isSymlinkArtifactState(value) {
1262
+ if (typeof value !== 'object' || value === null)
1263
+ return false;
1264
+ const record = value;
1265
+ // `target === undefined` keeps a readable-file state from masquerading as an
1266
+ // unresolved link recording, which would otherwise be validated against the
1267
+ // wrong guard mode on the warm path.
1268
+ return (isStatState(record.link) &&
1269
+ typeof record.symlinkTarget === 'string' &&
1270
+ record.target === undefined);
1271
+ }
1272
+ function isDependencyPathGuardResult(value) {
1273
+ if (value === null)
1274
+ return true;
1275
+ if (typeof value !== 'object')
1276
+ return false;
1277
+ const record = value;
1278
+ return (['directory', 'file', 'symlink', 'other'].includes(String(record.type)) &&
1279
+ isStatState(record.state) &&
1280
+ (record.type === 'symlink'
1281
+ ? typeof record.symlinkTarget === 'string'
1282
+ : record.symlinkTarget === undefined));
1283
+ }
1284
+ function isRuntimeVariant(value) {
1285
+ if (typeof value !== 'object' || value === null)
1286
+ return false;
1287
+ const record = value;
1288
+ return [
1289
+ 'executablePath',
1290
+ 'nodeVersion',
1291
+ 'platform',
1292
+ 'architecture',
1293
+ 'endianness',
1294
+ 'modulesAbi',
1295
+ 'napiAbi',
1296
+ 'libc',
1297
+ ].every((key) => typeof record[key] === 'string' && record[key].length > 0);
1298
+ }
1299
+ function isTraversalLimits(value) {
1300
+ if (typeof value !== 'object' || value === null)
1301
+ return false;
1302
+ const record = value;
1303
+ return Object.keys(DEFAULT_TRAVERSAL_LIMITS).every((key) => Number.isSafeInteger(record[key]) &&
1304
+ Number(record[key]) >= 1 &&
1305
+ Number(record[key]) <= DEFAULT_TRAVERSAL_LIMITS[key]);
1306
+ }
1307
+ function isDependencyRuntimeIdentity(value) {
1308
+ if (typeof value !== 'object' || value === null)
1309
+ return false;
1310
+ const dependency = value;
1311
+ return (typeof dependency.manifestPath === 'string' &&
1312
+ dependency.manifestPath.length > 0 &&
1313
+ (dependency.lockfilePath === null ||
1314
+ (typeof dependency.lockfilePath === 'string' && dependency.lockfilePath.length > 0)) &&
1315
+ dependency.canonicalization === DEPENDENCY_RUNTIME_CANONICALIZATION &&
1316
+ Number.isSafeInteger(dependency.packageCount) &&
1317
+ Number(dependency.packageCount) >= 1 &&
1318
+ Number.isSafeInteger(dependency.artifactCount) &&
1319
+ Number(dependency.artifactCount) >= 0 &&
1320
+ typeof dependency.digest === 'string' &&
1321
+ SHA256_PATTERN.test(dependency.digest));
1322
+ }
1323
+ function isSafeBuildRelativePath(value) {
1324
+ if (typeof value !== 'string' || value.length === 0 || node_path_1.default.posix.isAbsolute(value)) {
1325
+ return false;
1326
+ }
1327
+ const normalized = node_path_1.default.posix.normalize(value);
1328
+ return normalized === value && normalized !== '..' && !normalized.startsWith('../');
1329
+ }
1330
+ function isSafeBuildDirectoryGuardPath(value) {
1331
+ return value === '' || isSafeBuildRelativePath(value);
1332
+ }
1333
+ function isIdentityCachePayload(value, packageRoot, buildRoot, runtimeVariant, traversalLimits) {
1334
+ if (typeof value !== 'object' || value === null)
1335
+ return false;
1336
+ const record = value;
1337
+ if (record.schemaVersion !== IDENTITY_CACHE_SCHEMA_VERSION ||
1338
+ record.packageRoot !== packageRoot ||
1339
+ record.buildRoot !== buildRoot ||
1340
+ typeof record.packageVersion !== 'string' ||
1341
+ record.packageVersion.length === 0 ||
1342
+ (record.buildKind !== 'source' && record.buildKind !== 'distribution') ||
1343
+ record.buildCanonicalization !== BUILD_CANONICALIZATION ||
1344
+ record.dependencyCanonicalization !== DEPENDENCY_RUNTIME_CANONICALIZATION ||
1345
+ !isTraversalLimits(record.traversalLimits) ||
1346
+ !(0, node_util_1.isDeepStrictEqual)(record.traversalLimits, traversalLimits) ||
1347
+ !isRuntimeVariant(record.runtimeVariant) ||
1348
+ !(0, node_util_1.isDeepStrictEqual)(record.runtimeVariant, runtimeVariant) ||
1349
+ !isStatState(record.buildRootState) ||
1350
+ typeof record.buildDigest !== 'string' ||
1351
+ !SHA256_PATTERN.test(record.buildDigest) ||
1352
+ !isDependencyRuntimeIdentity(record.dependencyIdentity) ||
1353
+ !Array.isArray(record.buildEntries) ||
1354
+ !Array.isArray(record.buildDirectoryGuards) ||
1355
+ !Array.isArray(record.dependencyFileGuards) ||
1356
+ !Array.isArray(record.dependencyDirectoryGuards) ||
1357
+ !Array.isArray(record.dependencyPathGuards) ||
1358
+ !Array.isArray(record.artifactEntries) ||
1359
+ record.buildEntries.length > MAX_CACHE_ENTRIES ||
1360
+ record.buildDirectoryGuards.length > MAX_CACHE_ENTRIES ||
1361
+ record.dependencyFileGuards.length > MAX_CACHE_ENTRIES ||
1362
+ record.dependencyDirectoryGuards.length > MAX_CACHE_ENTRIES ||
1363
+ record.dependencyPathGuards.length > MAX_CACHE_ENTRIES ||
1364
+ record.artifactEntries.length > MAX_CACHE_ENTRIES) {
1365
+ return false;
1366
+ }
1367
+ const buildEntriesValid = record.buildEntries.every((entry) => {
1368
+ if (typeof entry !== 'object' || entry === null)
1369
+ return false;
1370
+ const item = entry;
1371
+ return (isSafeBuildRelativePath(item.relativePath) &&
1372
+ ['directory', 'file'].includes(String(item.kind)) &&
1373
+ isStatState(item.state) &&
1374
+ (item.kind === 'directory'
1375
+ ? item.digest === undefined
1376
+ : typeof item.digest === 'string' && SHA256_PATTERN.test(item.digest)));
1377
+ });
1378
+ const buildDirectoryGuardsValid = record.buildDirectoryGuards.every((entry) => {
1379
+ if (typeof entry !== 'object' || entry === null)
1380
+ return false;
1381
+ const item = entry;
1382
+ return (isSafeBuildDirectoryGuardPath(item.relativePath) &&
1383
+ isStatState(item.state) &&
1384
+ typeof item.entriesDigest === 'string' &&
1385
+ SHA256_PATTERN.test(item.entriesDigest));
1386
+ });
1387
+ const dependencyFileGuardsValid = record.dependencyFileGuards.every((entry) => {
1388
+ if (typeof entry !== 'object' || entry === null)
1389
+ return false;
1390
+ const item = entry;
1391
+ return (typeof item.absolutePath === 'string' &&
1392
+ node_path_1.default.isAbsolute(item.absolutePath) &&
1393
+ isReadableFileState(item.state));
1394
+ });
1395
+ const dependencyDirectoryGuardsValid = record.dependencyDirectoryGuards.every((entry) => {
1396
+ if (typeof entry !== 'object' || entry === null)
1397
+ return false;
1398
+ const item = entry;
1399
+ return (typeof item.absolutePath === 'string' &&
1400
+ node_path_1.default.isAbsolute(item.absolutePath) &&
1401
+ isStatState(item.state) &&
1402
+ typeof item.entriesDigest === 'string' &&
1403
+ SHA256_PATTERN.test(item.entriesDigest));
1404
+ });
1405
+ const dependencyPathGuardsValid = record.dependencyPathGuards.every((entry) => {
1406
+ if (typeof entry !== 'object' || entry === null)
1407
+ return false;
1408
+ const item = entry;
1409
+ return (typeof item.absolutePath === 'string' &&
1410
+ node_path_1.default.isAbsolute(item.absolutePath) &&
1411
+ isDependencyPathGuardResult(item.result));
1412
+ });
1413
+ const artifactEntriesValid = record.artifactEntries.every((entry) => {
1414
+ if (typeof entry !== 'object' || entry === null)
1415
+ return false;
1416
+ const item = entry;
1417
+ if (typeof item.absolutePath !== 'string' ||
1418
+ !node_path_1.default.isAbsolute(item.absolutePath) ||
1419
+ typeof item.canonicalPath !== 'string' ||
1420
+ typeof item.digest !== 'string' ||
1421
+ !SHA256_PATTERN.test(item.digest)) {
1422
+ return false;
1423
+ }
1424
+ return item.kind === 'unfollowed-symlink'
1425
+ ? isSymlinkArtifactState(item.state)
1426
+ : (item.kind === 'file' || item.kind === 'symlink') && isReadableFileState(item.state);
1427
+ });
1428
+ const hasBuildRootGuard = record.buildDirectoryGuards.some((entry) => typeof entry === 'object' &&
1429
+ entry !== null &&
1430
+ entry.relativePath === '');
1431
+ return (buildEntriesValid &&
1432
+ buildDirectoryGuardsValid &&
1433
+ hasBuildRootGuard &&
1434
+ dependencyFileGuardsValid &&
1435
+ dependencyDirectoryGuardsValid &&
1436
+ dependencyPathGuardsValid &&
1437
+ artifactEntriesValid);
1438
+ }
1439
+ function currentUid() {
1440
+ return typeof process.getuid === 'function' ? process.getuid() : null;
1441
+ }
1442
+ function isOwnedPrivateDirectory(candidate) {
1443
+ try {
1444
+ const stat = (0, node_fs_1.lstatSync)(candidate);
1445
+ if (!stat.isDirectory() || stat.isSymbolicLink())
1446
+ return false;
1447
+ const uid = currentUid();
1448
+ return uid !== null && stat.uid === uid && (stat.mode & 0o077) === 0;
1449
+ }
1450
+ catch {
1451
+ return false;
1452
+ }
1453
+ }
1454
+ function isSafeTemporaryParent(candidate) {
1455
+ try {
1456
+ const stat = (0, node_fs_1.lstatSync)(candidate);
1457
+ if (!stat.isDirectory() || stat.isSymbolicLink())
1458
+ return false;
1459
+ if (currentUid() === null)
1460
+ return false;
1461
+ // A shared temp root is safe only with the sticky bit: another UID then
1462
+ // cannot replace the private child after our atomic mkdir + owner check.
1463
+ const writableByOthers = (stat.mode & 0o022) !== 0;
1464
+ return !writableByOthers || (stat.mode & 0o1000) !== 0;
1465
+ }
1466
+ catch {
1467
+ return false;
1468
+ }
1469
+ }
1470
+ function ensurePrivateChild(parent, childName) {
1471
+ let resolvedParent;
1472
+ try {
1473
+ resolvedParent = node_fs_1.realpathSync.native(parent);
1474
+ }
1475
+ catch {
1476
+ return null;
1477
+ }
1478
+ if (!isSafeTemporaryParent(resolvedParent))
1479
+ return null;
1480
+ const candidate = node_path_1.default.join(resolvedParent, childName);
1481
+ try {
1482
+ // Non-recursive mkdir is intentional: it cannot follow an attacker-made
1483
+ // intermediate symlink. EEXIST is accepted only after the ownership/mode
1484
+ // validation below.
1485
+ (0, node_fs_1.mkdirSync)(candidate, { mode: 0o700 });
1486
+ }
1487
+ catch (error) {
1488
+ if (error.code !== 'EEXIST')
1489
+ return null;
1490
+ }
1491
+ return isOwnedPrivateDirectory(candidate) ? candidate : null;
1492
+ }
1493
+ function defaultCacheDirectory() {
1494
+ // On platforms without POSIX ownership APIs we cannot prove that a default
1495
+ // cache file is private to this process's user. Persistence is therefore
1496
+ // disabled unless the operator supplied an explicit trusted override.
1497
+ const uid = currentUid();
1498
+ if (uid === null)
1499
+ return null;
1500
+ // XDG_RUNTIME_DIR is already per-user and normally 0700. Use it only when
1501
+ // that contract is true; a spoofed/insecure value falls back to the sticky
1502
+ // OS temp root rather than becoming a cache-poisoning surface.
1503
+ const runtimeDir = process.env.XDG_RUNTIME_DIR;
1504
+ if (runtimeDir) {
1505
+ try {
1506
+ const resolvedRuntime = node_fs_1.realpathSync.native(runtimeDir);
1507
+ if (isOwnedPrivateDirectory(resolvedRuntime)) {
1508
+ const runtimeCache = ensurePrivateChild(resolvedRuntime, 'cgraph-analyzer-identity');
1509
+ if (runtimeCache)
1510
+ return runtimeCache;
1511
+ }
1512
+ }
1513
+ catch {
1514
+ /* fall through to the OS temp directory */
1515
+ }
1516
+ }
1517
+ let tempRoot;
1518
+ try {
1519
+ tempRoot = node_fs_1.realpathSync.native(node_os_1.default.tmpdir());
1520
+ }
1521
+ catch {
1522
+ return null;
1523
+ }
1524
+ return ensurePrivateChild(tempRoot, `cgraph-analyzer-identity-${uid}`);
1525
+ }
1526
+ const TRUSTED_CACHE_DIRECTORY_ENV = 'CGRAPH_ANALYZER_IDENTITY_CACHE_DIR';
1527
+ function pathsEqual(left, right) {
1528
+ return process.platform === 'win32' ? left.toLowerCase() === right.toLowerCase() : left === right;
1529
+ }
1530
+ function trustedEnvironmentCacheDirectory() {
1531
+ const configured = process.env[TRUSTED_CACHE_DIRECTORY_ENV];
1532
+ if (configured === undefined)
1533
+ return null;
1534
+ if (configured.length === 0 || configured.includes('\0') || !node_path_1.default.isAbsolute(configured)) {
1535
+ throw new Error(`${TRUSTED_CACHE_DIRECTORY_ENV} must name an absolute protected directory`);
1536
+ }
1537
+ const normalized = node_path_1.default.normalize(configured);
1538
+ let resolved;
1539
+ try {
1540
+ const link = (0, node_fs_1.lstatSync)(normalized);
1541
+ if (!link.isDirectory() || link.isSymbolicLink()) {
1542
+ throw new Error('not a real directory');
1543
+ }
1544
+ resolved = node_fs_1.realpathSync.native(normalized);
1545
+ }
1546
+ catch {
1547
+ throw new Error(`${TRUSTED_CACHE_DIRECTORY_ENV} must name a pre-existing protected non-symlink directory`);
1548
+ }
1549
+ // Reject junctions/symlinked ancestors as well as a symlink final component.
1550
+ // The environment variable is an explicit trust assertion, but its spelling
1551
+ // must still bind exactly to the directory the cache will use.
1552
+ if (!pathsEqual(node_path_1.default.resolve(normalized), resolved)) {
1553
+ throw new Error(`${TRUSTED_CACHE_DIRECTORY_ENV} must not traverse symbolic links or junctions`);
1554
+ }
1555
+ return resolved;
1556
+ }
1557
+ function cacheDirectory(options, packageRoot, buildRoot) {
1558
+ // An explicit location is a trusted operator/test override and therefore
1559
+ // remains authoritative, including when the secure default is unavailable.
1560
+ if (options.cacheDirectory) {
1561
+ const explicit = node_path_1.default.resolve(options.cacheDirectory);
1562
+ try {
1563
+ // Create it before any build/dependency directory guards are captured.
1564
+ // A cache nested immediately under a package root then changes that
1565
+ // parent's directory state once, not after we persist the first entry.
1566
+ (0, node_fs_1.mkdirSync)(explicit, { recursive: true, mode: 0o700 });
1567
+ }
1568
+ catch {
1569
+ /* persistence remains optional and will fail closed */
1570
+ }
1571
+ return explicit;
1572
+ }
1573
+ const configured = trustedEnvironmentCacheDirectory();
1574
+ if (configured) {
1575
+ if (isInside(packageRoot, configured) || isInside(buildRoot, configured)) {
1576
+ throw new Error(`${TRUSTED_CACHE_DIRECTORY_ENV} must be outside the analyzer package and build roots`);
1577
+ }
1578
+ return configured;
1579
+ }
1580
+ return defaultCacheDirectory();
1581
+ }
1582
+ function hasTrustedCacheOverride(options) {
1583
+ return (options.cacheDirectory !== undefined || process.env[TRUSTED_CACHE_DIRECTORY_ENV] !== undefined);
1584
+ }
1585
+ function identityCacheKey(packageRoot, buildRoot, runtimeVariant, traversalLimits) {
1586
+ return hashCanonicalFrames([
1587
+ [
1588
+ 'analyzer-identity-cache-key-v3',
1589
+ String(IDENTITY_CACHE_SCHEMA_VERSION),
1590
+ packageRoot,
1591
+ buildRoot,
1592
+ runtimeVariant.executablePath,
1593
+ runtimeVariant.nodeVersion,
1594
+ runtimeVariant.platform,
1595
+ runtimeVariant.architecture,
1596
+ runtimeVariant.endianness,
1597
+ runtimeVariant.modulesAbi,
1598
+ runtimeVariant.napiAbi,
1599
+ runtimeVariant.libc,
1600
+ JSON.stringify(traversalLimits),
1601
+ ],
1602
+ ]).slice('sha256:'.length);
1603
+ }
1604
+ const MAX_PROCESS_CACHE_ENTRIES = 12;
1605
+ const processIdentityCache = new Map();
1606
+ /** @internal Clear process-local reuse between simulated-process unit tests. */
1607
+ function _clearAnalyzerIdentityProcessCacheForTests() {
1608
+ processIdentityCache.clear();
1609
+ }
1610
+ function cachePathFor(packageRoot, buildRoot, runtimeVariant, traversalLimits, options) {
1611
+ const directory = cacheDirectory(options, packageRoot, buildRoot);
1612
+ if (!directory)
1613
+ return null;
1614
+ const key = identityCacheKey(packageRoot, buildRoot, runtimeVariant, traversalLimits);
1615
+ return node_path_1.default.join(directory, `${key}.json`);
1616
+ }
1617
+ function readCacheFile(target) {
1618
+ let descriptor = null;
1619
+ try {
1620
+ const noFollow = 'O_NOFOLLOW' in node_fs_1.constants ? node_fs_1.constants.O_NOFOLLOW : 0;
1621
+ descriptor = (0, node_fs_1.openSync)(target, node_fs_1.constants.O_RDONLY | noFollow);
1622
+ const stat = (0, node_fs_1.fstatSync)(descriptor);
1623
+ const uid = currentUid();
1624
+ if (!stat.isFile() ||
1625
+ stat.isSymbolicLink() ||
1626
+ stat.size > MAX_CACHE_FILE_BYTES ||
1627
+ (uid !== null && (stat.uid !== uid || (stat.mode & 0o077) !== 0))) {
1628
+ return null;
1629
+ }
1630
+ return (0, node_fs_1.readFileSync)(descriptor, 'utf8');
1631
+ }
1632
+ catch {
1633
+ return null;
1634
+ }
1635
+ finally {
1636
+ if (descriptor !== null)
1637
+ (0, node_fs_1.closeSync)(descriptor);
1638
+ }
1639
+ }
1640
+ function loadIdentityCache(packageRoot, buildRoot, runtimeVariant, traversalLimits, options) {
1641
+ // Invalid explicit cache configuration is an operator error, not an optional
1642
+ // cache miss. Resolve it outside the best-effort envelope read below.
1643
+ const target = cachePathFor(packageRoot, buildRoot, runtimeVariant, traversalLimits, options);
1644
+ try {
1645
+ // Validate any operator-selected persistence location on every call, even
1646
+ // when the payload itself is reusable from this process's guarded LRU.
1647
+ const key = identityCacheKey(packageRoot, buildRoot, runtimeVariant, traversalLimits);
1648
+ const local = processIdentityCache.get(key);
1649
+ if (local)
1650
+ return local;
1651
+ if (!target)
1652
+ return null;
1653
+ const raw = readCacheFile(target);
1654
+ if (raw === null)
1655
+ return null;
1656
+ const parsed = JSON.parse(raw);
1657
+ if (typeof parsed !== 'object' ||
1658
+ parsed === null ||
1659
+ typeof parsed.checksum !== 'string' ||
1660
+ !isIdentityCachePayload(parsed.payload, packageRoot, buildRoot, runtimeVariant, traversalLimits) ||
1661
+ parsed.checksum !== sha256(JSON.stringify(parsed.payload))) {
1662
+ return null;
1663
+ }
1664
+ return parsed.payload;
1665
+ }
1666
+ catch {
1667
+ return null;
1668
+ }
1669
+ }
1670
+ const CACHE_GUARD_PROBE_SCRIPT = String.raw `
1671
+ const fs = require('node:fs/promises');
1672
+ const crypto = require('node:crypto');
1673
+ const state = (value) => ({
1674
+ dev: String(value.dev), ino: String(value.ino), mode: String(value.mode),
1675
+ nlink: String(value.nlink), size: String(value.size),
1676
+ mtimeNs: String(value.mtimeNs), ctimeNs: String(value.ctimeNs),
1677
+ });
1678
+ const frame = (hash, fields) => {
1679
+ const fieldCount = Buffer.allocUnsafe(4);
1680
+ fieldCount.writeUInt32BE(fields.length);
1681
+ hash.update(fieldCount);
1682
+ for (const field of fields) {
1683
+ const bytes = Buffer.from(field);
1684
+ const length = Buffer.allocUnsafe(8);
1685
+ length.writeBigUInt64BE(BigInt(bytes.length));
1686
+ hash.update(length);
1687
+ hash.update(bytes);
1688
+ }
1689
+ };
1690
+ const inventoryDigest = (entries) => {
1691
+ const normalized = entries.map((entry) => ({
1692
+ name: entry.name,
1693
+ kind: entry.isDirectory() ? 'directory'
1694
+ : entry.isFile() ? 'file'
1695
+ : entry.isSymbolicLink() ? 'symlink' : 'other',
1696
+ })).sort((a, b) => Buffer.compare(Buffer.from(a.name), Buffer.from(b.name)));
1697
+ const hash = crypto.createHash('sha256');
1698
+ frame(hash, ['directory-entries-v1']);
1699
+ for (const entry of normalized) frame(hash, [entry.name, entry.kind]);
1700
+ return 'sha256:' + hash.digest('hex');
1701
+ };
1702
+ const probe = async (request) => {
1703
+ try {
1704
+ const link = await fs.lstat(request.absolutePath, { bigint: true });
1705
+ const type = link.isDirectory() ? 'directory'
1706
+ : link.isFile() ? 'file'
1707
+ : link.isSymbolicLink() ? 'symlink' : 'other';
1708
+ if (request.mode === 'link') return {
1709
+ type,
1710
+ state: state(link),
1711
+ ...(type === 'symlink' ? { symlinkTarget: await fs.readlink(request.absolutePath) } : {}),
1712
+ };
1713
+ if (request.mode === 'directory-inventory') {
1714
+ if (!link.isDirectory() || link.isSymbolicLink()) return null;
1715
+ const entriesDigest = inventoryDigest(
1716
+ await fs.readdir(request.absolutePath, { withFileTypes: true }),
1717
+ );
1718
+ const after = await fs.lstat(request.absolutePath, { bigint: true });
1719
+ if (JSON.stringify(state(link)) !== JSON.stringify(state(after))) return null;
1720
+ return { type: 'directory-inventory', state: state(after), entriesDigest };
1721
+ }
1722
+ const target = await fs.stat(request.absolutePath, { bigint: true });
1723
+ if (!target.isFile()) return null;
1724
+ const result = { type: 'readable-file', state: { link: state(link), target: state(target) } };
1725
+ if (link.isSymbolicLink()) result.state.symlinkTarget = await fs.readlink(request.absolutePath);
1726
+ return result;
1727
+ } catch { return null; }
1728
+ };
1729
+ let input = '';
1730
+ process.stdin.setEncoding('utf8');
1731
+ process.stdin.on('data', (chunk) => { input += chunk; });
1732
+ process.stdin.on('end', async () => {
1733
+ try {
1734
+ const requests = JSON.parse(input);
1735
+ const results = [];
1736
+ for (let offset = 0; offset < requests.length; offset += 512) {
1737
+ results.push(...await Promise.all(requests.slice(offset, offset + 512).map(probe)));
1738
+ }
1739
+ process.stdout.write(JSON.stringify(results));
1740
+ } catch { process.exitCode = 1; }
1741
+ });
1742
+ `;
1743
+ function snapshotCacheGuardDirect(request) {
1744
+ try {
1745
+ if (request.mode === 'readable-file') {
1746
+ return { type: 'readable-file', state: snapshotReadableFile(request.absolutePath) };
1747
+ }
1748
+ if (request.mode === 'directory-inventory') {
1749
+ const inventory = snapshotDirectoryInventory(request.absolutePath);
1750
+ return { type: 'directory-inventory', ...inventory };
1751
+ }
1752
+ const stat = (0, node_fs_1.lstatSync)(request.absolutePath, { bigint: true });
1753
+ const type = stat.isDirectory()
1754
+ ? 'directory'
1755
+ : stat.isFile()
1756
+ ? 'file'
1757
+ : stat.isSymbolicLink()
1758
+ ? 'symlink'
1759
+ : 'other';
1760
+ return {
1761
+ type,
1762
+ state: statState(stat),
1763
+ ...(type === 'symlink' ? { symlinkTarget: (0, node_fs_1.readlinkSync)(request.absolutePath) } : {}),
1764
+ };
1765
+ }
1766
+ catch {
1767
+ return null;
1768
+ }
1769
+ }
1770
+ function snapshotCacheGuards(requests) {
1771
+ if (requests.length < 128)
1772
+ return requests.map(snapshotCacheGuardDirect);
1773
+ try {
1774
+ const probe = (0, node_child_process_1.spawnSync)(process.execPath, ['--input-type=commonjs', '-e', CACHE_GUARD_PROBE_SCRIPT], {
1775
+ input: JSON.stringify(requests),
1776
+ encoding: 'utf8',
1777
+ maxBuffer: MAX_CACHE_FILE_BYTES,
1778
+ timeout: 30_000,
1779
+ windowsHide: true,
1780
+ });
1781
+ if (probe.status === 0 && !probe.error) {
1782
+ const parsed = JSON.parse(probe.stdout);
1783
+ if (Array.isArray(parsed) && parsed.length === requests.length) {
1784
+ return parsed;
1785
+ }
1786
+ }
1787
+ }
1788
+ catch {
1789
+ /* fall through to the slower in-process validator */
1790
+ }
1791
+ return requests.map(snapshotCacheGuardDirect);
1792
+ }
1793
+ function validateIdentityCache(cache, options) {
1794
+ const expected = new Map();
1795
+ const add = (request, result) => {
1796
+ const key = JSON.stringify([request.mode, request.absolutePath]);
1797
+ const prior = expected.get(key);
1798
+ if (expected.has(key) && !(0, node_util_1.isDeepStrictEqual)(prior, result)) {
1799
+ options.onCacheValidationFailure?.({ mode: request.mode, path: request.absolutePath });
1800
+ return false;
1801
+ }
1802
+ expected.set(key, result);
1803
+ return true;
1804
+ };
1805
+ if (!add({ absolutePath: cache.buildRoot, mode: 'link' }, { type: 'directory', state: cache.buildRootState })) {
1806
+ return false;
1807
+ }
1808
+ for (const entry of cache.buildEntries) {
1809
+ const absolutePath = node_path_1.default.join(cache.buildRoot, ...entry.relativePath.split('/'));
1810
+ if (!isInside(cache.buildRoot, absolutePath) ||
1811
+ !add({ absolutePath, mode: 'link' }, { type: entry.kind, state: entry.state })) {
1812
+ return false;
1813
+ }
1814
+ }
1815
+ for (const guard of cache.buildDirectoryGuards) {
1816
+ const absolutePath = guard.relativePath
1817
+ ? node_path_1.default.join(cache.buildRoot, ...guard.relativePath.split('/'))
1818
+ : cache.buildRoot;
1819
+ if (!isInside(cache.buildRoot, absolutePath) ||
1820
+ !add({ absolutePath, mode: 'directory-inventory' }, {
1821
+ type: 'directory-inventory',
1822
+ state: guard.state,
1823
+ entriesDigest: guard.entriesDigest,
1824
+ })) {
1825
+ return false;
1826
+ }
1827
+ }
1828
+ for (const guard of cache.dependencyDirectoryGuards) {
1829
+ if (!add({ absolutePath: guard.absolutePath, mode: 'directory-inventory' }, {
1830
+ type: 'directory-inventory',
1831
+ state: guard.state,
1832
+ entriesDigest: guard.entriesDigest,
1833
+ })) {
1834
+ return false;
1835
+ }
1836
+ }
1837
+ for (const guard of cache.dependencyPathGuards) {
1838
+ if (!add({ absolutePath: guard.absolutePath, mode: 'link' }, guard.result)) {
1839
+ return false;
1840
+ }
1841
+ }
1842
+ for (const guard of cache.dependencyFileGuards) {
1843
+ if (!add({ absolutePath: guard.absolutePath, mode: 'readable-file' }, { type: 'readable-file', state: guard.state })) {
1844
+ return false;
1845
+ }
1846
+ }
1847
+ for (const artifact of cache.artifactEntries) {
1848
+ // A recorded link is re-probed as a link, never as a readable file: the
1849
+ // readable-file probe resolves the target and would report `null` for the
1850
+ // very inputs this kind exists to describe, failing every warm validation.
1851
+ const probe = artifact.kind === 'unfollowed-symlink'
1852
+ ? {
1853
+ request: { absolutePath: artifact.absolutePath, mode: 'link' },
1854
+ expected: {
1855
+ type: 'symlink',
1856
+ state: artifact.state.link,
1857
+ symlinkTarget: artifact.state.symlinkTarget,
1858
+ },
1859
+ }
1860
+ : {
1861
+ request: { absolutePath: artifact.absolutePath, mode: 'readable-file' },
1862
+ expected: { type: 'readable-file', state: artifact.state },
1863
+ };
1864
+ if (!add(probe.request, probe.expected))
1865
+ return false;
1866
+ }
1867
+ const entries = [...expected.entries()];
1868
+ const requests = entries.map(([key]) => {
1869
+ const [mode, absolutePath] = JSON.parse(key);
1870
+ return { mode, absolutePath };
1871
+ });
1872
+ options.onCacheValidationPass?.({ guardCount: requests.length });
1873
+ const actual = snapshotCacheGuards(requests);
1874
+ const mismatch = actual.findIndex((result, index) => !(0, node_util_1.isDeepStrictEqual)(result, entries[index][1]));
1875
+ if (mismatch !== -1) {
1876
+ const [mode, absolutePath] = JSON.parse(entries[mismatch][0]);
1877
+ options.onCacheValidationFailure?.({ mode, path: absolutePath });
1878
+ return false;
1879
+ }
1880
+ return true;
1881
+ }
1882
+ function cachedBuildDigestForPath(cache, absolutePath) {
1883
+ if (!isInside(cache.buildRoot, absolutePath))
1884
+ return null;
1885
+ const relativePath = node_path_1.default.relative(cache.buildRoot, absolutePath).split(node_path_1.default.sep).join('/');
1886
+ const entry = cache.buildEntries.find((candidate) => candidate.kind === 'file' && candidate.relativePath === relativePath);
1887
+ return entry?.digest ?? null;
1888
+ }
1889
+ function dependencyFileGuards(inputs) {
1890
+ const guards = new Map();
1891
+ if (inputs.lockfilePath && inputs.lockfileState) {
1892
+ guards.set(inputs.lockfilePath, inputs.lockfileState);
1893
+ }
1894
+ for (const runtimePackage of inputs.packages) {
1895
+ guards.set(runtimePackage.manifestPath, runtimePackage.manifestState);
1896
+ }
1897
+ for (const manifest of inputs.vendoredManifests) {
1898
+ guards.set(manifest.absolutePath, manifest.state);
1899
+ }
1900
+ return [...guards.entries()]
1901
+ .map(([absolutePath, state]) => ({ absolutePath, state }))
1902
+ .sort((a, b) => compareBytes(a.absolutePath, b.absolutePath));
1903
+ }
1904
+ function dependencyDirectoryGuards(inputs) {
1905
+ return [...inputs.directoryGuards.entries()]
1906
+ .map(([absolutePath, guard]) => ({ absolutePath, ...guard }))
1907
+ .sort((a, b) => compareBytes(a.absolutePath, b.absolutePath));
1908
+ }
1909
+ function dependencyPathGuards(inputs) {
1910
+ return [...inputs.pathGuards.entries()]
1911
+ .map(([absolutePath, result]) => ({ absolutePath, result }))
1912
+ .sort((a, b) => compareBytes(a.absolutePath, b.absolutePath));
1913
+ }
1914
+ function persistIdentityCache(packageRoot, buildRoot, payload, previous, options) {
1915
+ if (previous && (0, node_util_1.isDeepStrictEqual)(previous, payload))
1916
+ return;
1917
+ const target = cachePathFor(packageRoot, buildRoot, payload.runtimeVariant, payload.traversalLimits, options);
1918
+ if (!target)
1919
+ return;
1920
+ const temporary = `${target}.${process.pid}.${(0, node_crypto_1.randomBytes)(6).toString('hex')}.tmp`;
1921
+ try {
1922
+ if (options.cacheDirectory) {
1923
+ (0, node_fs_1.mkdirSync)(node_path_1.default.dirname(target), { recursive: true, mode: 0o700 });
1924
+ }
1925
+ else if (!hasTrustedCacheOverride(options) &&
1926
+ !isOwnedPrivateDirectory(node_path_1.default.dirname(target))) {
1927
+ return;
1928
+ }
1929
+ const envelope = {
1930
+ payload,
1931
+ checksum: sha256(JSON.stringify(payload)),
1932
+ };
1933
+ (0, node_fs_1.writeFileSync)(temporary, `${JSON.stringify(envelope)}\n`, {
1934
+ encoding: 'utf8',
1935
+ flag: 'wx',
1936
+ mode: 0o600,
1937
+ });
1938
+ (0, node_fs_1.renameSync)(temporary, target);
1939
+ }
1940
+ catch {
1941
+ // The cache is optional (notably for read-only package/container setups).
1942
+ // A failed write only loses the optimization; identity remains fail-closed.
1943
+ try {
1944
+ (0, node_fs_1.unlinkSync)(temporary);
1945
+ }
1946
+ catch {
1947
+ /* already renamed or never created */
1948
+ }
1949
+ }
1950
+ }
1951
+ function rememberIdentityCache(payload) {
1952
+ const key = identityCacheKey(payload.packageRoot, payload.buildRoot, payload.runtimeVariant, payload.traversalLimits);
1953
+ processIdentityCache.delete(key);
1954
+ processIdentityCache.set(key, payload);
1955
+ while (processIdentityCache.size > MAX_PROCESS_CACHE_ENTRIES) {
1956
+ const oldest = processIdentityCache.keys().next().value;
1957
+ if (oldest === undefined)
1958
+ break;
1959
+ processIdentityCache.delete(oldest);
1960
+ }
1961
+ }
1962
+ function resolveInvokedArtifact(buildRoot, analyzerModulePath) {
1963
+ const argvEntry = process.argv[1];
1964
+ if (!argvEntry)
1965
+ return analyzerModulePath;
1966
+ try {
1967
+ const resolved = resolveExistingPath(argvEntry);
1968
+ return isInside(buildRoot, resolved) && (0, node_fs_1.lstatSync)(resolved).isFile()
1969
+ ? resolved
1970
+ : analyzerModulePath;
1971
+ }
1972
+ catch {
1973
+ return analyzerModulePath;
1974
+ }
1975
+ }
1976
+ function isNonEmptyString(value) {
1977
+ return typeof value === 'string' && value.length > 0;
1978
+ }
1979
+ function runnerRuntimeIdentity(runtimeVariant) {
1980
+ return {
1981
+ executablePath: runtimeVariant.executablePath,
1982
+ version: runtimeVariant.nodeVersion,
1983
+ platform: runtimeVariant.platform,
1984
+ architecture: runtimeVariant.architecture,
1985
+ modulesAbi: runtimeVariant.modulesAbi,
1986
+ libc: runtimeVariant.libc,
1987
+ };
1988
+ }
1989
+ function isAnalyzerRunnerIdentity(value) {
1990
+ if (typeof value !== 'object' || value === null)
1991
+ return false;
1992
+ const identity = value;
1993
+ const runtime = identity.runtime;
1994
+ const invoked = identity.invokedArtifact;
1995
+ const build = identity.build;
1996
+ const dependency = identity.dependencyRuntime;
1997
+ return (identity.schemaVersion === exports.ANALYZER_RUNNER_IDENTITY_SCHEMA_VERSION &&
1998
+ !!runtime &&
1999
+ isNonEmptyString(runtime.executablePath) &&
2000
+ isNonEmptyString(runtime.version) &&
2001
+ isNonEmptyString(runtime.platform) &&
2002
+ isNonEmptyString(runtime.architecture) &&
2003
+ isNonEmptyString(runtime.modulesAbi) &&
2004
+ isNonEmptyString(runtime.libc) &&
2005
+ isNonEmptyString(identity.cliVersion) &&
2006
+ !!invoked &&
2007
+ isNonEmptyString(invoked.path) &&
2008
+ typeof invoked.digest === 'string' &&
2009
+ SHA256_PATTERN.test(invoked.digest) &&
2010
+ !!build &&
2011
+ (build.kind === 'source' || build.kind === 'distribution') &&
2012
+ isNonEmptyString(build.rootPath) &&
2013
+ build.canonicalization === BUILD_CANONICALIZATION &&
2014
+ typeof build.digest === 'string' &&
2015
+ SHA256_PATTERN.test(build.digest) &&
2016
+ !!dependency &&
2017
+ isNonEmptyString(dependency.manifestPath) &&
2018
+ (dependency.lockfilePath === null || isNonEmptyString(dependency.lockfilePath)) &&
2019
+ dependency.canonicalization === DEPENDENCY_RUNTIME_CANONICALIZATION &&
2020
+ Number.isSafeInteger(dependency.packageCount) &&
2021
+ Number(dependency.packageCount) >= 1 &&
2022
+ Number.isSafeInteger(dependency.artifactCount) &&
2023
+ Number(dependency.artifactCount) >= 0 &&
2024
+ typeof dependency.digest === 'string' &&
2025
+ SHA256_PATTERN.test(dependency.digest));
2026
+ }
2027
+ /**
2028
+ * Normalize a raw diagnostic receipt for freshness comparison. The entrypoint
2029
+ * is the only excluded field; malformed/legacy receipts never compare equal.
2030
+ */
2031
+ function normalizeAnalyzerRunnerIdentityForComparison(identity) {
2032
+ if (!isAnalyzerRunnerIdentity(identity))
2033
+ return null;
2034
+ const { invokedArtifact: _diagnosticEntrypoint, ...semantic } = identity;
2035
+ return semantic;
2036
+ }
2037
+ /** Resolve the identity of the analyzer build and runtime executing now. */
2038
+ function resolveAnalyzerRunnerIdentity(analyzerModuleUrl, options = {}) {
2039
+ const analyzerModulePath = resolveExistingPath((0, node_url_1.fileURLToPath)(analyzerModuleUrl));
2040
+ const { packageRoot, buildRoot, kind } = resolveBuildRoot(analyzerModulePath);
2041
+ const runtimeVariant = resolveRuntimeVariant();
2042
+ const traversalLimits = resolveTraversalLimits(options);
2043
+ const previousCache = loadIdentityCache(packageRoot, buildRoot, runtimeVariant, traversalLimits, options);
2044
+ const invokedArtifactPath = resolveInvokedArtifact(buildRoot, analyzerModulePath);
2045
+ if (previousCache?.buildKind === kind) {
2046
+ const invokedDigest = cachedBuildDigestForPath(previousCache, invokedArtifactPath);
2047
+ if (invokedDigest) {
2048
+ const cachedIdentity = {
2049
+ schemaVersion: exports.ANALYZER_RUNNER_IDENTITY_SCHEMA_VERSION,
2050
+ runtime: runnerRuntimeIdentity(runtimeVariant),
2051
+ cliVersion: previousCache.packageVersion,
2052
+ invokedArtifact: { path: invokedArtifactPath, digest: invokedDigest },
2053
+ build: {
2054
+ kind,
2055
+ rootPath: buildRoot,
2056
+ canonicalization: BUILD_CANONICALIZATION,
2057
+ digest: previousCache.buildDigest,
2058
+ },
2059
+ dependencyRuntime: previousCache.dependencyIdentity,
2060
+ };
2061
+ // One batched pass is deliberately the last filesystem observation on
2062
+ // a warm hit. Repeating the complete inventory both doubles status
2063
+ // latency and still cannot close the post-check scheduling window.
2064
+ if (validateIdentityCache(previousCache, options)) {
2065
+ rememberIdentityCache(previousCache);
2066
+ return cachedIdentity;
2067
+ }
2068
+ }
2069
+ }
2070
+ const build = hashBuildTree(buildRoot, previousCache, options, traversalLimits);
2071
+ const dependencyInputs = collectDependencyInputs(packageRoot, options, traversalLimits);
2072
+ const dependencySnapshotBefore = dependencySnapshot(dependencyInputs);
2073
+ const dependency = hashDependencyRuntime(dependencyInputs, previousCache, options, runtimeVariant);
2074
+ const packageVersion = dependencyInputs.packages[0]?.manifest.version;
2075
+ if (typeof packageVersion !== 'string' || packageVersion.trim() === '') {
2076
+ throw new Error(`GitNexus package version is unavailable in ${packageRoot}`);
2077
+ }
2078
+ const buildSnapshotAfter = buildSnapshot(collectBuildEntries(buildRoot, options, traversalLimits));
2079
+ if (!(0, node_util_1.isDeepStrictEqual)(build.snapshot, buildSnapshotAfter)) {
2080
+ throw new Error(`Analyzer build changed while its identity was being computed: ${buildRoot}`);
2081
+ }
2082
+ const dependencySnapshotAfter = dependencySnapshot(collectDependencyInputs(packageRoot, options, traversalLimits));
2083
+ if (!(0, node_util_1.isDeepStrictEqual)(dependencySnapshotBefore, dependencySnapshotAfter)) {
2084
+ throw new Error(`Analyzer dependency runtime changed while its identity was being computed: ${packageRoot}`);
2085
+ }
2086
+ const nextCache = {
2087
+ schemaVersion: IDENTITY_CACHE_SCHEMA_VERSION,
2088
+ packageRoot,
2089
+ buildRoot,
2090
+ packageVersion,
2091
+ buildKind: kind,
2092
+ buildCanonicalization: BUILD_CANONICALIZATION,
2093
+ dependencyCanonicalization: DEPENDENCY_RUNTIME_CANONICALIZATION,
2094
+ traversalLimits,
2095
+ runtimeVariant,
2096
+ buildRootState: build.rootState,
2097
+ buildDigest: build.digest,
2098
+ buildEntries: build.entries,
2099
+ buildDirectoryGuards: build.directoryGuards,
2100
+ dependencyIdentity: dependency.identity,
2101
+ dependencyFileGuards: dependencyFileGuards(dependencyInputs),
2102
+ dependencyDirectoryGuards: dependencyDirectoryGuards(dependencyInputs),
2103
+ dependencyPathGuards: dependencyPathGuards(dependencyInputs),
2104
+ artifactEntries: dependency.entries,
2105
+ };
2106
+ const invokedDigest = cachedBuildDigestForPath(nextCache, invokedArtifactPath);
2107
+ if (!invokedDigest) {
2108
+ throw new Error(`Invoked analyzer artifact is absent from the validated build: ${invokedArtifactPath}`);
2109
+ }
2110
+ const identity = {
2111
+ schemaVersion: exports.ANALYZER_RUNNER_IDENTITY_SCHEMA_VERSION,
2112
+ runtime: runnerRuntimeIdentity(runtimeVariant),
2113
+ cliVersion: packageVersion,
2114
+ invokedArtifact: {
2115
+ path: invokedArtifactPath,
2116
+ digest: invokedDigest,
2117
+ },
2118
+ build: {
2119
+ kind,
2120
+ rootPath: buildRoot,
2121
+ canonicalization: BUILD_CANONICALIZATION,
2122
+ digest: build.digest,
2123
+ },
2124
+ dependencyRuntime: dependency.identity,
2125
+ };
2126
+ let validationFailure;
2127
+ const validationOptions = {
2128
+ ...options,
2129
+ onCacheValidationFailure: (failure) => {
2130
+ validationFailure = failure;
2131
+ options.onCacheValidationFailure?.(failure);
2132
+ },
2133
+ };
2134
+ if (!validateIdentityCache(nextCache, validationOptions)) {
2135
+ const mismatch = validationFailure
2136
+ ? ` (failed ${validationFailure.mode} guard: ${validationFailure.path})`
2137
+ : '';
2138
+ throw new Error(`Analyzer build or dependency runtime changed while its identity was being computed: ${packageRoot}${mismatch}`);
2139
+ }
2140
+ rememberIdentityCache(nextCache);
2141
+ persistIdentityCache(packageRoot, buildRoot, nextCache, previousCache, options);
2142
+ return identity;
2143
+ }
2144
+ /**
2145
+ * Semantic freshness comparison. Both receipts must be well-formed schema-v4
2146
+ * values; only the diagnostic entrypoint field is normalized away.
2147
+ */
2148
+ function analyzerRunnerIdentitiesEqual(indexedIdentity, currentIdentity) {
2149
+ const indexed = normalizeAnalyzerRunnerIdentityForComparison(indexedIdentity);
2150
+ const current = normalizeAnalyzerRunnerIdentityForComparison(currentIdentity);
2151
+ return indexed !== null && current !== null && (0, node_util_1.isDeepStrictEqual)(indexed, current);
2152
+ }
2153
+ /**
2154
+ * Capture analyzer identity before invoking a loader that may evaluate the
2155
+ * analyzer module graph. The explicit receipt is then threaded into analysis
2156
+ * and checked again immediately before metadata commit.
2157
+ */
2158
+ async function captureAnalyzerIdentityBeforeLoad(analyzerModuleUrl, loader, options = {}) {
2159
+ const runnerIdentity = resolveAnalyzerRunnerIdentity(analyzerModuleUrl, options);
2160
+ const loaded = await loader();
2161
+ return { runnerIdentity, loaded };
2162
+ }
2163
+ /** Re-resolve immediately before commit and reject analyzer mutation mid-run. */
2164
+ function finalizeAnalyzerRunnerIdentity(analyzerModuleUrl, startedWith, options = {}) {
2165
+ const finalIdentity = resolveAnalyzerRunnerIdentity(analyzerModuleUrl, options);
2166
+ if (!analyzerRunnerIdentitiesEqual(startedWith, finalIdentity)) {
2167
+ throw new Error('Analyzer build or dependency runtime changed during analysis; refusing to stamp metadata. ' +
2168
+ 'Retry with a stable GitNexus installation.');
2169
+ }
2170
+ return finalIdentity;
2171
+ }