@backbencher/brain 0.0.0-stage → 0.1.1

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 (319) hide show
  1. package/CHANGELOG.md +298 -0
  2. package/EMBEDDINGS.md +355 -0
  3. package/LICENSE +21 -0
  4. package/README.md +415 -2
  5. package/bin/bb +13 -0
  6. package/dist/analysis-exclusions-7ZJDXOVZ.js +35 -0
  7. package/dist/brain-index-health-Q5APTJQL.js +17 -0
  8. package/dist/brain-relationship-enrichment-MYR2AM4F.js +558 -0
  9. package/dist/brain.sql-EIM4WS4D.js +29 -0
  10. package/dist/chunk-22V37YTK.js +1758 -0
  11. package/dist/chunk-2BSKD7O3.js +40 -0
  12. package/dist/chunk-2DE27DVC.js +1314 -0
  13. package/dist/chunk-2RRBMNBQ.js +322 -0
  14. package/dist/chunk-2WXYNLBA.js +29 -0
  15. package/dist/chunk-3CTFIJJB.js +17 -0
  16. package/dist/chunk-3ZTOHHSW.js +2104 -0
  17. package/dist/chunk-4H22TLOT.js +543 -0
  18. package/dist/chunk-5BVAH26D.js +340 -0
  19. package/dist/chunk-62DHLYW6.js +3427 -0
  20. package/dist/chunk-7AN7U5IZ.js +371 -0
  21. package/dist/chunk-7DRPNVIN.js +782 -0
  22. package/dist/chunk-7MBYT777.js +174 -0
  23. package/dist/chunk-AHDWGM7Q.js +236 -0
  24. package/dist/chunk-B5GEAAYF.js +818 -0
  25. package/dist/chunk-BEIZZ73B.js +1479 -0
  26. package/dist/chunk-BQ3OVQ3P.js +125 -0
  27. package/dist/chunk-D2Q4Y47A.js +53 -0
  28. package/dist/chunk-DIYAGQJL.js +34 -0
  29. package/dist/chunk-DWKXZ6IF.js +336 -0
  30. package/dist/chunk-DYM4FQEE.js +90 -0
  31. package/dist/chunk-ENW4BTBA.js +930 -0
  32. package/dist/chunk-EWMWK7WN.js +75 -0
  33. package/dist/chunk-EY5ZZUUA.js +626 -0
  34. package/dist/chunk-GBI7V7KY.js +69 -0
  35. package/dist/chunk-GEFFO6O2.js +152 -0
  36. package/dist/chunk-HJEWO5EV.js +247 -0
  37. package/dist/chunk-HQSPJDHI.js +613 -0
  38. package/dist/chunk-KAET47ZL.js +125 -0
  39. package/dist/chunk-KCDECRJY.js +440 -0
  40. package/dist/chunk-L5TDYKYK.js +76 -0
  41. package/dist/chunk-MLKGABMK.js +9 -0
  42. package/dist/chunk-NO3U3FJQ.js +201 -0
  43. package/dist/chunk-OWISBAOF.js +61 -0
  44. package/dist/chunk-QKTRECYS.js +1119 -0
  45. package/dist/chunk-QLK6ED4Q.js +235 -0
  46. package/dist/chunk-R2GW4F6R.js +19 -0
  47. package/dist/chunk-RCMTFSEF.js +87 -0
  48. package/dist/chunk-RM3PY6TG.js +376 -0
  49. package/dist/chunk-ROMM6KMC.js +433 -0
  50. package/dist/chunk-SGGCSHAX.js +12050 -0
  51. package/dist/chunk-SK6YVHFZ.js +211 -0
  52. package/dist/chunk-SM7RFQRO.js +214 -0
  53. package/dist/chunk-UGR4QIGS.js +67 -0
  54. package/dist/chunk-UQNQZXHY.js +711 -0
  55. package/dist/chunk-UUNHHX4M.js +13 -0
  56. package/dist/chunk-VL52PL2T.js +71 -0
  57. package/dist/chunk-WIG36E67.js +196 -0
  58. package/dist/chunk-XHK3WWGB.js +7436 -0
  59. package/dist/chunk-XTQM2IP3.js +2863 -0
  60. package/dist/chunk-ZE2LG76D.js +1296 -0
  61. package/dist/chunk-ZETTGPAW.js +6950 -0
  62. package/dist/cli/index.js +37 -0
  63. package/dist/config-HZAAYDP3.js +10 -0
  64. package/dist/content-search-7XIP555Q.js +593 -0
  65. package/dist/database-76RP44JG.js +9 -0
  66. package/dist/doctor-ANGETYMN.js +395 -0
  67. package/dist/edit-engine-CG72LDZD.js +51 -0
  68. package/dist/engine-BKEOGN7N.js +11 -0
  69. package/dist/engine-VIW6NSKO.js +18 -0
  70. package/dist/fingerprint-OBLIMZLP.js +33 -0
  71. package/dist/framework-intelligence-3LORTPOX.js +860 -0
  72. package/dist/freshness-3K6VU5OT.js +174 -0
  73. package/dist/graph-engine-HBSQNPTB.js +21 -0
  74. package/dist/graph_request-Q6NKB6N2.js +350 -0
  75. package/dist/impact_request-LYP672WE.js +68 -0
  76. package/dist/index-space-IUUN5WYV.js +15 -0
  77. package/dist/index-status-7OVGYSGW.js +34 -0
  78. package/dist/index.js +159 -0
  79. package/dist/instance-FO6QZRLH.js +19 -0
  80. package/dist/language-detection-DFCAU5DZ.js +11 -0
  81. package/dist/lsp/server.js +4301 -0
  82. package/dist/mcp/server.js +132 -0
  83. package/dist/onboard-DIQZV2NW.js +161 -0
  84. package/dist/override-companion-pass-XR7MHTP7.js +117 -0
  85. package/dist/parallel-indexer-FK2XKZXY.js +88 -0
  86. package/dist/pool-worker.js +1153 -0
  87. package/dist/project-health-6XPENVAA.js +94 -0
  88. package/dist/project-index-5SDPHITE.js +57 -0
  89. package/dist/project-registry-ZJ3DVUGO.js +355 -0
  90. package/dist/real-bash-analyzer-EJAU5LW5.js +2201 -0
  91. package/dist/real-c-analyzer-HBD3FNNH.js +5942 -0
  92. package/dist/real-clean-analyzer-4EEZLPV5.js +3470 -0
  93. package/dist/real-clojure-analyzer-Q55EBOO2.js +2818 -0
  94. package/dist/real-cpp-analyzer-IVVOD25R.js +4101 -0
  95. package/dist/real-csharp-analyzer-ZBNLNJZJ.js +3298 -0
  96. package/dist/real-css-analyzer-6XRQLUN7.js +15 -0
  97. package/dist/real-dart-analyzer-OLR72EFW.js +3944 -0
  98. package/dist/real-elixir-analyzer-MQWGAH2I.js +2192 -0
  99. package/dist/real-erlang-analyzer-7BVPQEHO.js +1731 -0
  100. package/dist/real-fsharp-analyzer-C2UPFNOA.js +2390 -0
  101. package/dist/real-go-analyzer-UKGI43YS.js +4126 -0
  102. package/dist/real-graphql-analyzer-VE2EEFII.js +1442 -0
  103. package/dist/real-groovy-analyzer-W26VACFW.js +2285 -0
  104. package/dist/real-haskell-analyzer-5CVVAJRX.js +2533 -0
  105. package/dist/real-hcl-analyzer-5ZT4U43N.js +725 -0
  106. package/dist/real-html-analyzer-JWNX3C6I.js +1759 -0
  107. package/dist/real-java-analyzer-GU42VBGQ.js +4677 -0
  108. package/dist/real-javascript-analyzer-QA4QOJ6W.js +14 -0
  109. package/dist/real-json-analyzer-MPA6PRXL.js +1640 -0
  110. package/dist/real-julia-analyzer-22CWYQMN.js +2085 -0
  111. package/dist/real-kotlin-analyzer-QDZJLXXU.js +3956 -0
  112. package/dist/real-lua-analyzer-G7NHLYKS.js +1957 -0
  113. package/dist/real-makefile-analyzer-OCY3GT5N.js +920 -0
  114. package/dist/real-markdown-analyzer-TEJK2JXJ.js +1215 -0
  115. package/dist/real-matlab-analyzer-Q5SWLN7Z.js +480 -0
  116. package/dist/real-nim-analyzer-YLXSK563.js +3603 -0
  117. package/dist/real-objective-c-analyzer-D23ACC2R.js +1518 -0
  118. package/dist/real-perl-analyzer-WWTRHYDT.js +1865 -0
  119. package/dist/real-php-analyzer-JNKJHTCL.js +9368 -0
  120. package/dist/real-powershell-analyzer-XRSNQERV.js +2915 -0
  121. package/dist/real-protobuf-analyzer-3OKUCZ7T.js +867 -0
  122. package/dist/real-python-analyzer-VXAFWI72.js +4880 -0
  123. package/dist/real-r-analyzer-ZGAIDVI4.js +2596 -0
  124. package/dist/real-ruby-analyzer-X2KTLVBH.js +2429 -0
  125. package/dist/real-rust-analyzer-OFFHPL2A.js +3359 -0
  126. package/dist/real-scala-analyzer-TSTW27J6.js +3293 -0
  127. package/dist/real-scss-analyzer-AVWYPSOQ.js +1515 -0
  128. package/dist/real-solidity-analyzer-UCI2KWUB.js +2608 -0
  129. package/dist/real-sql-analyzer-F77ZMQXR.js +3840 -0
  130. package/dist/real-svelte-analyzer-SF7NRUO5.js +2532 -0
  131. package/dist/real-swift-analyzer-MN5QT3QN.js +2311 -0
  132. package/dist/real-toml-analyzer-66L7BDCQ.js +929 -0
  133. package/dist/real-typescript-analyzer-6KEMVDWQ.js +14 -0
  134. package/dist/real-vb-analyzer-VICVPOP7.js +3145 -0
  135. package/dist/real-vba-analyzer-HXLTTHKM.js +466 -0
  136. package/dist/real-vue-analyzer-QXR56CFV.js +2680 -0
  137. package/dist/real-xml-analyzer-YL465ZJW.js +2405 -0
  138. package/dist/real-yaml-analyzer-SUBCBNSI.js +3528 -0
  139. package/dist/real-zig-analyzer-HIQLZMGY.js +2430 -0
  140. package/dist/regex-search-NJKHMZNO.js +239 -0
  141. package/dist/rules-ACHPB7QU.js +152 -0
  142. package/dist/scoped-path-MJYWJZQX.js +11 -0
  143. package/dist/setup-show-NT5ZHYNE.js +15 -0
  144. package/dist/setup-wizard-G5YYR5T6.js +24 -0
  145. package/dist/symbol-search-GZJIM2J2.js +16 -0
  146. package/dist/taint-KXUHHPIE.js +202 -0
  147. package/dist/taint-analyzer-N7YLZU3N.js +163 -0
  148. package/dist/tool-handlers-OJDC35DI.js +72 -0
  149. package/dist/tool-registry-BPEJNSZG.js +17 -0
  150. package/dist/tool-validation-Q57YMMBX.js +32 -0
  151. package/dist/type-inference-QMT2RMLV.js +13 -0
  152. package/dist/vector-binary-LMY4QGAN.js +19 -0
  153. package/dist/version-ABP2TUL2.js +9 -0
  154. package/dist/wasm-NMWO3WXY.js +16 -0
  155. package/docs/EMBEDDINGS.md +132 -0
  156. package/docs/README.md +178 -0
  157. package/docs/data/m10-baseline.json +602 -0
  158. package/docs/data/m10-benchmark-baseline.json +57 -0
  159. package/docs/data/m10-benchmark-last-run.json +127 -0
  160. package/docs/data/m10-final-audit.json +215 -0
  161. package/docs/data/m12-final-audit.json +138 -0
  162. package/docs/data/m12-surface-matrix.json +50 -0
  163. package/docs/data/public-tools.json +1902 -0
  164. package/docs/indexing/DEAD-ASSETS.md +88 -0
  165. package/docs/indexing/INDEXER-REVIEW.md +634 -0
  166. package/docs/indexing/PERFORMANCE-FINDINGS.md +106 -0
  167. package/docs/indexing/README.md +427 -0
  168. package/docs/indexing/discovery-matrix.md +41 -0
  169. package/docs/indexing/embeddings.md +325 -0
  170. package/docs/indexing/environment-variables.md +77 -0
  171. package/docs/indexing/performance.md +323 -0
  172. package/docs/indexing/resume.md +345 -0
  173. package/docs/indexing/troubleshooting.md +391 -0
  174. package/docs/man/bb.1 +190 -0
  175. package/docs/tools/bb_analyze.md +147 -0
  176. package/docs/tools/bb_annotate.md +110 -0
  177. package/docs/tools/bb_annotations.md +106 -0
  178. package/docs/tools/bb_ask.md +101 -0
  179. package/docs/tools/bb_cross_language.md +103 -0
  180. package/docs/tools/bb_doc.md +169 -0
  181. package/docs/tools/bb_edit.md +185 -0
  182. package/docs/tools/bb_graph.md +169 -0
  183. package/docs/tools/bb_health.md +156 -0
  184. package/docs/tools/bb_impact.md +117 -0
  185. package/docs/tools/bb_index.md +108 -0
  186. package/docs/tools/bb_migrate.md +135 -0
  187. package/docs/tools/bb_patterns.md +140 -0
  188. package/docs/tools/bb_search.md +150 -0
  189. package/docs/tools/bb_security.md +100 -0
  190. package/docs/tools/bb_visualize.md +184 -0
  191. package/grammars/README.md +57 -0
  192. package/grammars/THIRD-PARTY-NOTICES.md +50 -0
  193. package/grammars/tree-sitter-c/LICENSE +21 -0
  194. package/grammars/tree-sitter-clojure/LICENSE.md +21 -0
  195. package/grammars/tree-sitter-cpp/LICENSE +21 -0
  196. package/grammars/tree-sitter-csharp/LICENSE +21 -0
  197. package/grammars/tree-sitter-css/LICENSE +21 -0
  198. package/grammars/tree-sitter-css/README.md +21 -0
  199. package/grammars/tree-sitter-dart/LICENSE +20 -0
  200. package/grammars/tree-sitter-dart-upstream/LICENSE +20 -0
  201. package/grammars/tree-sitter-dart-upstream/README.md +18 -0
  202. package/grammars/tree-sitter-elixir/LICENSE +176 -0
  203. package/grammars/tree-sitter-elixir/README.md +11 -0
  204. package/grammars/tree-sitter-fsharp/LICENSE +21 -0
  205. package/grammars/tree-sitter-fsharp/README.md +45 -0
  206. package/grammars/tree-sitter-graphql/LICENSE +21 -0
  207. package/grammars/tree-sitter-graphql/README.md +106 -0
  208. package/grammars/tree-sitter-groovy/LICENSE +21 -0
  209. package/grammars/tree-sitter-groovy/README.md +4 -0
  210. package/grammars/tree-sitter-haskell/LICENSE +21 -0
  211. package/grammars/tree-sitter-haskell/README.md +394 -0
  212. package/grammars/tree-sitter-hcl/CHANGELOG.md +102 -0
  213. package/grammars/tree-sitter-hcl/LICENSE +201 -0
  214. package/grammars/tree-sitter-hcl/README.md +39 -0
  215. package/grammars/tree-sitter-html/LICENSE +21 -0
  216. package/grammars/tree-sitter-html/README.md +21 -0
  217. package/grammars/tree-sitter-julia/LICENSE +21 -0
  218. package/grammars/tree-sitter-julia/README.md +24 -0
  219. package/grammars/tree-sitter-kotlin/LICENSE +9 -0
  220. package/grammars/tree-sitter-lua/LICENSE.md +21 -0
  221. package/grammars/tree-sitter-lua/README.md +29 -0
  222. package/grammars/tree-sitter-make/LICENSE +21 -0
  223. package/grammars/tree-sitter-matlab/LICENSE +19 -0
  224. package/grammars/tree-sitter-matlab/README.md +99 -0
  225. package/grammars/tree-sitter-nim/LICENSE +373 -0
  226. package/grammars/tree-sitter-nim/README.md +46 -0
  227. package/grammars/tree-sitter-objc/LICENSE +21 -0
  228. package/grammars/tree-sitter-objc/README.md +20 -0
  229. package/grammars/tree-sitter-perl/LICENSE +7 -0
  230. package/grammars/tree-sitter-perl/README.md +100 -0
  231. package/grammars/tree-sitter-powershell/LICENSE +21 -0
  232. package/grammars/tree-sitter-powershell/README.md +7 -0
  233. package/grammars/tree-sitter-proto/LICENSE +21 -0
  234. package/grammars/tree-sitter-proto/README.md +105 -0
  235. package/grammars/tree-sitter-r/LICENSE +9 -0
  236. package/grammars/tree-sitter-r/README.md +66 -0
  237. package/grammars/tree-sitter-ruby/LICENSE +22 -0
  238. package/grammars/tree-sitter-scss/LICENSE +21 -0
  239. package/grammars/tree-sitter-scss/README.md +17 -0
  240. package/grammars/tree-sitter-solidity/LICENSE +7 -0
  241. package/grammars/tree-sitter-sql/LICENSE +21 -0
  242. package/grammars/tree-sitter-typescript/LICENSE +21 -0
  243. package/grammars/tree-sitter-vba/LICENSE +21 -0
  244. package/grammars/tree-sitter-vba/README.md +738 -0
  245. package/grammars/tree-sitter-vbnet/LICENSE +21 -0
  246. package/grammars/tree-sitter-vbnet/README.md +322 -0
  247. package/grammars/tree-sitter-xml/LICENSE +19 -0
  248. package/grammars/tree-sitter-yaml/LICENSE +20 -0
  249. package/grammars/tree-sitter-yaml/README.md +21 -0
  250. package/package.json +184 -4
  251. package/wasm/dart.wasm +0 -0
  252. package/wasm/tree-sitter-bash.wasm +0 -0
  253. package/wasm/tree-sitter-c.wasm +0 -0
  254. package/wasm/tree-sitter-c_sharp.wasm +0 -0
  255. package/wasm/tree-sitter-clean.wasm +0 -0
  256. package/wasm/tree-sitter-clojure.wasm +0 -0
  257. package/wasm/tree-sitter-core_schema.wasm +0 -0
  258. package/wasm/tree-sitter-cpp.wasm +0 -0
  259. package/wasm/tree-sitter-css.wasm +0 -0
  260. package/wasm/tree-sitter-dart.wasm +0 -0
  261. package/wasm/tree-sitter-elisp.wasm +0 -0
  262. package/wasm/tree-sitter-elixir.wasm +0 -0
  263. package/wasm/tree-sitter-elm.wasm +0 -0
  264. package/wasm/tree-sitter-embedded_template.wasm +0 -0
  265. package/wasm/tree-sitter-erlang.wasm +0 -0
  266. package/wasm/tree-sitter-fsharp.wasm +0 -0
  267. package/wasm/tree-sitter-fsharp_signature.wasm +0 -0
  268. package/wasm/tree-sitter-go.wasm +0 -0
  269. package/wasm/tree-sitter-graphql.wasm +0 -0
  270. package/wasm/tree-sitter-groovy.wasm +0 -0
  271. package/wasm/tree-sitter-haskell.wasm +0 -0
  272. package/wasm/tree-sitter-hcl.wasm +0 -0
  273. package/wasm/tree-sitter-html.wasm +0 -0
  274. package/wasm/tree-sitter-java.wasm +0 -0
  275. package/wasm/tree-sitter-javascript.wasm +0 -0
  276. package/wasm/tree-sitter-json.wasm +0 -0
  277. package/wasm/tree-sitter-json_schema.wasm +0 -0
  278. package/wasm/tree-sitter-julia.wasm +0 -0
  279. package/wasm/tree-sitter-kotlin.wasm +0 -0
  280. package/wasm/tree-sitter-lua.wasm +0 -0
  281. package/wasm/tree-sitter-make.wasm +0 -0
  282. package/wasm/tree-sitter-markdown.wasm +0 -0
  283. package/wasm/tree-sitter-markdown_inline.wasm +0 -0
  284. package/wasm/tree-sitter-matlab.wasm +0 -0
  285. package/wasm/tree-sitter-nim.wasm +0 -0
  286. package/wasm/tree-sitter-objc.wasm +0 -0
  287. package/wasm/tree-sitter-ocaml.wasm +0 -0
  288. package/wasm/tree-sitter-perl.wasm +0 -0
  289. package/wasm/tree-sitter-php.wasm +0 -0
  290. package/wasm/tree-sitter-php_only.wasm +0 -0
  291. package/wasm/tree-sitter-powershell.wasm +0 -0
  292. package/wasm/tree-sitter-protobuf.wasm +0 -0
  293. package/wasm/tree-sitter-python.wasm +0 -0
  294. package/wasm/tree-sitter-ql.wasm +0 -0
  295. package/wasm/tree-sitter-r.wasm +0 -0
  296. package/wasm/tree-sitter-rescript.wasm +0 -0
  297. package/wasm/tree-sitter-ruby.wasm +0 -0
  298. package/wasm/tree-sitter-rust.wasm +0 -0
  299. package/wasm/tree-sitter-scala.wasm +0 -0
  300. package/wasm/tree-sitter-scss.wasm +0 -0
  301. package/wasm/tree-sitter-solidity.wasm +0 -0
  302. package/wasm/tree-sitter-sql.wasm +0 -0
  303. package/wasm/tree-sitter-svelte.wasm +0 -0
  304. package/wasm/tree-sitter-swift.wasm +0 -0
  305. package/wasm/tree-sitter-systemrdl.wasm +0 -0
  306. package/wasm/tree-sitter-terraform.wasm +0 -0
  307. package/wasm/tree-sitter-tlaplus.wasm +0 -0
  308. package/wasm/tree-sitter-toml.wasm +0 -0
  309. package/wasm/tree-sitter-tsx.wasm +0 -0
  310. package/wasm/tree-sitter-typescript.wasm +0 -0
  311. package/wasm/tree-sitter-vba.wasm +0 -0
  312. package/wasm/tree-sitter-vbnet.wasm +0 -0
  313. package/wasm/tree-sitter-vue.wasm +0 -0
  314. package/wasm/tree-sitter-xml.wasm +0 -0
  315. package/wasm/tree-sitter-yaml.wasm +0 -0
  316. package/wasm/tree-sitter-zig.wasm +0 -0
  317. package/wasm/tree-sitter.wasm +0 -0
  318. package/wasm/tsx.wasm +0 -0
  319. package/wasm/typescript.wasm +0 -0
@@ -0,0 +1,2915 @@
1
+ import {
2
+ ASTEngine
3
+ } from "./chunk-3ZTOHHSW.js";
4
+ import "./chunk-BQ3OVQ3P.js";
5
+ import "./chunk-SK6YVHFZ.js";
6
+ import "./chunk-2BSKD7O3.js";
7
+ import "./chunk-KAET47ZL.js";
8
+ import "./chunk-KCDECRJY.js";
9
+ import "./chunk-MLKGABMK.js";
10
+
11
+ // src/ast/real-powershell-analyzer.ts
12
+ var AUTOMATIC_VARIABLES = /* @__PURE__ */ new Set([
13
+ "this",
14
+ "_",
15
+ "psitem",
16
+ "args",
17
+ "input",
18
+ "psscriptroot",
19
+ "pscommandpath",
20
+ // `psmodulepath` IS NOT IN THIS SET, and its absence is a measured correction: the
21
+ // automatic variable is `$env:PSModulePath`, which the scope-prefix guard already keeps
22
+ // out of the declaration space, while `$PSModulePath` is not on
23
+ // about_Automatic_Variables at all. The list carried the bare spelling, so
24
+ // `ConsoleHost.Tests.ps1:403`'s `$psModulePath = & $powershell …` — a real local — declared
25
+ // nothing and every read of it was suppressed. Two rows over the whole admitted
26
+ // population, both real declarations.
27
+ "psversiontable",
28
+ "host",
29
+ "pid",
30
+ "lastexitcode",
31
+ "error",
32
+ "home",
33
+ "pwd",
34
+ "myinvocation",
35
+ "matches",
36
+ "null",
37
+ "true",
38
+ "false",
39
+ "ofs",
40
+ "shellid",
41
+ "stacktrace",
42
+ "executioncontext",
43
+ "psculture",
44
+ "psuiculture",
45
+ "psdebugcontext",
46
+ "psedition",
47
+ "iscoreclr",
48
+ "islinux",
49
+ "ismacos",
50
+ "iswindows",
51
+ "consolefilename",
52
+ "consoleverbosity",
53
+ "enabledexperimentalfeatures",
54
+ "errorview",
55
+ "maximumhistorycount",
56
+ "nestedpromptlevel",
57
+ "outputencoding",
58
+ // M60 #278 / ontology review 2026-10-27: `$PSHOME` and `$PROFILE` are on the SAME
59
+ // documented page (about_Automatic_Variables) as `$PWD` and `$SHELLID`, which this
60
+ // list already carries — and they were missing. `& "$PSHOME/pwsh"` therefore
61
+ // published `uses -> PSHOME` for an engine binding while `$PID` in the same file
62
+ // was suppressed, and F5 scored the row as an unexplained read against an oracle
63
+ // that had correctly refused it. The set is the documentation's, so the fix is to
64
+ // finish reading it rather than to invent a rule: these are bindings the engine
65
+ // supplies, not names the file declares or reads from program data.
66
+ "pshome",
67
+ "profile",
68
+ // The call-site family: `$PSCmdlet` is the cmdlet instance, `$PSBoundParameters`
69
+ // its bound arguments, and the invocation-info/event members are all documented on
70
+ // about_Automatic_Variables beside the ones above. Measured on OutErrorVariable.
71
+ // Tests.ps1: `$PSCmdlet.writeobject("foo")` published `uses -> PSCmdlet` — an
72
+ // engine binding read as program data — on three lines, after the pipeline-read
73
+ // case started reading `$PSCmdlet.Member(...)` receivers.
74
+ "pscmdlet",
75
+ "psboundparameters",
76
+ "pscommandline",
77
+ "pscmdletbindingmetadata",
78
+ "senderinfo",
79
+ "event",
80
+ "eventargs",
81
+ "eventsubscriber",
82
+ "sourceeventargs",
83
+ "sender",
84
+ "invocationinfo",
85
+ "psinvocationinfo",
86
+ "scriptstacktrace"
87
+ ]);
88
+ var FLOW_KEYWORDS = /* @__PURE__ */ new Set([
89
+ "break",
90
+ "continue",
91
+ "return",
92
+ "exit",
93
+ "throw",
94
+ "else",
95
+ "elseif",
96
+ "finally",
97
+ "trap",
98
+ "try",
99
+ "catch",
100
+ "in",
101
+ "notin",
102
+ "as",
103
+ "is",
104
+ "and",
105
+ "or",
106
+ "xor",
107
+ "not",
108
+ "band",
109
+ "bor",
110
+ "bxor",
111
+ "bnot",
112
+ "shl",
113
+ "shr",
114
+ "eq",
115
+ "ne",
116
+ "gt",
117
+ "ge",
118
+ "lt",
119
+ "le",
120
+ "like",
121
+ "notlike",
122
+ "match",
123
+ "notmatch",
124
+ "contains",
125
+ "notcontains",
126
+ "replace",
127
+ "split",
128
+ "join",
129
+ "f",
130
+ "r",
131
+ // A class member's accessor keyword: `set { … }` is not a cmdlet.
132
+ "get",
133
+ "set"
134
+ ]);
135
+ var isSpaceCode = (code) => code === 32 || code >= 9 && code <= 13 || code === 160 || code === 5760 || code >= 8192 && code <= 8202 || code === 8232 || code === 8233 || code === 8239 || code === 8287 || code === 12288 || code === 65279;
136
+ var isAsciiLetter = (c) => {
137
+ if (c === void 0) return false;
138
+ const code = c.charCodeAt(0);
139
+ return code >= 65 && code <= 90 || code >= 97 && code <= 122;
140
+ };
141
+ var isPlausibleSymbolName = (name) => {
142
+ if (name.length === 0) return false;
143
+ for (let i = 0; i < name.length; i++) {
144
+ const c = name.charCodeAt(i);
145
+ const ok = c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95 || c === 46 || c === 58 || c === 45;
146
+ if (!ok) return false;
147
+ }
148
+ return true;
149
+ };
150
+ var isNewSpelling = (name) => {
151
+ if (name.length !== 3) return false;
152
+ return (name.charCodeAt(0) | 32) === 110 && (name.charCodeAt(1) | 32) === 101 && (name.charCodeAt(2) | 32) === 119;
153
+ };
154
+ var stripLeadingDash = (text) => text.length > 0 && text.charCodeAt(0) === 45 ? text.slice(1) : text;
155
+ var stripTypeSuffixBrackets = (text) => {
156
+ let out = "";
157
+ let i = 0;
158
+ while (i < text.length) {
159
+ if (text.charCodeAt(i) === 91) {
160
+ let j = i + 1;
161
+ while (j < text.length && isSpaceCode(text.charCodeAt(j))) j++;
162
+ if (j < text.length && text.charCodeAt(j) === 93) {
163
+ i = j + 1;
164
+ continue;
165
+ }
166
+ }
167
+ out += text[i];
168
+ i++;
169
+ }
170
+ return out;
171
+ };
172
+ var hasTrailingExtension = (text) => {
173
+ const dot = text.lastIndexOf(".");
174
+ if (dot < 0 || dot === text.length - 1) return false;
175
+ for (let i = dot + 1; i < text.length; i++) {
176
+ const c = text.charCodeAt(i);
177
+ const alnum = c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122;
178
+ if (!alnum) return false;
179
+ }
180
+ return true;
181
+ };
182
+ var sourcedPartName = (target) => {
183
+ const normalized = target.split("\\").join("/");
184
+ const base = normalized.slice(normalized.lastIndexOf("/") + 1);
185
+ if (base.length === 0) return null;
186
+ return isPlausibleSymbolName(base) ? base : null;
187
+ };
188
+ var stripOpenCommentDelim = (line) => {
189
+ let i = 0;
190
+ while (i < line.length && isSpaceCode(line.charCodeAt(i))) i++;
191
+ if (line.charCodeAt(i) === 60 && line.charCodeAt(i + 1) === 35) return line.slice(i + 2);
192
+ return line;
193
+ };
194
+ var stripCloseCommentDelim = (line) => {
195
+ let end = line.length;
196
+ while (end > 0 && isSpaceCode(line.charCodeAt(end - 1))) end--;
197
+ if (end >= 2 && line.charCodeAt(end - 1) === 62 && line.charCodeAt(end - 2) === 35) {
198
+ return line.slice(0, end - 2);
199
+ }
200
+ return line;
201
+ };
202
+ var stripCommentMarkerRun = (text) => {
203
+ let i = 0;
204
+ while (i < text.length && text.charCodeAt(i) === 35) i++;
205
+ if (i > 0 && i < text.length && isSpaceCode(text.charCodeAt(i))) i++;
206
+ return text.slice(i);
207
+ };
208
+ var NAMED_BLOCK_KEYWORDS = /* @__PURE__ */ new Set(["begin", "process", "end", "clean", "dynamicparam"]);
209
+ var LANGUAGE_KEYWORD_COMMANDS = /* @__PURE__ */ new Set([
210
+ "inlinescript",
211
+ "parallel",
212
+ "sequence",
213
+ "workflow",
214
+ "param",
215
+ "function",
216
+ "class",
217
+ "enum",
218
+ "filter",
219
+ "data",
220
+ "default",
221
+ "hidden",
222
+ "static",
223
+ "type",
224
+ "namespace",
225
+ "module",
226
+ "export"
227
+ ]);
228
+ var DSC_KEYWORDS = /* @__PURE__ */ new Set(["configuration", "node"]);
229
+ var SWITCH_ARGUMENT_VALUES = /* @__PURE__ */ new Set([
230
+ "stop",
231
+ "silentlycontinue",
232
+ "continue",
233
+ "inquire",
234
+ "yes",
235
+ "no",
236
+ "none",
237
+ "ignore",
238
+ "true",
239
+ "false",
240
+ "0",
241
+ "1",
242
+ "pass",
243
+ "fail"
244
+ ]);
245
+ var IMPORT_MODULE_MODULE_PARAMETERS = /* @__PURE__ */ new Set(["name", "assembly"]);
246
+ var QUALIFIED_PREFIXES = ["$global:", "$script:", "$env:", "$using:", "$local:", "$private:", "$variable:"];
247
+ var carriesQualifierByte = (t) => {
248
+ for (let i = 0; i < t.length; i++) {
249
+ const c = t.charCodeAt(i);
250
+ if (c === 46 || c === 92 || c === 47 || c === 58) return true;
251
+ }
252
+ return false;
253
+ };
254
+ var FILE_DEFINABLE_SCOPE_PREFIXES = ["global", "script", "local", "private", "function"];
255
+ var hasScopePrefix = (t, prefixes) => {
256
+ for (const p of prefixes) {
257
+ if (t.length <= p.length + 1) continue;
258
+ if (t.charCodeAt(p.length) !== 58) continue;
259
+ let eq = true;
260
+ for (let i = 0; i < p.length; i++) {
261
+ let c = t.charCodeAt(i);
262
+ if (c >= 65 && c <= 90) c += 32;
263
+ if (c !== p.charCodeAt(i)) {
264
+ eq = false;
265
+ break;
266
+ }
267
+ }
268
+ if (eq) return true;
269
+ }
270
+ return false;
271
+ };
272
+ var hasFileDefinableScopePrefix = (t) => hasScopePrefix(t, FILE_DEFINABLE_SCOPE_PREFIXES);
273
+ var definableScopeOf = (name) => {
274
+ for (const p of FILE_DEFINABLE_SCOPE_PREFIXES) {
275
+ if (name.length <= p.length + 1) continue;
276
+ if (name.charCodeAt(p.length) !== 58) continue;
277
+ let eq = true;
278
+ for (let i = 0; i < p.length; i++) {
279
+ let c = name.charCodeAt(i);
280
+ if (c >= 65 && c <= 90) c += 32;
281
+ if (c !== p.charCodeAt(i)) {
282
+ eq = false;
283
+ break;
284
+ }
285
+ }
286
+ if (!eq) continue;
287
+ const bare = name.slice(p.length + 1);
288
+ if (bare.length === 0) return null;
289
+ return { scope: p, bare };
290
+ }
291
+ return null;
292
+ };
293
+ var isAsciiUpper = (c) => c >= 65 && c <= 90;
294
+ var isRelativeProjectFilePath = (t) => {
295
+ const n = t.length;
296
+ if (n < 4) return false;
297
+ const isPs = (i) => {
298
+ const c = t.charCodeAt(i);
299
+ return c === 112 || c === 80;
300
+ };
301
+ const isS = (i) => {
302
+ const c = t.charCodeAt(i);
303
+ return c === 115 || c === 83;
304
+ };
305
+ const isDot = (i) => t.charCodeAt(i) === 46;
306
+ const isOne = (i) => t.charCodeAt(i) === 49;
307
+ let endsAdmitted = isDot(n - 4) && isPs(n - 3) && isS(n - 2) && isOne(n - 1);
308
+ if (!endsAdmitted && n >= 5 && isDot(n - 5) && isPs(n - 4) && isS(n - 3) && isOne(n - 1)) {
309
+ const c = t.charCodeAt(n - 2);
310
+ endsAdmitted = c === 109 || c === 77 || c === 100 || c === 68;
311
+ }
312
+ if (!endsAdmitted) {
313
+ const dot = t.lastIndexOf(".");
314
+ if (dot > 0 && dot < t.length - 1) {
315
+ let ext = "";
316
+ for (let i = dot + 1; i < t.length; i++) {
317
+ const c = t.charCodeAt(i);
318
+ if (c >= 65 && c <= 90) {
319
+ ext += String.fromCharCode(c + 32);
320
+ continue;
321
+ }
322
+ if (c >= 97 && c <= 122) {
323
+ ext += String.fromCharCode(c);
324
+ continue;
325
+ }
326
+ ext = "";
327
+ break;
328
+ }
329
+ if (ext.length > 0 && (PROGRAM_EXTENSIONS.has(ext) || ext === "dll" || ext === "nupkg" || ext === "zip")) {
330
+ endsAdmitted = true;
331
+ }
332
+ }
333
+ }
334
+ if (!endsAdmitted) return false;
335
+ if (t.charCodeAt(0) === 47) return false;
336
+ if (t.charCodeAt(0) === 92) return false;
337
+ const colon = t.indexOf(":");
338
+ if (colon > 0) {
339
+ if (colon === 1) return false;
340
+ for (let i = 0; i < colon; i++) {
341
+ const c = t.charCodeAt(i);
342
+ const legal = c >= 97 && c <= 122 || c >= 65 && c <= 90 || c >= 48 && c <= 57 || c === 43 || c === 45 || c === 46;
343
+ if (!legal) return true;
344
+ }
345
+ return false;
346
+ }
347
+ return true;
348
+ };
349
+ var runtimeVariableTarget = (t) => {
350
+ if (t.length < 2) return null;
351
+ if (t.charCodeAt(0) !== 36) return null;
352
+ let i = 1;
353
+ while (i < t.length) {
354
+ const c = t.charCodeAt(i);
355
+ const isWord = c >= 65 && c <= 90 || c >= 97 && c <= 122 || c >= 48 && c <= 57 || c === 95;
356
+ if (!isWord) break;
357
+ i++;
358
+ }
359
+ if (i === 1) return null;
360
+ const name = t.slice(1, i);
361
+ const sep = t.charCodeAt(i);
362
+ if (sep === 58) return { name, isPath: false };
363
+ if (sep === 47 || sep === 92) return { name, isPath: true };
364
+ return null;
365
+ };
366
+ var PROGRAM_EXTENSIONS = /* @__PURE__ */ new Set(["exe", "cmd", "bat", "com", "msi"]);
367
+ var programNameOf = (t) => {
368
+ for (let i = 0; i < t.length; i++) {
369
+ const c = t.charCodeAt(i);
370
+ if (c === 47 || c === 92 || c === 36) return null;
371
+ }
372
+ const dot = t.lastIndexOf(".");
373
+ if (dot <= 0 || dot === t.length - 1) return null;
374
+ const ext = t.slice(dot + 1).toLowerCase();
375
+ return PROGRAM_EXTENSIONS.has(ext) ? t : null;
376
+ };
377
+ var isWordCode = (code) => code >= 48 && code <= 57 || code >= 65 && code <= 90 || code >= 97 && code <= 122 || code === 95 || code === 46;
378
+ var addTypeNamesIn = (text, roots, types) => {
379
+ const keywords = ["namespace", "class", "struct", "interface", "enum"];
380
+ let currentNamespace = "";
381
+ for (let i = 0; i < text.length; i++) {
382
+ for (const kw of keywords) {
383
+ if (i + kw.length > text.length) continue;
384
+ let ok = true;
385
+ for (let j = 0; j < kw.length; j++) {
386
+ if ((text.charCodeAt(i + j) | 32) !== kw.charCodeAt(j)) {
387
+ ok = false;
388
+ break;
389
+ }
390
+ }
391
+ if (!ok) continue;
392
+ if (i > 0 && isWordCode(text.charCodeAt(i - 1))) continue;
393
+ let k = i + kw.length;
394
+ if (k < text.length && isWordCode(text.charCodeAt(k))) continue;
395
+ while (k < text.length && isSpaceCode(text.charCodeAt(k))) k++;
396
+ const start = k;
397
+ while (k < text.length && isWordCode(text.charCodeAt(k))) k++;
398
+ if (k === start) continue;
399
+ const name = text.slice(start, k);
400
+ const dot = name.indexOf(".");
401
+ roots.add((dot > 0 ? name.slice(0, dot) : name).toLowerCase());
402
+ if (kw === "namespace") currentNamespace = name;
403
+ else if (types !== null && currentNamespace.length > 0) {
404
+ types.set(`${currentNamespace}.${name}`.toLowerCase(), `${currentNamespace}.${name}`);
405
+ }
406
+ i = k - 1;
407
+ break;
408
+ }
409
+ }
410
+ };
411
+ var externalReadingOf = (t) => {
412
+ if (t.charCodeAt(0) === 34 || t.charCodeAt(0) === 39) return null;
413
+ if (isRelativeProjectFilePath(t)) return null;
414
+ for (let i = 1; i < t.length; i++) {
415
+ if (t.charCodeAt(i) !== 45) continue;
416
+ if (isAsciiUpper(t.charCodeAt(0)) && isAsciiUpper(t.charCodeAt(i + 1))) return "cmdlet";
417
+ }
418
+ if (!carriesQualifierByte(t)) return null;
419
+ if (hasFileDefinableScopePrefix(t)) return null;
420
+ return "qualified";
421
+ };
422
+ var arbiterOwnerOf = (t) => {
423
+ const dot = t.lastIndexOf(".");
424
+ if (dot <= 0) return null;
425
+ return t.slice(0, dot);
426
+ };
427
+ var arbiterCanCheckOwner = (t) => {
428
+ const owner = arbiterOwnerOf(t);
429
+ if (owner === null) return true;
430
+ for (let i = 0; i < owner.length; i++) {
431
+ if (owner.charCodeAt(i) === 46) return true;
432
+ }
433
+ for (let i = 0; i < owner.length; i++) {
434
+ const c = owner.charCodeAt(i);
435
+ if (c === 58 || c === 47 || c === 32 || c === 9 || c === 10 || c === 13) return true;
436
+ }
437
+ return false;
438
+ };
439
+ var DOTNET_SYSTEM_NAMESPACES = /* @__PURE__ */ new Set([
440
+ "accessibility",
441
+ "buffers",
442
+ "codedom",
443
+ "collections",
444
+ "componentmodel",
445
+ "configuration",
446
+ "data",
447
+ "deployment",
448
+ "diagnostics",
449
+ "directoryservices",
450
+ "drawing",
451
+ "dynamic",
452
+ "formats",
453
+ "globalization",
454
+ "io",
455
+ "linq",
456
+ "management",
457
+ "media",
458
+ "net",
459
+ "numerics",
460
+ "printing",
461
+ "reflection",
462
+ "resources",
463
+ "runtime",
464
+ "security",
465
+ "servicemodel",
466
+ "serviceprocess",
467
+ "text",
468
+ "threading",
469
+ "transactions",
470
+ "web",
471
+ "windows",
472
+ "xml"
473
+ ]);
474
+ var FRAMEWORK_NESTED_NAMESPACES = /* @__PURE__ */ new Set([
475
+ "system.management.automation.language",
476
+ "system.management.automation.runspaces",
477
+ "system.management.automation.provider",
478
+ "system.management.automation.security",
479
+ "system.management.automation.remoting",
480
+ "system.management.automation.host",
481
+ "system.management.automation.moduleanalysis",
482
+ "system.management.automation.debugging",
483
+ "system.io.compression",
484
+ "system.io.isolatedstorage",
485
+ "system.io.memorymappedfiles",
486
+ "system.io.packaging",
487
+ "system.io.pipes",
488
+ "system.io.enumeration",
489
+ "system.collections.generic",
490
+ "system.collections.concurrent",
491
+ "system.collections.specialized",
492
+ "system.collections.objectmodel",
493
+ "system.collections.immutable",
494
+ "system.collections.frozen",
495
+ "system.security.cryptography.x509certificates",
496
+ "system.security.cryptography.certificates",
497
+ "system.text.json",
498
+ "system.text.regularexpressions",
499
+ "system.text.unicode",
500
+ "system.text.encodings.web",
501
+ "system.diagnostics.codeanalysis",
502
+ "system.diagnostics.tracing",
503
+ "system.diagnostics.eventlog",
504
+ "system.diagnostics.metrics",
505
+ "system.diagnostics.symbolstore"
506
+ ]);
507
+ var RESOLVED_FRAMEWORK_TYPES = /* @__PURE__ */ new Map([
508
+ // ── the documented TYPE ACCELERATORS (about_Type_Accelerators) ────────────────────────────────
509
+ ["adsi", "System.DirectoryServices.DirectoryEntry"],
510
+ ["adsisearcher", "System.DirectoryServices.DirectorySearcher"],
511
+ ["alias", "System.Management.Automation.AliasAttribute"],
512
+ ["allowemptycollection", "System.Management.Automation.AllowEmptyCollectionAttribute"],
513
+ ["allowemptystring", "System.Management.Automation.AllowEmptyStringAttribute"],
514
+ ["allow_null", "System.Management.Automation.AllowNullAttribute"],
515
+ ["array", "System.Array"],
516
+ ["bigint", "System.Numerics.BigInteger"],
517
+ ["bool", "System.Boolean"],
518
+ ["byte", "System.Byte"],
519
+ ["char", "System.Char"],
520
+ ["cimclass", "Microsoft.Management.Infrastructure.CimClass"],
521
+ ["cimconverter", "Microsoft.Management.Infrastructure.CimConverter"],
522
+ ["ciminstance", "Microsoft.Management.Infrastructure.CimInstance"],
523
+ ["cimtype", "Microsoft.Management.Infrastructure.CimType"],
524
+ ["cmdletbinding", "System.Management.Automation.CmdletBindingAttribute"],
525
+ ["cultureinfo", "System.Globalization.CultureInfo"],
526
+ ["datetime", "System.DateTime"],
527
+ ["decimal", "System.Decimal"],
528
+ ["double", "System.Double"],
529
+ ["dsclocalconfigurationmanager", "Microsoft.PowerShell.DesiredStateConfiguration.LocalConfigurationManager"],
530
+ ["dscproperty", "System.Management.Automation.DscPropertyAttribute"],
531
+ ["dscresource", "System.Management.Automation.DscResourceAttribute"],
532
+ ["float", "System.Single"],
533
+ ["guid", "System.Guid"],
534
+ ["hashtable", "System.Collections.Hashtable"],
535
+ ["initialsessionstate", "System.Management.Automation.Runspaces.InitialSessionState"],
536
+ ["int", "System.Int32"],
537
+ ["int16", "System.Int16"],
538
+ ["int32", "System.Int32"],
539
+ ["int64", "System.Int64"],
540
+ ["ipaddress", "System.Net.IPAddress"],
541
+ ["ipendpoint", "System.Net.IPEndPoint"],
542
+ ["long", "System.Int64"],
543
+ ["mailaddress", "System.Net.Mail.MailAddress"],
544
+ ["moduleinfo", "System.Management.Automation.PSModuleInfo"],
545
+ ["nullable", "System.Nullable"],
546
+ ["object", "System.Object"],
547
+ ["objectsecurity", "System.Security.AccessControl.ObjectSecurity"],
548
+ ["ordered", "System.Collections.Specialized.OrderedDictionary"],
549
+ ["outputtype", "System.Management.Automation.OutputTypeAttribute"],
550
+ ["parameter", "System.Management.Automation.ParameterAttribute"],
551
+ ["physicaladdress", "System.Net.NetworkInformation.PhysicalAddress"],
552
+ ["psobject", "System.Management.Automation.PSObject"],
553
+ ["pscredential", "System.Management.Automation.PSCredential"],
554
+ ["pscustomobject", "System.Management.Automation.PSCustomObject"],
555
+ ["pslistmodifier", "System.Management.Automation.PSListModifier"],
556
+ ["psprimitivedictionary", "System.Management.Automation.PSPrimitiveDictionary"],
557
+ ["pstypenameattribute", "System.Management.Automation.PSTypeNameAttribute"],
558
+ ["psvariable", "System.Management.Automation.PSVariable"],
559
+ ["ref", "System.Management.Automation.PSReference"],
560
+ ["regex", "System.Text.RegularExpressions.Regex"],
561
+ ["runspace", "System.Management.Automation.Runspaces.Runspace"],
562
+ ["runspacefactory", "System.Management.Automation.Runspaces.RunspaceFactory"],
563
+ ["sbyte", "System.SByte"],
564
+ ["scriptblock", "System.Management.Automation.ScriptBlock"],
565
+ ["securestring", "System.Security.SecureString"],
566
+ ["semver", "System.Management.Automation.SemanticVersion"],
567
+ ["short", "System.Int16"],
568
+ ["single", "System.Single"],
569
+ ["string", "System.String"],
570
+ ["switch", "System.Management.Automation.SwitchParameter"],
571
+ ["timespan", "System.TimeSpan"],
572
+ ["type", "System.Type"],
573
+ ["uint", "System.UInt32"],
574
+ ["uint16", "System.UInt16"],
575
+ ["uint32", "System.UInt32"],
576
+ ["uint64", "System.UInt64"],
577
+ ["ulong", "System.UInt64"],
578
+ ["uri", "System.Uri"],
579
+ ["ushort", "System.UInt16"],
580
+ ["validatecount", "System.Management.Automation.ValidateCountAttribute"],
581
+ ["validatedrive", "System.Management.Automation.ValidateDriveAttribute"],
582
+ ["validatelength", "System.Management.Automation.ValidateLengthAttribute"],
583
+ ["validatenotnull", "System.Management.Automation.ValidateNotNullAttribute"],
584
+ ["validatenotnullorempty", "System.Management.Automation.ValidateNotNullOrEmptyAttribute"],
585
+ ["validatepattern", "System.Management.Automation.ValidatePatternAttribute"],
586
+ ["validaterange", "System.Management.Automation.ValidateRangeAttribute"],
587
+ ["validatescript", "System.Management.Automation.ValidateScriptAttribute"],
588
+ ["validateset", "System.Management.Automation.ValidateSetAttribute"],
589
+ ["version", "System.Version"],
590
+ ["void", "System.Void"],
591
+ ["wildcardpattern", "System.Management.Automation.WildcardPattern"],
592
+ ["wmi", "System.Management.ManagementObject"],
593
+ ["wmiclass", "System.Management.ManagementClass"],
594
+ ["wmisearcher", "System.Management.ManagementObjectSearcher"],
595
+ ["xml", "System.Xml.XmlDocument"],
596
+ // ── NOT accelerators: resolved by the implicit-`System.` attempt, or by the loaded-assembly
597
+ // search, to the framework type of that name. Cited per line, and only where the resolution is
598
+ // a documented fact rather than a coin toss (see the block comment's "deliberately not" list).
599
+ ["bitconverter", "System.BitConverter"],
600
+ // System.BitConverter
601
+ ["console", "System.Console"],
602
+ // System.Console (implicit `System.`)
603
+ ["convert", "System.Convert"],
604
+ // System.Convert (implicit `System.`)
605
+ ["datetimeoffset", "System.DateTimeOffset"],
606
+ // System.DateTimeOffset
607
+ ["enum", "System.Enum"],
608
+ // System.Enum (implicit `System.`)
609
+ ["environment", "System.Environment"],
610
+ // System.Environment (implicit `System.`)
611
+ ["experimentalfeature", "System.Management.Automation.ExperimentalFeature"],
612
+ ["gc", "System.GC"],
613
+ // System.GC (implicit `System.`)
614
+ ["linebreakpoint", "System.Management.Automation.LineBreakpoint"],
615
+ ["math", "System.Math"],
616
+ // System.Math (implicit `System.`)
617
+ ["powershell", "System.Management.Automation.PowerShell"],
618
+ // assembly search: the PS host object
619
+ ["proxycommand", "System.Management.Automation.ProxyCommand"],
620
+ ["psserializer", "System.Management.Automation.PSSerializer"],
621
+ ["securityprotocoltype", "System.Net.SecurityProtocolType"],
622
+ ["servicepointmanager", "System.Net.ServicePointManager"],
623
+ ["tuple", "System.Tuple"],
624
+ // System.Tuple (implicit `System.`)
625
+ ["waithandle", "System.Threading.WaitHandle"]
626
+ ]);
627
+ var classMembersOf = (map, literal) => {
628
+ const exact = map.get(literal);
629
+ if (exact !== void 0) return { name: literal, members: exact };
630
+ const folded = literal.toLowerCase();
631
+ for (const [name, members] of map) if (name.toLowerCase() === folded) return { name, members };
632
+ return void 0;
633
+ };
634
+ var usingScopeRootOf = (t) => {
635
+ if (t.length <= 6) return null;
636
+ if (t.charCodeAt(5) !== 58) return null;
637
+ for (let i = 0; i < 5; i++) {
638
+ const c = t.charCodeAt(i) | 32;
639
+ if (c !== "using".charCodeAt(i)) return null;
640
+ }
641
+ return t.slice(0, 5);
642
+ };
643
+ var moduleQualifiedCommandOf = (t) => {
644
+ const at = t.indexOf("\\");
645
+ if (at <= 0) return null;
646
+ if (t.indexOf("\\", at + 1) >= 0) return null;
647
+ for (let i = 0; i < t.length; i++) if (t.charCodeAt(i) === 58) return null;
648
+ const leaf = t.slice(at + 1);
649
+ if (leaf.length === 0) return null;
650
+ const dot = leaf.lastIndexOf(".");
651
+ if (dot > 0) {
652
+ let ext = leaf.slice(dot + 1).toLowerCase();
653
+ if (ext === "exe" || ext === "ps1" || ext === "psm1" || ext === "psd1" || ext === "dll" || ext === "cmd" || ext === "bat" || ext === "com" || ext === "msi") return null;
654
+ }
655
+ return t.slice(0, at);
656
+ };
657
+ var envProviderRootOf = (t) => {
658
+ if (t.length <= 4) return null;
659
+ if (t.charCodeAt(3) !== 58) return null;
660
+ const a = t.charCodeAt(0) | 32, b = t.charCodeAt(1) | 32, c = t.charCodeAt(2) | 32;
661
+ if (!(a === 101 && b === 110 && c === 118)) return null;
662
+ return t.slice(0, 3);
663
+ };
664
+ var dotNetShorthandRootOf = (t) => {
665
+ const dot = t.indexOf(".");
666
+ if (dot <= 0) return null;
667
+ const root = t.slice(0, dot);
668
+ let folded = "";
669
+ for (let i = 0; i < root.length; i++) {
670
+ const c = root.charCodeAt(i);
671
+ folded += String.fromCharCode(c >= 65 && c <= 90 ? c + 32 : c);
672
+ }
673
+ return DOTNET_SYSTEM_NAMESPACES.has(folded) ? root : null;
674
+ };
675
+ var isCommandPositionCall = (rel) => {
676
+ if (rel.type !== ASTEngine.RelationshipType.Calls) return false;
677
+ const md = rel.metadata ?? {};
678
+ if (md.methodCall === true) return false;
679
+ if (md.staticMemberCall === true) return false;
680
+ return true;
681
+ };
682
+ var commandReadingOf = (t) => {
683
+ if (t.charCodeAt(0) === 34 || t.charCodeAt(0) === 39) return null;
684
+ if (isRelativeProjectFilePath(t)) return null;
685
+ if (hasFileDefinableScopePrefix(t)) return null;
686
+ return "command";
687
+ };
688
+ var isTypeAccelerator = (declared) => {
689
+ const bare = stripTypeSuffixBrackets(declared).trim();
690
+ return TYPE_ACCELERATORS.has(bare.toLowerCase()) || TYPE_ACCELERATORS.has(bare);
691
+ };
692
+ var TYPE_ACCELERATORS = /* @__PURE__ */ new Set([
693
+ // the primitives and their aliases
694
+ "bool",
695
+ "byte",
696
+ "char",
697
+ "datetime",
698
+ "decimal",
699
+ "double",
700
+ "float",
701
+ "int",
702
+ "int16",
703
+ "int32",
704
+ "int64",
705
+ "long",
706
+ "sbyte",
707
+ "short",
708
+ "single",
709
+ "string",
710
+ "uint",
711
+ "uint16",
712
+ "uint32",
713
+ "uint64",
714
+ "ulong",
715
+ "ushort",
716
+ "void",
717
+ "bigint",
718
+ // collections and the object model
719
+ "array",
720
+ "hashtable",
721
+ "ordered",
722
+ "object",
723
+ "psobject",
724
+ "pscustomobject",
725
+ "pslistmodifier",
726
+ "psprimitivedictionary",
727
+ "pscredential",
728
+ "scriptblock",
729
+ "switch",
730
+ "type",
731
+ "ref",
732
+ "reftype",
733
+ "void",
734
+ "xml",
735
+ "regex",
736
+ "wildcardpattern",
737
+ // values the engine names
738
+ "cultureinfo",
739
+ "guid",
740
+ "ipaddress",
741
+ "mailaddress",
742
+ "semver",
743
+ "timespan",
744
+ "uri",
745
+ "version",
746
+ "nullstring",
747
+ // CIM / WMI
748
+ "cimclass",
749
+ "cimconverter",
750
+ "ciminstance",
751
+ "cimsession",
752
+ "cimtype",
753
+ "wmi",
754
+ "wmiclass",
755
+ "wmisearcher",
756
+ // attributes (metadata, skipped before this set, listed for completeness)
757
+ "alias",
758
+ "allowemptycollection",
759
+ "allowemptystring",
760
+ "allownull",
761
+ "argumentcompleter",
762
+ "argumentcompletions",
763
+ "cmdletbinding",
764
+ "dscresource",
765
+ "experimentaction",
766
+ "experimental",
767
+ "experimentalfeature",
768
+ "feature",
769
+ "flags",
770
+ "nocompatiblecommand",
771
+ "norunspaceaffinity",
772
+ "parameter",
773
+ "psdefaultvalue",
774
+ "pstypenameattribute",
775
+ "supportswildcards",
776
+ "validatecount",
777
+ "validatedrive",
778
+ "validatelength",
779
+ "validatenotnull",
780
+ "validatenotnullorempty",
781
+ "validatepattern",
782
+ "validaterange",
783
+ "validatescript",
784
+ "validateset",
785
+ "validatetrusteddata",
786
+ "validateuserdrive"
787
+ ]);
788
+ var memberOverrides = (derived, base) => {
789
+ if (base.isHidden) return false;
790
+ if (base.isStatic) return false;
791
+ return derived.kind === base.kind;
792
+ };
793
+ var memberHeaderFacts = (node, nameNode) => {
794
+ const named = [];
795
+ for (let i = 0; i < node.childCount; i++) {
796
+ const c = node.child(i);
797
+ if (!c || !c.isNamed) continue;
798
+ named.push({ type: c.type, text: c.text });
799
+ }
800
+ const modifiers = [];
801
+ let prefix = "";
802
+ for (let i = 0; i < named.length; i++) {
803
+ if (named[i].type !== "class_attribute") continue;
804
+ const prevType = i > 0 ? named[i - 1].type : "";
805
+ const nextType = named[i + 1]?.type ?? "";
806
+ const isNamePrefix = (prevType === "attribute" || prevType === "type_literal") && nextType === "simple_name";
807
+ if (isNamePrefix) prefix = named[i].text;
808
+ else modifiers.push(named[i].text.toLowerCase());
809
+ }
810
+ const name = prefix ? prefix + nameNode.text : nameNode.text;
811
+ return { name, modifiers: modifiers.join(" ") };
812
+ };
813
+ var RealPowershellAnalyzer = class {
814
+ analyze(rootNode, content, filePath) {
815
+ const symbols = [];
816
+ const relationships = [];
817
+ const imports = [];
818
+ const exports = [];
819
+ const exportedNames = /* @__PURE__ */ new Set();
820
+ const relKeys = /* @__PURE__ */ new Set();
821
+ const frameworks = /* @__PURE__ */ new Set(["PowerShell"]);
822
+ const patterns = /* @__PURE__ */ new Set();
823
+ const fileName = this.basenameOf(filePath);
824
+ const lines = content.split("\n");
825
+ symbols.push({
826
+ name: fileName,
827
+ kind: ASTEngine.SymbolKind.Module,
828
+ range: this.nodeToRange(rootNode),
829
+ visibility: "public",
830
+ metadata: { powershellSpecific: true, isScript: true, scriptPath: filePath }
831
+ });
832
+ let cyclomaticComplexity = 1;
833
+ let dscDepth = 0;
834
+ const dscBlocks = /* @__PURE__ */ new Set();
835
+ const interfaceBlocks = /* @__PURE__ */ new Set();
836
+ const nodeKey = (n) => `${n.startIndex}:${n.endIndex}`;
837
+ const dscOwner = /* @__PURE__ */ new Map();
838
+ const frames = [{ owner: fileName, scope: fileName, containerLine: 1 }];
839
+ const top = () => frames[frames.length - 1];
840
+ const atFileScope = () => frames.length === 1;
841
+ const baseClassByClass = /* @__PURE__ */ new Map();
842
+ const classDeclaredSpelling = /* @__PURE__ */ new Map();
843
+ const classMembers = /* @__PURE__ */ new Map();
844
+ const overrideCandidates = [];
845
+ const evidenceClassFor = (type, isImport = false) => {
846
+ if (isImport) return "import_based";
847
+ switch (type) {
848
+ case ASTEngine.RelationshipType.Contains:
849
+ case ASTEngine.RelationshipType.Extends:
850
+ case ASTEngine.RelationshipType.Implements:
851
+ case ASTEngine.RelationshipType.Exports:
852
+ return "structural";
853
+ case ASTEngine.RelationshipType.References:
854
+ // `part_of` is read from a DIRECTIVE — the dot-source operator is syntax that
855
+ // names the part, and the edge exists because the directive exists. That is
856
+ // the fleet's `import_based` class (erlang ER-D46: "both are directives"), and
857
+ // it is the class css's plain-file `@import` relabel carries. The branch is
858
+ // LIVE rather than dead: the `isImport` short-circuit above fires only for
859
+ // rows that pass `import: true`, which a `part_of` row does not.
860
+ case ASTEngine.RelationshipType.PartOf:
861
+ return "import_based";
862
+ default:
863
+ return "usage_contextual";
864
+ }
865
+ };
866
+ const addRel = (source, target, type, confidence, line, metadata = {}) => {
867
+ if (!source || !target) return;
868
+ const key = `${source}\0${type}\0${target}\0${line}`;
869
+ if (relKeys.has(key)) return;
870
+ relKeys.add(key);
871
+ const isImport = metadata.import === true;
872
+ relationships.push({
873
+ source,
874
+ target,
875
+ type,
876
+ confidence,
877
+ line_number: line,
878
+ // The caller's metadata comes LAST so an explicit `evidenceClass` at a call
879
+ // site wins over the default — that is how the `overrides` edges say `inferred`
880
+ // without every other call site having to repeat the reasoning.
881
+ metadata: { evidenceClass: evidenceClassFor(type, isImport), ...metadata }
882
+ });
883
+ };
884
+ const stripDollar = (text) => {
885
+ if (text.length < 2 || text.charCodeAt(0) !== 36) return text;
886
+ if (text.charCodeAt(1) === 123 && text.charCodeAt(text.length - 1) === 125) {
887
+ return text.slice(2, -1);
888
+ }
889
+ return text.slice(1);
890
+ };
891
+ const unquoteTarget = (text) => {
892
+ if (text.length < 2) return text;
893
+ const first = text.charCodeAt(0);
894
+ if (first !== 34 && first !== 39) return text;
895
+ if (text.charCodeAt(text.length - 1) !== first) return text;
896
+ return text.slice(1, -1);
897
+ };
898
+ const isQualified = (raw) => {
899
+ const lower = raw.toLowerCase();
900
+ return QUALIFIED_PREFIXES.some((p) => lower.startsWith(p));
901
+ };
902
+ const isDeclarableVariable = (text) => !isQualified(text) || definableScopeOf(stripDollar(text.trim())) !== null;
903
+ const isAutomatic = (name) => AUTOMATIC_VARIABLES.has(name.toLowerCase());
904
+ const symbolMap = /* @__PURE__ */ new Map();
905
+ const pushSymbol = (name, kind, node, line, meta = {}, parameters, ownerOverride) => {
906
+ const raw = name.trim();
907
+ if (raw === "") return null;
908
+ if (isQualified(raw)) return null;
909
+ const owner = ownerOverride ?? top().owner;
910
+ const clean = stripDollar(raw);
911
+ if (clean === "") return null;
912
+ if (kind === ASTEngine.SymbolKind.Variable && isAutomatic(clean)) return null;
913
+ const key = `${owner}\0${clean.toLowerCase()}\0${kind}`;
914
+ const existing = symbolMap.get(key);
915
+ if (existing) return existing;
916
+ const sym = {
917
+ name: clean,
918
+ kind,
919
+ range: this.nodeToRange(node),
920
+ scope: owner,
921
+ ownerName: owner,
922
+ visibility: "public",
923
+ parameters,
924
+ metadata: { powershellSpecific: true, ...meta }
925
+ };
926
+ if (typeof meta.isStatic === "boolean") sym.isStatic = meta.isStatic;
927
+ if (typeof meta.isAbstract === "boolean") sym.isAbstract = meta.isAbstract;
928
+ if (Array.isArray(meta.annotations)) sym.decorators = meta.annotations;
929
+ if (typeof meta.returnType === "string" && meta.returnType) sym.returnType = meta.returnType;
930
+ if (typeof meta.docstring === "string" && meta.docstring) sym.docstring = meta.docstring;
931
+ symbols.push(sym);
932
+ symbolMap.set(key, sym);
933
+ return sym;
934
+ };
935
+ const addContains = (target, line, kind) => {
936
+ const owner = top().owner;
937
+ if (!owner || owner === target) return;
938
+ if ((kind === void 0 || kind === ASTEngine.SymbolKind.Variable) && isAutomatic(target)) return;
939
+ addRel(owner, target, ASTEngine.RelationshipType.Contains, 1, line ?? top().containerLine, {});
940
+ };
941
+ const declareSymbol = (rawName, kind, node, line, meta = {}, parameters, containsLine = line) => {
942
+ const name = stripDollar(rawName);
943
+ if (name === "") return;
944
+ const scoped = kind === ASTEngine.SymbolKind.Variable ? definableScopeOf(name) : null;
945
+ const bare = scoped === null ? name : scoped.bare;
946
+ const scopeOwner = scoped !== null && (scoped.scope === "script" || scoped.scope === "global") ? fileName : void 0;
947
+ if (kind === ASTEngine.SymbolKind.Variable && isAutomatic(bare)) return;
948
+ if (kind === ASTEngine.SymbolKind.Variable && scoped === null && isQualified(rawName)) return;
949
+ const key = `${scopeOwner ?? top().owner}\0${bare.toLowerCase()}\0${kind}`;
950
+ if (symbolMap.has(key)) return;
951
+ pushSymbol(
952
+ bare,
953
+ kind,
954
+ node,
955
+ line,
956
+ scoped === null ? meta : { ...meta, scopeQualifier: scoped.scope },
957
+ parameters,
958
+ scopeOwner
959
+ );
960
+ addContains(bare, containsLine, kind);
961
+ };
962
+ const emitMemberUses = (node, kind) => {
963
+ const member = this.findChildByType(node, "member_name");
964
+ if (!member) return;
965
+ let leaf = member.text;
966
+ if (!leaf) return;
967
+ let quotedMemberName = false;
968
+ if (leaf.length >= 2) {
969
+ const q = leaf.charCodeAt(0);
970
+ if ((q === 34 || q === 39) && leaf.charCodeAt(leaf.length - 1) === q) {
971
+ const inner = leaf.slice(1, -1);
972
+ if (inner.length > 0 && inner.indexOf("$") < 0) {
973
+ leaf = inner;
974
+ quotedMemberName = true;
975
+ }
976
+ }
977
+ }
978
+ if (leaf.charCodeAt(0) === 36) {
979
+ const raw = stripDollar(leaf.trim());
980
+ const name = raw.length > 1 && raw.charCodeAt(0) === 64 ? raw.slice(1) : raw;
981
+ if (!name || isAutomatic(name) || !isPlausibleSymbolName(name)) return;
982
+ addRel(
983
+ top().scope,
984
+ name,
985
+ ASTEngine.RelationshipType.Uses,
986
+ 0.9,
987
+ member.startPosition.row + 1,
988
+ {
989
+ powershellSpecific: true,
990
+ isMemberAccess: true,
991
+ isVariableReference: true,
992
+ isDynamicMemberName: true,
993
+ ...kind === "write" ? { isMemberWrite: true } : { isMemberReference: true }
994
+ }
995
+ );
996
+ return;
997
+ }
998
+ let hasNameChar = false;
999
+ for (let i = 0; i < leaf.length; i++) {
1000
+ const c = leaf.charCodeAt(i);
1001
+ if (c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 95) {
1002
+ hasNameChar = true;
1003
+ break;
1004
+ }
1005
+ }
1006
+ if (!hasNameChar || !isPlausibleSymbolName(leaf)) return;
1007
+ addRel(
1008
+ top().scope,
1009
+ leaf,
1010
+ ASTEngine.RelationshipType.Uses,
1011
+ kind === "write" ? 0.9 : 0.8,
1012
+ member.startPosition.row + 1,
1013
+ {
1014
+ powershellSpecific: true,
1015
+ isMemberAccess: true,
1016
+ // PSD14 — THE MEMBER'S NAME WAS WRITTEN IN QUOTES, so its punctuation is DATA. A quoted
1017
+ // name is how PowerShell spells a property whose name no identifier can carry
1018
+ // (`$build.triggerInfo.'pr.number'` is ONE property called `pr.number`), and the dot in
1019
+ // it is not a qualifier. The externality pass asks the arbiter's OWNER question about the
1020
+ // text before that dot — `pr` — which the project answers with a VARIABLE, so the row was
1021
+ // REFUTED for a claim the source never made. MEASURED: 2 whole-population rows. The row
1022
+ // and its `noDeclarationInFile` fact stay; only the name claim is withheld.
1023
+ ...quotedMemberName ? { isQuotedMemberName: true } : {},
1024
+ ...kind === "write" ? { isMemberWrite: true } : { isMemberReference: true }
1025
+ }
1026
+ );
1027
+ };
1028
+ const emitParameterDefaultReads = (sp) => {
1029
+ const def = findFirstChild(sp, "script_parameter_default");
1030
+ if (def) emitVariableReads(def, true, "isParameterDefault");
1031
+ };
1032
+ const emitVariableReads = (node, skipBlocks = false, marker) => {
1033
+ const work2 = [node];
1034
+ const seen = /* @__PURE__ */ new Set();
1035
+ while (work2.length > 0) {
1036
+ const n = work2.pop();
1037
+ if (n.type === "comment") continue;
1038
+ if (skipBlocks && (n.type === "script_block_expression" || n.type === "named_block")) continue;
1039
+ if (skipBlocks && n.type === "element_access") continue;
1040
+ if (n.type === "expandable_here_string_literal" && marker !== "isHereStringInterpolation") continue;
1041
+ if (n.type === "variable" || n.type === "braced_variable") {
1042
+ if (seen.has(n.startIndex)) continue;
1043
+ seen.add(n.startIndex);
1044
+ const trimmed = n.text.trim();
1045
+ const rawName = stripDollar(trimmed);
1046
+ const name = rawName.length > 1 && rawName.charCodeAt(0) === 64 ? rawName.slice(1) : rawName;
1047
+ const isScopedName = isQualified(n.text) || isQualified(`$${name}`);
1048
+ if (!isScopedName && !isPlausibleSymbolName(name)) continue;
1049
+ if (!name || isAutomatic(name)) continue;
1050
+ const isScoped = isScopedName;
1051
+ if (isScoped && name.indexOf(":") < 0) continue;
1052
+ const scopedRead = isScoped ? definableScopeOf(name) : null;
1053
+ const scopedOwner = scopedRead === null ? null : scopedRead.scope === "script" || scopedRead.scope === "global" ? fileName : top().owner;
1054
+ const publishedName = scopedRead === null ? name : `${scopedOwner}.${scopedRead.bare}`;
1055
+ const lead = n.text.length - n.text.trimStart().length;
1056
+ let skippedLines = 0;
1057
+ for (let i = 0; i < lead; i++) {
1058
+ if (n.text.charCodeAt(i) === 10) skippedLines++;
1059
+ }
1060
+ addRel(
1061
+ top().scope,
1062
+ publishedName,
1063
+ ASTEngine.RelationshipType.Uses,
1064
+ 0.9,
1065
+ n.startPosition.row + 1 + skippedLines,
1066
+ {
1067
+ powershellSpecific: true,
1068
+ isVariableReference: true,
1069
+ ...marker ? { [marker]: true } : {},
1070
+ ...scopedRead !== null ? { isScopedVariableRead: true, scopeQualifier: scopedRead.scope } : isScoped ? { isScopedEnvironmentRead: true } : {},
1071
+ // A read written in the BRACED spelling. The marker's job is to make the
1072
+ // normalization independently revertible: once `${a}` and `$a` are the
1073
+ // same string in the result, nothing distinguishes them, so a mutant for
1074
+ // "the braces come back" has nothing to key on and would be a no-op — a
1075
+ // mutant that cannot fail is decoration.
1076
+ ...trimmed.charCodeAt(0) === 36 && trimmed.charCodeAt(1) === 123 ? { isBracedVariable: true } : {}
1077
+ }
1078
+ );
1079
+ continue;
1080
+ }
1081
+ if (n.type === "member_access") {
1082
+ emitMemberUses(n, "read");
1083
+ continue;
1084
+ }
1085
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1086
+ const c = n.namedChild(i);
1087
+ if (c) work2.push(c);
1088
+ }
1089
+ }
1090
+ };
1091
+ const emitAssignmentReads = (node) => {
1092
+ const work2 = [node];
1093
+ while (work2.length > 0) {
1094
+ const n = work2.pop();
1095
+ if (n.type === "command") continue;
1096
+ if (n.type === "element_access") continue;
1097
+ if (n.type === "expandable_here_string_literal") continue;
1098
+ if (n.type === "variable" || n.type === "braced_variable" || n.type === "member_access") {
1099
+ emitVariableReads(n, true, "isAssignmentRead");
1100
+ continue;
1101
+ }
1102
+ if (n.type === "script_block_expression" || n.type === "named_block") continue;
1103
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1104
+ const c = n.namedChild(i);
1105
+ if (c) work2.push(c);
1106
+ }
1107
+ }
1108
+ };
1109
+ const isBranch = (type) => type === "if_statement" || type === "elseif_clause" || type === "while_statement" || type === "do_statement" || type === "for_statement" || type === "foreach_statement" || type === "switch_statement" || type === "try_statement" || type === "catch_clause" || type === "trap_statement" || // A `switch` CASE is a decision point. `switch_clause` was never named by
1110
+ // this lane, so a 10-case switch measured a cyclomatic complexity of 1
1111
+ // (measured: 174 `switch_clause` nodes in the production draw, all
1112
+ // uncounted).
1113
+ type === "switch_clause";
1114
+ const findFirstChild = (node, type) => {
1115
+ for (let i = 0; i < node.namedChildCount; i++) {
1116
+ const child = node.namedChild(i);
1117
+ if (child && child.type === type) return child;
1118
+ }
1119
+ return null;
1120
+ };
1121
+ const findDescendant = (node, type, maxDepth = 6) => {
1122
+ let frontier = [node];
1123
+ for (let d = 0; d < maxDepth && frontier.length > 0; d++) {
1124
+ const next = [];
1125
+ for (const n of frontier) {
1126
+ if (n.type === type) return n;
1127
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
1128
+ const child = n.namedChild(i);
1129
+ if (child) next.push(child);
1130
+ }
1131
+ }
1132
+ frontier = next;
1133
+ }
1134
+ return null;
1135
+ };
1136
+ const findDescendantVariable = (node) => findDescendant(node, "variable", 12);
1137
+ const nodeIsAVariableReference = (node) => node.type === "variable";
1138
+ const typeSpecText = (node) => {
1139
+ const specTextOf = (literal) => {
1140
+ const spec = findFirstChild(literal, "type_spec");
1141
+ if (spec) return spec.text;
1142
+ return this.stripBrackets(literal.text);
1143
+ };
1144
+ const direct = node.type === "type_literal" ? node : findFirstChild(node, "type_literal");
1145
+ if (direct) return specTextOf(direct);
1146
+ for (const list of this.collectChildrenByType(node, "attribute_list")) {
1147
+ for (const attr of this.collectChildrenByType(list, "attribute")) {
1148
+ const literal = findFirstChild(attr, "type_literal");
1149
+ if (literal) return specTextOf(literal);
1150
+ }
1151
+ }
1152
+ return "";
1153
+ };
1154
+ const collectParameters = (list) => {
1155
+ const params = [];
1156
+ if (!list) return params;
1157
+ for (let i = 0; i < list.namedChildCount; i++) {
1158
+ const p = list.namedChild(i);
1159
+ if (!p) continue;
1160
+ if (p.type !== "script_parameter" && p.type !== "class_method_parameter") continue;
1161
+ const varNode = findFirstChild(p, "variable");
1162
+ if (!varNode) continue;
1163
+ const declared = typeSpecText(p);
1164
+ const def = findFirstChild(p, "script_parameter_default");
1165
+ const entry = {
1166
+ name: stripDollar(varNode.text),
1167
+ // An untyped parameter is not `any` by declaration — it is untyped.
1168
+ // `[int]$Default = 0` and `$Default = 0` are different contracts and
1169
+ // the store's `parameters` JSON is the only place either survives.
1170
+ type: declared || "untyped",
1171
+ isOptional: def !== null
1172
+ };
1173
+ if (def) {
1174
+ const val = def.namedChildCount > 1 ? def.namedChild(1) : null;
1175
+ if (val) entry.defaultValue = val.text;
1176
+ }
1177
+ params.push(entry);
1178
+ }
1179
+ return params;
1180
+ };
1181
+ const commandNameOf = (node) => {
1182
+ const field = node.childForFieldName?.("command_name");
1183
+ const fieldNode = field ?? null;
1184
+ const nameNode = fieldNode ?? findFirstChild(node, "command_name");
1185
+ if (nameNode) return unquoteTarget(nameNode.text);
1186
+ const expr = findFirstChild(node, "command_name_expr");
1187
+ if (expr) {
1188
+ const inner = findFirstChild(expr, "command_name");
1189
+ if (inner) return inner.text;
1190
+ return unquoteTarget(expr.text);
1191
+ }
1192
+ return "";
1193
+ };
1194
+ const commandArgs = (node, sourceNodes, onlyParameters) => {
1195
+ const out = [];
1196
+ const elements = findFirstChild(node, "command_elements");
1197
+ if (!elements) return out;
1198
+ const isValueToken = (c) => !!c && (c.type === "generic_token" || c.type === "string_literal" || c.type === "expandable_string_literal");
1199
+ for (let i = 0; i < elements.namedChildCount; i++) {
1200
+ const c = elements.namedChild(i);
1201
+ if (!c) continue;
1202
+ if (c.type === "command_parameter") {
1203
+ const next = elements.namedChild(i + 1);
1204
+ let flag = "";
1205
+ {
1206
+ const raw = this.stripQuotes(c.text).trim();
1207
+ let s = raw.length > 0 && raw.charCodeAt(0) === 45 ? raw.slice(1) : raw;
1208
+ const colon = s.indexOf(":");
1209
+ if (colon >= 0) s = s.slice(0, colon);
1210
+ flag = s.toLowerCase();
1211
+ }
1212
+ if (next && isValueToken(next) && !SWITCH_ARGUMENT_VALUES.has(next.text.toLowerCase()) && (!onlyParameters || onlyParameters.has(flag))) {
1213
+ out.push(this.stripQuotes(next.text));
1214
+ sourceNodes?.push(next);
1215
+ }
1216
+ continue;
1217
+ }
1218
+ if (isValueToken(c)) {
1219
+ out.push(this.stripQuotes(c.text));
1220
+ sourceNodes?.push(c);
1221
+ continue;
1222
+ }
1223
+ if (c.type === "array_literal_expression" || c.type === "unary_expression") {
1224
+ const lit = this.findQuoted(c);
1225
+ if (lit) {
1226
+ const value = this.stripQuotes(lit.text);
1227
+ const v2 = findDescendantVariable(c);
1228
+ if (v2 && value.trim() === v2.text) {
1229
+ out.push(stripDollar(v2.text));
1230
+ sourceNodes?.push(v2);
1231
+ } else {
1232
+ out.push(value);
1233
+ sourceNodes?.push(lit);
1234
+ }
1235
+ continue;
1236
+ }
1237
+ const v = findDescendantVariable(c);
1238
+ if (v) {
1239
+ out.push(stripDollar(v.text));
1240
+ sourceNodes?.push(v);
1241
+ continue;
1242
+ }
1243
+ }
1244
+ }
1245
+ return out;
1246
+ };
1247
+ const looksLikeModulePath = (text) => text.length > 0 && (text.includes("/") || text.includes("\\") || hasTrailingExtension(text));
1248
+ const addImport = (moduleName, via, line, node) => {
1249
+ if (!moduleName) return;
1250
+ imports.push({
1251
+ module: moduleName,
1252
+ items: ["*"],
1253
+ isDefault: true,
1254
+ range: this.nodeToRange(node),
1255
+ metadata: { powershellSpecific: true, via }
1256
+ });
1257
+ const meta = { powershellSpecific: true, import: true, via, fileLevel: atFileScope() };
1258
+ addRel(top().scope, moduleName, ASTEngine.RelationshipType.Imports, 0.9, line, { ...meta });
1259
+ addRel(top().scope, moduleName, ASTEngine.RelationshipType.References, 0.9, line, { ...meta });
1260
+ };
1261
+ const isExportWildcard = (name) => {
1262
+ for (let i = 0; i < name.length; i++) {
1263
+ const code = name.charCodeAt(i);
1264
+ if (code === 42 || code === 63) return true;
1265
+ }
1266
+ return false;
1267
+ };
1268
+ const addExport = (name, via, line) => {
1269
+ if (!name || isExportWildcard(name)) return;
1270
+ if (exportedNames.has(name.toLowerCase())) return;
1271
+ exportedNames.add(name.toLowerCase());
1272
+ exports.push({ name, range: this.lineRange(line), metadata: { powershellSpecific: true, via } });
1273
+ addRel(top().scope, name, ASTEngine.RelationshipType.Exports, 1, line, {
1274
+ powershellSpecific: true,
1275
+ viaExportModuleMember: true,
1276
+ via,
1277
+ fileLevel: atFileScope()
1278
+ });
1279
+ };
1280
+ const detectFrameworks = (cmdName) => {
1281
+ const lower = cmdName.toLowerCase();
1282
+ if (lower.startsWith("get-az") || lower.startsWith("new-az") || lower.startsWith("set-az") || lower.startsWith("remove-az") || lower.startsWith("connect-az") || lower === "az" || lower.startsWith("az_")) frameworks.add("Azure Az Module");
1283
+ if (lower.startsWith("get-msol") || lower.startsWith("set-msol") || lower.startsWith("connect-msol"))
1284
+ frameworks.add("Azure AD / MSOnline");
1285
+ if (lower.startsWith("get-exo") || lower.startsWith("set-exo") || lower.startsWith("new-exo") || lower.startsWith("get-mailbox") || lower.startsWith("set-mailbox") || lower.startsWith("enable-mailbox")) frameworks.add("Exchange Online");
1286
+ if (lower.startsWith("get-sp") || lower.startsWith("set-sp") || lower.startsWith("new-sp") || lower.startsWith("connect-pnp") || lower.startsWith("get-pnp")) frameworks.add("SharePoint PnP");
1287
+ if (lower.startsWith("get-ad") || lower.startsWith("set-ad") || lower.startsWith("new-ad") || lower.startsWith("remove-ad"))
1288
+ frameworks.add("Active Directory");
1289
+ if (lower.startsWith("get-vm") || lower.startsWith("new-vm") || lower.startsWith("start-vm") || lower.startsWith("stop-vm"))
1290
+ frameworks.add("VMware / Hyper-V");
1291
+ if (lower.startsWith("invoke-rest") || lower.startsWith("invoke-web") || lower === "irm" || lower === "iwr")
1292
+ patterns.add("REST API Calls");
1293
+ if (lower.startsWith("write-") || lower.startsWith("read-")) patterns.add("I/O Operations");
1294
+ if (lower === "get-service" || lower === "stop-service" || lower === "start-service" || lower === "restart-service")
1295
+ patterns.add("Service Management");
1296
+ };
1297
+ const usingNamespacePrefixes = [];
1298
+ const typeNamesInFile = /* @__PURE__ */ new Set();
1299
+ const addTypeCreatedNames = /* @__PURE__ */ new Set();
1300
+ const addTypeDeclarations = /* @__PURE__ */ new Map();
1301
+ const recordAddTypeDeclaration = (full) => {
1302
+ if (full.length === 0) return;
1303
+ const key = full.toLowerCase();
1304
+ if (!addTypeDeclarations.has(key)) addTypeDeclarations.set(key, full);
1305
+ };
1306
+ {
1307
+ const stack = [rootNode];
1308
+ const hereStringBodies = [];
1309
+ let addTypeSeen = false;
1310
+ const valueNodeText = (n) => this.stripQuotes(n.text).trim();
1311
+ while (stack.length > 0) {
1312
+ const n = stack.pop();
1313
+ if (n.type === "class_statement" || n.type === "enum_statement") {
1314
+ const nameNode = findFirstChild(n, n.type === "class_statement" ? "class_name" : "enum_name");
1315
+ if (nameNode && nameNode.text) typeNamesInFile.add(nameNode.text.toLowerCase());
1316
+ }
1317
+ if (n.type === "string_literal" && n.text.length > 2 && n.text.charCodeAt(0) === 64) {
1318
+ hereStringBodies.push(n.text);
1319
+ }
1320
+ if (n.type === "command") {
1321
+ const nameNode = findFirstChild(n, "command_name");
1322
+ const leaf = nameNode ? nameNode.text.toLowerCase() : "";
1323
+ if (leaf === "using" || leaf === "add-type") {
1324
+ const elements = findFirstChild(n, "command_elements");
1325
+ const values = [];
1326
+ let addNamespace = "";
1327
+ let addName = "";
1328
+ if (elements) {
1329
+ for (let i = 0; i < elements.namedChildCount; i++) {
1330
+ const c = elements.namedChild(i);
1331
+ if (!c) continue;
1332
+ if (c.type === "command_parameter") {
1333
+ const next = elements.namedChild(i + 1);
1334
+ if (!next) continue;
1335
+ const flag = stripLeadingDash(this.stripQuotes(c.text)).toLowerCase();
1336
+ if (leaf === "add-type" && (flag === "namespace" || flag === "name")) {
1337
+ const spelled = valueNodeText(next);
1338
+ if (spelled.length > 0) addTypeCreatedNames.add(spelled.toLowerCase());
1339
+ if (spelled.length > 0) {
1340
+ if (flag === "namespace") addNamespace = spelled;
1341
+ else addName = spelled;
1342
+ }
1343
+ }
1344
+ continue;
1345
+ }
1346
+ if (c.type === "generic_token" || c.type === "string_literal" || c.type === "expandable_string_literal" || c.type === "array_literal_expression" || c.type === "unary_expression") values.push(c);
1347
+ }
1348
+ }
1349
+ if (leaf === "using") {
1350
+ if (values.length >= 2) {
1351
+ const directive = valueNodeText(values[0]).toLowerCase();
1352
+ const target = valueNodeText(values[1]);
1353
+ if (directive === "namespace" && target.length > 0) usingNamespacePrefixes.push(target);
1354
+ }
1355
+ } else {
1356
+ addTypeSeen = true;
1357
+ if (addNamespace.length > 0 && addName.length > 0) {
1358
+ recordAddTypeDeclaration(`${addNamespace}.${addName}`);
1359
+ }
1360
+ }
1361
+ }
1362
+ }
1363
+ for (let i = n.childCount - 1; i >= 0; i--) {
1364
+ const child = n.child(i);
1365
+ if (child) stack.push(child);
1366
+ }
1367
+ }
1368
+ if (addTypeSeen) {
1369
+ for (const body of hereStringBodies) {
1370
+ addTypeNamesIn(body, addTypeCreatedNames, addTypeDeclarations);
1371
+ }
1372
+ }
1373
+ }
1374
+ const work = [{ node: rootNode, exit: false }];
1375
+ while (work.length > 0) {
1376
+ const { node, exit } = work.pop();
1377
+ if (!node) continue;
1378
+ if (exit) {
1379
+ if (dscBlocks.has(nodeKey(node))) {
1380
+ dscBlocks.delete(nodeKey(node));
1381
+ const declared = dscOwner.get(nodeKey(node));
1382
+ dscOwner.delete(nodeKey(node));
1383
+ if (declared !== void 0) frames.pop();
1384
+ dscDepth--;
1385
+ }
1386
+ if (interfaceBlocks.has(nodeKey(node))) {
1387
+ interfaceBlocks.delete(nodeKey(node));
1388
+ frames.pop();
1389
+ }
1390
+ if (node.type === "function_statement" || node.type === "class_statement" || node.type === "enum_statement" || node.type === "class_method_definition") {
1391
+ frames.pop();
1392
+ }
1393
+ continue;
1394
+ }
1395
+ work.push({ node, exit: true });
1396
+ if (isBranch(node.type)) cyclomaticComplexity++;
1397
+ switch (node.type) {
1398
+ // ------------------------------------------------------- functions
1399
+ case "function_statement": {
1400
+ const nameNode = findFirstChild(node, "function_name");
1401
+ if (!nameNode) break;
1402
+ const fnName = nameNode.text;
1403
+ const inline = findFirstChild(node, "function_parameter_declaration");
1404
+ const inlineList = inline ? findFirstChild(inline, "parameter_list") : null;
1405
+ const direct = findFirstChild(node, "parameter_list");
1406
+ const blockList = this.findParamList(node);
1407
+ const params = collectParameters(inlineList ?? direct ?? blockList);
1408
+ const parameterForm = inlineList ? "inline" : direct ?? blockList ? "paramBlock" : "none";
1409
+ const annotations = [];
1410
+ annotations.push(...this.allAttributeTexts(node));
1411
+ for (const attr of annotations) {
1412
+ if (attr.includes("CmdletBinding")) patterns.add("Advanced Functions");
1413
+ if (attr.includes("Parameter")) patterns.add("Parameter Attributes");
1414
+ if (attr.includes("ValidateSet") || attr.includes("ValidateRange") || attr.includes("ValidatePattern"))
1415
+ patterns.add("Input Validation");
1416
+ }
1417
+ const line = nameNode.startPosition.row + 1;
1418
+ declareSymbol(fnName, ASTEngine.SymbolKind.Function, node, line, {
1419
+ ...annotations.length > 0 ? { annotations } : {},
1420
+ docstring: this.docstringAbove(nameNode.startPosition.row, lines),
1421
+ returnType: "any",
1422
+ parameterForm
1423
+ }, params);
1424
+ frames.push({ owner: fnName, scope: fnName, containerLine: line });
1425
+ {
1426
+ const paramList = inlineList ?? direct ?? blockList;
1427
+ if (paramList) {
1428
+ for (const sp of this.collectChildrenByType(paramList, "script_parameter")) {
1429
+ const v = findFirstChild(sp, "variable");
1430
+ if (!v) continue;
1431
+ if (isQualified(v.text)) continue;
1432
+ declareSymbol(stripDollar(v.text), ASTEngine.SymbolKind.Variable, v, v.startPosition.row + 1, {
1433
+ isParameter: true,
1434
+ type: typeSpecText(sp) || "untyped"
1435
+ });
1436
+ emitParameterDefaultReads(sp);
1437
+ }
1438
+ }
1439
+ }
1440
+ break;
1441
+ }
1442
+ // ------------------------------------------------- script parameters
1443
+ case "param_block": {
1444
+ if (!atFileScope()) break;
1445
+ const list = findFirstChild(node, "parameter_list");
1446
+ if (!list) break;
1447
+ for (const p of this.collectChildrenByType(list, "script_parameter")) {
1448
+ const varNode = findFirstChild(p, "variable");
1449
+ if (!varNode || isQualified(varNode.text)) continue;
1450
+ const vline = varNode.startPosition.row + 1;
1451
+ const declared = typeSpecText(p);
1452
+ const attrs = this.attributeTexts(p);
1453
+ declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, vline, {
1454
+ isScriptParameter: true,
1455
+ type: declared || "untyped",
1456
+ ...attrs ? { annotations: attrs } : {}
1457
+ });
1458
+ emitParameterDefaultReads(p);
1459
+ }
1460
+ break;
1461
+ }
1462
+ // ---------------------------------------------------------- classes
1463
+ case "class_statement": {
1464
+ const names = this.collectChildrenByType(node, "simple_name");
1465
+ if (names.length === 0) break;
1466
+ const className = names[0].text;
1467
+ if (!classDeclaredSpelling.has(className.toLowerCase())) classDeclaredSpelling.set(className.toLowerCase(), className);
1468
+ const baseClass = names.length > 1 ? names[1].text : "";
1469
+ const interfaces = names.slice(2).map((n) => n.text);
1470
+ const declLine = names[0].startPosition.row + 1;
1471
+ if (baseClass) {
1472
+ baseClassByClass.set(className, baseClass);
1473
+ addRel(className, baseClass, ASTEngine.RelationshipType.Extends, 1, declLine, {
1474
+ powershellSpecific: true
1475
+ });
1476
+ }
1477
+ for (const iface of interfaces) {
1478
+ addRel(className, iface, ASTEngine.RelationshipType.Implements, 0.85, declLine, {
1479
+ powershellSpecific: true
1480
+ });
1481
+ }
1482
+ if (!classMembers.has(className)) classMembers.set(className, /* @__PURE__ */ new Set());
1483
+ declareSymbol(className, ASTEngine.SymbolKind.Class, node, declLine, {
1484
+ ...baseClass ? { baseClass } : {},
1485
+ docstring: this.docstringAbove(names[0].startPosition.row, lines)
1486
+ });
1487
+ frames.push({ owner: className, scope: className, containerLine: declLine });
1488
+ break;
1489
+ }
1490
+ // ------------------------------------------------------------ enums
1491
+ case "enum_statement": {
1492
+ const nameNode = findFirstChild(node, "simple_name");
1493
+ if (!nameNode) break;
1494
+ const line = nameNode.startPosition.row + 1;
1495
+ declareSymbol(nameNode.text, ASTEngine.SymbolKind.Enum, node, line, {
1496
+ docstring: this.docstringAbove(nameNode.startPosition.row, lines)
1497
+ });
1498
+ frames.push({ owner: nameNode.text, scope: nameNode.text, containerLine: line });
1499
+ break;
1500
+ }
1501
+ case "enum_member": {
1502
+ const nameNode = findFirstChild(node, "simple_name");
1503
+ if (!nameNode) break;
1504
+ const line = nameNode.startPosition.row + 1;
1505
+ declareSymbol(nameNode.text, ASTEngine.SymbolKind.EnumMember, node, line, {});
1506
+ break;
1507
+ }
1508
+ // -------------------------------------------------- class members
1509
+ case "class_method_definition": {
1510
+ const nameNode = findFirstChild(node, "simple_name");
1511
+ if (!nameNode) break;
1512
+ const hdr = memberHeaderFacts(node, nameNode);
1513
+ const memberName = hdr.name;
1514
+ const className = top().owner;
1515
+ const isCtor = memberName === className;
1516
+ const line = nameNode.startPosition.row + 1;
1517
+ const params = collectParameters(this.findMethodParamList(node));
1518
+ const attrText = hdr.modifiers;
1519
+ const symName = isCtor ? "constructor" : memberName;
1520
+ const returnType = typeSpecText(node) || (isCtor ? "void" : "any");
1521
+ declareSymbol(symName, isCtor ? ASTEngine.SymbolKind.Constructor : ASTEngine.SymbolKind.Method, node, line, {
1522
+ isStatic: attrText.includes("static"),
1523
+ isHidden: attrText.includes("hidden"),
1524
+ returnType,
1525
+ ...this.allAttributeTexts(node).length > 0 ? { annotations: this.allAttributeTexts(node) } : {},
1526
+ docstring: this.docstringAbove(nameNode.startPosition.row, lines)
1527
+ }, params);
1528
+ if (!isCtor) {
1529
+ if (classMembers.has(className)) {
1530
+ classMembers.get(className).add({
1531
+ name: memberName,
1532
+ kind: "method",
1533
+ isStatic: attrText.includes("static"),
1534
+ isHidden: attrText.includes("hidden")
1535
+ });
1536
+ }
1537
+ overrideCandidates.push({
1538
+ className,
1539
+ memberName,
1540
+ line,
1541
+ fact: {
1542
+ name: memberName,
1543
+ kind: "method",
1544
+ isStatic: attrText.includes("static"),
1545
+ isHidden: attrText.includes("hidden")
1546
+ }
1547
+ });
1548
+ }
1549
+ frames.push({ owner: className, scope: symName, containerLine: line });
1550
+ break;
1551
+ }
1552
+ case "class_property_definition": {
1553
+ const varNode = findFirstChild(node, "variable");
1554
+ if (!varNode) break;
1555
+ const line = varNode.startPosition.row + 1;
1556
+ const attrText = memberHeaderFacts(node, varNode).modifiers;
1557
+ const className = top().owner;
1558
+ const name = stripDollar(varNode.text);
1559
+ const declared = typeSpecText(node);
1560
+ declareSymbol(name, ASTEngine.SymbolKind.Property, varNode, line, {
1561
+ isStatic: attrText.includes("static"),
1562
+ isHidden: attrText.includes("hidden"),
1563
+ type: declared || "untyped",
1564
+ docstring: this.docstringAbove(varNode.startPosition.row, lines)
1565
+ });
1566
+ if (classMembers.has(className)) {
1567
+ classMembers.get(className).add({
1568
+ name,
1569
+ kind: "property",
1570
+ isStatic: attrText.includes("static"),
1571
+ isHidden: attrText.includes("hidden")
1572
+ });
1573
+ }
1574
+ if (declared && !isTypeAccelerator(declared)) {
1575
+ addRel(className, declared, ASTEngine.RelationshipType.Uses, 0.8, line, {
1576
+ powershellSpecific: true,
1577
+ isTypeReference: true,
1578
+ viaMemberType: name
1579
+ });
1580
+ }
1581
+ break;
1582
+ }
1583
+ // ----------------------------------------------------- assignments
1584
+ case "assignment_expression": {
1585
+ const left = findFirstChild(node, "left_assignment_expression");
1586
+ if (left) {
1587
+ const op = findFirstChild(node, "assignement_operator");
1588
+ const opText = op ? op.text.trim() : "=";
1589
+ if (opText.length > 1) {
1590
+ const lhsVar = findDescendantVariable(left);
1591
+ if (lhsVar) {
1592
+ const lname = stripDollar(lhsVar.text.trim());
1593
+ if (isPlausibleSymbolName(lname) && !isQualified(lhsVar.text) && !isAutomatic(lname)) {
1594
+ addRel(top().scope, lname, ASTEngine.RelationshipType.Uses, 0.9, lhsVar.startPosition.row + 1, {
1595
+ powershellSpecific: true,
1596
+ isVariableReference: true,
1597
+ isCompoundAssignmentRead: true,
1598
+ assignmentOperator: opText
1599
+ });
1600
+ }
1601
+ }
1602
+ }
1603
+ }
1604
+ if (left) {
1605
+ const memberAccess = findDescendant(left, "member_access", 14);
1606
+ const elementAccess = findDescendant(left, "element_access", 14);
1607
+ if (memberAccess || elementAccess) {
1608
+ if (memberAccess) emitMemberUses(memberAccess, "write");
1609
+ } else {
1610
+ const varNode = findDescendantVariable(left);
1611
+ if (varNode && isDeclarableVariable(varNode.text)) {
1612
+ const line = varNode.startPosition.row + 1;
1613
+ declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, line, { isVariable: true });
1614
+ }
1615
+ }
1616
+ }
1617
+ const operator = findFirstChild(node, "assignement_operator");
1618
+ for (let i = 0; i < node.namedChildCount; i++) {
1619
+ const child = node.namedChild(i);
1620
+ if (!child || child.type === "assignement_operator") continue;
1621
+ if (child.type === "left_assignment_expression") continue;
1622
+ if (child.type !== "pipeline") continue;
1623
+ emitAssignmentReads(child);
1624
+ }
1625
+ break;
1626
+ }
1627
+ // --------------------------------------------------------- foreach
1628
+ case "foreach_statement": {
1629
+ const varNode = findFirstChild(node, "variable");
1630
+ if (varNode && isDeclarableVariable(varNode.text)) {
1631
+ const line = varNode.startPosition.row + 1;
1632
+ declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, line, { isLoopVariable: true });
1633
+ }
1634
+ break;
1635
+ }
1636
+ // --------------------------- expression reads (conditions, return/throw)
1637
+ //
1638
+ // A VARIABLE READ IS A READ WHEREVER IT APPEARS, and this lane was reading only
1639
+ // five contexts: assignment right-hand sides, command arguments, string
1640
+ // interpolation, member/element access and the ++/-- family. Conditions and
1641
+ // flow-control statements were not among them, so `if ($x)`, `while ($y)`,
1642
+ // `for (…; $i -lt $n; …)`, `switch ($x)`, `return $z` and `throw $e` published NO
1643
+ // `uses` edge at all. MEASURED on a six-shape reproduction: analyzer 0 reads,
1644
+ // independent oracle 5 (`$x`, `$y`, `$n`, `$z`, `$e`). Over the primary corpus
1645
+ // this is the residue the F5 sample kept naming — `uses -> PID` in a condition, a
1646
+ // `$null` check, a `return $result` — and it is a drop, not a modelling choice:
1647
+ // the same variable in the same position is read whether the statement around it
1648
+ // is an assignment or an `if`.
1649
+ //
1650
+ // Read from the CONDITION WRAPPERS only, and never the statement block: the body
1651
+ // is visited by this same switch, so descending into it here would double-read
1652
+ // every command argument and every interpolated string with a different marker
1653
+ // and lose their per-site attribution (the argument reader's own comment records
1654
+ // what that costs). `for_initializer` and `for_iterator` are deliberately NOT in
1655
+ // the list: the assignment case owns the initializer (and the `$i` there is a
1656
+ // DECLARATION, not a read), and the ++/-- case owns the iterator.
1657
+ // A PIPELINE HEAD IS A READ. `$result4 | Should -Be "x"` reads `$result4`, and
1658
+ // a bare `$Value` statement (`function f { $Value }`, which is how a PowerShell
1659
+ // function RETURNS the parameter) reads it too. Neither is a command: the
1660
+ // `command` branch reads only the ARGUMENTS of a command, so the variable at the
1661
+ // HEAD of a pipeline had no owner in this switch at all. MEASURED on the
1662
+ // ParameterBinding corpus file: nine `$resultN | Should` pipelines plus two bare
1663
+ // `$Value` bodies, 15 MISSING `uses` rows on one file; over the primary corpus
1664
+ // this was the second-largest recipe in the residue (`uses result` 73,
1665
+ // `uses actual` 19, `uses results` 14). A chain that CONTAINS a command is read
1666
+ // by the command branch and is skipped here.
1667
+ case "pipeline_chain": {
1668
+ const hasOwnReader = (n) => {
1669
+ if (n.type === "invokation_expression" || n.type === "post_increment_expression" || n.type === "pre_increment_expression" || n.type === "post_decrement_expression" || n.type === "pre_decrement_expression" || n.type === "sub_expression" || n.type === "expandable_string_literal" || n.type === "expandable_here_string_literal" || n.type === "command") return true;
1670
+ for (let i = 0; i < n.namedChildCount; i++) {
1671
+ const c = n.namedChild(i);
1672
+ if (c && hasOwnReader(c)) return true;
1673
+ }
1674
+ return false;
1675
+ };
1676
+ for (let i = 0; i < node.namedChildCount; i++) {
1677
+ const c = node.namedChild(i);
1678
+ if (!c || c.type === "command" || hasOwnReader(c)) continue;
1679
+ emitVariableReads(c, true, "isPipelineRead");
1680
+ }
1681
+ break;
1682
+ }
1683
+ case "if_statement":
1684
+ case "elseif_clause":
1685
+ case "while_statement":
1686
+ case "do_statement":
1687
+ case "for_statement":
1688
+ case "switch_statement":
1689
+ case "flow_control_statement": {
1690
+ for (let i = 0; i < node.namedChildCount; i++) {
1691
+ const c = node.namedChild(i);
1692
+ if (!c) continue;
1693
+ if (c.type === "pipeline" || c.type === "switch_condition" || c.type === "while_condition" || c.type === "for_condition") {
1694
+ emitVariableReads(c, true, "isExpressionRead");
1695
+ }
1696
+ }
1697
+ break;
1698
+ }
1699
+ // ----------------------------- a type literal is a TYPE REFERENCE
1700
+ //
1701
+ // A type named anywhere in an expression is a dependency on that type, and this
1702
+ // analyzer only published one when it happened to sit on a DECLARATION: PS12's
1703
+ // cast branch and the class-property branch. Everything else was invisible —
1704
+ // `$Node -is [Markdig.Syntax.Inlines.LinkInline]`, `@{ Alias = [System.Management.
1705
+ // Automation.AliasAttribute] }`, `[int[]]@(1,2)` in an argument, a type table
1706
+ // written as a hashtable of accelerator -> full type. MEASURED on the primary
1707
+ // corpus: 99 of the 126 truth edges in TypeAccelerator.Tests.ps1 (a file that IS
1708
+ // a table of types) and the single largest MISSING class after the oracle fixes
1709
+ // — 556 `uses string` and 151 `uses System` were the FRAGMENTED spellings of the
1710
+ // same rule, but the whole-name rows were still dropped.
1711
+ //
1712
+ // The exclusions are the positions where the type is DECLARATION METADATA rather
1713
+ // than a reference, and each is a lane rule that already exists:
1714
+ // · a parameter's type and a class method's RETURN type are stored on the
1715
+ // symbol (`type:`) and are not separate edges;
1716
+ // · an ATTRIBUTE's type names the attribute, which the attribute list already
1717
+ // records as annotations;
1718
+ // · a cast or class-property type is published by its own branch (above/below),
1719
+ // at the DECLARATION's site, so reading it here would double the row with a
1720
+ // different line;
1721
+ // · the object of `::` is a static call/member, and that edge already names the
1722
+ // type in full (`System.IO.Path::GetFullPath`), so a second `uses System.IO.
1723
+ // Path` is the same fact twice — the F5 oracle states the identical rule.
1724
+ case "type_literal": {
1725
+ if (this.withinRecovery(node)) break;
1726
+ let p = node.parent;
1727
+ let metadata = false;
1728
+ for (let d = 0; p && d < 10; d++) {
1729
+ if (p.type === "attribute" || p.type === "script_parameter" || p.type === "class_method_parameter" || p.type === "param_block" || p.type === "class_method_definition") {
1730
+ metadata = true;
1731
+ break;
1732
+ }
1733
+ if (p.type === "statement_list" || p.type === "script_block" || p.type === "program") break;
1734
+ p = p.parent;
1735
+ }
1736
+ if (metadata) break;
1737
+ const parent = node.parent;
1738
+ if (!parent) break;
1739
+ if (parent.type === "cast_expression" || parent.type === "class_property_definition") break;
1740
+ let isStaticObject = false;
1741
+ for (let i = 0; i < parent.childCount; i++) {
1742
+ const c = parent.child(i);
1743
+ if (c && c.startIndex === node.endIndex) {
1744
+ if (c.type === "::" || c.text === "::") isStaticObject = true;
1745
+ break;
1746
+ }
1747
+ }
1748
+ if (isStaticObject) break;
1749
+ const typeName = typeSpecText(node) || this.stripBrackets(node.text);
1750
+ if (!typeName || isTypeAccelerator(typeName)) break;
1751
+ let publishedType = typeName;
1752
+ let viaUsingNamespace = null;
1753
+ if (usingNamespacePrefixes.length > 0 && typeName.indexOf("[") < 0 && typeName.indexOf("]") < 0 && typeName.indexOf(",") < 0) {
1754
+ const dot = typeName.indexOf(".");
1755
+ if (dot > 0) {
1756
+ const rootText = typeName.slice(0, dot);
1757
+ const rootFolded = rootText.toLowerCase();
1758
+ if (rootFolded !== "system" && !typeNamesInFile.has(rootFolded) && !RESOLVED_FRAMEWORK_TYPES.has(rootFolded) && !DOTNET_SYSTEM_NAMESPACES.has(rootFolded)) {
1759
+ for (const prefix of usingNamespacePrefixes) {
1760
+ const prefixIsSystemRooted = prefix.length > 6 && prefix.charAt(prefix.length - 1) !== "." && (prefix.charCodeAt(0) | 32) === 115 && (prefix.charCodeAt(1) | 32) === 121 && (prefix.charCodeAt(2) | 32) === 115 && (prefix.charCodeAt(3) | 32) === 116 && (prefix.charCodeAt(4) | 32) === 101 && (prefix.charCodeAt(5) | 32) === 109 && prefix.charCodeAt(6) === 46;
1761
+ if (!prefixIsSystemRooted) continue;
1762
+ if (!FRAMEWORK_NESTED_NAMESPACES.has(`${prefix}.${rootText}`.toLowerCase())) continue;
1763
+ publishedType = `${prefix}.${typeName}`;
1764
+ viaUsingNamespace = prefix;
1765
+ break;
1766
+ }
1767
+ }
1768
+ }
1769
+ }
1770
+ addRel(top().scope, publishedType, ASTEngine.RelationshipType.Uses, 0.8, node.startPosition.row + 1, {
1771
+ powershellSpecific: true,
1772
+ isTypeReference: true,
1773
+ ...viaUsingNamespace === null ? {} : {
1774
+ viaUsingNamespace,
1775
+ typeSpellingAsWritten: typeName
1776
+ }
1777
+ });
1778
+ break;
1779
+ }
1780
+ // ------------------------------------------- typed declarations (PS12)
1781
+ case "cast_expression": {
1782
+ const typeLit = findFirstChild(node, "type_literal");
1783
+ const target = findFirstChild(node, "unary_expression");
1784
+ const varNode = target ? findFirstChild(target, "variable") : null;
1785
+ if (!varNode) {
1786
+ const castType = typeLit ? typeSpecText(node) : "";
1787
+ if (castType && !isTypeAccelerator(castType)) {
1788
+ addRel(top().scope, castType, ASTEngine.RelationshipType.Uses, 0.8, typeLit.startPosition.row + 1, {
1789
+ powershellSpecific: true,
1790
+ isTypeReference: true,
1791
+ isCastExpression: true
1792
+ });
1793
+ }
1794
+ break;
1795
+ }
1796
+ if (!isDeclarableVariable(varNode.text)) break;
1797
+ const line = varNode.startPosition.row + 1;
1798
+ const declared = typeSpecText(node);
1799
+ declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, line, {
1800
+ isVariable: true,
1801
+ isTypedDeclaration: true,
1802
+ type: declared || "untyped",
1803
+ ...this.allAttributeTexts(node).length > 0 ? { annotations: this.allAttributeTexts(node) } : {}
1804
+ });
1805
+ if (declared && !isTypeAccelerator(declared)) {
1806
+ addRel(top().scope, declared, ASTEngine.RelationshipType.Uses, 0.8, line, {
1807
+ powershellSpecific: true,
1808
+ isTypeReference: true
1809
+ });
1810
+ }
1811
+ break;
1812
+ }
1813
+ case "unary_expression":
1814
+ case "expression_with_unary_operator": {
1815
+ const typeLit = findFirstChild(node, "type_literal");
1816
+ const varNode = findFirstChild(node, "variable");
1817
+ if (!typeLit || !varNode || !isDeclarableVariable(varNode.text)) break;
1818
+ const line = varNode.startPosition.row + 1;
1819
+ const declared = typeSpecText(node);
1820
+ declareSymbol(varNode.text, ASTEngine.SymbolKind.Variable, varNode, line, {
1821
+ isVariable: true,
1822
+ type: declared || "untyped"
1823
+ });
1824
+ if (declared && !TYPE_ACCELERATORS.has(declared)) {
1825
+ addRel(top().scope, declared, ASTEngine.RelationshipType.Uses, 0.8, line, {
1826
+ powershellSpecific: true,
1827
+ isTypeReference: true
1828
+ });
1829
+ }
1830
+ break;
1831
+ }
1832
+ // ------------------------------------------------- hashtable (PS15)
1833
+ case "hash_entry": {
1834
+ const keyNode = findFirstChild(node, "key_expression");
1835
+ if (!keyNode) break;
1836
+ const keyName = keyNode.text;
1837
+ if (!keyName) break;
1838
+ let quoted = false;
1839
+ let interpolating = false;
1840
+ if (keyName.length >= 2) {
1841
+ const q = keyName.charCodeAt(0);
1842
+ if ((q === 34 || q === 39) && keyName.charCodeAt(keyName.length - 1) === q) {
1843
+ quoted = true;
1844
+ const inner = keyName.slice(1, -1);
1845
+ if (q === 34 && inner.includes("$")) interpolating = true;
1846
+ }
1847
+ }
1848
+ if (quoted) {
1849
+ if (interpolating) break;
1850
+ } else if (keyName.includes("$") || keyName.includes(" ")) break;
1851
+ const line = keyNode.startPosition.row + 1;
1852
+ declareSymbol(keyName, ASTEngine.SymbolKind.Property, keyNode, line, {
1853
+ isHashtableKey: true,
1854
+ type: "untyped"
1855
+ });
1856
+ break;
1857
+ }
1858
+ // ------------------------------------------------ member reads (PS9)
1859
+ case "member_access": {
1860
+ emitMemberUses(node, "read");
1861
+ break;
1862
+ }
1863
+ case "element_access": {
1864
+ const keyRoot = node.namedChildCount > 1 ? node.namedChild(1) : null;
1865
+ if (!keyRoot) break;
1866
+ const idx = findDescendant(keyRoot, "string_literal", 12) ?? findDescendant(keyRoot, "variable", 12) ?? findDescendant(keyRoot, "integer_literal", 12) ?? findDescendant(keyRoot, "real_literal", 12);
1867
+ if (!idx) break;
1868
+ const idxLine = idx.startPosition.row + 1;
1869
+ if (idx.type === "string_literal") {
1870
+ const key = this.stripQuotes(idx.text);
1871
+ if (key && !key.includes("$")) {
1872
+ addRel(top().scope, key, ASTEngine.RelationshipType.Uses, 0.8, idxLine, {
1873
+ powershellSpecific: true,
1874
+ isElementAccess: true,
1875
+ isConstantKey: true
1876
+ });
1877
+ }
1878
+ } else if (idx.type === "variable") {
1879
+ addRel(top().scope, stripDollar(idx.text), ASTEngine.RelationshipType.Uses, 0.9, idxLine, {
1880
+ powershellSpecific: true,
1881
+ isElementAccess: true,
1882
+ isVariableReference: true
1883
+ });
1884
+ }
1885
+ break;
1886
+ }
1887
+ // ------------------------------- interpolation (PS16, the groovy G7)
1888
+ case "sub_expression": {
1889
+ const inner = node.namedChildCount > 0 ? node.namedChild(0) : null;
1890
+ if (inner) emitVariableReads(inner);
1891
+ break;
1892
+ }
1893
+ case "expandable_string_literal": {
1894
+ emitVariableReads(node);
1895
+ break;
1896
+ }
1897
+ // F9: an INCREMENT or DECREMENT reads its operand. `$n++` / `++$n` are the
1898
+ // one remaining place a variable is read and nothing was published — the F9
1899
+ // audit found these four types in the "the analyzer never names it" set, and
1900
+ // probing them confirmed all four emit nothing at all, so `$n++` left the
1901
+ // graph with `contains -> n` and no read.
1902
+ //
1903
+ // A `uses` is the right edge and not a weaker one: the operand is genuinely
1904
+ // read, and the WRITE half has no edge in this lane's model because a write to
1905
+ // a local is not a fact the graph can express (a member write can, which is
1906
+ // what `isMemberWrite` is for). The metadata says which operation it was so a
1907
+ // reader is not left guessing, and so a fixture can pin the distinction.
1908
+ case "post_increment_expression":
1909
+ case "post_decrement_expression":
1910
+ case "pre_increment_expression":
1911
+ case "pre_decrement_expression": {
1912
+ const operand = findDescendant(node, "variable", 6);
1913
+ if (operand) {
1914
+ const name = stripDollar(operand.text.trim());
1915
+ if (isPlausibleSymbolName(name) && !isQualified(operand.text) && !isAutomatic(name)) {
1916
+ addRel(top().scope, name, ASTEngine.RelationshipType.Uses, 0.9, operand.startPosition.row + 1, {
1917
+ powershellSpecific: true,
1918
+ isVariableReference: true,
1919
+ isCounterUpdate: true,
1920
+ isPostfix: node.type.startsWith("post_")
1921
+ });
1922
+ }
1923
+ }
1924
+ break;
1925
+ }
1926
+ case "expandable_here_string_literal": {
1927
+ emitVariableReads(node, false, "isHereStringInterpolation");
1928
+ break;
1929
+ }
1930
+ // ---------------------------------------- invocation (PS17/PS18/PS19)
1931
+ case "invokation_expression": {
1932
+ const typeLit = findFirstChild(node, "type_literal");
1933
+ const member = findFirstChild(node, "member_name");
1934
+ const line = (member ?? typeLit ?? node).startPosition.row + 1;
1935
+ const argList = findFirstChild(node, "argument_list");
1936
+ if (argList) emitVariableReads(argList, true, "isArgumentReference");
1937
+ if (typeLit) {
1938
+ const typeName = typeSpecText(node) || this.stripBrackets(typeLit.text);
1939
+ if (!typeName) break;
1940
+ const memberName = member?.text ?? "";
1941
+ if (isNewSpelling(memberName)) {
1942
+ addRel(top().scope, typeName, ASTEngine.RelationshipType.Instantiates, 0.9, line, {
1943
+ powershellSpecific: true,
1944
+ dotNetNew: true
1945
+ });
1946
+ } else if (memberName) {
1947
+ addRel(top().scope, `${typeName}.${memberName}`, ASTEngine.RelationshipType.Calls, 0.8, line, {
1948
+ powershellSpecific: true,
1949
+ staticMemberCall: true,
1950
+ staticType: typeName
1951
+ });
1952
+ }
1953
+ break;
1954
+ }
1955
+ const receiver = findFirstChild(node, "variable");
1956
+ if (member) {
1957
+ const dynamicTarget = member.text.length > 0 && member.text.charCodeAt(0) === 36;
1958
+ if (dynamicTarget) {
1959
+ const dynName = stripDollar(member.text);
1960
+ if (isPlausibleSymbolName(dynName)) {
1961
+ addRel(top().scope, dynName, ASTEngine.RelationshipType.Uses, 0.9, member.startPosition.row + 1, {
1962
+ powershellSpecific: true,
1963
+ isDynamicMethodTarget: true,
1964
+ isVariableReference: true
1965
+ });
1966
+ }
1967
+ break;
1968
+ }
1969
+ addRel(top().scope, member.text, ASTEngine.RelationshipType.Calls, 0.6, line, {
1970
+ powershellSpecific: true,
1971
+ methodCall: true,
1972
+ on: receiver ? stripDollar(receiver.text) : ""
1973
+ });
1974
+ if (receiver) {
1975
+ const name = stripDollar(receiver.text);
1976
+ if (name && !isQualified(receiver.text) && !isAutomatic(name)) {
1977
+ addRel(top().scope, name, ASTEngine.RelationshipType.Uses, 0.9, receiver.startPosition.row + 1, {
1978
+ powershellSpecific: true,
1979
+ isReceiverReference: true,
1980
+ isVariableReference: true
1981
+ });
1982
+ }
1983
+ }
1984
+ }
1985
+ break;
1986
+ }
1987
+ // ------------------------------------------------------- commands
1988
+ case "command": {
1989
+ const cmdNameNode = findFirstChild(node, "command_name");
1990
+ if (cmdNameNode && this.nameWithinRecovery(cmdNameNode)) break;
1991
+ const cmdName = commandNameOf(node);
1992
+ if (!cmdName) break;
1993
+ const leaf = cmdName.includes("::") ? cmdName.slice(cmdName.lastIndexOf("::") + 2) : cmdName;
1994
+ const lowerLeaf = leaf.toLowerCase();
1995
+ const nameLine = (findFirstChild(node, "command_name") ?? node).startPosition.row + 1;
1996
+ if (lowerLeaf === "interface") {
1997
+ const ifaceElements = findFirstChild(node, "command_elements");
1998
+ const ifaceBlock = ifaceElements ? findDescendant(ifaceElements, "script_block_expression") : null;
1999
+ const ifaceName = ifaceElements ? this.collectChildrenByType(ifaceElements, "generic_token")[0] ?? null : null;
2000
+ if (ifaceBlock && ifaceName) {
2001
+ const ifaceLine = ifaceName.startPosition.row + 1;
2002
+ declareSymbol(ifaceName.text, ASTEngine.SymbolKind.Interface, ifaceName, ifaceLine, {
2003
+ powershellSpecific: true,
2004
+ docstring: this.docstringAbove(ifaceName.startPosition.row, lines)
2005
+ });
2006
+ interfaceBlocks.add(nodeKey(node));
2007
+ frames.push({ owner: ifaceName.text, scope: ifaceName.text, containerLine: ifaceLine });
2008
+ }
2009
+ break;
2010
+ }
2011
+ if (NAMED_BLOCK_KEYWORDS.has(lowerLeaf)) break;
2012
+ if (FLOW_KEYWORDS.has(lowerLeaf)) break;
2013
+ if (LANGUAGE_KEYWORD_COMMANDS.has(lowerLeaf)) break;
2014
+ const elements = findFirstChild(node, "command_elements");
2015
+ const blockArg = elements ? findDescendant(elements, "script_block_expression") : null;
2016
+ const firstToken = elements ? this.collectChildrenByType(elements, "generic_token")[0] ?? null : null;
2017
+ const isAssignmentForm = !!firstToken && firstToken.text === "=";
2018
+ if (DSC_KEYWORDS.has(lowerLeaf) && blockArg) {
2019
+ const declared = elements && elements.namedChildCount > 1 ? elements.namedChild(1) : null;
2020
+ if (lowerLeaf === "configuration" && declared && declared.type === "generic_token") {
2021
+ const line = declared.startPosition.row + 1;
2022
+ declareSymbol(declared.text, ASTEngine.SymbolKind.Class, declared, line, { isDscConfiguration: true });
2023
+ dscDepth++;
2024
+ dscBlocks.add(nodeKey(node));
2025
+ dscOwner.set(nodeKey(node), declared.text);
2026
+ frames.push({ owner: declared.text, scope: declared.text, containerLine: line });
2027
+ }
2028
+ break;
2029
+ }
2030
+ if (dscDepth > 0) {
2031
+ if (blockArg && firstToken && firstToken.type === "generic_token") {
2032
+ const line = firstToken.startPosition.row + 1;
2033
+ declareSymbol(firstToken.text, ASTEngine.SymbolKind.Class, firstToken, line, {
2034
+ isDscResource: true,
2035
+ dscResourceType: leaf
2036
+ });
2037
+ dscDepth++;
2038
+ dscBlocks.add(nodeKey(node));
2039
+ dscOwner.set(nodeKey(node), firstToken.text);
2040
+ frames.push({ owner: firstToken.text, scope: firstToken.text, containerLine: line });
2041
+ break;
2042
+ }
2043
+ if (isAssignmentForm) {
2044
+ declareSymbol(leaf, ASTEngine.SymbolKind.Property, node, nameLine, { isDscProperty: true });
2045
+ break;
2046
+ }
2047
+ break;
2048
+ }
2049
+ const op = findFirstChild(node, "command_invokation_operator");
2050
+ if (op) {
2051
+ const target = unquoteTarget(leaf);
2052
+ if (looksLikeModulePath(target)) {
2053
+ const part = sourcedPartName(target);
2054
+ if (op.text === "." && part !== null) {
2055
+ addRel(top().scope, part, ASTEngine.RelationshipType.PartOf, 0.9, nameLine, {
2056
+ powershellSpecific: true,
2057
+ invokationOperator: op.text,
2058
+ // MANDATORY on every `part_of` row (src/ast/types.ts:170-183): this
2059
+ // file carries the directive and names the part, so it is the
2060
+ // library side of the pair. A dot-sourced file does not know it is
2061
+ // being sourced, so PowerShell has only the one role.
2062
+ partRole: "library",
2063
+ partPath: target
2064
+ });
2065
+ } else {
2066
+ addRel(top().scope, target, ASTEngine.RelationshipType.Calls, 0.9, nameLine, {
2067
+ powershellSpecific: true,
2068
+ invokationOperator: op.text
2069
+ });
2070
+ }
2071
+ } else if (target.startsWith("$")) {
2072
+ const invoked = stripDollar(target);
2073
+ if (isPlausibleSymbolName(invoked)) {
2074
+ addRel(top().scope, invoked, ASTEngine.RelationshipType.Uses, 0.9, nameLine, {
2075
+ powershellSpecific: true,
2076
+ isInvokedVariable: true,
2077
+ isVariableReference: true
2078
+ });
2079
+ }
2080
+ } else if (target.length > 0 && isPlausibleSymbolName(target)) {
2081
+ addRel(top().scope, target, ASTEngine.RelationshipType.Calls, 0.9, nameLine, {
2082
+ powershellSpecific: true,
2083
+ invokationOperator: op.text
2084
+ });
2085
+ }
2086
+ break;
2087
+ }
2088
+ detectFrameworks(leaf);
2089
+ if (leaf === "Import-Module" || leaf === "ipmo") {
2090
+ const argNodes = [];
2091
+ const args = commandArgs(node, argNodes, IMPORT_MODULE_MODULE_PARAMETERS);
2092
+ for (let ai = 0; ai < args.length; ai++) {
2093
+ const arg = args[ai];
2094
+ if (SWITCH_ARGUMENT_VALUES.has(arg.toLowerCase())) continue;
2095
+ const argNode = argNodes[ai];
2096
+ if (argNode && nodeIsAVariableReference(argNode)) continue;
2097
+ addImport(arg, leaf, nameLine, node);
2098
+ }
2099
+ const argElementsRead = findFirstChild(node, "command_elements");
2100
+ if (argElementsRead) emitVariableReads(argElementsRead, true, "isArgumentReference");
2101
+ break;
2102
+ }
2103
+ if (leaf === "New-Object") {
2104
+ const comObjectOut = { node: null };
2105
+ const typeNameOut = { node: null };
2106
+ const comObject = this.parameterValue(node, "ComObject", comObjectOut) ?? this.parameterValue(node, "comobject", comObjectOut);
2107
+ const typeName = this.parameterValue(node, "TypeName", typeNameOut) ?? this.parameterValue(node, "typename", typeNameOut);
2108
+ const positionalNodes = [];
2109
+ const positional = commandArgs(node, positionalNodes)[0];
2110
+ const constructed = comObject ?? typeName ?? positional;
2111
+ const constructedFrom = comObject ? comObjectOut.node : typeName ? typeNameOut.node : positionalNodes[0] ?? null;
2112
+ if (constructed && !isTypeAccelerator(constructed)) {
2113
+ if (constructedFrom && nodeIsAVariableReference(constructedFrom)) {
2114
+ addRel(top().scope, constructed, ASTEngine.RelationshipType.Uses, 0.9, nameLine, {
2115
+ powershellSpecific: true,
2116
+ viaNewObject: true,
2117
+ isDynamicTypeName: true,
2118
+ isVariableReference: true
2119
+ });
2120
+ } else {
2121
+ addRel(top().scope, constructed, ASTEngine.RelationshipType.Instantiates, 0.9, nameLine, {
2122
+ powershellSpecific: true,
2123
+ viaNewObject: true,
2124
+ ...comObject ? { isComObject: true } : {}
2125
+ });
2126
+ }
2127
+ }
2128
+ break;
2129
+ }
2130
+ if (leaf === "Export-ModuleMember") {
2131
+ const elements2 = findFirstChild(node, "command_elements");
2132
+ if (elements2) {
2133
+ const isFlag = (t) => t === "function" || t === "cmdlet" || t === "variable" || t === "alias";
2134
+ for (let i = 0; i < elements2.namedChildCount; i++) {
2135
+ const c = elements2.namedChild(i);
2136
+ if (!c) continue;
2137
+ if (c.type === "command_parameter") {
2138
+ const flag = stripLeadingDash(stripDollar(c.text)).toLowerCase();
2139
+ if (!isFlag(flag)) continue;
2140
+ const via = `-${flag}`;
2141
+ for (let j = i + 1; j < elements2.namedChildCount; j++) {
2142
+ const v = elements2.namedChild(j);
2143
+ if (!v) break;
2144
+ if (v.type === "command_parameter") break;
2145
+ if (v.type === "command_argument_sep") continue;
2146
+ if (v.type === "generic_token" || v.type === "string_literal" || v.type === "expandable_string_literal") {
2147
+ addExport(this.stripQuotes(v.text), via, v.startPosition.row + 1);
2148
+ } else {
2149
+ for (const t of this.literalTokens(v)) addExport(t, via, v.startPosition.row + 1);
2150
+ }
2151
+ }
2152
+ continue;
2153
+ }
2154
+ if (c.type === "array_literal_expression" || c.type === "unary_expression" || c.type === "command_argument_sep" || c.type === "string_literal" || c.type === "generic_token") {
2155
+ for (const t of this.literalTokens(c)) addExport(t, leaf, c.startPosition.row + 1);
2156
+ }
2157
+ }
2158
+ }
2159
+ break;
2160
+ }
2161
+ if (lowerLeaf === "using") {
2162
+ const args = commandArgs(node);
2163
+ if (args.length >= 2) {
2164
+ const directive = args[0].toLowerCase();
2165
+ addImport(args[1], directive === "namespace" ? "using namespace" : "using module", nameLine, node);
2166
+ }
2167
+ break;
2168
+ }
2169
+ const argElements = findFirstChild(node, "command_elements");
2170
+ if (argElements) emitVariableReads(argElements, true, "isArgumentReference");
2171
+ addRel(top().scope, leaf, ASTEngine.RelationshipType.Calls, leaf.includes("-") ? 0.9 : 0.6, nameLine, {
2172
+ powershellSpecific: true
2173
+ });
2174
+ break;
2175
+ }
2176
+ // ------------------------------------------------------- comments
2177
+ case "comment": {
2178
+ const text = node.text;
2179
+ if (text.startsWith("#requires") || text.startsWith("#Requires")) {
2180
+ const line = node.startPosition.row + 1;
2181
+ const moduleNames = this.extractRequiresModules(text);
2182
+ for (const m of moduleNames) addImport(m.name, m.via, line, node);
2183
+ const version = this.extractRequiresValue(text, "version");
2184
+ let versionShaped = !!version;
2185
+ let versionDots = 0;
2186
+ for (let vi = 0; versionShaped && vi < version.length; vi++) {
2187
+ const ch = version.charCodeAt(vi);
2188
+ if (ch === 46) {
2189
+ versionDots++;
2190
+ continue;
2191
+ }
2192
+ if (ch < 48 || ch > 57) versionShaped = false;
2193
+ }
2194
+ if (version && versionShaped && versionDots >= 1) {
2195
+ addRel(top().scope, version, ASTEngine.RelationshipType.References, 0.9, line, {
2196
+ powershellSpecific: true,
2197
+ requiresVersion: true,
2198
+ fileLevel: atFileScope()
2199
+ });
2200
+ }
2201
+ }
2202
+ break;
2203
+ }
2204
+ }
2205
+ for (let i = node.childCount - 1; i >= 0; i--) {
2206
+ const child = node.child(i);
2207
+ if (child) work.push({ node: child, exit: false });
2208
+ }
2209
+ }
2210
+ for (const cand of overrideCandidates) {
2211
+ let baseWritten = baseClassByClass.get(cand.className);
2212
+ let guard = 0;
2213
+ while (baseWritten && guard++ < 8) {
2214
+ const base = classDeclaredSpelling.get(baseWritten.toLowerCase()) ?? baseWritten;
2215
+ const inherited = classMembers.get(base);
2216
+ const wanted = cand.memberName.toLowerCase();
2217
+ const inheritedFact = inherited ? [...inherited].find((m) => m.name.toLowerCase() === wanted) : void 0;
2218
+ if (inheritedFact && memberOverrides(cand.fact, inheritedFact)) {
2219
+ addRel(cand.className, base, ASTEngine.RelationshipType.Overrides, 0.9, cand.line, {
2220
+ powershellSpecific: true,
2221
+ overridesMember: cand.memberName,
2222
+ // The one edge in this lane that is INFERRED rather than read: PowerShell
2223
+ // has no `override` keyword, so this is a name match between a subclass
2224
+ // member and the base's declared member set. Calling it `structural` would
2225
+ // overstate the evidence, and the tier is the whole point of emitting one.
2226
+ evidenceClass: "inferred"
2227
+ });
2228
+ break;
2229
+ }
2230
+ baseWritten = baseClassByClass.get(base) ?? baseClassByClass.get(baseWritten);
2231
+ }
2232
+ }
2233
+ const declaredFolded = /* @__PURE__ */ new Set();
2234
+ const declaredQualified = /* @__PURE__ */ new Set();
2235
+ for (const s of symbols) {
2236
+ declaredFolded.add(s.name.toLowerCase());
2237
+ const sc = s.scope;
2238
+ if (typeof sc === "string" && sc.length > 0) declaredQualified.add(`${sc}.${s.name}`.toLowerCase());
2239
+ }
2240
+ const declaredInFile = (name) => declaredFolded.has(name.toLowerCase()) || declaredQualified.has(name.toLowerCase());
2241
+ const foldedValueSpellings = /* @__PURE__ */ new Map();
2242
+ for (const sym of symbols) {
2243
+ const kind = String(sym.kind ?? "");
2244
+ if (kind !== "variable" && kind !== "property" && kind !== "parameter") continue;
2245
+ if (!sym.name) continue;
2246
+ const key = sym.name.toLowerCase();
2247
+ let set = foldedValueSpellings.get(key);
2248
+ if (!set) foldedValueSpellings.set(key, set = /* @__PURE__ */ new Set());
2249
+ set.add(sym.name);
2250
+ }
2251
+ for (const rel of relationships) {
2252
+ if (rel.type !== ASTEngine.RelationshipType.Uses) continue;
2253
+ const md = rel.metadata;
2254
+ if (!md) continue;
2255
+ if (md.isVariableReference !== true && md.isMemberAccess !== true && md.isMemberReference !== true) continue;
2256
+ const t = String(rel.target ?? "");
2257
+ if (t.length === 0 || t.indexOf(".") >= 0 || t.indexOf(":") >= 0) continue;
2258
+ if (declaredFolded.has(t.toLowerCase()) && symbols.some((sym) => sym.name === t)) continue;
2259
+ const spellings = foldedValueSpellings.get(t.toLowerCase());
2260
+ if (spellings === void 0 || spellings.size !== 1) continue;
2261
+ const declared = [...spellings][0];
2262
+ if (declared === t) continue;
2263
+ rel.target = declared;
2264
+ md.caseFoldedTo = declared;
2265
+ md.powershellCaseFolded = true;
2266
+ }
2267
+ const ownedByThisFile = (name) => {
2268
+ const dot = name.lastIndexOf(".");
2269
+ if (dot <= 0) return false;
2270
+ return declaredInFile(name.slice(0, dot));
2271
+ };
2272
+ {
2273
+ const variablesByName = /* @__PURE__ */ new Map();
2274
+ const qnOccurrences = /* @__PURE__ */ new Map();
2275
+ for (const s of symbols) {
2276
+ const scope = String(s.scope ?? "");
2277
+ const qn = scope.length > 0 ? `${scope}.${s.name}` : String(s.name);
2278
+ qnOccurrences.set(qn, (qnOccurrences.get(qn) ?? 0) + 1);
2279
+ if (s.kind !== ASTEngine.SymbolKind.Variable) continue;
2280
+ const key = String(s.name).toLowerCase();
2281
+ const list = variablesByName.get(key) ?? [];
2282
+ list.push({ qn, scope, name: String(s.name) });
2283
+ variablesByName.set(key, list);
2284
+ }
2285
+ for (const rel of relationships) {
2286
+ if (rel.type !== ASTEngine.RelationshipType.Uses) continue;
2287
+ if (rel.metadata?.isVariableReference !== true) continue;
2288
+ const target = String(rel.target ?? "");
2289
+ if (target.length === 0) continue;
2290
+ let isBareIdentifier = true;
2291
+ for (let i = 0; i < target.length; i++) {
2292
+ const c = target.charCodeAt(i);
2293
+ if (c === 46 || c === 58 || c === 36 || c === 91 || c === 93 || c === 40 || c === 41) {
2294
+ isBareIdentifier = false;
2295
+ break;
2296
+ }
2297
+ }
2298
+ if (!isBareIdentifier) continue;
2299
+ const source = String(rel.source ?? "");
2300
+ const visible = (variablesByName.get(target.toLowerCase()) ?? []).filter(
2301
+ (c) => c.scope.length === 0 || source === c.scope || source.startsWith(`${c.scope}.`)
2302
+ );
2303
+ if (visible.length !== 1) continue;
2304
+ const hit = visible[0];
2305
+ if ((qnOccurrences.get(hit.qn) ?? 0) !== 1) continue;
2306
+ rel.target = hit.qn;
2307
+ const md = rel.metadata ?? (rel.metadata = {});
2308
+ md.referencedName = target;
2309
+ md.boundToVariable = true;
2310
+ }
2311
+ }
2312
+ for (const rel of relationships) {
2313
+ const target = rel.target;
2314
+ if (target == null || target.length === 0) continue;
2315
+ if (rel.metadata?.boundToVariable === true) continue;
2316
+ const staticMeta = rel.metadata ?? {};
2317
+ if (staticMeta.staticMemberCall === true && typeof staticMeta.staticType === "string") {
2318
+ const declaredOwner = classMembersOf(classMembers, staticMeta.staticType);
2319
+ const memberLeafRaw = target.slice(target.lastIndexOf(".") + 1);
2320
+ const memberLeaf = memberLeafRaw.toLowerCase();
2321
+ if (declaredOwner) {
2322
+ const declaredMember = [...declaredOwner.members].find((m) => m.name.toLowerCase() === memberLeaf);
2323
+ if (declaredMember) {
2324
+ rel.target = `${declaredOwner.name}.${declaredMember.name}`;
2325
+ continue;
2326
+ }
2327
+ rel.metadata.ownerMemberAbsent = { owner: declaredOwner.name, member: memberLeafRaw };
2328
+ continue;
2329
+ }
2330
+ const resolvedType = RESOLVED_FRAMEWORK_TYPES.get(staticMeta.staticType.toLowerCase());
2331
+ if (resolvedType !== void 0) {
2332
+ rel.target = `${resolvedType}.${memberLeafRaw}`;
2333
+ const resolvedMeta = rel.metadata ?? (rel.metadata = {});
2334
+ resolvedMeta.staticTypeResolved = resolvedType;
2335
+ resolvedMeta.typeAccelerator = staticMeta.staticType;
2336
+ continue;
2337
+ }
2338
+ if (staticMeta.staticType.indexOf(".") > 0) {
2339
+ } else {
2340
+ rel.metadata.ownerMemberAbsent = { owner: staticMeta.staticType, member: memberLeafRaw };
2341
+ continue;
2342
+ }
2343
+ }
2344
+ const type = rel.type;
2345
+ if (declaredInFile(target)) continue;
2346
+ if (ownedByThisFile(target)) {
2347
+ if (type === ASTEngine.RelationshipType.Calls || type === ASTEngine.RelationshipType.Uses) {
2348
+ rel.metadata.noDeclarationInFile = true;
2349
+ }
2350
+ continue;
2351
+ }
2352
+ const runtime = runtimeVariableTarget(target);
2353
+ let hasLetter = false;
2354
+ for (let i = 0; i < target.length; i++) {
2355
+ const c = target.charCodeAt(i);
2356
+ if (c >= 65 && c <= 90 || c >= 97 && c <= 122) {
2357
+ hasLetter = true;
2358
+ break;
2359
+ }
2360
+ }
2361
+ let keyHasPathByte = false;
2362
+ for (let i = 0; i < target.length; i++) {
2363
+ const c = target.charCodeAt(i);
2364
+ if (c === 47 || c === 92) {
2365
+ keyHasPathByte = true;
2366
+ break;
2367
+ }
2368
+ }
2369
+ const isQuotedMemberNameWithDot = rel.metadata?.isQuotedMemberName === true && target.indexOf(".") > 0;
2370
+ const isDataNameKey = rel.metadata?.isConstantKey === true && !keyHasPathByte || isQuotedMemberNameWithDot;
2371
+ const programName = rel.type === ASTEngine.RelationshipType.Calls && isCommandPositionCall(rel) ? programNameOf(target) : null;
2372
+ if (programName !== null) {
2373
+ rel.metadata.sourcePathClaim = [programName];
2374
+ continue;
2375
+ }
2376
+ let runtimeCreatedType = false;
2377
+ if (addTypeCreatedNames.size > 0 && (rel.type === ASTEngine.RelationshipType.Instantiates || rel.type === ASTEngine.RelationshipType.Uses)) {
2378
+ const typeDot = target.indexOf(".");
2379
+ const typeRoot = (typeDot > 0 ? target.slice(0, typeDot) : target).toLowerCase();
2380
+ runtimeCreatedType = addTypeCreatedNames.has(typeRoot) || addTypeCreatedNames.has(target.toLowerCase());
2381
+ }
2382
+ let callIntoCompiledType = false;
2383
+ if (addTypeDeclarations.size > 0 && rel.type === ASTEngine.RelationshipType.Calls) {
2384
+ const callDot = target.lastIndexOf(".");
2385
+ if (callDot > 0) {
2386
+ callIntoCompiledType = addTypeDeclarations.has(target.slice(0, callDot).toLowerCase());
2387
+ }
2388
+ }
2389
+ let runtimeBindingRead = false;
2390
+ if (rel.type === ASTEngine.RelationshipType.Uses && rel.metadata?.isVariableReference === true) {
2391
+ if (target.length > 0 && target.indexOf(".") < 0 && target.indexOf(":") < 0 && target.charCodeAt(0) !== 36) {
2392
+ runtimeBindingRead = true;
2393
+ }
2394
+ }
2395
+ const via = isDataNameKey || runtime !== null || runtimeCreatedType || callIntoCompiledType || !hasLetter ? null : externalReadingOf(target) ?? (isCommandPositionCall(rel) ? commandReadingOf(target) : null) ?? (runtimeBindingRead ? "runtime-binding" : null);
2396
+ if (via) {
2397
+ rel.metadata.externalTarget = true;
2398
+ rel.metadata.externalVia = via;
2399
+ if (via === "runtime-binding") rel.metadata.noDeclarationInFile = true;
2400
+ const moduleName = moduleQualifiedCommandOf(target);
2401
+ if (moduleName !== null) {
2402
+ rel.metadata.externalReason = "module-namespace";
2403
+ rel.metadata.externalSpecifier = moduleName;
2404
+ }
2405
+ if (arbiterCanCheckOwner(target)) rel.metadata.isExternalTarget = true;
2406
+ continue;
2407
+ }
2408
+ if (type === ASTEngine.RelationshipType.Calls || type === ASTEngine.RelationshipType.Uses) {
2409
+ rel.metadata.noDeclarationInFile = true;
2410
+ }
2411
+ }
2412
+ const detectedPatterns = [...patterns];
2413
+ const detectedFrameworks = [...frameworks];
2414
+ const psDeclaredHere = /* @__PURE__ */ new Set();
2415
+ for (const sym of symbols) {
2416
+ if (sym.name) psDeclaredHere.add(sym.name);
2417
+ const q = sym.qualifiedName;
2418
+ if (typeof q === "string" && q.length > 0) psDeclaredHere.add(q);
2419
+ }
2420
+ let psNamespaceClaims = 0;
2421
+ let psShorthandClaims = 0;
2422
+ for (const rel of relationships) {
2423
+ if (rel.type !== ASTEngine.RelationshipType.Uses && rel.type !== ASTEngine.RelationshipType.Calls && rel.type !== ASTEngine.RelationshipType.Instantiates) continue;
2424
+ const tgt = String(rel.target ?? "");
2425
+ const md = rel.metadata;
2426
+ if (!md || typeof md !== "object") continue;
2427
+ if (md.isExternal === true) continue;
2428
+ let roots = null;
2429
+ const envRoot = envProviderRootOf(tgt);
2430
+ const usingRoot = usingScopeRootOf(tgt);
2431
+ if (tgt === "System" || tgt.startsWith("System.")) roots = ["System"];
2432
+ else if (envRoot !== null) roots = [envRoot];
2433
+ else if (usingRoot !== null) roots = [usingRoot];
2434
+ else if (md.dotNetNew === true) {
2435
+ const shorthand = dotNetShorthandRootOf(tgt);
2436
+ if (shorthand !== null) {
2437
+ roots = [shorthand];
2438
+ if (psDeclaredHere.has(tgt)) roots = null;
2439
+ }
2440
+ }
2441
+ if (roots === null) continue;
2442
+ if (psDeclaredHere.has(tgt)) continue;
2443
+ md.languageNamespace = roots;
2444
+ if (roots[0] === "System") psNamespaceClaims++;
2445
+ else psShorthandClaims++;
2446
+ }
2447
+ return {
2448
+ language: "powershell",
2449
+ symbols,
2450
+ relationships,
2451
+ imports,
2452
+ exports,
2453
+ errors: [],
2454
+ // engine-owned syntax errors (engine.ts merges treeErrors)
2455
+ metadata: {
2456
+ complexity: symbols.length + relationships.length,
2457
+ linesOfCode: lines.length,
2458
+ cyclomaticComplexity,
2459
+ framework: detectedFrameworks[0],
2460
+ patterns: detectedPatterns,
2461
+ detectedFrameworks,
2462
+ frameworkPatterns: detectedPatterns.map((p) => ({
2463
+ framework: "PowerShell",
2464
+ patterns: [{ type: "usage", detected: true, description: p }]
2465
+ })),
2466
+ architecturalInsights: [],
2467
+ architecturalScore: Math.min(100, 60 + relationships.length),
2468
+ codeQuality: "Excellent",
2469
+ hasTypeInference: false,
2470
+ hasFrameworkIntelligence: true,
2471
+ analysisVersion: "6.0-ps-file-owner-lines-scope-reads"
2472
+ }
2473
+ };
2474
+ }
2475
+ // ------------------------------------------------------------- helpers
2476
+ /** PS1 — the file's own basename, extension included. */
2477
+ basenameOf(filePath) {
2478
+ const normalized = filePath.split("\\").join("/");
2479
+ const base = normalized.slice(normalized.lastIndexOf("/") + 1);
2480
+ return base.length > 0 ? base : "script.ps1";
2481
+ }
2482
+ lineRange(line) {
2483
+ return {
2484
+ startLine: line,
2485
+ startColumn: 1,
2486
+ endLine: line,
2487
+ endColumn: 1,
2488
+ startIndex: 0,
2489
+ endIndex: 0
2490
+ };
2491
+ }
2492
+ nodeToRange(node) {
2493
+ return {
2494
+ startLine: node.startPosition.row + 1,
2495
+ startColumn: node.startPosition.column + 1,
2496
+ endLine: node.endPosition.row + 1,
2497
+ endColumn: node.endPosition.column + 1,
2498
+ startIndex: node.startIndex,
2499
+ endIndex: node.endIndex
2500
+ };
2501
+ }
2502
+ collectChildrenByType(node, type) {
2503
+ const results = [];
2504
+ for (let i = 0; i < node.namedChildCount; i++) {
2505
+ const child = node.namedChild(i);
2506
+ if (child && child.type === type) results.push(child);
2507
+ }
2508
+ return results;
2509
+ }
2510
+ stripQuotes(text) {
2511
+ if (text.length >= 2) {
2512
+ const first = text.charCodeAt(0);
2513
+ const last = text.charCodeAt(text.length - 1);
2514
+ if ((first === 34 || first === 39) && last === first) return text.slice(1, -1);
2515
+ }
2516
+ return text;
2517
+ }
2518
+ stripBrackets(text) {
2519
+ if (text.length >= 2 && text.charCodeAt(0) === 91 && text.charCodeAt(text.length - 1) === 93) {
2520
+ return text.slice(1, -1);
2521
+ }
2522
+ return text;
2523
+ }
2524
+ findQuoted(node) {
2525
+ for (let i = 0; i < node.namedChildCount; i++) {
2526
+ const child = node.namedChild(i);
2527
+ if (!child) continue;
2528
+ if (child.type === "string_literal" || child.type === "expandable_string_literal") return child;
2529
+ const nested = this.findQuoted(child);
2530
+ if (nested) return nested;
2531
+ }
2532
+ return null;
2533
+ }
2534
+ /**
2535
+ * PS6 — is this node inside a recovery region? A `command` whose name lives
2536
+ * under an ERROR/MISSING node is recovery noise, not a call. bash BD5
2537
+ * established the rule; this lane needed it because its own
2538
+ * `if`-as-expression shape manufactures `command` nodes out of an ERROR
2539
+ * region with `hasError = false`.
2540
+ */
2541
+ withinRecovery(node) {
2542
+ const st = [node];
2543
+ while (st.length > 0) {
2544
+ const x = st.pop();
2545
+ if (x.type === "ERROR" || x.isMissing) return true;
2546
+ for (let i = 0; i < x.childCount; i++) {
2547
+ const c = x.child(i);
2548
+ if (c) st.push(c);
2549
+ }
2550
+ }
2551
+ return false;
2552
+ }
2553
+ /**
2554
+ * PS6 — does this NAME live inside a recovery region?
2555
+ *
2556
+ * The command branch needs the ANCESTOR question, not the subtree one, because
2557
+ * PS6's wording is about where the name sits: a `command` reconstructed from a
2558
+ * broken line keeps a clean name token and a clean subtree, accompanied by an
2559
+ * ERROR sibling, so a subtree test passes it. The one exemption is the
2560
+ * whole-file wrapper: the grammar wraps a CmdletBinding function's body in one
2561
+ * ERROR node covering the entire file, and the commands inside it are real
2562
+ * (MethodInvocation.Tests.ps1; the oracle expects them).
2563
+ */
2564
+ nameWithinRecovery(node) {
2565
+ let top = node;
2566
+ while (top.parent) top = top.parent;
2567
+ const topEnd = top.endIndex;
2568
+ let n = node.parent;
2569
+ let d = 0;
2570
+ while (n && d < 24) {
2571
+ if (n.type === "ERROR" || n.isMissing) {
2572
+ const wholeFile = n.startIndex === 0 && n.endIndex === topEnd;
2573
+ if (!wholeFile) return true;
2574
+ }
2575
+ n = n.parent;
2576
+ d++;
2577
+ }
2578
+ return false;
2579
+ }
2580
+ /** The value of a two-token parameter (`-TypeName Foo`) or `-Name:Foo`. */
2581
+ parameterValue(node, flag, out) {
2582
+ const elements = this.findChildByType(node, "command_elements");
2583
+ if (!elements) return null;
2584
+ const wanted = stripLeadingDash(flag).toLowerCase();
2585
+ for (let i = 0; i < elements.namedChildCount; i++) {
2586
+ const c = elements.namedChild(i);
2587
+ if (!c || c.type !== "command_parameter") continue;
2588
+ if (stripLeadingDash(this.stripQuotes(c.text)).toLowerCase() !== wanted) continue;
2589
+ const next = this.valueAfterNode(elements, i);
2590
+ if (next && (next.type === "generic_token" || next.type === "string_literal" || next.type === "expandable_string_literal")) {
2591
+ if (out) out.node = next;
2592
+ return this.stripQuotes(next.text);
2593
+ }
2594
+ }
2595
+ return null;
2596
+ }
2597
+ /**
2598
+ * The value token that FOLLOWS the named child at `fromIndex`, skipping the
2599
+ * grammar's `command_argument_sep` separators.
2600
+ *
2601
+ * This is the whole of the two-token parameter form, and reading
2602
+ * `namedChild(i + 1)` instead is the whole bug: measured shape for
2603
+ * `Import-Module -Name SqlServer -ErrorAction SilentlyContinue` is
2604
+ * `[sep, command_parameter(-Name), sep, generic_token(SqlServer), sep,
2605
+ * command_parameter(-ErrorAction), sep, generic_token(SilentlyContinue)]`, so
2606
+ * the value sits at `i + 3`. Every `i + 1` read found a separator, matched no
2607
+ * branch, and read NO two-token parameter value anywhere in the language.
2608
+ * Also worth recording: this grammar gives `command_parameter` NO `argument`
2609
+ * field, so a probe for one could only ever return null.
2610
+ */
2611
+ valueAfterNode(elements, fromIndex) {
2612
+ for (let i = fromIndex + 1; i < elements.namedChildCount; i++) {
2613
+ const c = elements.namedChild(i);
2614
+ if (!c) continue;
2615
+ if (c.type === "command_argument_sep") continue;
2616
+ return c;
2617
+ }
2618
+ return null;
2619
+ }
2620
+ /** Every bare string/number token under a node, in source order. */
2621
+ literalTokens(node) {
2622
+ const out = [];
2623
+ const work = [node];
2624
+ while (work.length > 0) {
2625
+ const n = work.pop();
2626
+ if (n.type === "string_literal" || n.type === "expandable_string_literal" || n.type === "generic_token") {
2627
+ out.push(this.stripQuotes(n.text));
2628
+ continue;
2629
+ }
2630
+ for (let i = n.namedChildCount - 1; i >= 0; i--) {
2631
+ const c = n.namedChild(i);
2632
+ if (c) work.push(c);
2633
+ }
2634
+ }
2635
+ return out.reverse();
2636
+ }
2637
+ findChildByType(node, type) {
2638
+ for (let i = 0; i < node.namedChildCount; i++) {
2639
+ const child = node.namedChild(i);
2640
+ if (child && child.type === type) return child;
2641
+ }
2642
+ return null;
2643
+ }
2644
+ findParamList(node) {
2645
+ const block = this.findChildByType(node, "script_block");
2646
+ if (block) {
2647
+ const paramBlock = this.findChildByType(block, "param_block");
2648
+ if (paramBlock) return this.findChildByType(paramBlock, "parameter_list");
2649
+ }
2650
+ return null;
2651
+ }
2652
+ findMethodParamList(node) {
2653
+ const paramDecl = this.findChildByType(node, "class_method_parameter_list");
2654
+ if (!paramDecl) return null;
2655
+ return this.findChildByType(paramDecl, "parameter_list") ?? paramDecl;
2656
+ }
2657
+ // PS27 REMOVED: `classAttributeTexts` had no callers left. It collected EVERY
2658
+ // `class_attribute` on a class member, which is correct for a member and wrong
2659
+ // for the pinned wasm's split — where one of those nodes is the front of the
2660
+ // NAME — and `memberHeaderFacts` replaces it with the position scan. It is
2661
+ // deleted rather than left in place because a private method that reads a
2662
+ // member's modifiers the wrong way is a trap for whoever calls it next, and
2663
+ // `class-inherit-psa2` will not catch a re-introduction: no fixture reads
2664
+ // `isStatic` off a split-shaped member.
2665
+ /**
2666
+ * PS11 — every attribute attached to a declaration, from BOTH places
2667
+ * PowerShell allows one: directly on the declaration, and on its `param_block`
2668
+ * (which is where `[CmdletBinding()]` and `[OutputType()]` actually sit).
2669
+ * Scoped to a parameter/attribute-list chain, so the `path` of a
2670
+ * `command_parameter` is never mistaken for a declaration attribute.
2671
+ */
2672
+ allAttributeTexts(node) {
2673
+ const out = [];
2674
+ for (const direct of this.collectChildrenByType(node, "attribute")) out.push(direct.text);
2675
+ const blocks = this.collectChildrenByType(node, "param_block");
2676
+ for (const sb of this.collectChildrenByType(node, "script_block")) {
2677
+ blocks.push(...this.collectChildrenByType(sb, "param_block"));
2678
+ }
2679
+ for (const block of blocks) {
2680
+ for (const list of this.collectChildrenByType(block, "attribute_list")) {
2681
+ for (const attr of this.collectChildrenByType(list, "attribute")) out.push(attr.text);
2682
+ }
2683
+ }
2684
+ for (const list of this.collectChildrenByType(node, "attribute_list")) {
2685
+ for (const attr of this.collectChildrenByType(list, "attribute")) out.push(attr.text);
2686
+ }
2687
+ return out;
2688
+ }
2689
+ /** An `attribute_list`'s `attribute` children, as raw text. */
2690
+ attributeTexts(node) {
2691
+ const lists = this.collectChildrenByType(node, "attribute_list");
2692
+ if (lists.length === 0) return void 0;
2693
+ const texts = [];
2694
+ for (const list of lists) {
2695
+ for (const attr of this.collectChildrenByType(list, "attribute")) texts.push(attr.text);
2696
+ }
2697
+ return texts.length > 0 ? texts : void 0;
2698
+ }
2699
+ /**
2700
+ * PS23 — the comment-based help block immediately above a declaration. A
2701
+ * PowerShell function's documentation IS its `.SYNOPSIS`/`.DESCRIPTION`
2702
+ * comment block (`about_Comment_Based_Help`), and the pre-wave lane read no
2703
+ * comment but `#requires`, so `docstring` was `null` for every symbol in the
2704
+ * language. `row` is the 0-based row of the declaration's NAME.
2705
+ */
2706
+ docstringAbove(row, lines) {
2707
+ let i = row - 1;
2708
+ if (i < 0 || lines[i] === void 0 || lines[i].trim() === "") return void 0;
2709
+ if (lines[i].trim().endsWith("#>")) {
2710
+ let open = -1;
2711
+ for (let k = i; k >= 0; k--) {
2712
+ if (lines[k].includes("<#")) {
2713
+ open = k;
2714
+ break;
2715
+ }
2716
+ }
2717
+ if (open < 0) return void 0;
2718
+ const last = i;
2719
+ const body = lines.slice(open, last + 1).map((l, k) => k === 0 ? stripOpenCommentDelim(l) : k === last - open ? stripCloseCommentDelim(l) : l).join("\n").trim();
2720
+ return body.length > 0 ? body : void 0;
2721
+ }
2722
+ const collected = [];
2723
+ while (i >= 0) {
2724
+ const raw = lines[i];
2725
+ if (raw === void 0) break;
2726
+ const trimmed = raw.trim();
2727
+ if (trimmed === "" || !trimmed.startsWith("#") || trimmed.startsWith("#>")) break;
2728
+ collected.unshift(trimmed);
2729
+ i--;
2730
+ }
2731
+ if (collected.length === 0) return void 0;
2732
+ const text = stripCommentMarkerRun(collected.join("\n")).trim();
2733
+ return text.length > 0 ? text : void 0;
2734
+ }
2735
+ /**
2736
+ * PS24 — EVERY module name a `#requires -Module[s]` declares, which is not the
2737
+ * same question `extractRequiresValue` answers.
2738
+ *
2739
+ * The reader above stops at the first space, tab or comma, which is right for
2740
+ * `-Version 7.0` and wrong twice for a module list. Measured on the corpus's own
2741
+ * two files that use the hashtable form
2742
+ * (`ansible/.../win_app_control/files/{New-AnsiblePowerShellSignature,
2743
+ * Set-ManifestSignature}.ps1`, both carrying
2744
+ * `#Requires -Modules @{ ModuleName = 'OpenAuthenticode'; ModuleVersion = '0.5.0' }`):
2745
+ * the old reader returned the literal `@{` as the module name, so `imports[]` stored
2746
+ * a row whose `path` was `@{` and `addImport` published `imports`/`references` edges
2747
+ * to `@{`. Those edges died one stage later — `filterNoiseRelationships` in
2748
+ * `src/ast/pool-worker.ts` drops any target whose leaf contains `{` — which is why
2749
+ * the census reads `imports[] 552` against `imports 550`, and the two rows that
2750
+ * vanish are exactly these two files. A garbage edge killed by shared machinery is
2751
+ * still a garbage row already written, and the module it should have named
2752
+ * (`OpenAuthenticode`) is a dependency the file genuinely declares.
2753
+ *
2754
+ * The comma list is the second miss, and rarer than the hashtable:
2755
+ * `#requires -Modules Pester, PoshRSJob` names two modules and the old reader
2756
+ * returned `Pester` alone, because the loop stopped at the comma. Measured over the
2757
+ * 1,092-file admitted corpus: ONE occurrence of a comma-separated `#requires` module
2758
+ * list — `#Requires -Modules PSScriptAnalyzer, PSSA-PSCustomUseLiteralPath` at
2759
+ * `ansible/test/lib/ansible_test/_util/controller/sanity/pslint/pslint.ps1:2` — so
2760
+ * `PSSA-PSCustomUseLiteralPath` was a declared dependency this lane read and dropped.
2761
+ *
2762
+ * MEASURED DELTA, and the population it is measured over. Only a file carrying a
2763
+ * `#requires -Module(s)` directive can be affected, and 59 of the 1,092 admitted files
2764
+ * do, so those 59 are the whole population and the delta is exact rather than
2765
+ * sampled. Through production `parseFile`, before -> after: `imports[]` records
2766
+ * 101 -> 102, `imports` edges 99 -> 102, `references` edges 105 -> 108. The records /
2767
+ * edges gap of 2 that exists BEFORE the fix is the two `@{` rows: they were written
2768
+ * and then dropped by the pool-worker's brace rule, which is why the whole-corpus
2769
+ * census reads `imports[] 552` against `imports 550`.
2770
+ *
2771
+ * Zero regex, like the reader above, for the reason recorded there. Three shapes are
2772
+ * recognized and nothing else: a bare list, a comma-continued bare list, and a
2773
+ * hashtable whose `ModuleName` key names the module. A hashtable with no
2774
+ * `ModuleName` names no module and yields nothing — `@{ ModuleVersion = '1.0' }` is
2775
+ * not a module declaration, and returning the flag's value for it is how `@{`
2776
+ * reached `imports[]` in the first place.
2777
+ */
2778
+ extractRequiresModules(text) {
2779
+ const names = [];
2780
+ const seen = /* @__PURE__ */ new Set();
2781
+ let via = "requires_list";
2782
+ const lower = text.toLowerCase();
2783
+ const add = (raw) => {
2784
+ const v = this.stripQuotes(raw.trim());
2785
+ if (v.length === 0 || seen.has(v)) return;
2786
+ seen.add(v);
2787
+ names.push({ name: v, via });
2788
+ };
2789
+ for (let i = 0; i < text.length; i++) {
2790
+ if (text[i] !== "-") continue;
2791
+ let j = i + 1;
2792
+ while (isAsciiLetter(lower[j])) j++;
2793
+ const flag = lower.slice(i + 1, j);
2794
+ if (flag !== "module" && flag !== "modules") continue;
2795
+ while (text[j] === " " || text[j] === " ") j++;
2796
+ if (j >= text.length) break;
2797
+ for (; ; ) {
2798
+ if (text[j] === "@" && text[j + 1] === "{") {
2799
+ const ht = this.readHashtableModuleName(text, j + 1);
2800
+ via = "requires_hashtable";
2801
+ if (ht) add(ht);
2802
+ while (text[j] !== void 0 && text[j] !== "}") j++;
2803
+ if (text[j] === "}") j++;
2804
+ } else {
2805
+ let end = j;
2806
+ while (text[end] !== void 0 && text[end] !== " " && text[end] !== " " && text[end] !== "," && text[end] !== ";") end++;
2807
+ if (end > j) add(text.slice(j, end));
2808
+ j = end;
2809
+ }
2810
+ while (text[j] === " " || text[j] === " ") j++;
2811
+ if (text[j] !== ",") break;
2812
+ j++;
2813
+ while (text[j] === " " || text[j] === " ") j++;
2814
+ if (j >= text.length) break;
2815
+ }
2816
+ i = j - 1;
2817
+ }
2818
+ return names;
2819
+ }
2820
+ /**
2821
+ * PS24 — the `ModuleName` value inside a `#requires -Modules @{ … }` hashtable,
2822
+ * starting at the `{`. Char-scanned and brace-balanced with quote awareness, because
2823
+ * a `}` inside a quoted `ModuleVersion` must not close the hashtable.
2824
+ *
2825
+ * Returns null when the hashtable carries no `ModuleName` key. PowerShell's module
2826
+ * hashtable is `{ ModuleName = 'X'; ModuleVersion | RequiredVersion | GUID = … }`, so
2827
+ * the key is the only thing in it that names a module.
2828
+ */
2829
+ readHashtableModuleName(text, openBrace) {
2830
+ const lower = text.toLowerCase();
2831
+ let depth = 0;
2832
+ let quote = "";
2833
+ for (let i = openBrace; i < text.length; i++) {
2834
+ const c = text[i];
2835
+ if (quote) {
2836
+ if (c === quote) quote = "";
2837
+ continue;
2838
+ }
2839
+ if (c === "'" || c === '"' || c === "`") {
2840
+ quote = c;
2841
+ continue;
2842
+ }
2843
+ if (c === "{") {
2844
+ depth++;
2845
+ continue;
2846
+ }
2847
+ if (c === "}") {
2848
+ depth--;
2849
+ if (depth === 0) return null;
2850
+ continue;
2851
+ }
2852
+ if (depth !== 1) continue;
2853
+ if (!isAsciiLetter(c) && c !== "_") continue;
2854
+ let e = i;
2855
+ while (isAsciiLetter(lower[e]) || lower[e] === "_") e++;
2856
+ const key = lower.slice(i, e);
2857
+ let v = e;
2858
+ while (text[v] === " " || text[v] === " ") v++;
2859
+ if (text[v] !== "=") {
2860
+ i = e - 1;
2861
+ continue;
2862
+ }
2863
+ v++;
2864
+ while (text[v] === " " || text[v] === " ") v++;
2865
+ let vend = v;
2866
+ let vq = "";
2867
+ while (vend < text.length) {
2868
+ const vc = text[vend];
2869
+ if (vq) {
2870
+ if (vc === vq) vq = "";
2871
+ vend++;
2872
+ continue;
2873
+ }
2874
+ if (vc === "'" || vc === '"' || vc === "`") {
2875
+ vq = vc;
2876
+ vend++;
2877
+ continue;
2878
+ }
2879
+ if (vc === ";" || vc === "}" || vc === " " || vc === " ") break;
2880
+ vend++;
2881
+ }
2882
+ if (key === "modulename") {
2883
+ const val = this.stripQuotes(text.slice(v, vend));
2884
+ return val.length > 0 ? val : null;
2885
+ }
2886
+ i = vend - 1;
2887
+ }
2888
+ return null;
2889
+ }
2890
+ /**
2891
+ * PS22 — a `#requires` value by name, char/index scanned. Comments are opaque
2892
+ * to the grammar, so this one place is text by necessity, and it is declared
2893
+ * as such here and in the M12 regex census (which records zero regex
2894
+ * usages for this lane: no `/…/` literal is used).
2895
+ */
2896
+ extractRequiresValue(text, name) {
2897
+ const lower = text.toLowerCase();
2898
+ const key = name.toLowerCase();
2899
+ for (let i = 0; i < text.length; i++) {
2900
+ if (text[i] !== "-") continue;
2901
+ if (!lower.slice(i + 1).startsWith(key)) continue;
2902
+ let j = i + 1 + name.length;
2903
+ while (j < text.length && (text[j] === " " || text[j] === " ")) j++;
2904
+ if (j >= text.length) return null;
2905
+ let end = j;
2906
+ while (end < text.length && text[end] !== " " && text[end] !== " " && text[end] !== ",") end++;
2907
+ const value = this.stripQuotes(text.slice(j, end));
2908
+ return value.length > 0 ? value : null;
2909
+ }
2910
+ return null;
2911
+ }
2912
+ };
2913
+ export {
2914
+ RealPowershellAnalyzer
2915
+ };