@ryanstark24/sfgraph-core 1.2.0 → 1.6.0

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 (877) hide show
  1. package/dist/analyze/apex-platform.d.ts +23 -0
  2. package/dist/analyze/apex-platform.d.ts.map +1 -0
  3. package/dist/analyze/apex-platform.js +112 -0
  4. package/dist/analyze/apex-platform.js.map +1 -0
  5. package/dist/analyze/audit-graph.d.ts +51 -3
  6. package/dist/analyze/audit-graph.d.ts.map +1 -1
  7. package/dist/analyze/audit-graph.js +382 -14
  8. package/dist/analyze/audit-graph.js.map +1 -1
  9. package/dist/analyze/component-scope.d.ts +82 -0
  10. package/dist/analyze/component-scope.d.ts.map +1 -0
  11. package/dist/analyze/component-scope.js +207 -0
  12. package/dist/analyze/component-scope.js.map +1 -0
  13. package/dist/analyze/containment.d.ts +91 -0
  14. package/dist/analyze/containment.d.ts.map +1 -0
  15. package/dist/analyze/containment.js +175 -0
  16. package/dist/analyze/containment.js.map +1 -0
  17. package/dist/analyze/cross-org.d.ts +35 -0
  18. package/dist/analyze/cross-org.d.ts.map +1 -1
  19. package/dist/analyze/cross-org.js +102 -3
  20. package/dist/analyze/cross-org.js.map +1 -1
  21. package/dist/analyze/dead-code.d.ts +99 -3
  22. package/dist/analyze/dead-code.d.ts.map +1 -1
  23. package/dist/analyze/dead-code.js +282 -10
  24. package/dist/analyze/dead-code.js.map +1 -1
  25. package/dist/analyze/dependencies.d.ts +21 -2
  26. package/dist/analyze/dependencies.d.ts.map +1 -1
  27. package/dist/analyze/dependencies.js +102 -12
  28. package/dist/analyze/dependencies.js.map +1 -1
  29. package/dist/analyze/dependents.d.ts +116 -1
  30. package/dist/analyze/dependents.d.ts.map +1 -1
  31. package/dist/analyze/dependents.js +285 -13
  32. package/dist/analyze/dependents.js.map +1 -1
  33. package/dist/analyze/deployment-manifest/generate.d.ts +24 -0
  34. package/dist/analyze/deployment-manifest/generate.d.ts.map +1 -1
  35. package/dist/analyze/deployment-manifest/generate.js +40 -12
  36. package/dist/analyze/deployment-manifest/generate.js.map +1 -1
  37. package/dist/analyze/deployment-manifest/member-name-formatters.d.ts.map +1 -1
  38. package/dist/analyze/deployment-manifest/member-name-formatters.js +1 -0
  39. package/dist/analyze/deployment-manifest/member-name-formatters.js.map +1 -1
  40. package/dist/analyze/deployment-manifest/member-types.d.ts.map +1 -1
  41. package/dist/analyze/deployment-manifest/member-types.js +2 -0
  42. package/dist/analyze/deployment-manifest/member-types.js.map +1 -1
  43. package/dist/analyze/diff-seeds.d.ts +33 -0
  44. package/dist/analyze/diff-seeds.d.ts.map +1 -0
  45. package/dist/analyze/diff-seeds.js +299 -0
  46. package/dist/analyze/diff-seeds.js.map +1 -0
  47. package/dist/analyze/edge-filters.d.ts +62 -0
  48. package/dist/analyze/edge-filters.d.ts.map +1 -0
  49. package/dist/analyze/edge-filters.js +127 -0
  50. package/dist/analyze/edge-filters.js.map +1 -0
  51. package/dist/analyze/field-scope.d.ts +25 -0
  52. package/dist/analyze/field-scope.d.ts.map +1 -0
  53. package/dist/analyze/field-scope.js +94 -0
  54. package/dist/analyze/field-scope.js.map +1 -0
  55. package/dist/analyze/find-nodes.d.ts +48 -12
  56. package/dist/analyze/find-nodes.d.ts.map +1 -1
  57. package/dist/analyze/find-nodes.js +323 -12
  58. package/dist/analyze/find-nodes.js.map +1 -1
  59. package/dist/analyze/findings.d.ts +1 -1
  60. package/dist/analyze/findings.d.ts.map +1 -1
  61. package/dist/analyze/findings.js +15 -3
  62. package/dist/analyze/findings.js.map +1 -1
  63. package/dist/analyze/git-diff.d.ts +27 -1
  64. package/dist/analyze/git-diff.d.ts.map +1 -1
  65. package/dist/analyze/git-diff.js +198 -26
  66. package/dist/analyze/git-diff.js.map +1 -1
  67. package/dist/analyze/governor.d.ts +25 -4
  68. package/dist/analyze/governor.d.ts.map +1 -1
  69. package/dist/analyze/governor.js +82 -18
  70. package/dist/analyze/governor.js.map +1 -1
  71. package/dist/analyze/index.d.ts +6 -0
  72. package/dist/analyze/index.d.ts.map +1 -1
  73. package/dist/analyze/index.js +6 -0
  74. package/dist/analyze/index.js.map +1 -1
  75. package/dist/analyze/path-to-qname.d.ts +9 -0
  76. package/dist/analyze/path-to-qname.d.ts.map +1 -1
  77. package/dist/analyze/path-to-qname.js +58 -15
  78. package/dist/analyze/path-to-qname.js.map +1 -1
  79. package/dist/analyze/populate.d.ts +40 -0
  80. package/dist/analyze/populate.d.ts.map +1 -1
  81. package/dist/analyze/populate.js +203 -58
  82. package/dist/analyze/populate.js.map +1 -1
  83. package/dist/analyze/pseudo-endpoints.d.ts +67 -0
  84. package/dist/analyze/pseudo-endpoints.d.ts.map +1 -0
  85. package/dist/analyze/pseudo-endpoints.js +78 -0
  86. package/dist/analyze/pseudo-endpoints.js.map +1 -0
  87. package/dist/analyze/security.d.ts +17 -0
  88. package/dist/analyze/security.d.ts.map +1 -1
  89. package/dist/analyze/security.js +103 -18
  90. package/dist/analyze/security.js.map +1 -1
  91. package/dist/analyze/test-coverage.d.ts +48 -0
  92. package/dist/analyze/test-coverage.d.ts.map +1 -1
  93. package/dist/analyze/test-coverage.js +72 -1
  94. package/dist/analyze/test-coverage.js.map +1 -1
  95. package/dist/analyze/wip-changed-files.d.ts +88 -0
  96. package/dist/analyze/wip-changed-files.d.ts.map +1 -0
  97. package/dist/analyze/wip-changed-files.js +422 -0
  98. package/dist/analyze/wip-changed-files.js.map +1 -0
  99. package/dist/analyze/wip-impact.d.ts +59 -0
  100. package/dist/analyze/wip-impact.d.ts.map +1 -1
  101. package/dist/analyze/wip-impact.js +199 -116
  102. package/dist/analyze/wip-impact.js.map +1 -1
  103. package/dist/domain/apex-platform.d.ts +10 -0
  104. package/dist/domain/apex-platform.d.ts.map +1 -0
  105. package/dist/domain/apex-platform.js +231 -0
  106. package/dist/domain/apex-platform.js.map +1 -0
  107. package/dist/domain/index.d.ts +2 -0
  108. package/dist/domain/index.d.ts.map +1 -1
  109. package/dist/domain/index.js +2 -0
  110. package/dist/domain/index.js.map +1 -1
  111. package/dist/domain/metadata-category.d.ts +1 -0
  112. package/dist/domain/metadata-category.d.ts.map +1 -1
  113. package/dist/domain/metadata-category.js +1 -0
  114. package/dist/domain/metadata-category.js.map +1 -1
  115. package/dist/domain/rel-types.d.ts +7 -0
  116. package/dist/domain/rel-types.d.ts.map +1 -1
  117. package/dist/domain/rel-types.js +7 -0
  118. package/dist/domain/rel-types.js.map +1 -1
  119. package/dist/domain/standard-sobjects.d.ts +20 -0
  120. package/dist/domain/standard-sobjects.d.ts.map +1 -0
  121. package/dist/domain/standard-sobjects.js +183 -0
  122. package/dist/domain/standard-sobjects.js.map +1 -0
  123. package/dist/embedding/cache.d.ts +41 -0
  124. package/dist/embedding/cache.d.ts.map +1 -0
  125. package/dist/embedding/cache.js +69 -0
  126. package/dist/embedding/cache.js.map +1 -0
  127. package/dist/embedding/embed.d.ts +59 -0
  128. package/dist/embedding/embed.d.ts.map +1 -1
  129. package/dist/embedding/embed.js +207 -29
  130. package/dist/embedding/embed.js.map +1 -1
  131. package/dist/embedding/index.d.ts +7 -0
  132. package/dist/embedding/index.d.ts.map +1 -1
  133. package/dist/embedding/index.js +4 -0
  134. package/dist/embedding/index.js.map +1 -1
  135. package/dist/embedding/pool.d.ts +66 -6
  136. package/dist/embedding/pool.d.ts.map +1 -1
  137. package/dist/embedding/pool.js +156 -6
  138. package/dist/embedding/pool.js.map +1 -1
  139. package/dist/embedding/queue.d.ts +166 -9
  140. package/dist/embedding/queue.d.ts.map +1 -1
  141. package/dist/embedding/queue.js +397 -39
  142. package/dist/embedding/queue.js.map +1 -1
  143. package/dist/embedding/tokenize.d.ts +18 -0
  144. package/dist/embedding/tokenize.d.ts.map +1 -0
  145. package/dist/embedding/tokenize.js +43 -0
  146. package/dist/embedding/tokenize.js.map +1 -0
  147. package/dist/embedding/worker.d.ts +0 -5
  148. package/dist/embedding/worker.d.ts.map +1 -1
  149. package/dist/embedding/worker.js +27 -4
  150. package/dist/embedding/worker.js.map +1 -1
  151. package/dist/extractors/filesystem/index.d.ts +21 -1
  152. package/dist/extractors/filesystem/index.d.ts.map +1 -1
  153. package/dist/extractors/filesystem/index.js +195 -38
  154. package/dist/extractors/filesystem/index.js.map +1 -1
  155. package/dist/extractors/filesystem/vlocity-datapacks.d.ts +27 -0
  156. package/dist/extractors/filesystem/vlocity-datapacks.d.ts.map +1 -0
  157. package/dist/extractors/filesystem/vlocity-datapacks.js +201 -0
  158. package/dist/extractors/filesystem/vlocity-datapacks.js.map +1 -0
  159. package/dist/extractors/interfaces/metadata-source.d.ts +40 -0
  160. package/dist/extractors/interfaces/metadata-source.d.ts.map +1 -1
  161. package/dist/extractors/live-org/bulk-retrieve.d.ts +206 -1
  162. package/dist/extractors/live-org/bulk-retrieve.d.ts.map +1 -1
  163. package/dist/extractors/live-org/bulk-retrieve.js +706 -94
  164. package/dist/extractors/live-org/bulk-retrieve.js.map +1 -1
  165. package/dist/extractors/live-org/capabilities.d.ts +15 -0
  166. package/dist/extractors/live-org/capabilities.d.ts.map +1 -1
  167. package/dist/extractors/live-org/capabilities.js +54 -9
  168. package/dist/extractors/live-org/capabilities.js.map +1 -1
  169. package/dist/extractors/live-org/extractors/apex.d.ts +12 -2
  170. package/dist/extractors/live-org/extractors/apex.d.ts.map +1 -1
  171. package/dist/extractors/live-org/extractors/apex.js +171 -75
  172. package/dist/extractors/live-org/extractors/apex.js.map +1 -1
  173. package/dist/extractors/live-org/extractors/custom-settings.d.ts +4 -0
  174. package/dist/extractors/live-org/extractors/custom-settings.d.ts.map +1 -0
  175. package/dist/extractors/live-org/extractors/custom-settings.js +76 -0
  176. package/dist/extractors/live-org/extractors/custom-settings.js.map +1 -0
  177. package/dist/extractors/live-org/extractors/flow.d.ts +2 -1
  178. package/dist/extractors/live-org/extractors/flow.d.ts.map +1 -1
  179. package/dist/extractors/live-org/extractors/flow.js +12 -3
  180. package/dist/extractors/live-org/extractors/flow.js.map +1 -1
  181. package/dist/extractors/live-org/extractors/generic-metadata.d.ts +23 -3
  182. package/dist/extractors/live-org/extractors/generic-metadata.d.ts.map +1 -1
  183. package/dist/extractors/live-org/extractors/generic-metadata.js +110 -25
  184. package/dist/extractors/live-org/extractors/generic-metadata.js.map +1 -1
  185. package/dist/extractors/live-org/extractors/generic-sobject.js.map +1 -1
  186. package/dist/extractors/live-org/extractors/integration.d.ts +2 -1
  187. package/dist/extractors/live-org/extractors/integration.d.ts.map +1 -1
  188. package/dist/extractors/live-org/extractors/integration.js +14 -5
  189. package/dist/extractors/live-org/extractors/integration.js.map +1 -1
  190. package/dist/extractors/live-org/extractors/lwc.d.ts +2 -1
  191. package/dist/extractors/live-org/extractors/lwc.d.ts.map +1 -1
  192. package/dist/extractors/live-org/extractors/lwc.js +95 -47
  193. package/dist/extractors/live-org/extractors/lwc.js.map +1 -1
  194. package/dist/extractors/live-org/extractors/mcd-baseline.d.ts.map +1 -1
  195. package/dist/extractors/live-org/extractors/mcd-baseline.js +2 -4
  196. package/dist/extractors/live-org/extractors/mcd-baseline.js.map +1 -1
  197. package/dist/extractors/live-org/extractors/object.d.ts +2 -11
  198. package/dist/extractors/live-org/extractors/object.d.ts.map +1 -1
  199. package/dist/extractors/live-org/extractors/object.js +127 -226
  200. package/dist/extractors/live-org/extractors/object.js.map +1 -1
  201. package/dist/extractors/live-org/extractors/omnistudio-retrieve.d.ts.map +1 -1
  202. package/dist/extractors/live-org/extractors/omnistudio-retrieve.js.map +1 -1
  203. package/dist/extractors/live-org/extractors/omnistudio.d.ts +11 -1
  204. package/dist/extractors/live-org/extractors/omnistudio.d.ts.map +1 -1
  205. package/dist/extractors/live-org/extractors/omnistudio.js +104 -16
  206. package/dist/extractors/live-org/extractors/omnistudio.js.map +1 -1
  207. package/dist/extractors/live-org/extractors/security.d.ts +12 -1
  208. package/dist/extractors/live-org/extractors/security.d.ts.map +1 -1
  209. package/dist/extractors/live-org/extractors/security.js +29 -7
  210. package/dist/extractors/live-org/extractors/security.js.map +1 -1
  211. package/dist/extractors/live-org/rate-limit.d.ts +48 -16
  212. package/dist/extractors/live-org/rate-limit.d.ts.map +1 -1
  213. package/dist/extractors/live-org/rate-limit.js +235 -40
  214. package/dist/extractors/live-org/rate-limit.js.map +1 -1
  215. package/dist/extractors/live-org/retry.d.ts +136 -0
  216. package/dist/extractors/live-org/retry.d.ts.map +1 -0
  217. package/dist/extractors/live-org/retry.js +416 -0
  218. package/dist/extractors/live-org/retry.js.map +1 -0
  219. package/dist/extractors/live-org/vlocity/runner.d.ts +2 -1
  220. package/dist/extractors/live-org/vlocity/runner.d.ts.map +1 -1
  221. package/dist/extractors/live-org/vlocity/runner.js +124 -72
  222. package/dist/extractors/live-org/vlocity/runner.js.map +1 -1
  223. package/dist/extractors/vlocity-shared/row.d.ts +19 -0
  224. package/dist/extractors/vlocity-shared/row.d.ts.map +1 -0
  225. package/dist/extractors/vlocity-shared/row.js +69 -0
  226. package/dist/extractors/vlocity-shared/row.js.map +1 -0
  227. package/dist/index.d.ts +1 -0
  228. package/dist/index.d.ts.map +1 -1
  229. package/dist/index.js +2 -0
  230. package/dist/index.js.map +1 -1
  231. package/dist/ingest/delta.d.ts +70 -0
  232. package/dist/ingest/delta.d.ts.map +1 -0
  233. package/dist/ingest/delta.js +534 -0
  234. package/dist/ingest/delta.js.map +1 -0
  235. package/dist/ingest/detect-deletions-guard.d.ts +79 -0
  236. package/dist/ingest/detect-deletions-guard.d.ts.map +1 -0
  237. package/dist/ingest/detect-deletions-guard.js +122 -0
  238. package/dist/ingest/detect-deletions-guard.js.map +1 -0
  239. package/dist/ingest/index.d.ts +8 -1
  240. package/dist/ingest/index.d.ts.map +1 -1
  241. package/dist/ingest/index.js +4 -0
  242. package/dist/ingest/index.js.map +1 -1
  243. package/dist/ingest/live-ingest.d.ts +180 -0
  244. package/dist/ingest/live-ingest.d.ts.map +1 -1
  245. package/dist/ingest/live-ingest.js +1617 -625
  246. package/dist/ingest/live-ingest.js.map +1 -1
  247. package/dist/ingest/lock.d.ts +47 -0
  248. package/dist/ingest/lock.d.ts.map +1 -0
  249. package/dist/ingest/lock.js +222 -0
  250. package/dist/ingest/lock.js.map +1 -0
  251. package/dist/ingest/member-rows.d.ts +88 -0
  252. package/dist/ingest/member-rows.d.ts.map +1 -0
  253. package/dist/ingest/member-rows.js +152 -0
  254. package/dist/ingest/member-rows.js.map +1 -0
  255. package/dist/ingest/offline-org.d.ts +8 -0
  256. package/dist/ingest/offline-org.d.ts.map +1 -0
  257. package/dist/ingest/offline-org.js +41 -0
  258. package/dist/ingest/offline-org.js.map +1 -0
  259. package/dist/ingest/parse-member.d.ts +53 -0
  260. package/dist/ingest/parse-member.d.ts.map +1 -0
  261. package/dist/ingest/parse-member.js +276 -0
  262. package/dist/ingest/parse-member.js.map +1 -0
  263. package/dist/ingest/parse-pool.d.ts +139 -0
  264. package/dist/ingest/parse-pool.d.ts.map +1 -0
  265. package/dist/ingest/parse-pool.js +593 -0
  266. package/dist/ingest/parse-pool.js.map +1 -0
  267. package/dist/ingest/parse-worker.d.ts +3 -0
  268. package/dist/ingest/parse-worker.d.ts.map +1 -0
  269. package/dist/ingest/parse-worker.js +100 -0
  270. package/dist/ingest/parse-worker.js.map +1 -0
  271. package/dist/ingest/profile.d.ts +27 -0
  272. package/dist/ingest/profile.d.ts.map +1 -0
  273. package/dist/ingest/profile.js +48 -0
  274. package/dist/ingest/profile.js.map +1 -0
  275. package/dist/ingest/resolver-gates.d.ts +12 -0
  276. package/dist/ingest/resolver-gates.d.ts.map +1 -0
  277. package/dist/ingest/resolver-gates.js +53 -0
  278. package/dist/ingest/resolver-gates.js.map +1 -0
  279. package/dist/parsers/apex/arity-resolver.d.ts +16 -3
  280. package/dist/parsers/apex/arity-resolver.d.ts.map +1 -1
  281. package/dist/parsers/apex/arity-resolver.js +31 -2
  282. package/dist/parsers/apex/arity-resolver.js.map +1 -1
  283. package/dist/parsers/apex/ast-extractor.d.ts +4 -0
  284. package/dist/parsers/apex/ast-extractor.d.ts.map +1 -1
  285. package/dist/parsers/apex/ast-extractor.js +128 -29
  286. package/dist/parsers/apex/ast-extractor.js.map +1 -1
  287. package/dist/parsers/apex/class-refs.d.ts +78 -0
  288. package/dist/parsers/apex/class-refs.d.ts.map +1 -0
  289. package/dist/parsers/apex/class-refs.js +465 -0
  290. package/dist/parsers/apex/class-refs.js.map +1 -0
  291. package/dist/parsers/apex/class.d.ts +158 -0
  292. package/dist/parsers/apex/class.d.ts.map +1 -1
  293. package/dist/parsers/apex/class.js +1073 -172
  294. package/dist/parsers/apex/class.js.map +1 -1
  295. package/dist/parsers/apex/dynamic-soql.d.ts +52 -0
  296. package/dist/parsers/apex/dynamic-soql.d.ts.map +1 -0
  297. package/dist/parsers/apex/dynamic-soql.js +427 -0
  298. package/dist/parsers/apex/dynamic-soql.js.map +1 -0
  299. package/dist/parsers/apex/field-access.d.ts +65 -0
  300. package/dist/parsers/apex/field-access.d.ts.map +1 -0
  301. package/dist/parsers/apex/field-access.js +438 -0
  302. package/dist/parsers/apex/field-access.js.map +1 -0
  303. package/dist/parsers/apex/interface-ref-resolver.d.ts +32 -0
  304. package/dist/parsers/apex/interface-ref-resolver.d.ts.map +1 -0
  305. package/dist/parsers/apex/interface-ref-resolver.js +64 -0
  306. package/dist/parsers/apex/interface-ref-resolver.js.map +1 -0
  307. package/dist/parsers/apex/name-refs.d.ts +83 -0
  308. package/dist/parsers/apex/name-refs.d.ts.map +1 -0
  309. package/dist/parsers/apex/name-refs.js +465 -0
  310. package/dist/parsers/apex/name-refs.js.map +1 -0
  311. package/dist/parsers/apex/page-refs.d.ts +11 -0
  312. package/dist/parsers/apex/page-refs.d.ts.map +1 -0
  313. package/dist/parsers/apex/page-refs.js +34 -0
  314. package/dist/parsers/apex/page-refs.js.map +1 -0
  315. package/dist/parsers/apex/ref-resolver.d.ts +58 -0
  316. package/dist/parsers/apex/ref-resolver.d.ts.map +1 -0
  317. package/dist/parsers/apex/ref-resolver.js +348 -0
  318. package/dist/parsers/apex/ref-resolver.js.map +1 -0
  319. package/dist/parsers/apex/soql-fields.d.ts +75 -0
  320. package/dist/parsers/apex/soql-fields.d.ts.map +1 -0
  321. package/dist/parsers/apex/soql-fields.js +338 -0
  322. package/dist/parsers/apex/soql-fields.js.map +1 -0
  323. package/dist/parsers/apex/trigger.d.ts.map +1 -1
  324. package/dist/parsers/apex/trigger.js +100 -2
  325. package/dist/parsers/apex/trigger.js.map +1 -1
  326. package/dist/parsers/apex/vlocity-calls.d.ts +25 -0
  327. package/dist/parsers/apex/vlocity-calls.d.ts.map +1 -0
  328. package/dist/parsers/apex/vlocity-calls.js +225 -0
  329. package/dist/parsers/apex/vlocity-calls.js.map +1 -0
  330. package/dist/parsers/aura/bundle.d.ts +39 -0
  331. package/dist/parsers/aura/bundle.d.ts.map +1 -0
  332. package/dist/parsers/aura/bundle.js +269 -0
  333. package/dist/parsers/aura/bundle.js.map +1 -0
  334. package/dist/parsers/aura/embed-resolver.d.ts +17 -0
  335. package/dist/parsers/aura/embed-resolver.d.ts.map +1 -0
  336. package/dist/parsers/aura/embed-resolver.js +58 -0
  337. package/dist/parsers/aura/embed-resolver.js.map +1 -0
  338. package/dist/parsers/aura/index.d.ts +4 -0
  339. package/dist/parsers/aura/index.d.ts.map +1 -0
  340. package/dist/parsers/aura/index.js +6 -0
  341. package/dist/parsers/aura/index.js.map +1 -0
  342. package/dist/parsers/common.d.ts +9 -0
  343. package/dist/parsers/common.d.ts.map +1 -1
  344. package/dist/parsers/common.js +17 -0
  345. package/dist/parsers/common.js.map +1 -1
  346. package/dist/parsers/cross-flavor-resolver.js +8 -2
  347. package/dist/parsers/cross-flavor-resolver.js.map +1 -1
  348. package/dist/parsers/flow/invocable-resolver.d.ts.map +1 -1
  349. package/dist/parsers/flow/invocable-resolver.js +2 -0
  350. package/dist/parsers/flow/invocable-resolver.js.map +1 -1
  351. package/dist/parsers/flow/parser.d.ts +9 -0
  352. package/dist/parsers/flow/parser.d.ts.map +1 -1
  353. package/dist/parsers/flow/parser.js +327 -2
  354. package/dist/parsers/flow/parser.js.map +1 -1
  355. package/dist/parsers/flow/relationship-resolver.d.ts +34 -0
  356. package/dist/parsers/flow/relationship-resolver.d.ts.map +1 -0
  357. package/dist/parsers/flow/relationship-resolver.js +199 -0
  358. package/dist/parsers/flow/relationship-resolver.js.map +1 -0
  359. package/dist/parsers/generic/opaque-node-parser.d.ts +5 -1
  360. package/dist/parsers/generic/opaque-node-parser.d.ts.map +1 -1
  361. package/dist/parsers/generic/opaque-node-parser.js +37 -5
  362. package/dist/parsers/generic/opaque-node-parser.js.map +1 -1
  363. package/dist/parsers/generic/reflection-walker.d.ts +7 -0
  364. package/dist/parsers/generic/reflection-walker.d.ts.map +1 -1
  365. package/dist/parsers/generic/reflection-walker.js +38 -13
  366. package/dist/parsers/generic/reflection-walker.js.map +1 -1
  367. package/dist/parsers/hash-salts.d.ts +72 -0
  368. package/dist/parsers/hash-salts.d.ts.map +1 -0
  369. package/dist/parsers/hash-salts.js +91 -0
  370. package/dist/parsers/hash-salts.js.map +1 -0
  371. package/dist/parsers/index.d.ts +2 -0
  372. package/dist/parsers/index.d.ts.map +1 -1
  373. package/dist/parsers/index.js +2 -0
  374. package/dist/parsers/index.js.map +1 -1
  375. package/dist/parsers/lwc/bundle.d.ts +1 -0
  376. package/dist/parsers/lwc/bundle.d.ts.map +1 -1
  377. package/dist/parsers/lwc/bundle.js +37 -2
  378. package/dist/parsers/lwc/bundle.js.map +1 -1
  379. package/dist/parsers/lwc/field-key-resolver.d.ts +32 -0
  380. package/dist/parsers/lwc/field-key-resolver.d.ts.map +1 -0
  381. package/dist/parsers/lwc/field-key-resolver.js +92 -0
  382. package/dist/parsers/lwc/field-key-resolver.js.map +1 -0
  383. package/dist/parsers/lwc/html-visitor.d.ts +1 -1
  384. package/dist/parsers/lwc/html-visitor.d.ts.map +1 -1
  385. package/dist/parsers/lwc/html-visitor.js +81 -3
  386. package/dist/parsers/lwc/html-visitor.js.map +1 -1
  387. package/dist/parsers/lwc/js-visitor.d.ts +16 -0
  388. package/dist/parsers/lwc/js-visitor.d.ts.map +1 -1
  389. package/dist/parsers/lwc/js-visitor.js +354 -12
  390. package/dist/parsers/lwc/js-visitor.js.map +1 -1
  391. package/dist/parsers/lwc/omnidef.d.ts +10 -0
  392. package/dist/parsers/lwc/omnidef.d.ts.map +1 -0
  393. package/dist/parsers/lwc/omnidef.js +127 -0
  394. package/dist/parsers/lwc/omnidef.js.map +1 -0
  395. package/dist/parsers/lwc/xml-meta.d.ts.map +1 -1
  396. package/dist/parsers/lwc/xml-meta.js +16 -1
  397. package/dist/parsers/lwc/xml-meta.js.map +1 -1
  398. package/dist/parsers/object/formula-hop-resolver.d.ts +33 -0
  399. package/dist/parsers/object/formula-hop-resolver.d.ts.map +1 -0
  400. package/dist/parsers/object/formula-hop-resolver.js +60 -0
  401. package/dist/parsers/object/formula-hop-resolver.js.map +1 -0
  402. package/dist/parsers/object/formula-refs.d.ts +40 -0
  403. package/dist/parsers/object/formula-refs.d.ts.map +1 -0
  404. package/dist/parsers/object/formula-refs.js +129 -0
  405. package/dist/parsers/object/formula-refs.js.map +1 -0
  406. package/dist/parsers/object/object.d.ts +3 -0
  407. package/dist/parsers/object/object.d.ts.map +1 -1
  408. package/dist/parsers/object/object.js +54 -57
  409. package/dist/parsers/object/object.js.map +1 -1
  410. package/dist/parsers/omnistudio/data-transform.d.ts.map +1 -1
  411. package/dist/parsers/omnistudio/data-transform.js +40 -7
  412. package/dist/parsers/omnistudio/data-transform.js.map +1 -1
  413. package/dist/parsers/omnistudio/integration-procedure.d.ts.map +1 -1
  414. package/dist/parsers/omnistudio/integration-procedure.js +6 -6
  415. package/dist/parsers/omnistudio/integration-procedure.js.map +1 -1
  416. package/dist/parsers/omnistudio/overlap-detector.d.ts.map +1 -1
  417. package/dist/parsers/omnistudio/overlap-detector.js +29 -11
  418. package/dist/parsers/omnistudio/overlap-detector.js.map +1 -1
  419. package/dist/parsers/omnistudio/process.d.ts.map +1 -1
  420. package/dist/parsers/omnistudio/process.js +6 -6
  421. package/dist/parsers/omnistudio/process.js.map +1 -1
  422. package/dist/parsers/omnistudio/remote-refs.d.ts +11 -0
  423. package/dist/parsers/omnistudio/remote-refs.d.ts.map +1 -0
  424. package/dist/parsers/omnistudio/remote-refs.js +17 -0
  425. package/dist/parsers/omnistudio/remote-refs.js.map +1 -0
  426. package/dist/parsers/omnistudio/ui-card.d.ts.map +1 -1
  427. package/dist/parsers/omnistudio/ui-card.js +6 -6
  428. package/dist/parsers/omnistudio/ui-card.js.map +1 -1
  429. package/dist/parsers/resolver-prune.d.ts +14 -0
  430. package/dist/parsers/resolver-prune.d.ts.map +1 -0
  431. package/dist/parsers/resolver-prune.js +29 -0
  432. package/dist/parsers/resolver-prune.js.map +1 -0
  433. package/dist/parsers/rules/_engine.d.ts.map +1 -1
  434. package/dist/parsers/rules/_engine.js +4 -0
  435. package/dist/parsers/rules/_engine.js.map +1 -1
  436. package/dist/parsers/rules/_engine.ts +3 -0
  437. package/dist/parsers/rules/_loader.d.ts +8 -0
  438. package/dist/parsers/rules/_loader.d.ts.map +1 -1
  439. package/dist/parsers/rules/_loader.js +17 -0
  440. package/dist/parsers/rules/_loader.js.map +1 -1
  441. package/dist/parsers/rules/_loader.ts +19 -0
  442. package/dist/parsers/rules/_schema.d.ts +12 -12
  443. package/dist/parsers/rules/_selectors.d.ts +1 -0
  444. package/dist/parsers/rules/_selectors.d.ts.map +1 -1
  445. package/dist/parsers/rules/_selectors.js +6 -0
  446. package/dist/parsers/rules/_selectors.js.map +1 -1
  447. package/dist/parsers/rules/_selectors.ts +6 -0
  448. package/dist/parsers/rules/apex-component.yml +49 -4
  449. package/dist/parsers/rules/apex-page.yml +49 -4
  450. package/dist/parsers/rules/approval-process.yml +25 -5
  451. package/dist/parsers/rules/custom-metadata-type.yml +14 -7
  452. package/dist/parsers/rules/custom-setting.yml +24 -0
  453. package/dist/parsers/rules/custom-tab.yml +41 -0
  454. package/dist/parsers/rules/lightning-page.yml +4 -2
  455. package/dist/parsers/rules/workflow.yml +30 -10
  456. package/dist/parsers/vf/refs.d.ts +39 -0
  457. package/dist/parsers/vf/refs.d.ts.map +1 -0
  458. package/dist/parsers/vf/refs.js +152 -0
  459. package/dist/parsers/vf/refs.js.map +1 -0
  460. package/dist/parsers/vlocity/apex-resolver.d.ts +21 -0
  461. package/dist/parsers/vlocity/apex-resolver.d.ts.map +1 -0
  462. package/dist/parsers/vlocity/apex-resolver.js +115 -0
  463. package/dist/parsers/vlocity/apex-resolver.js.map +1 -0
  464. package/dist/parsers/vlocity/common.d.ts +72 -1
  465. package/dist/parsers/vlocity/common.d.ts.map +1 -1
  466. package/dist/parsers/vlocity/common.js +332 -2
  467. package/dist/parsers/vlocity/common.js.map +1 -1
  468. package/dist/parsers/vlocity/data-raptor.d.ts +9 -0
  469. package/dist/parsers/vlocity/data-raptor.d.ts.map +1 -1
  470. package/dist/parsers/vlocity/data-raptor.js +148 -17
  471. package/dist/parsers/vlocity/data-raptor.js.map +1 -1
  472. package/dist/parsers/vlocity/datapack-refs.d.ts +22 -0
  473. package/dist/parsers/vlocity/datapack-refs.d.ts.map +1 -0
  474. package/dist/parsers/vlocity/datapack-refs.js +126 -0
  475. package/dist/parsers/vlocity/datapack-refs.js.map +1 -0
  476. package/dist/parsers/vlocity/dr-relationship-resolver.d.ts +26 -0
  477. package/dist/parsers/vlocity/dr-relationship-resolver.d.ts.map +1 -0
  478. package/dist/parsers/vlocity/dr-relationship-resolver.js +137 -0
  479. package/dist/parsers/vlocity/dr-relationship-resolver.js.map +1 -0
  480. package/dist/parsers/vlocity/integration-procedure.d.ts.map +1 -1
  481. package/dist/parsers/vlocity/integration-procedure.js +15 -48
  482. package/dist/parsers/vlocity/integration-procedure.js.map +1 -1
  483. package/dist/parsers/vlocity/name-match-resolver.d.ts +21 -0
  484. package/dist/parsers/vlocity/name-match-resolver.d.ts.map +1 -0
  485. package/dist/parsers/vlocity/name-match-resolver.js +97 -0
  486. package/dist/parsers/vlocity/name-match-resolver.js.map +1 -0
  487. package/dist/parsers/vlocity/omni-script.d.ts.map +1 -1
  488. package/dist/parsers/vlocity/omni-script.js +18 -32
  489. package/dist/parsers/vlocity/omni-script.js.map +1 -1
  490. package/dist/parsers/vlocity/vlocity-card.d.ts.map +1 -1
  491. package/dist/parsers/vlocity/vlocity-card.js +30 -36
  492. package/dist/parsers/vlocity/vlocity-card.js.map +1 -1
  493. package/dist/render/mermaid/dependency-graph.d.ts.map +1 -1
  494. package/dist/render/mermaid/dependency-graph.js +13 -1
  495. package/dist/render/mermaid/dependency-graph.js.map +1 -1
  496. package/dist/render/mermaid/diff.d.ts.map +1 -1
  497. package/dist/render/mermaid/diff.js +4 -2
  498. package/dist/render/mermaid/diff.js.map +1 -1
  499. package/dist/render/mermaid/shared.d.ts +7 -0
  500. package/dist/render/mermaid/shared.d.ts.map +1 -1
  501. package/dist/render/mermaid/shared.js +15 -6
  502. package/dist/render/mermaid/shared.js.map +1 -1
  503. package/dist/render/sarif.d.ts.map +1 -1
  504. package/dist/render/sarif.js +1 -3
  505. package/dist/render/sarif.js.map +1 -1
  506. package/dist/storage/index.d.ts +3 -3
  507. package/dist/storage/index.d.ts.map +1 -1
  508. package/dist/storage/index.js +2 -2
  509. package/dist/storage/index.js.map +1 -1
  510. package/dist/storage/interfaces.d.ts +212 -1
  511. package/dist/storage/interfaces.d.ts.map +1 -1
  512. package/dist/storage/sqlite/graph-store.d.ts +366 -3
  513. package/dist/storage/sqlite/graph-store.d.ts.map +1 -1
  514. package/dist/storage/sqlite/graph-store.js +1755 -68
  515. package/dist/storage/sqlite/graph-store.js.map +1 -1
  516. package/dist/storage/sqlite/migrations.d.ts +11 -0
  517. package/dist/storage/sqlite/migrations.d.ts.map +1 -1
  518. package/dist/storage/sqlite/migrations.js +245 -11
  519. package/dist/storage/sqlite/migrations.js.map +1 -1
  520. package/dist/storage/sqlite/rename-stability.d.ts +1 -1
  521. package/dist/storage/sqlite/rename-stability.d.ts.map +1 -1
  522. package/dist/storage/sqlite/rename-stability.js +2 -6
  523. package/dist/storage/sqlite/rename-stability.js.map +1 -1
  524. package/dist/storage/sqlite/snapshot-store.d.ts +79 -3
  525. package/dist/storage/sqlite/snapshot-store.d.ts.map +1 -1
  526. package/dist/storage/sqlite/snapshot-store.js +500 -100
  527. package/dist/storage/sqlite/snapshot-store.js.map +1 -1
  528. package/dist/storage/sqlite/snapshot-versions.d.ts +51 -0
  529. package/dist/storage/sqlite/snapshot-versions.d.ts.map +1 -0
  530. package/dist/storage/sqlite/snapshot-versions.js +209 -0
  531. package/dist/storage/sqlite/snapshot-versions.js.map +1 -0
  532. package/dist/storage/sqlite/vector-store.d.ts +12 -0
  533. package/dist/storage/sqlite/vector-store.d.ts.map +1 -1
  534. package/dist/storage/sqlite/vector-store.js +57 -14
  535. package/dist/storage/sqlite/vector-store.js.map +1 -1
  536. package/dist/util/process-owner.d.ts +35 -0
  537. package/dist/util/process-owner.d.ts.map +1 -0
  538. package/dist/util/process-owner.js +87 -0
  539. package/dist/util/process-owner.js.map +1 -0
  540. package/dist/util/worker-heap-cap.d.ts +10 -0
  541. package/dist/util/worker-heap-cap.d.ts.map +1 -0
  542. package/dist/util/worker-heap-cap.js +65 -0
  543. package/dist/util/worker-heap-cap.js.map +1 -0
  544. package/package.json +6 -7
  545. package/dist/analyze/__tests__/audit-graph.test.d.ts +0 -2
  546. package/dist/analyze/__tests__/audit-graph.test.d.ts.map +0 -1
  547. package/dist/analyze/__tests__/audit-graph.test.js +0 -122
  548. package/dist/analyze/__tests__/audit-graph.test.js.map +0 -1
  549. package/dist/analyze/__tests__/cross-org-two-store.test.d.ts +0 -2
  550. package/dist/analyze/__tests__/cross-org-two-store.test.d.ts.map +0 -1
  551. package/dist/analyze/__tests__/cross-org-two-store.test.js +0 -68
  552. package/dist/analyze/__tests__/cross-org-two-store.test.js.map +0 -1
  553. package/dist/analyze/__tests__/dead-code-confidence.test.d.ts +0 -2
  554. package/dist/analyze/__tests__/dead-code-confidence.test.d.ts.map +0 -1
  555. package/dist/analyze/__tests__/dead-code-confidence.test.js +0 -66
  556. package/dist/analyze/__tests__/dead-code-confidence.test.js.map +0 -1
  557. package/dist/analyze/__tests__/deployment-manifest.test.d.ts +0 -2
  558. package/dist/analyze/__tests__/deployment-manifest.test.d.ts.map +0 -1
  559. package/dist/analyze/__tests__/deployment-manifest.test.js +0 -66
  560. package/dist/analyze/__tests__/deployment-manifest.test.js.map +0 -1
  561. package/dist/analyze/__tests__/find-nodes.test.d.ts +0 -2
  562. package/dist/analyze/__tests__/find-nodes.test.d.ts.map +0 -1
  563. package/dist/analyze/__tests__/find-nodes.test.js +0 -129
  564. package/dist/analyze/__tests__/find-nodes.test.js.map +0 -1
  565. package/dist/analyze/__tests__/findings.test.d.ts +0 -2
  566. package/dist/analyze/__tests__/findings.test.d.ts.map +0 -1
  567. package/dist/analyze/__tests__/findings.test.js +0 -90
  568. package/dist/analyze/__tests__/findings.test.js.map +0 -1
  569. package/dist/analyze/__tests__/governor-risks-detection.test.d.ts +0 -2
  570. package/dist/analyze/__tests__/governor-risks-detection.test.d.ts.map +0 -1
  571. package/dist/analyze/__tests__/governor-risks-detection.test.js +0 -70
  572. package/dist/analyze/__tests__/governor-risks-detection.test.js.map +0 -1
  573. package/dist/analyze/__tests__/populate.test.d.ts +0 -2
  574. package/dist/analyze/__tests__/populate.test.d.ts.map +0 -1
  575. package/dist/analyze/__tests__/populate.test.js +0 -129
  576. package/dist/analyze/__tests__/populate.test.js.map +0 -1
  577. package/dist/analyze/__tests__/traversal-caps.test.d.ts +0 -2
  578. package/dist/analyze/__tests__/traversal-caps.test.d.ts.map +0 -1
  579. package/dist/analyze/__tests__/traversal-caps.test.js +0 -129
  580. package/dist/analyze/__tests__/traversal-caps.test.js.map +0 -1
  581. package/dist/analyze/__tests__/wip-impact.test.d.ts +0 -2
  582. package/dist/analyze/__tests__/wip-impact.test.d.ts.map +0 -1
  583. package/dist/analyze/__tests__/wip-impact.test.js +0 -134
  584. package/dist/analyze/__tests__/wip-impact.test.js.map +0 -1
  585. package/dist/embedding/__tests__/embed.test.d.ts +0 -2
  586. package/dist/embedding/__tests__/embed.test.d.ts.map +0 -1
  587. package/dist/embedding/__tests__/embed.test.js +0 -72
  588. package/dist/embedding/__tests__/embed.test.js.map +0 -1
  589. package/dist/embedding/__tests__/queue.test.d.ts +0 -2
  590. package/dist/embedding/__tests__/queue.test.d.ts.map +0 -1
  591. package/dist/embedding/__tests__/queue.test.js +0 -64
  592. package/dist/embedding/__tests__/queue.test.js.map +0 -1
  593. package/dist/extractors/__tests__/network-egress.test.d.ts +0 -2
  594. package/dist/extractors/__tests__/network-egress.test.d.ts.map +0 -1
  595. package/dist/extractors/__tests__/network-egress.test.js +0 -32
  596. package/dist/extractors/__tests__/network-egress.test.js.map +0 -1
  597. package/dist/extractors/filesystem/__tests__/filesystem.test.d.ts +0 -2
  598. package/dist/extractors/filesystem/__tests__/filesystem.test.d.ts.map +0 -1
  599. package/dist/extractors/filesystem/__tests__/filesystem.test.js +0 -96
  600. package/dist/extractors/filesystem/__tests__/filesystem.test.js.map +0 -1
  601. package/dist/extractors/live-org/__tests__/_jsforce-mock.d.ts +0 -38
  602. package/dist/extractors/live-org/__tests__/_jsforce-mock.d.ts.map +0 -1
  603. package/dist/extractors/live-org/__tests__/_jsforce-mock.js +0 -96
  604. package/dist/extractors/live-org/__tests__/_jsforce-mock.js.map +0 -1
  605. package/dist/extractors/live-org/__tests__/auth.test.d.ts +0 -2
  606. package/dist/extractors/live-org/__tests__/auth.test.d.ts.map +0 -1
  607. package/dist/extractors/live-org/__tests__/auth.test.js +0 -85
  608. package/dist/extractors/live-org/__tests__/auth.test.js.map +0 -1
  609. package/dist/extractors/live-org/__tests__/bulk-retrieve.test.d.ts +0 -2
  610. package/dist/extractors/live-org/__tests__/bulk-retrieve.test.d.ts.map +0 -1
  611. package/dist/extractors/live-org/__tests__/bulk-retrieve.test.js +0 -150
  612. package/dist/extractors/live-org/__tests__/bulk-retrieve.test.js.map +0 -1
  613. package/dist/extractors/live-org/__tests__/capabilities.test.d.ts +0 -2
  614. package/dist/extractors/live-org/__tests__/capabilities.test.d.ts.map +0 -1
  615. package/dist/extractors/live-org/__tests__/capabilities.test.js +0 -54
  616. package/dist/extractors/live-org/__tests__/capabilities.test.js.map +0 -1
  617. package/dist/extractors/live-org/__tests__/discovery.test.d.ts +0 -2
  618. package/dist/extractors/live-org/__tests__/discovery.test.d.ts.map +0 -1
  619. package/dist/extractors/live-org/__tests__/discovery.test.js +0 -55
  620. package/dist/extractors/live-org/__tests__/discovery.test.js.map +0 -1
  621. package/dist/extractors/live-org/__tests__/dispatch.test.d.ts +0 -2
  622. package/dist/extractors/live-org/__tests__/dispatch.test.d.ts.map +0 -1
  623. package/dist/extractors/live-org/__tests__/dispatch.test.js +0 -68
  624. package/dist/extractors/live-org/__tests__/dispatch.test.js.map +0 -1
  625. package/dist/extractors/live-org/__tests__/extractor-enrichment.test.d.ts +0 -2
  626. package/dist/extractors/live-org/__tests__/extractor-enrichment.test.d.ts.map +0 -1
  627. package/dist/extractors/live-org/__tests__/extractor-enrichment.test.js +0 -312
  628. package/dist/extractors/live-org/__tests__/extractor-enrichment.test.js.map +0 -1
  629. package/dist/extractors/live-org/__tests__/liveness.test.d.ts +0 -2
  630. package/dist/extractors/live-org/__tests__/liveness.test.d.ts.map +0 -1
  631. package/dist/extractors/live-org/__tests__/liveness.test.js +0 -148
  632. package/dist/extractors/live-org/__tests__/liveness.test.js.map +0 -1
  633. package/dist/extractors/live-org/__tests__/mcd-baseline.test.d.ts +0 -2
  634. package/dist/extractors/live-org/__tests__/mcd-baseline.test.d.ts.map +0 -1
  635. package/dist/extractors/live-org/__tests__/mcd-baseline.test.js +0 -174
  636. package/dist/extractors/live-org/__tests__/mcd-baseline.test.js.map +0 -1
  637. package/dist/extractors/live-org/__tests__/metadata-read-batch.test.d.ts +0 -2
  638. package/dist/extractors/live-org/__tests__/metadata-read-batch.test.d.ts.map +0 -1
  639. package/dist/extractors/live-org/__tests__/metadata-read-batch.test.js +0 -75
  640. package/dist/extractors/live-org/__tests__/metadata-read-batch.test.js.map +0 -1
  641. package/dist/extractors/live-org/__tests__/omnistudio-retrieve.test.d.ts +0 -2
  642. package/dist/extractors/live-org/__tests__/omnistudio-retrieve.test.d.ts.map +0 -1
  643. package/dist/extractors/live-org/__tests__/omnistudio-retrieve.test.js +0 -135
  644. package/dist/extractors/live-org/__tests__/omnistudio-retrieve.test.js.map +0 -1
  645. package/dist/extractors/live-org/__tests__/pools.test.d.ts +0 -2
  646. package/dist/extractors/live-org/__tests__/pools.test.d.ts.map +0 -1
  647. package/dist/extractors/live-org/__tests__/pools.test.js +0 -75
  648. package/dist/extractors/live-org/__tests__/pools.test.js.map +0 -1
  649. package/dist/extractors/live-org/__tests__/rate-limit.test.d.ts +0 -2
  650. package/dist/extractors/live-org/__tests__/rate-limit.test.d.ts.map +0 -1
  651. package/dist/extractors/live-org/__tests__/rate-limit.test.js +0 -78
  652. package/dist/extractors/live-org/__tests__/rate-limit.test.js.map +0 -1
  653. package/dist/extractors/live-org/__tests__/read-only-proxy.test.d.ts +0 -2
  654. package/dist/extractors/live-org/__tests__/read-only-proxy.test.d.ts.map +0 -1
  655. package/dist/extractors/live-org/__tests__/read-only-proxy.test.js +0 -376
  656. package/dist/extractors/live-org/__tests__/read-only-proxy.test.js.map +0 -1
  657. package/dist/extractors/live-org/__tests__/soql-rebatch.test.d.ts +0 -2
  658. package/dist/extractors/live-org/__tests__/soql-rebatch.test.d.ts.map +0 -1
  659. package/dist/extractors/live-org/__tests__/soql-rebatch.test.js +0 -118
  660. package/dist/extractors/live-org/__tests__/soql-rebatch.test.js.map +0 -1
  661. package/dist/extractors/live-org/__tests__/source-member.test.d.ts +0 -2
  662. package/dist/extractors/live-org/__tests__/source-member.test.d.ts.map +0 -1
  663. package/dist/extractors/live-org/__tests__/source-member.test.js +0 -84
  664. package/dist/extractors/live-org/__tests__/source-member.test.js.map +0 -1
  665. package/dist/extractors/live-org/__tests__/vlocity-runner.test.d.ts +0 -2
  666. package/dist/extractors/live-org/__tests__/vlocity-runner.test.d.ts.map +0 -1
  667. package/dist/extractors/live-org/__tests__/vlocity-runner.test.js +0 -115
  668. package/dist/extractors/live-org/__tests__/vlocity-runner.test.js.map +0 -1
  669. package/dist/ingest/__tests__/full-sync-deletion.test.d.ts +0 -2
  670. package/dist/ingest/__tests__/full-sync-deletion.test.d.ts.map +0 -1
  671. package/dist/ingest/__tests__/full-sync-deletion.test.js +0 -200
  672. package/dist/ingest/__tests__/full-sync-deletion.test.js.map +0 -1
  673. package/dist/ingest/__tests__/incremental-deletion.test.d.ts +0 -2
  674. package/dist/ingest/__tests__/incremental-deletion.test.d.ts.map +0 -1
  675. package/dist/ingest/__tests__/incremental-deletion.test.js +0 -92
  676. package/dist/ingest/__tests__/incremental-deletion.test.js.map +0 -1
  677. package/dist/ingest/__tests__/live-ingest.test.d.ts +0 -2
  678. package/dist/ingest/__tests__/live-ingest.test.d.ts.map +0 -1
  679. package/dist/ingest/__tests__/live-ingest.test.js +0 -155
  680. package/dist/ingest/__tests__/live-ingest.test.js.map +0 -1
  681. package/dist/ingest/__tests__/multi-org-orchestrator.test.d.ts +0 -2
  682. package/dist/ingest/__tests__/multi-org-orchestrator.test.d.ts.map +0 -1
  683. package/dist/ingest/__tests__/multi-org-orchestrator.test.js +0 -108
  684. package/dist/ingest/__tests__/multi-org-orchestrator.test.js.map +0 -1
  685. package/dist/ingest/__tests__/read-only-wrapping.test.d.ts +0 -2
  686. package/dist/ingest/__tests__/read-only-wrapping.test.d.ts.map +0 -1
  687. package/dist/ingest/__tests__/read-only-wrapping.test.js +0 -62
  688. package/dist/ingest/__tests__/read-only-wrapping.test.js.map +0 -1
  689. package/dist/parsers/__tests__/_harness.d.ts +0 -13
  690. package/dist/parsers/__tests__/_harness.d.ts.map +0 -1
  691. package/dist/parsers/__tests__/_harness.js +0 -84
  692. package/dist/parsers/__tests__/_harness.js.map +0 -1
  693. package/dist/parsers/__tests__/apex.golden.test.d.ts +0 -2
  694. package/dist/parsers/__tests__/apex.golden.test.d.ts.map +0 -1
  695. package/dist/parsers/__tests__/apex.golden.test.js +0 -28
  696. package/dist/parsers/__tests__/apex.golden.test.js.map +0 -1
  697. package/dist/parsers/__tests__/cross-flavor.test.d.ts +0 -2
  698. package/dist/parsers/__tests__/cross-flavor.test.d.ts.map +0 -1
  699. package/dist/parsers/__tests__/cross-flavor.test.js +0 -56
  700. package/dist/parsers/__tests__/cross-flavor.test.js.map +0 -1
  701. package/dist/parsers/__tests__/edge-provenance.test.d.ts +0 -2
  702. package/dist/parsers/__tests__/edge-provenance.test.d.ts.map +0 -1
  703. package/dist/parsers/__tests__/edge-provenance.test.js +0 -44
  704. package/dist/parsers/__tests__/edge-provenance.test.js.map +0 -1
  705. package/dist/parsers/__tests__/flow.golden.test.d.ts +0 -2
  706. package/dist/parsers/__tests__/flow.golden.test.d.ts.map +0 -1
  707. package/dist/parsers/__tests__/flow.golden.test.js +0 -17
  708. package/dist/parsers/__tests__/flow.golden.test.js.map +0 -1
  709. package/dist/parsers/__tests__/integration.golden.test.d.ts +0 -2
  710. package/dist/parsers/__tests__/integration.golden.test.d.ts.map +0 -1
  711. package/dist/parsers/__tests__/integration.golden.test.js +0 -31
  712. package/dist/parsers/__tests__/integration.golden.test.js.map +0 -1
  713. package/dist/parsers/__tests__/long-tail.golden.test.d.ts +0 -2
  714. package/dist/parsers/__tests__/long-tail.golden.test.d.ts.map +0 -1
  715. package/dist/parsers/__tests__/long-tail.golden.test.js +0 -115
  716. package/dist/parsers/__tests__/long-tail.golden.test.js.map +0 -1
  717. package/dist/parsers/__tests__/lwc-bindings.test.d.ts +0 -2
  718. package/dist/parsers/__tests__/lwc-bindings.test.d.ts.map +0 -1
  719. package/dist/parsers/__tests__/lwc-bindings.test.js +0 -146
  720. package/dist/parsers/__tests__/lwc-bindings.test.js.map +0 -1
  721. package/dist/parsers/__tests__/lwc.golden.test.d.ts +0 -2
  722. package/dist/parsers/__tests__/lwc.golden.test.d.ts.map +0 -1
  723. package/dist/parsers/__tests__/lwc.golden.test.js +0 -16
  724. package/dist/parsers/__tests__/lwc.golden.test.js.map +0 -1
  725. package/dist/parsers/__tests__/mcd-gap-fills.test.d.ts +0 -2
  726. package/dist/parsers/__tests__/mcd-gap-fills.test.d.ts.map +0 -1
  727. package/dist/parsers/__tests__/mcd-gap-fills.test.js +0 -75
  728. package/dist/parsers/__tests__/mcd-gap-fills.test.js.map +0 -1
  729. package/dist/parsers/__tests__/object-inline-fields.test.d.ts +0 -2
  730. package/dist/parsers/__tests__/object-inline-fields.test.d.ts.map +0 -1
  731. package/dist/parsers/__tests__/object-inline-fields.test.js +0 -115
  732. package/dist/parsers/__tests__/object-inline-fields.test.js.map +0 -1
  733. package/dist/parsers/__tests__/object.golden.test.d.ts +0 -2
  734. package/dist/parsers/__tests__/object.golden.test.d.ts.map +0 -1
  735. package/dist/parsers/__tests__/object.golden.test.js +0 -33
  736. package/dist/parsers/__tests__/object.golden.test.js.map +0 -1
  737. package/dist/parsers/__tests__/omnistudio-overlap.test.d.ts +0 -2
  738. package/dist/parsers/__tests__/omnistudio-overlap.test.d.ts.map +0 -1
  739. package/dist/parsers/__tests__/omnistudio-overlap.test.js +0 -163
  740. package/dist/parsers/__tests__/omnistudio-overlap.test.js.map +0 -1
  741. package/dist/parsers/__tests__/omnistudio.golden.test.d.ts +0 -2
  742. package/dist/parsers/__tests__/omnistudio.golden.test.d.ts.map +0 -1
  743. package/dist/parsers/__tests__/omnistudio.golden.test.js +0 -54
  744. package/dist/parsers/__tests__/omnistudio.golden.test.js.map +0 -1
  745. package/dist/parsers/__tests__/reflection-walker.test.d.ts +0 -2
  746. package/dist/parsers/__tests__/reflection-walker.test.d.ts.map +0 -1
  747. package/dist/parsers/__tests__/reflection-walker.test.js +0 -162
  748. package/dist/parsers/__tests__/reflection-walker.test.js.map +0 -1
  749. package/dist/parsers/__tests__/registry.test.d.ts +0 -2
  750. package/dist/parsers/__tests__/registry.test.d.ts.map +0 -1
  751. package/dist/parsers/__tests__/registry.test.js +0 -23
  752. package/dist/parsers/__tests__/registry.test.js.map +0 -1
  753. package/dist/parsers/__tests__/security.golden.test.d.ts +0 -2
  754. package/dist/parsers/__tests__/security.golden.test.d.ts.map +0 -1
  755. package/dist/parsers/__tests__/security.golden.test.js +0 -31
  756. package/dist/parsers/__tests__/security.golden.test.js.map +0 -1
  757. package/dist/parsers/__tests__/vlocity.golden.test.d.ts +0 -2
  758. package/dist/parsers/__tests__/vlocity.golden.test.d.ts.map +0 -1
  759. package/dist/parsers/__tests__/vlocity.golden.test.js +0 -109
  760. package/dist/parsers/__tests__/vlocity.golden.test.js.map +0 -1
  761. package/dist/parsers/__tests__/worker-pool.test.d.ts +0 -2
  762. package/dist/parsers/__tests__/worker-pool.test.d.ts.map +0 -1
  763. package/dist/parsers/__tests__/worker-pool.test.js +0 -48
  764. package/dist/parsers/__tests__/worker-pool.test.js.map +0 -1
  765. package/dist/parsers/apex/__tests__/arity-resolver.test.d.ts +0 -2
  766. package/dist/parsers/apex/__tests__/arity-resolver.test.d.ts.map +0 -1
  767. package/dist/parsers/apex/__tests__/arity-resolver.test.js +0 -135
  768. package/dist/parsers/apex/__tests__/arity-resolver.test.js.map +0 -1
  769. package/dist/parsers/apex/__tests__/ast-extractor.test.d.ts +0 -2
  770. package/dist/parsers/apex/__tests__/ast-extractor.test.d.ts.map +0 -1
  771. package/dist/parsers/apex/__tests__/ast-extractor.test.js +0 -146
  772. package/dist/parsers/apex/__tests__/ast-extractor.test.js.map +0 -1
  773. package/dist/parsers/apex/__tests__/is-test-attribute.test.d.ts +0 -2
  774. package/dist/parsers/apex/__tests__/is-test-attribute.test.d.ts.map +0 -1
  775. package/dist/parsers/apex/__tests__/is-test-attribute.test.js +0 -54
  776. package/dist/parsers/apex/__tests__/is-test-attribute.test.js.map +0 -1
  777. package/dist/parsers/apex/__tests__/regex-arg-count.test.d.ts +0 -2
  778. package/dist/parsers/apex/__tests__/regex-arg-count.test.d.ts.map +0 -1
  779. package/dist/parsers/apex/__tests__/regex-arg-count.test.js +0 -82
  780. package/dist/parsers/apex/__tests__/regex-arg-count.test.js.map +0 -1
  781. package/dist/parsers/flow/__tests__/invocable-resolver.test.d.ts +0 -2
  782. package/dist/parsers/flow/__tests__/invocable-resolver.test.d.ts.map +0 -1
  783. package/dist/parsers/flow/__tests__/invocable-resolver.test.js +0 -91
  784. package/dist/parsers/flow/__tests__/invocable-resolver.test.js.map +0 -1
  785. package/dist/parsers/rules/__tests__/applies-to.test.d.ts +0 -2
  786. package/dist/parsers/rules/__tests__/applies-to.test.d.ts.map +0 -1
  787. package/dist/parsers/rules/__tests__/applies-to.test.js +0 -31
  788. package/dist/parsers/rules/__tests__/applies-to.test.js.map +0 -1
  789. package/dist/parsers/rules/__tests__/applies-to.test.ts +0 -40
  790. package/dist/parsers/rules/__tests__/engine.test.d.ts +0 -2
  791. package/dist/parsers/rules/__tests__/engine.test.d.ts.map +0 -1
  792. package/dist/parsers/rules/__tests__/engine.test.js +0 -127
  793. package/dist/parsers/rules/__tests__/engine.test.js.map +0 -1
  794. package/dist/parsers/rules/__tests__/engine.test.ts +0 -138
  795. package/dist/parsers/rules/__tests__/loader.test.d.ts +0 -2
  796. package/dist/parsers/rules/__tests__/loader.test.d.ts.map +0 -1
  797. package/dist/parsers/rules/__tests__/loader.test.js +0 -31
  798. package/dist/parsers/rules/__tests__/loader.test.js.map +0 -1
  799. package/dist/parsers/rules/__tests__/loader.test.ts +0 -37
  800. package/dist/parsers/rules/__tests__/schema.test.d.ts +0 -2
  801. package/dist/parsers/rules/__tests__/schema.test.d.ts.map +0 -1
  802. package/dist/parsers/rules/__tests__/schema.test.js +0 -33
  803. package/dist/parsers/rules/__tests__/schema.test.js.map +0 -1
  804. package/dist/parsers/rules/__tests__/schema.test.ts +0 -37
  805. package/dist/parsers/rules/__tests__/selectors.test.d.ts +0 -2
  806. package/dist/parsers/rules/__tests__/selectors.test.d.ts.map +0 -1
  807. package/dist/parsers/rules/__tests__/selectors.test.js +0 -51
  808. package/dist/parsers/rules/__tests__/selectors.test.js.map +0 -1
  809. package/dist/parsers/rules/__tests__/selectors.test.ts +0 -62
  810. package/dist/render/__tests__/sarif.test.d.ts +0 -2
  811. package/dist/render/__tests__/sarif.test.d.ts.map +0 -1
  812. package/dist/render/__tests__/sarif.test.js +0 -129
  813. package/dist/render/__tests__/sarif.test.js.map +0 -1
  814. package/dist/render/mermaid/__tests__/mermaid.test.d.ts +0 -2
  815. package/dist/render/mermaid/__tests__/mermaid.test.d.ts.map +0 -1
  816. package/dist/render/mermaid/__tests__/mermaid.test.js +0 -74
  817. package/dist/render/mermaid/__tests__/mermaid.test.js.map +0 -1
  818. package/dist/storage/__tests__/analysis-tables-migration.test.d.ts +0 -2
  819. package/dist/storage/__tests__/analysis-tables-migration.test.d.ts.map +0 -1
  820. package/dist/storage/__tests__/analysis-tables-migration.test.js +0 -44
  821. package/dist/storage/__tests__/analysis-tables-migration.test.js.map +0 -1
  822. package/dist/storage/__tests__/graph-store.test.d.ts +0 -2
  823. package/dist/storage/__tests__/graph-store.test.d.ts.map +0 -1
  824. package/dist/storage/__tests__/graph-store.test.js +0 -132
  825. package/dist/storage/__tests__/graph-store.test.js.map +0 -1
  826. package/dist/storage/__tests__/identifier.test.d.ts +0 -2
  827. package/dist/storage/__tests__/identifier.test.d.ts.map +0 -1
  828. package/dist/storage/__tests__/identifier.test.js +0 -35
  829. package/dist/storage/__tests__/identifier.test.js.map +0 -1
  830. package/dist/storage/__tests__/load-better-sqlite3.test.d.ts +0 -2
  831. package/dist/storage/__tests__/load-better-sqlite3.test.d.ts.map +0 -1
  832. package/dist/storage/__tests__/load-better-sqlite3.test.js +0 -65
  833. package/dist/storage/__tests__/load-better-sqlite3.test.js.map +0 -1
  834. package/dist/storage/__tests__/migrations.test.d.ts +0 -2
  835. package/dist/storage/__tests__/migrations.test.d.ts.map +0 -1
  836. package/dist/storage/__tests__/migrations.test.js +0 -93
  837. package/dist/storage/__tests__/migrations.test.js.map +0 -1
  838. package/dist/storage/__tests__/perf-50k.test.d.ts +0 -2
  839. package/dist/storage/__tests__/perf-50k.test.d.ts.map +0 -1
  840. package/dist/storage/__tests__/perf-50k.test.js +0 -67
  841. package/dist/storage/__tests__/perf-50k.test.js.map +0 -1
  842. package/dist/storage/__tests__/rename-stability.test.d.ts +0 -2
  843. package/dist/storage/__tests__/rename-stability.test.d.ts.map +0 -1
  844. package/dist/storage/__tests__/rename-stability.test.js +0 -135
  845. package/dist/storage/__tests__/rename-stability.test.js.map +0 -1
  846. package/dist/storage/__tests__/snapshot-store.test.d.ts +0 -2
  847. package/dist/storage/__tests__/snapshot-store.test.d.ts.map +0 -1
  848. package/dist/storage/__tests__/snapshot-store.test.js +0 -117
  849. package/dist/storage/__tests__/snapshot-store.test.js.map +0 -1
  850. package/dist/storage/__tests__/snippets.test.d.ts +0 -2
  851. package/dist/storage/__tests__/snippets.test.d.ts.map +0 -1
  852. package/dist/storage/__tests__/snippets.test.js +0 -88
  853. package/dist/storage/__tests__/snippets.test.js.map +0 -1
  854. package/dist/storage/__tests__/vector-store-load-failure.test.d.ts +0 -2
  855. package/dist/storage/__tests__/vector-store-load-failure.test.d.ts.map +0 -1
  856. package/dist/storage/__tests__/vector-store-load-failure.test.js +0 -42
  857. package/dist/storage/__tests__/vector-store-load-failure.test.js.map +0 -1
  858. package/dist/storage/__tests__/vector-store.test.d.ts +0 -2
  859. package/dist/storage/__tests__/vector-store.test.d.ts.map +0 -1
  860. package/dist/storage/__tests__/vector-store.test.js +0 -87
  861. package/dist/storage/__tests__/vector-store.test.js.map +0 -1
  862. package/dist/telemetry/__tests__/event-schema.test.d.ts +0 -2
  863. package/dist/telemetry/__tests__/event-schema.test.d.ts.map +0 -1
  864. package/dist/telemetry/__tests__/event-schema.test.js +0 -56
  865. package/dist/telemetry/__tests__/event-schema.test.js.map +0 -1
  866. package/dist/telemetry/__tests__/machine-id.test.d.ts +0 -2
  867. package/dist/telemetry/__tests__/machine-id.test.d.ts.map +0 -1
  868. package/dist/telemetry/__tests__/machine-id.test.js +0 -33
  869. package/dist/telemetry/__tests__/machine-id.test.js.map +0 -1
  870. package/dist/telemetry/__tests__/pipeline.test.d.ts +0 -2
  871. package/dist/telemetry/__tests__/pipeline.test.d.ts.map +0 -1
  872. package/dist/telemetry/__tests__/pipeline.test.js +0 -68
  873. package/dist/telemetry/__tests__/pipeline.test.js.map +0 -1
  874. package/dist/telemetry/__tests__/sanitizer.test.d.ts +0 -2
  875. package/dist/telemetry/__tests__/sanitizer.test.d.ts.map +0 -1
  876. package/dist/telemetry/__tests__/sanitizer.test.js +0 -143
  877. package/dist/telemetry/__tests__/sanitizer.test.js.map +0 -1
@@ -2,8 +2,88 @@ import { createHash } from "node:crypto";
2
2
  import { asOrgId, asQualifiedName, asSha256 } from "@ryanstark24/sfgraph-shared";
3
3
  import { METADATA_CATEGORY, REL_TYPES } from "../../domain/index.js";
4
4
  import { makeEdge, makeNode, stripNs } from "../common.js";
5
+ import { APEX_NODE_HASH_SALT } from "../hash-salts.js";
6
+ import { NEW_INSTANCE_RE, STATIC_CALL_RE, emitClassRefEdges, emitDescribeRefEdges, emitStaticCallEdges, emitTypeRefEdges, isUserClassName, localVarNames, } from "./class-refs.js";
5
7
  import { parseAnnotations, stripCommentsAndStrings } from "./common.js";
6
- const METHOD_RE = /(?:@[\w()=,'"\s.]+\s+)*(?:(public|private|protected|global|virtual|override|abstract|static|webservice|with\s+sharing|without\s+sharing|inherited\s+sharing|testmethod|final)\s+)+([A-Za-z_][\w<>,.\s\[\]]*?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*\{/gi;
8
+ import { PLACEHOLDER, dynamicSoqlClauseFields, foldDynamicSoql, maskApex, parseDynamicSoql, } from "./dynamic-soql.js";
9
+ import { classScopes, collectDecls, extractFieldAccesses, paramDecls, } from "./field-access.js";
10
+ import { emitInstanceCallEdges, emitNameRefEdges, makeTypeLookup, recordTypeAt, } from "./name-refs.js";
11
+ import { emitPageRefEdges } from "./page-refs.js";
12
+ import { blankBracketGroups, findInlineQueries, soqlFieldRefs } from "./soql-fields.js";
13
+ import { classStringConstants, extractVlocityCalls, extractVlocityDispatch, stripCommentsOnly, } from "./vlocity-calls.js";
14
+ export { APEX_NODE_HASH_SALT } from "../hash-salts.js";
15
+ /**
16
+ * apex-parser (antlr4ts) keeps a DFA cache on each grammar's static ATN. It is
17
+ * per isolate, it is never trimmed, and it grows with every distinct input:
18
+ * over a large Vlocity corpus it held ~190 MB live (and a 2-4x larger V8 heap) in each
19
+ * parse worker. The cache is only a memo for prediction, so dropping it does
20
+ * not change any parse result. Release it after this many syntax checks in the
21
+ * current isolate (each parse worker, or the main thread in serial mode).
22
+ * Measured on a large Vlocity repo, 4 workers: 50 cut peak RSS by ~1.07 GB for ~5% more parse
23
+ * time.
24
+ */
25
+ export const APEX_DFA_CLEAR_INTERVAL = 50;
26
+ let apexChecksSinceDfaClear = 0;
27
+ /** Count one apex-parser syntax check; clear the lexer and parser DFA when due. */
28
+ function noteApexSyntaxCheck(lexer, parser) {
29
+ apexChecksSinceDfaClear++;
30
+ if (apexChecksSinceDfaClear < APEX_DFA_CLEAR_INTERVAL)
31
+ return;
32
+ apexChecksSinceDfaClear = 0;
33
+ lexer?.interpreter?.clearDFA();
34
+ parser?.interpreter?.clearDFA();
35
+ }
36
+ /** Test hook: restart the DFA-release counter for this isolate. */
37
+ export function resetApexDfaCounterForTest() {
38
+ apexChecksSinceDfaClear = 0;
39
+ }
40
+ // Modifiers are optional (`*`, not `+`): a method with NO explicit access
41
+ // modifier is implicitly private in Apex and is still a real method — the old
42
+ // `+` silently dropped every unmodified helper/inner-class method. Allowing
43
+ // zero modifiers means the regex can also touch control-flow that happens to
44
+ // look like `<word> <word>(...) {` (notably `else if (x) {`), so findMethods
45
+ // rejects any match whose return-type or name is a reserved keyword.
46
+ const METHOD_RE = /(?:@[\w()=,'"\s.]+\s+)*(?:(public|private|protected|global|virtual|override|abstract|static|webservice|with\s+sharing|without\s+sharing|inherited\s+sharing|testmethod|final)\s+)*([A-Za-z_][\w<>,.\s\[\]]*?)\s+([A-Za-z_]\w*)\s*\(([^)]*)\)\s*\{/gi;
47
+ // Apex reserved words that can appear as `<kw> <kw>(...) {` but are NOT method
48
+ // declarations. If the captured return-type or method-name is one of these, the
49
+ // match is control flow / a statement, not a method.
50
+ const NON_METHOD_KEYWORDS = new Set([
51
+ "if",
52
+ "else",
53
+ "for",
54
+ "while",
55
+ "do",
56
+ "switch",
57
+ "catch",
58
+ "try",
59
+ "finally",
60
+ "return",
61
+ "new",
62
+ "throw",
63
+ "when",
64
+ ]);
65
+ // Access/sharing/definition modifiers. When the regex makes modifiers optional,
66
+ // a CONSTRUCTOR `public Foo(...) {` backtracks to returnType="public", name="Foo"
67
+ // (the class name) — a phantom method. A real method's return type is a TYPE,
68
+ // never a modifier, so a modifier captured AS the return type means we matched a
69
+ // constructor (or other non-method) and must drop it.
70
+ const MODIFIER_KEYWORDS = new Set([
71
+ "public",
72
+ "private",
73
+ "protected",
74
+ "global",
75
+ "virtual",
76
+ "override",
77
+ "abstract",
78
+ "static",
79
+ "webservice",
80
+ "with",
81
+ "without",
82
+ "inherited",
83
+ "sharing",
84
+ "testmethod",
85
+ "final",
86
+ ]);
7
87
  function findMatchingBrace(src, openIdx) {
8
88
  let depth = 0;
9
89
  for (let i = openIdx; i < src.length; i++) {
@@ -18,48 +98,208 @@ function findMatchingBrace(src, openIdx) {
18
98
  }
19
99
  return -1;
20
100
  }
101
+ /**
102
+ * Split `raw` on commas at generic/bracket/paren depth 0, so
103
+ * `Map<Id, Contact> a, List<String> b` is two items, not three.
104
+ */
105
+ function splitTopLevel(raw) {
106
+ const out = [];
107
+ let depth = 0;
108
+ let cur = "";
109
+ for (const c of raw) {
110
+ if (c === "<" || c === "(" || c === "[" || c === "{")
111
+ depth++;
112
+ else if ((c === ">" || c === ")" || c === "]" || c === "}") && depth > 0)
113
+ depth--;
114
+ if (c === "," && depth === 0) {
115
+ out.push(cur);
116
+ cur = "";
117
+ }
118
+ else
119
+ cur += c;
120
+ }
121
+ out.push(cur);
122
+ return out.map((x) => x.trim()).filter((x) => x.length > 0);
123
+ }
21
124
  function parseParams(raw) {
22
- const t = raw.trim();
23
- if (!t)
24
- return [];
25
- return t.split(",").map((s) => s.trim());
125
+ return splitTopLevel(raw);
126
+ }
127
+ /** `Database.Batchable<SObject>` -> `Database.Batchable` (whitespace-normalised). */
128
+ function stripTypeArgs(t) {
129
+ const lt = t.indexOf("<");
130
+ return (lt >= 0 ? t.slice(0, lt) : t).replace(/\s+/g, "");
131
+ }
132
+ /** Remove `@Name` / `@Name(...)` annotations (balanced parens) from `text`. */
133
+ function stripAnnotations(text) {
134
+ let out = "";
135
+ for (let i = 0; i < text.length; i++) {
136
+ if (text[i] !== "@") {
137
+ out += text[i];
138
+ continue;
139
+ }
140
+ let j = i + 1;
141
+ while (j < text.length && /\w/.test(text[j] ?? ""))
142
+ j++;
143
+ let k = j;
144
+ while (k < text.length && /\s/.test(text[k] ?? ""))
145
+ k++;
146
+ if (text[k] === "(") {
147
+ let d = 0;
148
+ for (; k < text.length; k++) {
149
+ if (text[k] === "(")
150
+ d++;
151
+ else if (text[k] === ")" && --d === 0)
152
+ break;
153
+ }
154
+ j = k + 1;
155
+ }
156
+ out += " ";
157
+ i = j - 1;
158
+ }
159
+ return out;
26
160
  }
161
+ /**
162
+ * Header of the file's OUTER type: the first `class`/`interface`/`enum` keyword at
163
+ * brace depth 0 (so an inner class can never be taken for the outer one),
164
+ * with `extends` / `implements` clauses parsed by a depth-aware split so
165
+ * generic arguments (`Database.Batchable<SObject>`) neither break the match
166
+ * nor split the list. Type arguments are dropped from the emitted names.
167
+ * `src` must have comments and strings stripped.
168
+ */
27
169
  function extractClassHeader(src) {
28
- const headerMatch = src.match(/(?:@[\w()=,'"\s.]+\s+)*((?:public|private|global|virtual|abstract|with\s+sharing|without\s+sharing|inherited\s+sharing|\s)+)?(class|interface)\s+([A-Za-z_]\w*)(?:\s+extends\s+([A-Za-z_][\w.]*))?(?:\s+implements\s+([A-Za-z_][\w.,\s]*))?\s*\{/i);
29
- if (!headerMatch) {
30
- return {
31
- name: "",
32
- extendsClass: null,
33
- implementsList: [],
34
- isInterface: false,
35
- isTest: false,
36
- modifiers: [],
37
- };
170
+ const empty = {
171
+ name: "",
172
+ extendsClass: null,
173
+ extendsList: [],
174
+ implementsList: [],
175
+ isInterface: false,
176
+ isTest: false,
177
+ modifiers: [],
178
+ };
179
+ // `enum` too: a top-level enum file declares its modifiers like a class does (cycle 3 T6 fix 1).
180
+ const kwRe = /\b(class|interface|enum)\s+([A-Za-z_]\w*)/gi;
181
+ let depth = 0;
182
+ let scanned = 0;
183
+ let hit = null;
184
+ for (let m = kwRe.exec(src); m !== null; m = kwRe.exec(src)) {
185
+ for (; scanned < m.index; scanned++) {
186
+ if (src[scanned] === "{")
187
+ depth++;
188
+ else if (src[scanned] === "}")
189
+ depth--;
190
+ }
191
+ if (depth === 0) {
192
+ hit = m;
193
+ break;
194
+ }
38
195
  }
39
- const mods = (headerMatch[1] ?? "")
196
+ if (!hit)
197
+ return empty;
198
+ const open = src.indexOf("{", hit.index + hit[0].length);
199
+ if (open < 0)
200
+ return empty;
201
+ const isInterface = (hit[1] ?? "").toLowerCase() === "interface";
202
+ const name = hit[2] ?? "";
203
+ // Everything before the keyword at depth 0 is annotations + modifiers. Only
204
+ // the trailing statement matters (a preceding `}`/`;` cannot occur before
205
+ // the outer type, but be defensive).
206
+ const prefixStart = Math.max(src.lastIndexOf("}", hit.index), src.lastIndexOf(";", hit.index));
207
+ const prefix = src.slice(prefixStart + 1, hit.index);
208
+ const isTest = parseAnnotations(prefix).some((a) => a.name.toLowerCase() === "istest");
209
+ const modifiers = stripAnnotations(prefix)
40
210
  .trim()
41
211
  .split(/\s+/)
42
212
  .filter((s) => s.length > 0);
43
- const isInterface = (headerMatch[2] ?? "").toLowerCase() === "interface";
44
- const name = headerMatch[3] ?? "";
45
- const extendsClass = headerMatch[4] ?? null;
46
- const implementsList = (headerMatch[5] ?? "")
47
- .split(",")
48
- .map((s) => s.trim())
49
- .filter((s) => s.length > 0);
50
- // Class-level annotations live INSIDE headerMatch[0] (the regex consumes
51
- // them via the leading `(?:@[\w()=,'"\\s.]+\\s+)*` group), not before it.
52
- // Slicing src up to headerStart yielded an empty string and always returned
53
- // isTest=false — fixed: parse annotations out of the matched header text
54
- // up to the `class`/`interface` keyword.
55
- const headerText = headerMatch[0];
56
- const keywordIdx = headerText.search(/\b(class|interface)\b/i);
57
- const annotationsRegion = keywordIdx > 0 ? headerText.slice(0, keywordIdx) : "";
58
- const classAnnotations = parseAnnotations(annotationsRegion).map((a) => a.name.toLowerCase());
59
- const isTest = classAnnotations.includes("istest");
60
- return { name, extendsClass, implementsList, isInterface, isTest, modifiers: mods };
61
- }
62
- function findMethods(src) {
213
+ const tail = src.slice(hit.index + hit[0].length, open);
214
+ const extIdx = tail.search(/\bextends\b/i);
215
+ const implIdx = tail.search(/\bimplements\b/i);
216
+ const clause = (from, kwLen) => {
217
+ if (from < 0)
218
+ return "";
219
+ const ends = [extIdx, implIdx].filter((x) => x > from);
220
+ return tail.slice(from + kwLen, ends.length > 0 ? Math.min(...ends) : tail.length);
221
+ };
222
+ const extendsRaw = clause(extIdx, "extends".length);
223
+ const implementsRaw = clause(implIdx, "implements".length);
224
+ // An interface may extend several interfaces; a class extends one class.
225
+ const extendsList = splitTopLevel(extendsRaw)
226
+ .map(stripTypeArgs)
227
+ .filter((x) => x);
228
+ const implementsList = splitTopLevel(implementsRaw)
229
+ .map(stripTypeArgs)
230
+ .filter((x) => x.length > 0);
231
+ return {
232
+ name,
233
+ extendsClass: extendsList[0] ?? null,
234
+ extendsList,
235
+ implementsList,
236
+ isInterface,
237
+ isTest,
238
+ modifiers,
239
+ };
240
+ }
241
+ /**
242
+ * The bundled apex-parser grammar predates Apex's null-coalescing operator, so
243
+ * `a ?? b` is a syntax error to it. For grammar validation only, rewrite every
244
+ * code-level `??` (never inside comments or strings) to `||`: same length, so
245
+ * token offsets stay valid, and the same binary-expression shape. The original
246
+ * source is still used for every extraction and snippet.
247
+ */
248
+ export function rewriteNullCoalescing(src) {
249
+ if (!src.includes("??"))
250
+ return src;
251
+ const masked = maskApex(src);
252
+ const out = src.split("");
253
+ for (let i = masked.indexOf("??"); i >= 0; i = masked.indexOf("??", i + 2)) {
254
+ out[i] = "|";
255
+ out[i + 1] = "|";
256
+ }
257
+ return out.join("");
258
+ }
259
+ /**
260
+ * Human-readable message for a parse failure. apex-parser's
261
+ * ThrowingErrorListener throws a plain `{ line, column, message }` object (not
262
+ * an Error), which `String(err)` turned into "[object Object]".
263
+ */
264
+ export function formatParseError(err) {
265
+ const MAX = 300;
266
+ const clip = (s) => (s.length > MAX ? `${s.slice(0, MAX)}…` : s);
267
+ if (err instanceof Error)
268
+ return clip(err.message);
269
+ if (err && typeof err === "object") {
270
+ const e = err;
271
+ const msg = typeof e.message === "string" ? e.message : JSON.stringify(err);
272
+ const pos = typeof e.line === "number" ? `${e.line}:${typeof e.column === "number" ? e.column : 0} ` : "";
273
+ return clip(`${pos}${msg}`);
274
+ }
275
+ return clip(String(err));
276
+ }
277
+ /**
278
+ * Raw (comment/string-preserving) body of the method's declaration: the first
279
+ * not-yet-used `name(<same arity>) {` in `masked` that isn't a call (`.name(`)
280
+ * or constructor invocation. Matching and brace-balancing run on `masked`; the
281
+ * slice is taken from `src` at the same offsets. Null when not found.
282
+ */
283
+ function declRawBody(src, masked, m, used) {
284
+ const re = new RegExp(`(?<![.\\w])${m.name}\\s*\\(([^)]*)\\)\\s*\\{`, "g");
285
+ for (const hit of masked.matchAll(re)) {
286
+ const at = hit.index ?? 0;
287
+ if (used.has(at))
288
+ continue;
289
+ if (/\bnew\s+$/.test(masked.slice(Math.max(0, at - 5), at)))
290
+ continue;
291
+ if (parseParams(hit[1] ?? "").length !== m.params.length)
292
+ continue;
293
+ const open = at + hit[0].length - 1;
294
+ const close = findMatchingBrace(masked, open);
295
+ if (close <= 0)
296
+ return null;
297
+ used.add(at);
298
+ return { text: src.slice(open + 1, close), start: open + 1 };
299
+ }
300
+ return null;
301
+ }
302
+ function findMethods(src, className) {
63
303
  const out = [];
64
304
  const re = new RegExp(METHOD_RE.source, "gi");
65
305
  let m = re.exec(src);
@@ -67,6 +307,19 @@ function findMethods(src) {
67
307
  const returnType = (m[2] ?? "").trim();
68
308
  const name = m[3] ?? "";
69
309
  const params = parseParams(m[4] ?? "");
310
+ // Reject non-methods that share the `<word> <word>(...) {` shape:
311
+ // - control flow: `else if (x) {` → returnType="else", name="if".
312
+ // - a CONSTRUCTOR: `public Foo(...) {` backtracks to returnType="public"
313
+ // (a modifier) / name=<class name>. A real method's return type is a
314
+ // type, never a modifier, and its name is never the class name.
315
+ if (NON_METHOD_KEYWORDS.has(returnType.toLowerCase()) ||
316
+ NON_METHOD_KEYWORDS.has(name.toLowerCase()) ||
317
+ MODIFIER_KEYWORDS.has(returnType.toLowerCase()) ||
318
+ (className !== undefined && name === className)) {
319
+ re.lastIndex = (m.index ?? 0) + 1;
320
+ m = re.exec(src);
321
+ continue;
322
+ }
70
323
  // Skip constructors (returnType empty or equals class name when first letter is uppercase)
71
324
  // We'll keep ctor handling outside.
72
325
  const openBraceIdx = (m.index ?? 0) + m[0].length - 1;
@@ -110,95 +363,507 @@ function extractApiVersion(metaXml) {
110
363
  const m = metaXml.match(/<apiVersion>([^<]+)<\/apiVersion>/);
111
364
  return m?.[1] ? m[1].trim() : null;
112
365
  }
366
+ /**
367
+ * `<shadowsManaged>ns</shadowsManaged>` entries the live extractor adds to the
368
+ * metaXml envelope when this local class shares its (namespace-less) qname
369
+ * with managed-package classes. Not a Salesforce field.
370
+ */
371
+ function extractShadowsManaged(metaXml) {
372
+ if (!metaXml)
373
+ return [];
374
+ const out = [];
375
+ for (const m of metaXml.matchAll(/<shadowsManaged>([^<]+)<\/shadowsManaged>/g)) {
376
+ const ns = m[1]?.trim();
377
+ if (ns)
378
+ out.push(ns);
379
+ }
380
+ return out;
381
+ }
113
382
  function sha256(s) {
114
383
  return createHash("sha256").update(s).digest("hex");
115
384
  }
116
- const SOQL_RE = /\[\s*SELECT\b([^\]]*?)\bFROM\s+([A-Za-z_][\w.]*)/gi;
117
- const SOSL_RE = /\[\s*FIND\b/gi;
118
- const DML_RE = /\b(insert|update|delete|upsert|undelete|merge)\s+([A-Za-z_][\w.]*)/gi;
119
- const FIELD_ACCESS_RE = /\b([A-Z][A-Za-z0-9_]*(?:__r)?)\.([A-Za-z_][\w]*?)(?:__c)?\b/g;
120
- const NEW_INSTANCE_RE = /\bnew\s+([A-Z][A-Za-z0-9_]*)\s*\(/g;
121
- const STATIC_CALL_RE = /\b([A-Z][A-Za-z0-9_]*)\.([a-z][A-Za-z0-9_]*)\s*\(/g;
122
385
  /**
123
- * Count the arguments in a balanced parenthesised arg list starting one
124
- * character after `openParenIdx` (which must point at the `(` opening the
125
- * call). Handles nested parens, string literals, and single-line `//`
126
- * plus block `/* * /` comments. Returns the arg count, or `null` if the
127
- * caller's parens never close inside `body` (truncated source / unbalanced).
386
+ * Compute the character ranges of loop BODIES (`{ … }` opened by for/while/
387
+ * do) within a method body. Used to stamp `inLoop: true` on SOQL/DML edges in
388
+ * the regex extraction path so governor-risk detection works on the DEFAULT
389
+ * parser mode — not only under SFGRAPH_APEX_PARSER=ast (the AST extractor
390
+ * stamps inLoop separately). The body passed here has strings/comments
391
+ * stripped, so brace counting is reliable.
128
392
  *
129
- * Used by the regex pass to emit `ApexMethod:Foo.bar(2)` instead of the
130
- * `(?)` ambiguity placeholder — turning most regex-mode CALLS edges into
131
- * precise-arity edges that bypass the arity resolver's overload fan-out.
132
- * Cases that genuinely can't be counted (lambdas, splat, comment-split
133
- * args) fall back to `(?)` — preserving the existing ambiguous-fan-out
134
- * semantics on the long tail.
393
+ * A for-EACH header query — `for (Account a : [SELECT …]) {…}` — runs once and
394
+ * is correctly EXCLUDED: its index falls before the loop body's opening brace,
395
+ * not inside the recorded range. Known limitation (shared with the populate
396
+ * body-scan): a braceless single-statement loop body isn't tracked.
135
397
  */
136
- function countCallArgs(body, openParenIdx) {
137
- // empty args → arity 0; we detect by peeking for the matching `)` after
138
- // any whitespace before counting commas.
139
- let depth = 1;
140
- let i = openParenIdx + 1;
141
- let argCount = 0;
142
- let sawNonWhitespace = false;
143
- while (i < body.length && depth > 0) {
398
+ export function computeLoopRanges(body) {
399
+ const ranges = [];
400
+ const stack = [];
401
+ // Brace positions known to open a loop body. Marking the SPECIFIC `{` (rather
402
+ // than a sticky `pendingLoop` flag) is what fixes braceless loops: a
403
+ // `for (...) doIt();` or `do x(); while(y);` has no brace, so the next
404
+ // UNRELATED `{` is no longer mis-marked as a loop body (the old flag bled into
405
+ // it, producing false SOQL/DML-in-loop governor findings).
406
+ const loopBraces = new Set();
407
+ const wordBefore = (i) => /\W/.test(body[i - 1] ?? " ");
408
+ const boundaryAt = (i) => /\W/.test(body[i] ?? " ");
409
+ const skipWs = (i) => {
410
+ let j = i;
411
+ while (j < body.length && /\s/.test(body[j] ?? ""))
412
+ j += 1;
413
+ return j;
414
+ };
415
+ // Index of the `)` matching the `(` at `open`, or -1.
416
+ const matchParen = (open) => {
417
+ let d = 0;
418
+ for (let j = open; j < body.length; j++) {
419
+ if (body[j] === "(")
420
+ d += 1;
421
+ else if (body[j] === ")" && --d === 0)
422
+ return j;
423
+ }
424
+ return -1;
425
+ };
426
+ // End (`;` at depth 0) of the single statement starting at `from`.
427
+ const stmtEnd = (from) => {
428
+ let d = 0;
429
+ for (let j = from; j < body.length; j++) {
430
+ const c = body[j];
431
+ if (c === "(" || c === "{" || c === "[")
432
+ d += 1;
433
+ else if (c === ")" || c === "}" || c === "]")
434
+ d -= 1;
435
+ else if (c === ";" && d === 0)
436
+ return j;
437
+ }
438
+ return body.length - 1;
439
+ };
440
+ for (let i = 0; i < body.length; i++) {
144
441
  const ch = body[i];
145
- // Line comment
146
- if (ch === "/" && body[i + 1] === "/") {
147
- const nl = body.indexOf("\n", i + 2);
148
- i = nl < 0 ? body.length : nl + 1;
442
+ let kwLen = 0;
443
+ if (ch === "f" && body.startsWith("for", i) && wordBefore(i) && boundaryAt(i + 3))
444
+ kwLen = 3;
445
+ else if (ch === "w" && body.startsWith("while", i) && wordBefore(i) && boundaryAt(i + 5))
446
+ kwLen = 5;
447
+ else if (ch === "d" && body.startsWith("do", i) && wordBefore(i) && boundaryAt(i + 2))
448
+ kwLen = 2;
449
+ if (kwLen > 0) {
450
+ let j = skipWs(i + kwLen);
451
+ // for/while carry a `(condition)` before the body; `do` does not. Skip the
452
+ // condition so its inner `;`s (e.g. `for (i=0; i<n; i++)`) aren't read as a
453
+ // braceless body terminator.
454
+ if ((kwLen === 3 || kwLen === 5) && body[j] === "(") {
455
+ const close = matchParen(j);
456
+ if (close < 0)
457
+ continue;
458
+ j = skipWs(close + 1);
459
+ }
460
+ if (body[j] === "{") {
461
+ loopBraces.add(j); // braced body — recorded when the stack pops it
462
+ }
463
+ else {
464
+ ranges.push({ start: j - 1, end: stmtEnd(j) }); // braceless single statement
465
+ }
466
+ i = j - 1;
149
467
  continue;
150
468
  }
151
- // Block comment
152
- if (ch === "/" && body[i + 1] === "*") {
153
- const close = body.indexOf("*/", i + 2);
154
- i = close < 0 ? body.length : close + 2;
469
+ if (ch === "{") {
470
+ stack.push({ openIdx: i, isLoop: loopBraces.has(i) });
471
+ }
472
+ else if (ch === "}") {
473
+ const top = stack.pop();
474
+ if (top?.isLoop)
475
+ ranges.push({ start: top.openIdx, end: i });
476
+ }
477
+ }
478
+ return ranges;
479
+ }
480
+ // Inline SOQL queries are found by soql-fields findInlineQueries: from
481
+ // `[SELECT` to the `]` that closes it at bracket depth 0, outside string
482
+ // literals (a bind such as `:ids[0]` does not end the query). The object is
483
+ // taken by parseSoql, which finds the FROM at paren-depth 0 so a child subquery
484
+ // (`(SELECT … FROM Contacts)`) or a semi-join (`WHERE Id IN (SELECT … FROM
485
+ // Contact)`) doesn't get mistaken for the outer object.
486
+ export const SOSL_RE = /\[\s*FIND\b/gi;
487
+ /** Dynamic query entry points (the query text is a runtime string). */
488
+ export const DYNAMIC_QUERY_RE = /\b(Database|Search)\s*\.\s*(query|queryWithBinds|countQuery|countQueryWithBinds|getQueryLocator|getQueryLocatorWithBinds)\s*\(/gi;
489
+ /**
490
+ * T15 I-5: one EXECUTES_SOSL `SOSL:*` edge when `code` has an inline SOSL
491
+ * query, stamped `inLoop` when any of them sits inside a loop body.
492
+ */
493
+ export function emitSoslEdge(ctx, srcQname, code, inLoopAt, edges) {
494
+ let found = false;
495
+ let inLoop = false;
496
+ for (const m of code.matchAll(new RegExp(SOSL_RE.source, "gi"))) {
497
+ found = true;
498
+ if (inLoopAt(m.index ?? 0))
499
+ inLoop = true;
500
+ }
501
+ if (found) {
502
+ edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_SOSL, "SOSL:*", inLoop ? { inLoop: true } : {}));
503
+ }
504
+ }
505
+ /**
506
+ * T15 I-5: the dynamic query calls (`Database.query`, `Search.query`, ...)
507
+ * inside a loop body of `code` (strings/comments stripped), as sorted
508
+ * distinct canonical names. Stamped on the method / trigger node as
509
+ * `dynamicQueryInLoop`; empty when none.
510
+ */
511
+ export function dynamicQueriesInLoop(code, inLoopAt) {
512
+ const out = new Set();
513
+ for (const m of code.matchAll(new RegExp(DYNAMIC_QUERY_RE.source, "gi"))) {
514
+ if (!inLoopAt(m.index ?? 0))
155
515
  continue;
516
+ const cls = (m[1] ?? "").toLowerCase() === "search" ? "Search" : "Database";
517
+ const op = (m[2] ?? "").toLowerCase();
518
+ const canon = [
519
+ "query",
520
+ "queryWithBinds",
521
+ "countQuery",
522
+ "countQueryWithBinds",
523
+ "getQueryLocator",
524
+ "getQueryLocatorWithBinds",
525
+ ].find((k) => k.toLowerCase() === op) ?? op;
526
+ out.add(`${cls}.${canon}`);
527
+ }
528
+ return [...out].sort();
529
+ }
530
+ /**
531
+ * Extract the outer object + SELECT clause from a SOQL query's inner text (the
532
+ * part after `[SELECT`). The object is the identifier after the first `FROM`
533
+ * encountered at parenthesis depth 0; subquery/semi-join FROMs (depth > 0) are
534
+ * skipped. Returns object=null for a malformed query.
535
+ */
536
+ export function parseSoql(inner) {
537
+ let depth = 0;
538
+ for (let i = 0; i < inner.length; i++) {
539
+ const c = inner[i];
540
+ if (c === "(")
541
+ depth += 1;
542
+ else if (c === ")") {
543
+ if (depth > 0)
544
+ depth -= 1;
156
545
  }
157
- // String literal — Apex uses single-quoted strings with `\\` and `\'` escapes
158
- if (ch === "'") {
159
- i += 1;
160
- while (i < body.length) {
161
- const c = body[i];
162
- if (c === "\\") {
163
- i += 2;
164
- continue;
165
- }
166
- if (c === "'") {
167
- i += 1;
168
- break;
169
- }
170
- i += 1;
171
- }
172
- sawNonWhitespace = true;
546
+ else if (depth === 0 &&
547
+ (c === "f" || c === "F") &&
548
+ /^from\b/i.test(inner.slice(i)) &&
549
+ /\W/.test(inner[i - 1] ?? " ")) {
550
+ const m = inner.slice(i + 4).match(/^\s+([A-Za-z_][\w.]*)/);
551
+ if (m?.[1])
552
+ return { object: m[1], selectClause: inner.slice(0, i) };
553
+ }
554
+ }
555
+ return { object: null, selectClause: inner };
556
+ }
557
+ export const DML_RE = /\b(insert|update|delete|upsert|undelete|merge)\s+([A-Za-z_][\w.]*|\[)/gi;
558
+ /** `Database.insert(recs)`, `Database.update(recs, false)`, `Database.INSERT(…)`:
559
+ * the Database class DML methods (Apex is case-insensitive). Group 2 is the
560
+ * first argument when it is a plain name. */
561
+ export const DATABASE_DML_RE = /\bDatabase\s*\.\s*(insert|update|upsert|delete|undelete|merge)\s*\(\s*([A-Za-z_][\w.]*)?/gi;
562
+ /**
563
+ * EXECUTES_DML `DML:<op>` for DML statements (`insert x;`, `delete [SELECT …]`)
564
+ * and Database DML calls (`Database.insert(x)`) in `code` (strings/comments
565
+ * stripped), stamped `inLoop` when inside a loop body. `target` is the record
566
+ * expression's name when there is one.
567
+ *
568
+ * When the record expression's sObject type is known (see `recordTypeAt`:
569
+ * `new T(…)`, an inline query, a variable `objects.lookup` types, or
570
+ * `Trigger.new` / `Trigger.old`), also EXECUTES_DML `CustomObject:<T>`, like
571
+ * EXECUTES_SOQL, one per object with the sorted operations in `ops`. That edge
572
+ * carries no `inLoop`: the governor checks read the `DML:<op>` marker, so a
573
+ * statement in a loop is reported once.
574
+ */
575
+ export function emitDmlEdges(ctx, srcQname, code, inLoopAt, edges, objects = {}) {
576
+ const found = [];
577
+ for (const m of code.matchAll(new RegExp(DML_RE.source, "gi"))) {
578
+ const raw = m[2] ?? "";
579
+ const i = m.index ?? 0;
580
+ const target = raw === "[" || raw.toLowerCase() === "new" ? undefined : raw;
581
+ found.push({ i, op: (m[1] ?? "").toLowerCase(), target, arg: i + m[0].length - raw.length });
582
+ }
583
+ for (const m of code.matchAll(new RegExp(DATABASE_DML_RE.source, "gi"))) {
584
+ const t = m[2] && m[2].toLowerCase() !== "new" ? m[2] : undefined;
585
+ const i = m.index ?? 0;
586
+ const arg = i + m[0].length - (m[2] ?? "").length;
587
+ found.push({ i, op: (m[1] ?? "").toLowerCase(), target: t, arg });
588
+ }
589
+ found.sort((a, b) => a.i - b.i);
590
+ const byObject = new Map();
591
+ for (const f of found) {
592
+ edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_DML, `DML:${f.op}`, {
593
+ ...(f.target !== undefined ? { target: f.target } : {}),
594
+ ...(inLoopAt(f.i) ? { inLoop: true } : {}),
595
+ }));
596
+ const object = recordTypeAt(code, f.arg, objects);
597
+ if (!object)
598
+ continue;
599
+ const obj = stripNs(object, ctx.namespace);
600
+ const hit = byObject.get(obj.toLowerCase()) ?? { object: obj, ops: new Set() };
601
+ hit.ops.add(f.op);
602
+ byObject.set(obj.toLowerCase(), hit);
603
+ }
604
+ for (const { object, ops } of byObject.values()) {
605
+ edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_DML, `CustomObject:${object}`, {
606
+ ops: [...ops].sort(),
607
+ }));
608
+ }
609
+ }
610
+ /**
611
+ * Inline `[SELECT …]` queries in `code`: EXECUTES_SOQL on the outer FROM
612
+ * object (with `query`, `inLoop`) and READS_FIELD for every plain field the
613
+ * outer query names (SELECT list, WHERE / HAVING operands, GROUP BY, ORDER
614
+ * BY). Dotted relationship fields are not this object's fields: they are
615
+ * appended to `paths`.
616
+ */
617
+ export function emitInlineSoqlEdges(ctx, srcQname, code, inLoopAt, edges, paths, children) {
618
+ for (const sq of findInlineQueries(code)) {
619
+ // Bind expressions (`:ids[0]`, an inline query held in a bind) are Apex,
620
+ // not this query's SOQL: blank their bracket groups for the grammar pass.
621
+ const inner = blankBracketGroups(sq.inner);
622
+ const { object } = parseSoql(inner);
623
+ const obj = stripNs(object ?? "", ctx.namespace);
624
+ edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_SOQL, `CustomObject:${obj}`, {
625
+ query: sq.text.trim(),
626
+ ...(inLoopAt(sq.index) ? { inLoop: true } : {}),
627
+ }));
628
+ if (!obj)
173
629
  continue;
630
+ const refs = soqlFieldRefs(inner);
631
+ for (const f of refs.fields)
632
+ edges.push(makeEdge(ctx, srcQname, REL_TYPES.READS_FIELD, `CustomField:${obj}.${f}`));
633
+ for (const p of refs.paths)
634
+ paths.push({ object: obj, path: p, rel: REL_TYPES.READS_FIELD });
635
+ // `TYPEOF What WHEN Account THEN Name …`: branch fields are read on the branch's type.
636
+ for (const b of refs.typeOf) {
637
+ const bObj = stripNs(b.object, ctx.namespace);
638
+ for (const f of b.fields)
639
+ edges.push(makeEdge(ctx, srcQname, REL_TYPES.READS_FIELD, `CustomField:${bObj}.${f}`));
640
+ for (const p of b.paths)
641
+ paths.push({ object: bObj, path: p, rel: REL_TYPES.READS_FIELD });
174
642
  }
175
- if (ch === "(")
643
+ if (children)
644
+ addChildRelationships(children, obj, parseSoql(inner).selectClause);
645
+ }
646
+ }
647
+ /** Literal custom-field names: `'Name__c'` (optional namespace), nothing else in the quotes. */
648
+ const FIELD_LITERAL_RE = /'((?:[A-Za-z][A-Za-z0-9]*__)?[A-Za-z][A-Za-z0-9_]*__c)'/g;
649
+ /** Declared types that are never an sObject. */
650
+ const NOT_SOBJECT = new Set(["sobject", "string", "object", "id", "map", "list", "set"]);
651
+ /** Records `apexFieldNameLiterals` on a method node (see ApexFieldNameLiteral). */
652
+ function setFieldNameLiterals(node, rawDecl, decls) {
653
+ const objects = new Set();
654
+ for (const d of decls) {
655
+ for (const t of [d.type, d.elem]) {
656
+ if (!t || t.includes(".") || NOT_SOBJECT.has(t.toLowerCase()))
657
+ continue;
658
+ if (/^[A-Za-z]\w*$/.test(t))
659
+ objects.add(t);
660
+ }
661
+ }
662
+ if (objects.size === 0)
663
+ return;
664
+ const fields = new Set();
665
+ for (const m of stripCommentsOnly(rawDecl).matchAll(FIELD_LITERAL_RE)) {
666
+ if (m[1])
667
+ fields.add(m[1]);
668
+ }
669
+ if (fields.size === 0)
670
+ return;
671
+ const objs = [...objects].sort().slice(0, 20);
672
+ node.attributes.apexFieldNameLiterals = [...fields]
673
+ .sort()
674
+ .slice(0, 200)
675
+ .map((field) => ({ field, objects: objs }));
676
+ }
677
+ /** Append the depth-1 `(SELECT …)` groups of `selectClause`, deduped on
678
+ * (object, relationship) with their fields merged. One linear pass. */
679
+ function addChildRelationships(into, object, selectClause) {
680
+ let depth = 0;
681
+ let start = -1;
682
+ for (let i = 0; i < selectClause.length; i++) {
683
+ const c = selectClause[i];
684
+ if (c === "(") {
176
685
  depth += 1;
177
- else if (ch === ")") {
686
+ if (depth === 1)
687
+ start = i + 1;
688
+ }
689
+ else if (c === ")" && depth > 0) {
178
690
  depth -= 1;
179
- if (depth === 0)
180
- break;
691
+ if (depth !== 0)
692
+ continue;
693
+ const sub = /^\s*select\b/i.exec(selectClause.slice(start, i));
694
+ if (!sub)
695
+ continue;
696
+ const refs = soqlFieldRefs(selectClause.slice(start + sub[0].length, i));
697
+ if (!refs.object || refs.object.includes("."))
698
+ continue;
699
+ const hit = into.find((r) => r.object.toLowerCase() === object.toLowerCase() &&
700
+ r.relationship.toLowerCase() === refs.object?.toLowerCase());
701
+ const rec = hit ?? { object, relationship: refs.object, fields: [] };
702
+ if (!hit)
703
+ into.push(rec);
704
+ for (const f of refs.fields)
705
+ if (!rec.fields.some((x) => x.toLowerCase() === f.toLowerCase()))
706
+ rec.fields.push(f);
181
707
  }
182
- else if (ch === "," && depth === 1) {
183
- argCount += 1;
708
+ }
709
+ }
710
+ /** Store child-relationship subqueries on a node (only when there are any). */
711
+ function setChildRelationships(node, rels) {
712
+ if (!node || rels.length === 0)
713
+ return;
714
+ node.attributes.apexChildRelationships = rels;
715
+ }
716
+ /** Append `add` to `into`, deduped on (object, path, rel) case-insensitively. */
717
+ function mergePaths(into, add) {
718
+ const seen = new Set(into.map((p) => `${p.object}|${p.path}|${p.rel}`.toLowerCase()));
719
+ for (const p of add) {
720
+ const k = `${p.object}|${p.path}|${p.rel}`.toLowerCase();
721
+ if (seen.has(k))
722
+ continue;
723
+ seen.add(k);
724
+ into.push(p);
725
+ }
726
+ }
727
+ const FIELD_ACCESS_RE = /\b([A-Z][A-Za-z0-9_]*(?:__r)?)\.([A-Za-z_][\w]*?)(?:__c)?\b/g;
728
+ const NAMED_CRED_RE = /['"]callout:([A-Za-z_][\w]*)/g;
729
+ /**
730
+ * Push READS_FIELD / WRITES_FIELD edges for member-access field refs, skipping
731
+ * any (rel, dst) already emitted for this source since `fromIdx` (e.g. a
732
+ * READS_FIELD from a SOQL select list) so existing edge attributes are kept.
733
+ */
734
+ export function emitFieldAccessEdges(ctx, srcQname, accesses, edges, fromIdx) {
735
+ const seen = new Set();
736
+ for (let i = fromIdx; i < edges.length; i++) {
737
+ const e = edges[i];
738
+ if (e && String(e.srcQualifiedName) === srcQname)
739
+ seen.add(`${e.relType}|${String(e.dstQualifiedName).toLowerCase()}`);
740
+ }
741
+ const push = (rel, dst) => {
742
+ const key = `${rel}|${dst.toLowerCase()}`;
743
+ if (seen.has(key))
744
+ return;
745
+ seen.add(key);
746
+ edges.push(makeEdge(ctx, srcQname, rel, dst, { via: "member-access" }));
747
+ };
748
+ for (const a of accesses) {
749
+ const dst = `CustomField:${stripNs(a.object, ctx.namespace)}.${a.field}`;
750
+ if (a.read)
751
+ push(REL_TYPES.READS_FIELD, dst);
752
+ if (a.write)
753
+ push(REL_TYPES.WRITES_FIELD, dst);
754
+ }
755
+ }
756
+ /**
757
+ * READS_FIELD `dynamic: true` for the outer SELECT fields and the WHERE /
758
+ * HAVING / GROUP BY / ORDER BY field operands of each folded dynamic SOQL
759
+ * string in `queries` (see foldDynamicSoql, dynamicSoqlClauseFields). Only
760
+ * when the FROM object is literal text; computed fields are skipped. A
761
+ * (src, dst) READS_FIELD already emitted since `fromIdx` is not repeated.
762
+ * Returns the number of clause field operands skipped because FROM is
763
+ * computed (see setDynamicSoqlSkips).
764
+ */
765
+ export function emitDynamicSoqlEdges(ctx, srcQname, queries, edges, fromIdx) {
766
+ const seen = new Set();
767
+ // Objects this source already queries (static SOQL): an added dynamic edge
768
+ // would replace that edge (the store merges last-wins) and could drop its
769
+ // `inLoop`, so those keep the static edge alone.
770
+ const queried = new Set();
771
+ for (let i = fromIdx; i < edges.length; i++) {
772
+ const e = edges[i];
773
+ if (!e || String(e.srcQualifiedName) !== srcQname)
774
+ continue;
775
+ if (e.relType === REL_TYPES.READS_FIELD)
776
+ seen.add(String(e.dstQualifiedName).toLowerCase());
777
+ else if (e.relType === REL_TYPES.EXECUTES_SOQL)
778
+ queried.add(String(e.dstQualifiedName).toLowerCase());
779
+ }
780
+ let skipped = 0;
781
+ for (const q of queries) {
782
+ const parsed = parseDynamicSoql(q);
783
+ const clause = dynamicSoqlClauseFields(q, parsed);
784
+ skipped += clause.skipped;
785
+ if (!parsed)
786
+ continue;
787
+ const obj = stripNs(parsed.object, ctx.namespace);
788
+ // The object the query reads, like a static `[SELECT …]`. No `inLoop`:
789
+ // loop detection for dynamic queries is `dynamicQueryInLoop` on the node.
790
+ const objDst = `CustomObject:${obj}`;
791
+ if (!queried.has(objDst.toLowerCase())) {
792
+ queried.add(objDst.toLowerCase());
793
+ edges.push(makeEdge(ctx, srcQname, REL_TYPES.EXECUTES_SOQL, objDst, {
794
+ query: q.split(PLACEHOLDER).join("…").trim().slice(0, 500),
795
+ dynamic: true,
796
+ }));
184
797
  }
185
- else if (ch && /\S/.test(ch)) {
186
- sawNonWhitespace = true;
798
+ for (const fname of [...parsed.fields, ...clause.fields]) {
799
+ const dst = `CustomField:${obj}.${fname}`;
800
+ if (seen.has(dst.toLowerCase()))
801
+ continue;
802
+ seen.add(dst.toLowerCase());
803
+ edges.push(makeEdge(ctx, srcQname, REL_TYPES.READS_FIELD, dst, { dynamic: true }));
187
804
  }
188
- i += 1;
189
805
  }
190
- if (depth !== 0)
191
- return null; // unbalanced — let caller emit `(?)`
192
- // No commas + no non-whitespace content between parens = arity 0.
193
- // Any non-whitespace + N commas = N+1 args.
194
- return sawNonWhitespace ? argCount + 1 : 0;
806
+ return skipped;
195
807
  }
196
- const NAMED_CRED_RE = /['"]callout:([A-Za-z_][\w]*)/g;
808
+ /**
809
+ * Parse stat on a source node: `dynamicSoqlFieldsSkipped`, the dynamic SOQL
810
+ * clause field operands not attributed because the query's FROM is computed
811
+ * (only when there are any). The apex ref resolver sums it.
812
+ */
813
+ export function setDynamicSoqlSkips(node, skipped) {
814
+ if (!node || skipped <= 0)
815
+ return;
816
+ const attrs = node.attributes;
817
+ attrs.dynamicSoqlFieldsSkipped = (attrs.dynamicSoqlFieldsSkipped ?? 0) + skipped;
818
+ }
819
+ /**
820
+ * Collapse repeated EXECUTES_SOQL / EXECUTES_DML facts with the same
821
+ * (src, rel, dst) into one, in place of first occurrence. The store merges
822
+ * edges last-wins on that key, so without this an out-of-loop query after an
823
+ * in-loop one on the same object erased `inLoop`. The kept fact carries
824
+ * `inLoop: true` if any occurrence did (taking that occurrence's attributes),
825
+ * and `count` = number of occurrences when more than one.
826
+ */
827
+ export function aggregateGovernorFacts(edges) {
828
+ const out = [];
829
+ const byKey = new Map();
830
+ for (const e of edges) {
831
+ if (e.relType !== REL_TYPES.EXECUTES_SOQL && e.relType !== REL_TYPES.EXECUTES_DML) {
832
+ out.push(e);
833
+ continue;
834
+ }
835
+ const key = `${e.relType}|${String(e.srcQualifiedName)}|${String(e.dstQualifiedName).toLowerCase()}`;
836
+ const prev = byKey.get(key);
837
+ if (!prev) {
838
+ const copy = { ...e, attributes: { ...e.attributes } };
839
+ byKey.set(key, { edge: copy, count: 1 });
840
+ out.push(copy);
841
+ continue;
842
+ }
843
+ prev.count += 1;
844
+ const attrs = e.attributes;
845
+ if (attrs.inLoop === true && prev.edge.attributes.inLoop !== true)
846
+ prev.edge.attributes = { ...attrs };
847
+ prev.edge.attributes.count = prev.count;
848
+ }
849
+ return out;
850
+ }
851
+ const LOWER_NEW_RE = /\bnew\s+([a-z][A-Za-z0-9_]*)\s*\(/g;
852
+ const LOWER_STATIC_CALL_RE = /(?<![\w.])([a-z][A-Za-z0-9_]*)\.([A-Za-z][A-Za-z0-9_]*)\s*\(/g;
853
+ const NOT_A_CLASS = new Set(["this", "super"]);
197
854
  function parseClassNameAndIsTest(body) {
198
855
  const testTargets = new Set();
856
+ const lowerTargets = [];
199
857
  const m = body.match(/@isTest\s*\(\s*seeAllData\s*=\s*[^)]*\)|@isTest/i);
200
858
  if (!m)
201
- return { isTest: false, testTargets };
859
+ return { isTest: false, testTargets, lowerTargets };
860
+ for (const re of [LOWER_NEW_RE, LOWER_STATIC_CALL_RE]) {
861
+ for (const x of body.matchAll(new RegExp(re.source, "g"))) {
862
+ const t = x[1] ?? "";
863
+ if (t && !NOT_A_CLASS.has(t))
864
+ lowerTargets.push(t);
865
+ }
866
+ }
202
867
  // crude: find class names referenced via `new X(` or `X.foo(`
203
868
  const re1 = new RegExp(NEW_INSTANCE_RE.source, "g");
204
869
  let mm = re1.exec(body);
@@ -214,7 +879,7 @@ function parseClassNameAndIsTest(body) {
214
879
  testTargets.add(mm[1]);
215
880
  mm = re2.exec(body);
216
881
  }
217
- return { isTest: true, testTargets };
882
+ return { isTest: true, testTargets, lowerTargets };
218
883
  }
219
884
  export class ApexClassParser {
220
885
  category = METADATA_CATEGORY.APEX_CLASS;
@@ -224,7 +889,29 @@ export class ApexClassParser {
224
889
  const edges = [];
225
890
  const snippets = [];
226
891
  const className = stripNs(input.className, ctx.namespace);
227
- const fileHash = sha256(input.body);
892
+ const shadowsManaged = extractShadowsManaged(input.metaXml);
893
+ const shadowAttr = shadowsManaged.length > 0 ? { shadowsManaged } : {};
894
+ // Fold the collision into the hash (only when present, so every other
895
+ // node keeps its hash): node merge skips attributes when the hash is
896
+ // unchanged, and the stored local node must pick up the attr.
897
+ const fileHash = sha256(`${APEX_NODE_HASH_SALT}\n${input.body}${shadowsManaged.length > 0 ? `\nshadowsManaged:${shadowsManaged.join(",")}` : ""}`);
898
+ // Managed-package Apex (and any member whose Body the extractor couldn't
899
+ // fetch) arrives with an empty body. That is NOT a parse failure — the
900
+ // class exists, we just can't see its source. Emit a bare node so
901
+ // referencing edges resolve to a real target, instead of a misleading
902
+ // ParseError (the apex-parser throws a non-Error on empty input, which the
903
+ // catch below would record as the useless message "[object Object]").
904
+ if (input.body.trim() === "") {
905
+ nodes.push(makeNode(ctx, "ApexClass", `ApexClass:${className}`, {
906
+ name: className,
907
+ modifiers: [],
908
+ isTest: false,
909
+ apiVersion: extractApiVersion(input.metaXml),
910
+ bodyUnavailable: true,
911
+ ...shadowAttr,
912
+ }, fileHash));
913
+ return { nodes, edges, snippets };
914
+ }
228
915
  // Read parser mode once per parse — env var lets us flip between
229
916
  // regex (the legacy path), ast (the new precise extractor), and both
230
917
  // (regex authoritative; ast diff logged for shadow validation).
@@ -238,25 +925,48 @@ export class ApexClassParser {
238
925
  // When parserMode != "regex", retain the parsed compilationUnit tree
239
926
  // so the AST extractor can walk it without parsing twice.
240
927
  let parsedTree = null;
928
+ let lexer = null;
929
+ let parser = null;
241
930
  try {
242
931
  // Lazy-load to avoid pulling apex-parser into other parsers' worker.
243
932
  const apex = await import("apex-parser");
244
933
  const { ApexLexer, ApexParser, CommonTokenStream, CaseInsensitiveInputStream, ThrowingErrorListener, } = apex;
245
- const input1 = new CaseInsensitiveInputStream("file", input.body);
246
- const lexer = new ApexLexer(input1);
934
+ const input1 = new CaseInsensitiveInputStream("file", rewriteNullCoalescing(input.body));
935
+ lexer = new ApexLexer(input1);
247
936
  lexer.removeErrorListeners();
248
937
  lexer.addErrorListener(new ThrowingErrorListener());
249
938
  const tokens = new CommonTokenStream(lexer);
250
- const parser = new ApexParser(tokens);
939
+ parser = new ApexParser(tokens);
251
940
  parser.removeErrorListeners();
252
941
  parser.addErrorListener(new ThrowingErrorListener());
253
942
  parsedTree = parser.compilationUnit();
254
943
  }
255
944
  catch (err) {
256
- const errMsg = err instanceof Error ? err.message : String(err);
945
+ const errMsg = formatParseError(err);
257
946
  nodes.push(makeNode(ctx, "ParseError", `ParseError:ApexClass:${className}`, { className, message: errMsg }, sha256(`${className}:${errMsg}`)));
947
+ // Keep a stub for the type itself so grants, references and calls to it
948
+ // still resolve; `parseFailed` marks it as having no extracted body.
949
+ const stubSrc = stripCommentsAndStrings(input.body);
950
+ const stubHeader = extractClassHeader(stubSrc);
951
+ const stubLabel = stubHeader.isInterface ? "ApexInterface" : "ApexClass";
952
+ // Cheap fallback when the broken body defeats the header scan: an
953
+ // `@isTest` annotation before the first class keyword.
954
+ const stubIsTest = stubHeader.isTest || /@istest\b[^{;]*?\bclass\b/i.test(stubSrc);
955
+ nodes.push(makeNode(ctx, stubLabel, `${stubLabel}:${className}`, {
956
+ name: className,
957
+ modifiers: [],
958
+ isTest: stubIsTest,
959
+ apiVersion: extractApiVersion(input.metaXml),
960
+ parseFailed: true,
961
+ ...shadowAttr,
962
+ }, fileHash));
258
963
  return { nodes, edges };
259
964
  }
965
+ finally {
966
+ // The tree (if kept) does not depend on the DFA cache.
967
+ if (parser)
968
+ noteApexSyntaxCheck(lexer, parser);
969
+ }
260
970
  const cleaned = stripCommentsAndStrings(input.body);
261
971
  const header = extractClassHeader(cleaned);
262
972
  const effectiveName = header.name || className;
@@ -264,21 +974,78 @@ export class ApexClassParser {
264
974
  ? `ApexInterface:${effectiveName}`
265
975
  : `ApexClass:${effectiveName}`;
266
976
  const apiVersion = extractApiVersion(input.metaXml);
977
+ const dispatch = extractVlocityDispatch(input.body, header.implementsList);
978
+ // `final` String fields: an IP / DataRaptor name passed as a constant.
979
+ const stringConstants = classStringConstants(input.body);
267
980
  nodes.push(makeNode(ctx, header.isInterface ? "ApexInterface" : "ApexClass", classQname, {
268
981
  name: effectiveName,
269
982
  modifiers: header.modifiers,
270
983
  isTest: header.isTest,
271
984
  apiVersion,
985
+ ...(dispatch ? { vlocityDispatch: dispatch } : {}),
986
+ ...shadowAttr,
272
987
  }, fileHash));
273
- if (header.extendsClass) {
988
+ if (header.isInterface) {
989
+ // An interface extends interfaces, possibly several.
990
+ for (const parent of header.extendsList) {
991
+ edges.push(makeEdge(ctx, classQname, REL_TYPES.EXTENDS, `ApexInterface:${stripNs(parent, ctx.namespace)}`));
992
+ }
993
+ }
994
+ else if (header.extendsClass) {
274
995
  edges.push(makeEdge(ctx, classQname, REL_TYPES.EXTENDS, `ApexClass:${stripNs(header.extendsClass, ctx.namespace)}`));
275
996
  }
276
997
  for (const impl of header.implementsList) {
277
998
  edges.push(makeEdge(ctx, classQname, REL_TYPES.IMPLEMENTS, `ApexInterface:${stripNs(impl, ctx.namespace)}`));
278
999
  }
279
1000
  // Methods
280
- const methods = findMethods(cleaned);
1001
+ const usedDecls = new Set();
1002
+ const maskedBody = maskApex(input.body);
1003
+ const methods = findMethods(cleaned, effectiveName);
1004
+ // Member-access field refs: each method falls back to the fields/properties
1005
+ // declared directly in ITS enclosing class body (constructor/initializer
1006
+ // locals and other inner classes' fields are excluded); types declared in
1007
+ // this file (inner classes, enums, interfaces) are never SObjects.
1008
+ const scopes = classScopes(cleaned);
1009
+ const localTypes = new Set([...cleaned.matchAll(/\b(?:class|enum|interface)\s+([A-Za-z_]\w*)/gi)].map((x) => (x[1] ?? "").toLowerCase()));
1010
+ // Fields of the method's class and of every enclosing class: an inner
1011
+ // class can use its outer class's (static) fields.
1012
+ const enclosingFields = (pos) => {
1013
+ const out = [];
1014
+ const seen = new Set();
1015
+ for (let c = scopes.classAt(pos); c >= 0 && !seen.has(c); c = scopes.classAt(c)) {
1016
+ seen.add(c);
1017
+ out.push(...scopes.fields(c));
1018
+ }
1019
+ return out;
1020
+ };
1021
+ const refScope = {
1022
+ selfName: effectiveName,
1023
+ localTypes,
1024
+ // File-wide set: used for code outside method bodies and for test-class
1025
+ // linkage. Each method gets its own set below (its params, its body's
1026
+ // declarations and its enclosing class's fields).
1027
+ localVars: localVarNames([
1028
+ ...collectDecls(cleaned),
1029
+ ...methods.flatMap((m) => paramDecls(m.params)),
1030
+ ]),
1031
+ };
1032
+ // Methods declared in this file (inner classes' too: all are qnamed
1033
+ // under the outer class), for same-class call targets.
1034
+ const declared = new Map();
281
1035
  for (const m of methods) {
1036
+ const d = declared.get(m.name.toLowerCase()) ?? { name: m.name, arities: new Set() };
1037
+ d.arities.add(m.params.length);
1038
+ declared.set(m.name.toLowerCase(), d);
1039
+ }
1040
+ const parentName = header.isInterface ? null : header.extendsClass;
1041
+ emitPageRefEdges(ctx, classQname, cleaned, edges);
1042
+ const methodQueries = [];
1043
+ const methodNodes = new Map();
1044
+ const classPaths = [];
1045
+ const classChildren = [];
1046
+ const declRanges = [];
1047
+ for (const m of methods) {
1048
+ const methodEdgeStart = edges.length;
282
1049
  const methodQname = `ApexMethod:${effectiveName}.${m.name}(${m.params.length})`;
283
1050
  const annotationsLower = m.annotations.map((a) => a.toLowerCase());
284
1051
  const auraEnabled = annotationsLower.includes("auraenabled");
@@ -288,7 +1055,7 @@ export class ApexClassParser {
288
1055
  const isTestMethod = annotationsLower.includes("istest") || annotationsLower.includes("testsetup");
289
1056
  const httpVerbs = ["httpget", "httppost", "httpput", "httppatch", "httpdelete"].filter((v) => annotationsLower.includes(v));
290
1057
  const isTestLabel = isTestMethod || (header.isTest && m.modifiers.includes("static"));
291
- nodes.push(makeNode(ctx, isTestLabel ? "TestMethod" : "ApexMethod", methodQname, {
1058
+ const methodNode = makeNode(ctx, isTestLabel ? "TestMethod" : "ApexMethod", methodQname, {
292
1059
  name: m.name,
293
1060
  arity: m.params.length,
294
1061
  returnType: m.returnType,
@@ -307,7 +1074,9 @@ export class ApexClassParser {
307
1074
  // or @isTest on the enclosing class with the method static.
308
1075
  // Never derived from filename.
309
1076
  isTest: isTestLabel,
310
- }, sha256(m.body)));
1077
+ }, sha256(`${APEX_NODE_HASH_SALT}\n${m.body}`));
1078
+ nodes.push(methodNode);
1079
+ methodNodes.set(methodQname, methodNode);
311
1080
  edges.push(makeEdge(ctx, classQname, REL_TYPES.CONTAINS_METHOD, methodQname));
312
1081
  // Snippet: emit the raw method body from the ORIGINAL source (preserves
313
1082
  // comments + strings) keyed on the method's qname.
@@ -340,49 +1109,67 @@ export class ApexClassParser {
340
1109
  if (endLine != null)
341
1110
  snippetRec.endLine = endLine;
342
1111
  snippets.push(snippetRec);
1112
+ // Apex -> Vlocity calls (IP / DataRaptor), extracted from the raw body.
1113
+ // `rawBody` above locates the method by first textual occurrence of its
1114
+ // name (which can be a call site), so locate the declaration properly here.
1115
+ const decl = declRawBody(input.body, maskedBody, m, usedDecls);
1116
+ if (decl)
1117
+ declRanges.push(decl);
1118
+ const rawDecl = decl?.text ?? null;
1119
+ const vc = extractVlocityCalls(rawDecl ?? "", stringConstants, m.params);
1120
+ for (const k of vc.ipKeys)
1121
+ edges.push(makeEdge(ctx, methodQname, REL_TYPES.APEX_CALLS_IP, `IntegrationProcedure:${k}`));
1122
+ for (const d of vc.drNames)
1123
+ edges.push(makeEdge(ctx, methodQname, REL_TYPES.APEX_CALLS_DR, `DataRaptor:${d}`));
1124
+ if (rawDecl !== null) {
1125
+ setFieldNameLiterals(methodNode, rawDecl, [
1126
+ ...paramDecls(m.params),
1127
+ ...collectDecls(m.body),
1128
+ ]);
1129
+ }
343
1130
  // Body analysis (regex path — gated when parserMode === "ast").
344
1131
  const body = m.body;
1132
+ const methodPaths = [];
1133
+ const accessPaths = [];
1134
+ const fieldAccesses = extractFieldAccesses(body, {
1135
+ decls: [...paramDecls(m.params), ...collectDecls(body)],
1136
+ outer: scopes.fields(scopes.classAt(m.startIdx)),
1137
+ excludedTypes: localTypes,
1138
+ paths: accessPaths,
1139
+ });
345
1140
  if (parserMode === "ast") {
1141
+ setRelationshipPaths(methodNode, accessPaths);
1142
+ emitFieldAccessEdges(ctx, methodQname, fieldAccesses, edges, methodEdgeStart);
346
1143
  // AST mode handles per-method edges in a single tree walk after this loop;
347
1144
  // skip the regex extraction here to avoid duplicate / inconsistent edges.
348
1145
  continue;
349
1146
  }
350
- // SOQL
351
- const soqlRe = new RegExp(SOQL_RE.source, "gi");
352
- let sq = soqlRe.exec(body);
353
- while (sq !== null) {
354
- const obj = stripNs(sq[2] ?? "", ctx.namespace);
355
- edges.push(makeEdge(ctx, methodQname, REL_TYPES.EXECUTES_SOQL, `CustomObject:${obj}`, {
356
- query: sq[0]?.trim(),
357
- }));
358
- // READS_FIELD for SELECT clause fields
359
- const selectFields = (sq[1] ?? "")
360
- .split(",")
361
- .map((s) => s.trim())
362
- .filter((s) => s.length > 0 && !/[()*]/.test(s));
363
- for (const f of selectFields) {
364
- const fname = f.split(/\s+/)[0] ?? "";
365
- if (/^[A-Za-z_][\w]*$/.test(fname)) {
366
- edges.push(makeEdge(ctx, methodQname, REL_TYPES.READS_FIELD, `CustomField:${obj}.${fname}`));
367
- }
368
- }
369
- sq = soqlRe.exec(body);
370
- }
371
- // SOSL
372
- const soslRe = new RegExp(SOSL_RE.source, "gi");
373
- if (soslRe.test(body)) {
374
- edges.push(makeEdge(ctx, methodQname, REL_TYPES.EXECUTES_SOSL, "SOSL:*"));
375
- }
376
- // DML
377
- const dmlRe = new RegExp(DML_RE.source, "gi");
378
- let dm = dmlRe.exec(body);
379
- while (dm !== null) {
380
- const op = (dm[1] ?? "").toLowerCase();
381
- edges.push(makeEdge(ctx, methodQname, REL_TYPES.EXECUTES_DML, `DML:${op}`, {
382
- target: dm[2],
383
- }));
384
- dm = dmlRe.exec(body);
1147
+ // Loop-body ranges for governor in-loop detection (regex path). The AST
1148
+ // extractor stamps inLoop itself; this gives the DEFAULT (regex) path the
1149
+ // same signal so governor_risk_check works without SFGRAPH_APEX_PARSER=ast.
1150
+ const loopRanges = computeLoopRanges(body);
1151
+ const inLoopAt = (idx) => loopRanges.some((r) => idx > r.start && idx < r.end);
1152
+ // SOQL: EXECUTES_SOQL + READS_FIELD for the outer query's fields.
1153
+ const methodChildren = [];
1154
+ emitInlineSoqlEdges(ctx, methodQname, body, inLoopAt, edges, methodPaths, methodChildren);
1155
+ setChildRelationships(methodNode, methodChildren);
1156
+ mergePaths(methodPaths, accessPaths);
1157
+ setRelationshipPaths(methodNode, methodPaths);
1158
+ emitFieldAccessEdges(ctx, methodQname, fieldAccesses, edges, methodEdgeStart);
1159
+ // SOSL (inLoop when any SOSL sits in a loop body) and dynamic queries
1160
+ // in a loop body (governor risks, T15 I-5).
1161
+ emitSoslEdge(ctx, methodQname, body, inLoopAt, edges);
1162
+ const dynInLoop = dynamicQueriesInLoop(body, inLoopAt);
1163
+ if (dynInLoop.length > 0) {
1164
+ methodNode.attributes.dynamicQueryInLoop = dynInLoop;
385
1165
  }
1166
+ // DML (statements and Database.<op>(…) calls), linked to the object
1167
+ // when the record expression's type is known.
1168
+ const outerFields = enclosingFields(m.startIdx);
1169
+ emitDmlEdges(ctx, methodQname, body, inLoopAt, edges, {
1170
+ lookup: makeTypeLookup([...paramDecls(m.params), ...collectDecls(body)], outerFields),
1171
+ excludedTypes: localTypes,
1172
+ });
386
1173
  // Named credentials (scan the ORIGINAL method body — strings are stripped from `body`).
387
1174
  const ncSource = (() => {
388
1175
  const idx = input.body.indexOf(m.name);
@@ -398,30 +1185,89 @@ export class ApexClassParser {
398
1185
  let nc = ncRe.exec(ncSource);
399
1186
  while (nc !== null) {
400
1187
  edges.push(makeEdge(ctx, methodQname, REL_TYPES.CALLS_NAMED_CREDENTIAL, `NamedCredential:${stripNs(nc[1] ?? "", ctx.namespace)}`));
401
- nc = ncRe.exec(body);
1188
+ // Continue over the SAME source we started on (ncSource). Re-executing a
1189
+ // stateful /g regex against a different string (`body`) reset its
1190
+ // lastIndex semantics and dropped every named credential after the first.
1191
+ nc = ncRe.exec(ncSource);
402
1192
  }
403
- // Calls to other static methods (best-effort)
404
- const scRe = new RegExp(STATIC_CALL_RE.source, "g");
405
- let sc = scRe.exec(body);
406
- while (sc !== null) {
407
- const target = sc[1] ?? "";
408
- const targetMethod = sc[2] ?? "";
409
- if (target !== effectiveName && /^[A-Z]/.test(target)) {
410
- // The regex's match index + match length lands one char before
411
- // the `(`. Walk forward past any whitespace to find it, then
412
- // count balanced args. When counting succeeds we emit a precise
413
- // arity dst (same shape AST pass uses), which skips the
414
- // arity-resolver's overload fan-out entirely.
415
- const matchEnd = scRe.lastIndex - 1;
416
- const arity = countCallArgs(body, matchEnd);
417
- const dstArity = arity == null ? "?" : String(arity);
418
- const attrs = arity == null
419
- ? { unresolvedArity: true }
420
- : { resolvedBy: "regex-arg-count", arity };
421
- edges.push(makeEdge(ctx, methodQname, REL_TYPES.CALLS, `ApexMethod:${stripNs(target, ctx.namespace)}.${targetMethod}(${dstArity})`, attrs));
1193
+ // Calls to other static methods (best-effort), constructors and
1194
+ // static-member references.
1195
+ // Scanned on the length-preserving mask (quotes kept, contents blanked)
1196
+ // so a string argument still counts toward the call's arity.
1197
+ const callCode = rawDecl === null ? body : maskApex(rawDecl);
1198
+ const methodScope = {
1199
+ ...refScope,
1200
+ localVars: localVarNames([
1201
+ ...paramDecls(m.params),
1202
+ ...collectDecls(body),
1203
+ ...enclosingFields(m.startIdx),
1204
+ ]),
1205
+ };
1206
+ emitStaticCallEdges(ctx, methodQname, callCode, effectiveName, edges, methodScope.localVars);
1207
+ const callLookup = makeTypeLookup([...paramDecls(m.params), ...collectDecls(callCode)], outerFields);
1208
+ emitInstanceCallEdges(ctx, methodQname, callCode, {
1209
+ lookup: callLookup,
1210
+ scope: methodScope,
1211
+ selfName: effectiveName,
1212
+ parentName,
1213
+ declared,
1214
+ fields: outerFields,
1215
+ }, edges, methodEdgeStart);
1216
+ emitNameRefEdges(ctx, methodQname, callCode, rawDecl, { lookup: callLookup, scope: methodScope }, edges, methodEdgeStart);
1217
+ emitClassRefEdges(ctx, methodQname, callCode, methodScope, edges);
1218
+ // The signature is outside `callCode`: prepend it so parameter and
1219
+ // return types count for the method too.
1220
+ const sig = `${m.returnType} ${m.name}(${m.params.join(", ")});\n`;
1221
+ emitTypeRefEdges(ctx, methodQname, sig + callCode, methodScope, edges, methodEdgeStart);
1222
+ emitDescribeRefEdges(ctx, methodQname, callCode, edges);
1223
+ const queries = rawDecl === null ? [] : foldDynamicSoql(rawDecl);
1224
+ methodQueries.push(...queries);
1225
+ setDynamicSoqlSkips(methodNode, emitDynamicSoqlEdges(ctx, methodQname, queries, edges, methodEdgeStart));
1226
+ }
1227
+ // Code outside method bodies (field initializers, constructors, static
1228
+ // blocks, method signatures) is attributed to the class itself.
1229
+ if (parserMode !== "ast") {
1230
+ const outside = maskedBody.split("");
1231
+ for (const d of declRanges) {
1232
+ for (let k = d.start; k < d.start + d.text.length; k++) {
1233
+ if (outside[k] !== "\n")
1234
+ outside[k] = " ";
422
1235
  }
423
- sc = scRe.exec(body);
424
1236
  }
1237
+ const rest = outside.join("");
1238
+ emitStaticCallEdges(ctx, classQname, rest, effectiveName, edges, refScope.localVars);
1239
+ // Outside method bodies a name is typed only when every declaration of
1240
+ // it agrees (no positional scoping across constructors / initialisers).
1241
+ const restDecls = collectDecls(rest);
1242
+ const restLookup = makeTypeLookup([], restDecls);
1243
+ emitInstanceCallEdges(ctx, classQname, rest, {
1244
+ lookup: restLookup,
1245
+ scope: refScope,
1246
+ selfName: effectiveName,
1247
+ parentName,
1248
+ declared,
1249
+ fields: restDecls,
1250
+ }, edges);
1251
+ emitNameRefEdges(ctx, classQname, rest, input.body, { lookup: restLookup, scope: refScope }, edges);
1252
+ emitClassRefEdges(ctx, classQname, rest, refScope, edges);
1253
+ emitTypeRefEdges(ctx, classQname, rest, refScope, edges);
1254
+ emitDescribeRefEdges(ctx, classQname, rest, edges);
1255
+ // Dynamic SOQL outside method bodies (constants, constructors): every
1256
+ // folded query in the file minus those already attributed to a method.
1257
+ const left = new Map();
1258
+ for (const q of methodQueries)
1259
+ left.set(q, (left.get(q) ?? 0) + 1);
1260
+ const classQueries = foldDynamicSoql(input.body).filter((q) => {
1261
+ const n = left.get(q) ?? 0;
1262
+ if (n > 0)
1263
+ left.set(q, n - 1);
1264
+ return n === 0;
1265
+ });
1266
+ setDynamicSoqlSkips(nodes.find((n) => String(n.qualifiedName) === classQname), emitDynamicSoqlEdges(ctx, classQname, classQueries, edges, 0));
1267
+ // Inline SOQL outside method bodies (static field initialisers,
1268
+ // constructors, initializer blocks) belongs to the class.
1269
+ const restLoops = computeLoopRanges(rest);
1270
+ emitInlineSoqlEdges(ctx, classQname, rest, (idx) => restLoops.some((r) => idx > r.start && idx < r.end), edges, classPaths, classChildren);
425
1271
  }
426
1272
  // AST-mode (and shadow mode): walk the parsed compilationUnit and emit
427
1273
  // edges that the regex pass cannot derive (real call arity, dotted field
@@ -438,13 +1284,33 @@ export class ApexClassParser {
438
1284
  if (parserMode === "both") {
439
1285
  // Tag AST edges so a shadow diff can distinguish overlap.
440
1286
  for (const e of extraction.edges) {
441
- e.attributes.resolvedBy = "ast-shadow";
1287
+ e.attributes.callResolution = "ast-shadow";
442
1288
  edges.push(e);
443
1289
  }
444
1290
  }
445
1291
  else {
446
- for (const e of extraction.edges)
1292
+ // Member-access field edges were already emitted per method; the AST
1293
+ // dotted-ref / select-list READS_FIELD to the same (src, dst) would
1294
+ // only duplicate them.
1295
+ const fieldKeys = new Set(edges
1296
+ .filter((e) => e.relType === REL_TYPES.READS_FIELD || e.relType === REL_TYPES.WRITES_FIELD)
1297
+ .map((e) => `${e.relType}|${String(e.srcQualifiedName)}|${String(e.dstQualifiedName).toLowerCase()}`));
1298
+ for (const e of extraction.edges) {
1299
+ const key = `${e.relType}|${String(e.srcQualifiedName)}|${String(e.dstQualifiedName).toLowerCase()}`;
1300
+ if (fieldKeys.has(key))
1301
+ continue;
1302
+ if (e.relType === REL_TYPES.READS_FIELD || e.relType === REL_TYPES.WRITES_FIELD)
1303
+ fieldKeys.add(key);
447
1304
  edges.push(e);
1305
+ }
1306
+ }
1307
+ if (parserMode === "ast") {
1308
+ // "both" keeps the regex paths (regex is authoritative there).
1309
+ for (const [qn, paths] of extraction.relationshipPaths) {
1310
+ const target = methodNodes.get(qn) ?? nodes.find((n) => String(n.qualifiedName) === classQname);
1311
+ if (target)
1312
+ setRelationshipPaths(target, paths);
1313
+ }
448
1314
  }
449
1315
  if (process.env.SFGRAPH_DEBUG_INGEST === "1" && extraction.diagnostics.length > 0) {
450
1316
  ctx.logger?.debug?.("apex ast diagnostics", {
@@ -462,16 +1328,51 @@ export class ApexClassParser {
462
1328
  }
463
1329
  // Test class linkage
464
1330
  if (header.isTest) {
465
- const { testTargets } = parseClassNameAndIsTest(cleaned);
1331
+ const { testTargets, lowerTargets } = parseClassNameAndIsTest(cleaned);
1332
+ // Lowercase-initial names (`new myController()`) are legal class
1333
+ // names but indistinguishable from variables here: keep them as
1334
+ // candidates the post-merge resolver matches against known classes.
1335
+ const candidates = [];
1336
+ for (const t of lowerTargets) {
1337
+ if (t.toLowerCase() === effectiveName.toLowerCase())
1338
+ continue;
1339
+ if (!isUserClassName(t, refScope) || refScope.localVars.has(t.toLowerCase()))
1340
+ continue;
1341
+ if (candidates.some((c) => c.toLowerCase() === t.toLowerCase()))
1342
+ continue;
1343
+ candidates.push(t);
1344
+ }
1345
+ const classNode = nodes.find((n) => String(n.qualifiedName) === classQname);
1346
+ if (classNode && candidates.length > 0)
1347
+ classNode.attributes.testTargetCandidates = candidates;
466
1348
  for (const t of testTargets) {
467
1349
  if (t === effectiveName)
468
1350
  continue;
469
- if (!/^[A-Z]/.test(t))
1351
+ // Only plausible user classes: not Apex system types/namespaces
1352
+ // (Database, System, Test, String), SObjects, types declared in this
1353
+ // file, or declared variables/fields used as call receivers.
1354
+ if (!isUserClassName(t, refScope) || refScope.localVars.has(t.toLowerCase()))
470
1355
  continue;
471
1356
  edges.push(makeEdge(ctx, classQname, REL_TYPES.IS_TEST_FOR, `ApexClass:${stripNs(t, ctx.namespace)}`));
472
1357
  }
473
1358
  }
474
- return { nodes, edges, snippets };
1359
+ setChildRelationships(nodes.find((n) => String(n.qualifiedName) === classQname), classChildren);
1360
+ if (classPaths.length > 0) {
1361
+ const classNode = nodes.find((n) => String(n.qualifiedName) === classQname);
1362
+ if (classNode)
1363
+ setRelationshipPaths(classNode, classPaths);
1364
+ }
1365
+ return { nodes, edges: aggregateGovernorFacts(edges), snippets };
475
1366
  }
476
1367
  }
1368
+ /** Store relationship paths on a node (only when there are any). */
1369
+ function setRelationshipPaths(node, paths) {
1370
+ if (paths.length === 0)
1371
+ return;
1372
+ const attrs = node.attributes;
1373
+ const cur = attrs.apexRelationshipPaths ?? [];
1374
+ const merged = [...cur];
1375
+ mergePaths(merged, paths);
1376
+ attrs.apexRelationshipPaths = merged;
1377
+ }
477
1378
  //# sourceMappingURL=class.js.map