@opensip-cli/graph-typescript 0.1.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 (344) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +8 -0
  3. package/README.md +31 -0
  4. package/dist/__tests__/_pipeline.d.ts +40 -0
  5. package/dist/__tests__/_pipeline.d.ts.map +1 -0
  6. package/dist/__tests__/_pipeline.js +67 -0
  7. package/dist/__tests__/_pipeline.js.map +1 -0
  8. package/dist/__tests__/acceptance/_fixture-runner.d.ts +13 -0
  9. package/dist/__tests__/acceptance/_fixture-runner.d.ts.map +1 -0
  10. package/dist/__tests__/acceptance/_fixture-runner.js +55 -0
  11. package/dist/__tests__/acceptance/_fixture-runner.js.map +1 -0
  12. package/dist/__tests__/acceptance/alias-resolution.test.d.ts +8 -0
  13. package/dist/__tests__/acceptance/alias-resolution.test.d.ts.map +1 -0
  14. package/dist/__tests__/acceptance/alias-resolution.test.js +39 -0
  15. package/dist/__tests__/acceptance/alias-resolution.test.js.map +1 -0
  16. package/dist/__tests__/acceptance/arrow-callback-resolution.test.d.ts +8 -0
  17. package/dist/__tests__/acceptance/arrow-callback-resolution.test.d.ts.map +1 -0
  18. package/dist/__tests__/acceptance/arrow-callback-resolution.test.js +33 -0
  19. package/dist/__tests__/acceptance/arrow-callback-resolution.test.js.map +1 -0
  20. package/dist/__tests__/acceptance/constructor-calls.test.d.ts +7 -0
  21. package/dist/__tests__/acceptance/constructor-calls.test.d.ts.map +1 -0
  22. package/dist/__tests__/acceptance/constructor-calls.test.js +36 -0
  23. package/dist/__tests__/acceptance/constructor-calls.test.js.map +1 -0
  24. package/dist/__tests__/acceptance/interface-dispatch.test.d.ts +8 -0
  25. package/dist/__tests__/acceptance/interface-dispatch.test.d.ts.map +1 -0
  26. package/dist/__tests__/acceptance/interface-dispatch.test.js +44 -0
  27. package/dist/__tests__/acceptance/interface-dispatch.test.js.map +1 -0
  28. package/dist/__tests__/acceptance/inventory-completeness.test.d.ts +9 -0
  29. package/dist/__tests__/acceptance/inventory-completeness.test.d.ts.map +1 -0
  30. package/dist/__tests__/acceptance/inventory-completeness.test.js +72 -0
  31. package/dist/__tests__/acceptance/inventory-completeness.test.js.map +1 -0
  32. package/dist/__tests__/acceptance/jsx-resolution.test.d.ts +7 -0
  33. package/dist/__tests__/acceptance/jsx-resolution.test.d.ts.map +1 -0
  34. package/dist/__tests__/acceptance/jsx-resolution.test.js +46 -0
  35. package/dist/__tests__/acceptance/jsx-resolution.test.js.map +1 -0
  36. package/dist/__tests__/acceptance/module-init.test.d.ts +10 -0
  37. package/dist/__tests__/acceptance/module-init.test.d.ts.map +1 -0
  38. package/dist/__tests__/acceptance/module-init.test.js +40 -0
  39. package/dist/__tests__/acceptance/module-init.test.js.map +1 -0
  40. package/dist/__tests__/acceptance/projectdir-normalization.test.d.ts +7 -0
  41. package/dist/__tests__/acceptance/projectdir-normalization.test.d.ts.map +1 -0
  42. package/dist/__tests__/acceptance/projectdir-normalization.test.js +51 -0
  43. package/dist/__tests__/acceptance/projectdir-normalization.test.js.map +1 -0
  44. package/dist/__tests__/body-twin-edges.test.d.ts +12 -0
  45. package/dist/__tests__/body-twin-edges.test.d.ts.map +1 -0
  46. package/dist/__tests__/body-twin-edges.test.js +85 -0
  47. package/dist/__tests__/body-twin-edges.test.js.map +1 -0
  48. package/dist/__tests__/cache-key.test.d.ts +8 -0
  49. package/dist/__tests__/cache-key.test.d.ts.map +1 -0
  50. package/dist/__tests__/cache-key.test.js +86 -0
  51. package/dist/__tests__/cache-key.test.js.map +1 -0
  52. package/dist/__tests__/depends-on-emission.test.d.ts +19 -0
  53. package/dist/__tests__/depends-on-emission.test.d.ts.map +1 -0
  54. package/dist/__tests__/depends-on-emission.test.js +165 -0
  55. package/dist/__tests__/depends-on-emission.test.js.map +1 -0
  56. package/dist/__tests__/discover.test.d.ts +9 -0
  57. package/dist/__tests__/discover.test.d.ts.map +1 -0
  58. package/dist/__tests__/discover.test.js +72 -0
  59. package/dist/__tests__/discover.test.js.map +1 -0
  60. package/dist/__tests__/edge-helpers/method-target.test.d.ts +8 -0
  61. package/dist/__tests__/edge-helpers/method-target.test.d.ts.map +1 -0
  62. package/dist/__tests__/edge-helpers/method-target.test.js +96 -0
  63. package/dist/__tests__/edge-helpers/method-target.test.js.map +1 -0
  64. package/dist/__tests__/edge-helpers/resolve-decl.test.d.ts +13 -0
  65. package/dist/__tests__/edge-helpers/resolve-decl.test.d.ts.map +1 -0
  66. package/dist/__tests__/edge-helpers/resolve-decl.test.js +176 -0
  67. package/dist/__tests__/edge-helpers/resolve-decl.test.js.map +1 -0
  68. package/dist/__tests__/edge-resolvers/branches.test.d.ts +11 -0
  69. package/dist/__tests__/edge-resolvers/branches.test.d.ts.map +1 -0
  70. package/dist/__tests__/edge-resolvers/branches.test.js +305 -0
  71. package/dist/__tests__/edge-resolvers/branches.test.js.map +1 -0
  72. package/dist/__tests__/edge-resolvers/contract.test.d.ts +8 -0
  73. package/dist/__tests__/edge-resolvers/contract.test.d.ts.map +1 -0
  74. package/dist/__tests__/edge-resolvers/contract.test.js +30 -0
  75. package/dist/__tests__/edge-resolvers/contract.test.js.map +1 -0
  76. package/dist/__tests__/edge-resolvers/resolver-units.test.d.ts +17 -0
  77. package/dist/__tests__/edge-resolvers/resolver-units.test.d.ts.map +1 -0
  78. package/dist/__tests__/edge-resolvers/resolver-units.test.js +286 -0
  79. package/dist/__tests__/edge-resolvers/resolver-units.test.js.map +1 -0
  80. package/dist/__tests__/fast-vs-exact.test.d.ts +16 -0
  81. package/dist/__tests__/fast-vs-exact.test.d.ts.map +1 -0
  82. package/dist/__tests__/fast-vs-exact.test.js +116 -0
  83. package/dist/__tests__/fast-vs-exact.test.js.map +1 -0
  84. package/dist/__tests__/graph-cli.test.d.ts +9 -0
  85. package/dist/__tests__/graph-cli.test.d.ts.map +1 -0
  86. package/dist/__tests__/graph-cli.test.js +417 -0
  87. package/dist/__tests__/graph-cli.test.js.map +1 -0
  88. package/dist/__tests__/heap-preflight.test.d.ts +10 -0
  89. package/dist/__tests__/heap-preflight.test.d.ts.map +1 -0
  90. package/dist/__tests__/heap-preflight.test.js +166 -0
  91. package/dist/__tests__/heap-preflight.test.js.map +1 -0
  92. package/dist/__tests__/inventory-differential.test.d.ts +15 -0
  93. package/dist/__tests__/inventory-differential.test.d.ts.map +1 -0
  94. package/dist/__tests__/inventory-differential.test.js +319 -0
  95. package/dist/__tests__/inventory-differential.test.js.map +1 -0
  96. package/dist/__tests__/inventory-helpers/hash-body.test.d.ts +11 -0
  97. package/dist/__tests__/inventory-helpers/hash-body.test.d.ts.map +1 -0
  98. package/dist/__tests__/inventory-helpers/hash-body.test.js +34 -0
  99. package/dist/__tests__/inventory-helpers/hash-body.test.js.map +1 -0
  100. package/dist/__tests__/inventory-helpers/synthesize-name.test.d.ts +9 -0
  101. package/dist/__tests__/inventory-helpers/synthesize-name.test.d.ts.map +1 -0
  102. package/dist/__tests__/inventory-helpers/synthesize-name.test.js +30 -0
  103. package/dist/__tests__/inventory-helpers/synthesize-name.test.js.map +1 -0
  104. package/dist/__tests__/inventory-property-tests.test.d.ts +24 -0
  105. package/dist/__tests__/inventory-property-tests.test.d.ts.map +1 -0
  106. package/dist/__tests__/inventory-property-tests.test.js +521 -0
  107. package/dist/__tests__/inventory-property-tests.test.js.map +1 -0
  108. package/dist/__tests__/inventory-shape-coverage.test.d.ts +16 -0
  109. package/dist/__tests__/inventory-shape-coverage.test.d.ts.map +1 -0
  110. package/dist/__tests__/inventory-shape-coverage.test.js +586 -0
  111. package/dist/__tests__/inventory-shape-coverage.test.js.map +1 -0
  112. package/dist/__tests__/inventory-visitors/contract.test.d.ts +9 -0
  113. package/dist/__tests__/inventory-visitors/contract.test.d.ts.map +1 -0
  114. package/dist/__tests__/inventory-visitors/contract.test.js +33 -0
  115. package/dist/__tests__/inventory-visitors/contract.test.js.map +1 -0
  116. package/dist/__tests__/lang-adapter-contract.test.d.ts +19 -0
  117. package/dist/__tests__/lang-adapter-contract.test.d.ts.map +1 -0
  118. package/dist/__tests__/lang-adapter-contract.test.js +880 -0
  119. package/dist/__tests__/lang-adapter-contract.test.js.map +1 -0
  120. package/dist/__tests__/lang-adapter-registry.test.d.ts +10 -0
  121. package/dist/__tests__/lang-adapter-registry.test.d.ts.map +1 -0
  122. package/dist/__tests__/lang-adapter-registry.test.js +151 -0
  123. package/dist/__tests__/lang-adapter-registry.test.js.map +1 -0
  124. package/dist/__tests__/normalize-project-dir.test.d.ts +5 -0
  125. package/dist/__tests__/normalize-project-dir.test.d.ts.map +1 -0
  126. package/dist/__tests__/normalize-project-dir.test.js +47 -0
  127. package/dist/__tests__/normalize-project-dir.test.js.map +1 -0
  128. package/dist/__tests__/orchestrate.test.d.ts +8 -0
  129. package/dist/__tests__/orchestrate.test.d.ts.map +1 -0
  130. package/dist/__tests__/orchestrate.test.js +213 -0
  131. package/dist/__tests__/orchestrate.test.js.map +1 -0
  132. package/dist/__tests__/parse-fast.test.d.ts +9 -0
  133. package/dist/__tests__/parse-fast.test.d.ts.map +1 -0
  134. package/dist/__tests__/parse-fast.test.js +114 -0
  135. package/dist/__tests__/parse-fast.test.js.map +1 -0
  136. package/dist/__tests__/rules/_helpers.d.ts +18 -0
  137. package/dist/__tests__/rules/_helpers.d.ts.map +1 -0
  138. package/dist/__tests__/rules/_helpers.js +62 -0
  139. package/dist/__tests__/rules/_helpers.js.map +1 -0
  140. package/dist/__tests__/rules/duplicated-function-body.test.d.ts +5 -0
  141. package/dist/__tests__/rules/duplicated-function-body.test.d.ts.map +1 -0
  142. package/dist/__tests__/rules/duplicated-function-body.test.js +39 -0
  143. package/dist/__tests__/rules/duplicated-function-body.test.js.map +1 -0
  144. package/dist/__tests__/rules/orphan-subtree.test.d.ts +5 -0
  145. package/dist/__tests__/rules/orphan-subtree.test.d.ts.map +1 -0
  146. package/dist/__tests__/rules/orphan-subtree.test.js +75 -0
  147. package/dist/__tests__/rules/orphan-subtree.test.js.map +1 -0
  148. package/dist/__tests__/rules/rule-hints-integration.test.d.ts +20 -0
  149. package/dist/__tests__/rules/rule-hints-integration.test.d.ts.map +1 -0
  150. package/dist/__tests__/rules/rule-hints-integration.test.js +279 -0
  151. package/dist/__tests__/rules/rule-hints-integration.test.js.map +1 -0
  152. package/dist/__tests__/tool-register.test.d.ts +16 -0
  153. package/dist/__tests__/tool-register.test.d.ts.map +1 -0
  154. package/dist/__tests__/tool-register.test.js +210 -0
  155. package/dist/__tests__/tool-register.test.js.map +1 -0
  156. package/dist/__tests__/value-reference-unit.test.d.ts +13 -0
  157. package/dist/__tests__/value-reference-unit.test.d.ts.map +1 -0
  158. package/dist/__tests__/value-reference-unit.test.js +226 -0
  159. package/dist/__tests__/value-reference-unit.test.js.map +1 -0
  160. package/dist/__tests__/value-reference.test.d.ts +15 -0
  161. package/dist/__tests__/value-reference.test.d.ts.map +1 -0
  162. package/dist/__tests__/value-reference.test.js +115 -0
  163. package/dist/__tests__/value-reference.test.js.map +1 -0
  164. package/dist/__tests__/walk-fast.test.d.ts +8 -0
  165. package/dist/__tests__/walk-fast.test.d.ts.map +1 -0
  166. package/dist/__tests__/walk-fast.test.js +51 -0
  167. package/dist/__tests__/walk-fast.test.js.map +1 -0
  168. package/dist/__tests__/walk-reexports.test.d.ts +7 -0
  169. package/dist/__tests__/walk-reexports.test.d.ts.map +1 -0
  170. package/dist/__tests__/walk-reexports.test.js +79 -0
  171. package/dist/__tests__/walk-reexports.test.js.map +1 -0
  172. package/dist/cache-key.d.ts +41 -0
  173. package/dist/cache-key.d.ts.map +1 -0
  174. package/dist/cache-key.js +94 -0
  175. package/dist/cache-key.js.map +1 -0
  176. package/dist/discover.d.ts +25 -0
  177. package/dist/discover.d.ts.map +1 -0
  178. package/dist/discover.js +124 -0
  179. package/dist/discover.js.map +1 -0
  180. package/dist/edge-helpers/cross-package-context.d.ts +34 -0
  181. package/dist/edge-helpers/cross-package-context.d.ts.map +1 -0
  182. package/dist/edge-helpers/cross-package-context.js +97 -0
  183. package/dist/edge-helpers/cross-package-context.js.map +1 -0
  184. package/dist/edge-helpers/declaration-to-node.d.ts +58 -0
  185. package/dist/edge-helpers/declaration-to-node.d.ts.map +1 -0
  186. package/dist/edge-helpers/declaration-to-node.js +93 -0
  187. package/dist/edge-helpers/declaration-to-node.js.map +1 -0
  188. package/dist/edge-helpers/find-catalog-entry.d.ts +24 -0
  189. package/dist/edge-helpers/find-catalog-entry.d.ts.map +1 -0
  190. package/dist/edge-helpers/find-catalog-entry.js +45 -0
  191. package/dist/edge-helpers/find-catalog-entry.js.map +1 -0
  192. package/dist/edge-helpers/method-target.d.ts +22 -0
  193. package/dist/edge-helpers/method-target.d.ts.map +1 -0
  194. package/dist/edge-helpers/method-target.js +46 -0
  195. package/dist/edge-helpers/method-target.js.map +1 -0
  196. package/dist/edge-helpers/resolution-trace.d.ts +24 -0
  197. package/dist/edge-helpers/resolution-trace.d.ts.map +1 -0
  198. package/dist/edge-helpers/resolution-trace.js +38 -0
  199. package/dist/edge-helpers/resolution-trace.js.map +1 -0
  200. package/dist/edge-helpers/resolve-decl.d.ts +44 -0
  201. package/dist/edge-helpers/resolve-decl.d.ts.map +1 -0
  202. package/dist/edge-helpers/resolve-decl.js +170 -0
  203. package/dist/edge-helpers/resolve-decl.js.map +1 -0
  204. package/dist/edge-helpers/unalias-symbol.d.ts +10 -0
  205. package/dist/edge-helpers/unalias-symbol.d.ts.map +1 -0
  206. package/dist/edge-helpers/unalias-symbol.js +29 -0
  207. package/dist/edge-helpers/unalias-symbol.js.map +1 -0
  208. package/dist/edge-resolvers/__tests__/boundary.test.d.ts +15 -0
  209. package/dist/edge-resolvers/__tests__/boundary.test.d.ts.map +1 -0
  210. package/dist/edge-resolvers/__tests__/boundary.test.js +227 -0
  211. package/dist/edge-resolvers/__tests__/boundary.test.js.map +1 -0
  212. package/dist/edge-resolvers/__tests__/callee-anchor.test.d.ts +10 -0
  213. package/dist/edge-resolvers/__tests__/callee-anchor.test.d.ts.map +1 -0
  214. package/dist/edge-resolvers/__tests__/callee-anchor.test.js +63 -0
  215. package/dist/edge-resolvers/__tests__/callee-anchor.test.js.map +1 -0
  216. package/dist/edge-resolvers/__tests__/syntactic.test.d.ts +12 -0
  217. package/dist/edge-resolvers/__tests__/syntactic.test.d.ts.map +1 -0
  218. package/dist/edge-resolvers/__tests__/syntactic.test.js +215 -0
  219. package/dist/edge-resolvers/__tests__/syntactic.test.js.map +1 -0
  220. package/dist/edge-resolvers/boundary.d.ts +41 -0
  221. package/dist/edge-resolvers/boundary.d.ts.map +1 -0
  222. package/dist/edge-resolvers/boundary.js +130 -0
  223. package/dist/edge-resolvers/boundary.js.map +1 -0
  224. package/dist/edge-resolvers/catalog-fallback.d.ts +13 -0
  225. package/dist/edge-resolvers/catalog-fallback.d.ts.map +1 -0
  226. package/dist/edge-resolvers/catalog-fallback.js +30 -0
  227. package/dist/edge-resolvers/catalog-fallback.js.map +1 -0
  228. package/dist/edge-resolvers/direct-call.d.ts +10 -0
  229. package/dist/edge-resolvers/direct-call.d.ts.map +1 -0
  230. package/dist/edge-resolvers/direct-call.js +45 -0
  231. package/dist/edge-resolvers/direct-call.js.map +1 -0
  232. package/dist/edge-resolvers/jsx-element.d.ts +16 -0
  233. package/dist/edge-resolvers/jsx-element.d.ts.map +1 -0
  234. package/dist/edge-resolvers/jsx-element.js +67 -0
  235. package/dist/edge-resolvers/jsx-element.js.map +1 -0
  236. package/dist/edge-resolvers/new-expression.d.ts +11 -0
  237. package/dist/edge-resolvers/new-expression.d.ts.map +1 -0
  238. package/dist/edge-resolvers/new-expression.js +56 -0
  239. package/dist/edge-resolvers/new-expression.js.map +1 -0
  240. package/dist/edge-resolvers/polymorphic.d.ts +8 -0
  241. package/dist/edge-resolvers/polymorphic.d.ts.map +1 -0
  242. package/dist/edge-resolvers/polymorphic.js +57 -0
  243. package/dist/edge-resolvers/polymorphic.js.map +1 -0
  244. package/dist/edge-resolvers/property-access.d.ts +10 -0
  245. package/dist/edge-resolvers/property-access.d.ts.map +1 -0
  246. package/dist/edge-resolvers/property-access.js +56 -0
  247. package/dist/edge-resolvers/property-access.js.map +1 -0
  248. package/dist/edge-resolvers/syntactic.d.ts +105 -0
  249. package/dist/edge-resolvers/syntactic.d.ts.map +1 -0
  250. package/dist/edge-resolvers/syntactic.js +294 -0
  251. package/dist/edge-resolvers/syntactic.js.map +1 -0
  252. package/dist/edge-resolvers/types.d.ts +37 -0
  253. package/dist/edge-resolvers/types.d.ts.map +1 -0
  254. package/dist/edge-resolvers/types.js +10 -0
  255. package/dist/edge-resolvers/types.js.map +1 -0
  256. package/dist/edges-value-reference.d.ts +26 -0
  257. package/dist/edges-value-reference.d.ts.map +1 -0
  258. package/dist/edges-value-reference.js +138 -0
  259. package/dist/edges-value-reference.js.map +1 -0
  260. package/dist/edges.d.ts +65 -0
  261. package/dist/edges.d.ts.map +1 -0
  262. package/dist/edges.js +392 -0
  263. package/dist/edges.js.map +1 -0
  264. package/dist/index.d.ts +47 -0
  265. package/dist/index.d.ts.map +1 -0
  266. package/dist/index.js +346 -0
  267. package/dist/index.js.map +1 -0
  268. package/dist/inventory-helpers/classify-visibility.d.ts +13 -0
  269. package/dist/inventory-helpers/classify-visibility.d.ts.map +1 -0
  270. package/dist/inventory-helpers/classify-visibility.js +56 -0
  271. package/dist/inventory-helpers/classify-visibility.js.map +1 -0
  272. package/dist/inventory-helpers/extract-decorators.d.ts +6 -0
  273. package/dist/inventory-helpers/extract-decorators.d.ts.map +1 -0
  274. package/dist/inventory-helpers/extract-decorators.js +33 -0
  275. package/dist/inventory-helpers/extract-decorators.js.map +1 -0
  276. package/dist/inventory-helpers/extract-params.d.ts +7 -0
  277. package/dist/inventory-helpers/extract-params.d.ts.map +1 -0
  278. package/dist/inventory-helpers/extract-params.js +28 -0
  279. package/dist/inventory-helpers/extract-params.js.map +1 -0
  280. package/dist/inventory-helpers/hash-body.d.ts +43 -0
  281. package/dist/inventory-helpers/hash-body.d.ts.map +1 -0
  282. package/dist/inventory-helpers/hash-body.js +52 -0
  283. package/dist/inventory-helpers/hash-body.js.map +1 -0
  284. package/dist/inventory-helpers/synthesize-name.d.ts +16 -0
  285. package/dist/inventory-helpers/synthesize-name.d.ts.map +1 -0
  286. package/dist/inventory-helpers/synthesize-name.js +17 -0
  287. package/dist/inventory-helpers/synthesize-name.js.map +1 -0
  288. package/dist/inventory-visitors/arrow-function.d.ts +8 -0
  289. package/dist/inventory-visitors/arrow-function.d.ts.map +1 -0
  290. package/dist/inventory-visitors/arrow-function.js +55 -0
  291. package/dist/inventory-visitors/arrow-function.js.map +1 -0
  292. package/dist/inventory-visitors/class-static-init.d.ts +17 -0
  293. package/dist/inventory-visitors/class-static-init.d.ts.map +1 -0
  294. package/dist/inventory-visitors/class-static-init.js +45 -0
  295. package/dist/inventory-visitors/class-static-init.js.map +1 -0
  296. package/dist/inventory-visitors/constructor-declaration.d.ts +7 -0
  297. package/dist/inventory-visitors/constructor-declaration.d.ts.map +1 -0
  298. package/dist/inventory-visitors/constructor-declaration.js +47 -0
  299. package/dist/inventory-visitors/constructor-declaration.js.map +1 -0
  300. package/dist/inventory-visitors/function-declaration.d.ts +7 -0
  301. package/dist/inventory-visitors/function-declaration.d.ts.map +1 -0
  302. package/dist/inventory-visitors/function-declaration.js +59 -0
  303. package/dist/inventory-visitors/function-declaration.js.map +1 -0
  304. package/dist/inventory-visitors/function-expression.d.ts +7 -0
  305. package/dist/inventory-visitors/function-expression.d.ts.map +1 -0
  306. package/dist/inventory-visitors/function-expression.js +56 -0
  307. package/dist/inventory-visitors/function-expression.js.map +1 -0
  308. package/dist/inventory-visitors/getter-setter.d.ts +7 -0
  309. package/dist/inventory-visitors/getter-setter.d.ts.map +1 -0
  310. package/dist/inventory-visitors/getter-setter.js +63 -0
  311. package/dist/inventory-visitors/getter-setter.js.map +1 -0
  312. package/dist/inventory-visitors/method-declaration.d.ts +7 -0
  313. package/dist/inventory-visitors/method-declaration.d.ts.map +1 -0
  314. package/dist/inventory-visitors/method-declaration.js +75 -0
  315. package/dist/inventory-visitors/method-declaration.js.map +1 -0
  316. package/dist/inventory-visitors/module-init.d.ts +15 -0
  317. package/dist/inventory-visitors/module-init.d.ts.map +1 -0
  318. package/dist/inventory-visitors/module-init.js +40 -0
  319. package/dist/inventory-visitors/module-init.js.map +1 -0
  320. package/dist/inventory-visitors/types.d.ts +26 -0
  321. package/dist/inventory-visitors/types.d.ts.map +1 -0
  322. package/dist/inventory-visitors/types.js +14 -0
  323. package/dist/inventory-visitors/types.js.map +1 -0
  324. package/dist/normalize-project-dir.d.ts +14 -0
  325. package/dist/normalize-project-dir.d.ts.map +1 -0
  326. package/dist/normalize-project-dir.js +36 -0
  327. package/dist/normalize-project-dir.js.map +1 -0
  328. package/dist/parse-fast.d.ts +41 -0
  329. package/dist/parse-fast.d.ts.map +1 -0
  330. package/dist/parse-fast.js +91 -0
  331. package/dist/parse-fast.js.map +1 -0
  332. package/dist/parse.d.ts +42 -0
  333. package/dist/parse.d.ts.map +1 -0
  334. package/dist/parse.js +75 -0
  335. package/dist/parse.js.map +1 -0
  336. package/dist/test-file.d.ts +7 -0
  337. package/dist/test-file.d.ts.map +1 -0
  338. package/dist/test-file.js +42 -0
  339. package/dist/test-file.js.map +1 -0
  340. package/dist/walk.d.ts +125 -0
  341. package/dist/walk.d.ts.map +1 -0
  342. package/dist/walk.js +393 -0
  343. package/dist/walk.js.map +1 -0
  344. package/package.json +57 -0
@@ -0,0 +1,880 @@
1
+ // @fitness-ignore-file file-length-limit -- Contract test suite covering the 9 behavioral invariants (I-1..I-9) for the TS/Python/Rust GraphLanguageAdapter cohort exercised here; splitting per-language fragments the single-source contract document those tests verify.
2
+ /**
3
+ * GraphLanguageAdapter contract test suite.
4
+ *
5
+ * Validates each of the 9 behavioral invariants (I-1 through I-9)
6
+ * defined in docs/plans/11-graph-language-adapter-contract.md §3
7
+ * against the TS/Python/Rust adapters covered in this suite. Additional
8
+ * adapter cohorts can add `describe` blocks against their own fixtures while
9
+ * referencing the same invariants.
10
+ *
11
+ * Each test names the invariant it covers in the title so reviewers
12
+ * can map a failure straight to the contract clause.
13
+ *
14
+ * The fixture is a small in-memory project written under a temp
15
+ * directory: a couple of source files, a tsconfig.json. We exercise
16
+ * `discoverFiles → parseProject → walkProject → resolveCallSites`
17
+ * twice (for determinism checks) and inspect the outputs.
18
+ */
19
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
20
+ import { tmpdir } from 'node:os';
21
+ import { join } from 'node:path';
22
+ import { pythonGraphAdapter } from '@opensip-cli/graph-python';
23
+ import { rustGraphAdapter } from '@opensip-cli/graph-rust';
24
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
25
+ import { typescriptGraphAdapter } from '../index.js';
26
+ const FIXTURE_TSCONFIG = JSON.stringify({
27
+ compilerOptions: {
28
+ target: 'ES2022',
29
+ module: 'Node16',
30
+ moduleResolution: 'Node16',
31
+ lib: ['ES2022'],
32
+ strict: true,
33
+ esModuleInterop: true,
34
+ skipLibCheck: true,
35
+ rootDir: '.',
36
+ },
37
+ include: ['**/*.ts'],
38
+ });
39
+ const FIXTURE_FILES = {
40
+ 'src/main.ts': `
41
+ import { helper } from './util.js';
42
+ export function entry(): number {
43
+ const r = helper(2);
44
+ return r * 2;
45
+ }
46
+ function unused(): void {
47
+ console.log('orphan');
48
+ }
49
+ `,
50
+ 'src/util.ts': `
51
+ export function helper(x: number): number {
52
+ return x + 1;
53
+ }
54
+ export const arrow = (n: number) => n + 2;
55
+ `,
56
+ };
57
+ function setupFixture(dir) {
58
+ mkdirSync(dir, { recursive: true });
59
+ writeFileSync(join(dir, 'tsconfig.json'), FIXTURE_TSCONFIG, 'utf8');
60
+ for (const [rel, content] of Object.entries(FIXTURE_FILES)) {
61
+ const p = join(dir, rel);
62
+ mkdirSync(p.slice(0, Math.max(0, p.lastIndexOf('/'))), { recursive: true });
63
+ writeFileSync(p, content, 'utf8');
64
+ }
65
+ }
66
+ function buildPipeline(adapter = typescriptGraphAdapter, dir) {
67
+ const discovery = adapter.discoverFiles({ cwd: dir });
68
+ const parsed = adapter.parseProject({
69
+ projectDirAbs: discovery.projectDirAbs,
70
+ files: discovery.files,
71
+ compilerOptions: discovery.compilerOptions,
72
+ resolutionMode: 'exact',
73
+ });
74
+ const walk = adapter.walkProject({
75
+ project: parsed.project,
76
+ projectDirAbs: discovery.projectDirAbs,
77
+ files: discovery.files,
78
+ });
79
+ const catalog = {
80
+ version: '3.0',
81
+ tool: 'graph',
82
+ language: adapter.id,
83
+ builtAt: '2026-05-18T00:00:00.000Z',
84
+ cacheKey: adapter.cacheKey({
85
+ projectDirAbs: discovery.projectDirAbs,
86
+ configPathAbs: discovery.configPathAbs,
87
+ compilerOptions: discovery.compilerOptions,
88
+ resolutionMode: 'exact',
89
+ }),
90
+ functions: walk.occurrences,
91
+ };
92
+ return { project: parsed.project, walk, catalog, discovery };
93
+ }
94
+ function canonicalizeWalkOutput(walk) {
95
+ // Hash-only summary so we don't compare opaque AST node refs.
96
+ const occurrences = new Map();
97
+ for (const arr of Object.values(walk.occurrences)) {
98
+ for (const o of arr) {
99
+ occurrences.set(o.bodyHash, (occurrences.get(o.bodyHash) ?? 0) + 1);
100
+ }
101
+ }
102
+ const callSiteSummary = walk.callSites
103
+ .map((r) => `${r.kind}|${r.ownerHash}|${r.childHash ?? ''}`)
104
+ .sort();
105
+ return { occurrences, callSiteSummary };
106
+ }
107
+ describe('GraphLanguageAdapter contract — TypeScript', () => {
108
+ let dir;
109
+ beforeEach(() => {
110
+ dir = mkdtempSync(join(tmpdir(), 'graph-contract-'));
111
+ setupFixture(dir);
112
+ });
113
+ afterEach(() => {
114
+ rmSync(dir, { recursive: true, force: true });
115
+ });
116
+ // ── adapter shape ────────────────────────────────────────────
117
+ it('exposes the six required adapter methods + identity fields', () => {
118
+ expect(typescriptGraphAdapter.id).toBe('typescript');
119
+ expect(typescriptGraphAdapter.fileExtensions).toContain('.ts');
120
+ expect(typescriptGraphAdapter.fileExtensions).toContain('.tsx');
121
+ expect(typescriptGraphAdapter.displayName).toBeDefined();
122
+ expect(typeof typescriptGraphAdapter.discoverFiles).toBe('function');
123
+ expect(typeof typescriptGraphAdapter.parseProject).toBe('function');
124
+ expect(typeof typescriptGraphAdapter.walkProject).toBe('function');
125
+ expect(typeof typescriptGraphAdapter.resolveCallSites).toBe('function');
126
+ expect(typeof typescriptGraphAdapter.cacheKey).toBe('function');
127
+ });
128
+ // ── I-1: walkProject is deterministic ────────────────────────
129
+ it('I-1 — walkProject is deterministic across two runs over the same project', () => {
130
+ const a = buildPipeline(typescriptGraphAdapter, dir);
131
+ const b = buildPipeline(typescriptGraphAdapter, dir);
132
+ const ca = canonicalizeWalkOutput(a.walk);
133
+ const cb = canonicalizeWalkOutput(b.walk);
134
+ expect(cb.occurrences).toEqual(ca.occurrences);
135
+ expect(cb.callSiteSummary).toEqual(ca.callSiteSummary);
136
+ });
137
+ // ── I-2: bodyHash collisions are intentional duplicates ──────
138
+ it('I-2 — different function bodies produce different bodyHashes', () => {
139
+ const { walk } = buildPipeline(typescriptGraphAdapter, dir);
140
+ const allOccs = [];
141
+ for (const arr of Object.values(walk.occurrences))
142
+ allOccs.push(...arr);
143
+ // Map bodyHash → count of occurrences.
144
+ const byHash = new Map();
145
+ for (const o of allOccs)
146
+ byHash.set(o.bodyHash, (byHash.get(o.bodyHash) ?? 0) + 1);
147
+ // No collisions in the fixture (every helper/entry/unused/arrow has
148
+ // a different body).
149
+ const collisions = [...byHash.values()].filter((n) => n > 1);
150
+ expect(collisions).toHaveLength(0);
151
+ });
152
+ // ── I-3: every CallSiteRecord.ownerHash exists in occurrences ──
153
+ it('I-3 — every CallSiteRecord.ownerHash maps to a known occurrence', () => {
154
+ const { walk } = buildPipeline(typescriptGraphAdapter, dir);
155
+ const knownHashes = new Set();
156
+ for (const arr of Object.values(walk.occurrences)) {
157
+ for (const o of arr)
158
+ knownHashes.add(o.bodyHash);
159
+ }
160
+ for (const r of walk.callSites) {
161
+ expect(knownHashes.has(r.ownerHash)).toBe(true);
162
+ }
163
+ });
164
+ // ── I-4: resolveCallSites doesn't mutate catalog ─────────────
165
+ it('I-4 — resolveCallSites does not mutate the input catalog', async () => {
166
+ const { walk, catalog, project } = buildPipeline(typescriptGraphAdapter, dir);
167
+ const before = JSON.stringify(catalog);
168
+ await typescriptGraphAdapter.resolveCallSites({
169
+ project,
170
+ catalog,
171
+ callSites: walk.callSites,
172
+ projectDirAbs: dir,
173
+ resolutionMode: 'exact',
174
+ });
175
+ const after = JSON.stringify(catalog);
176
+ expect(after).toBe(before);
177
+ });
178
+ // ── I-5: every CallEdge.to references a catalog bodyHash or is empty ──
179
+ it('I-5 — every CallEdge.to references a catalog bodyHash or is empty', async () => {
180
+ const { walk, catalog, project } = buildPipeline(typescriptGraphAdapter, dir);
181
+ const knownHashes = new Set();
182
+ for (const arr of Object.values(catalog.functions)) {
183
+ for (const o of arr)
184
+ knownHashes.add(o.bodyHash);
185
+ }
186
+ const resolved = await typescriptGraphAdapter.resolveCallSites({
187
+ project,
188
+ catalog,
189
+ callSites: walk.callSites,
190
+ projectDirAbs: dir,
191
+ resolutionMode: 'exact',
192
+ });
193
+ for (const edges of resolved.edgesByOwner.values()) {
194
+ for (const e of edges) {
195
+ if (e.to.length === 0)
196
+ continue; // unresolved is allowed
197
+ for (const target of e.to) {
198
+ expect(knownHashes.has(target)).toBe(true);
199
+ }
200
+ }
201
+ }
202
+ });
203
+ // ── I-6: cacheKey is stable for stable input ─────────────────
204
+ it('I-6 — cacheKey is stable for the same projectDir / configPath input', () => {
205
+ const { discovery } = buildPipeline(typescriptGraphAdapter, dir);
206
+ const k1 = typescriptGraphAdapter.cacheKey({
207
+ projectDirAbs: discovery.projectDirAbs,
208
+ configPathAbs: discovery.configPathAbs,
209
+ compilerOptions: discovery.compilerOptions,
210
+ resolutionMode: 'exact',
211
+ });
212
+ const k2 = typescriptGraphAdapter.cacheKey({
213
+ projectDirAbs: discovery.projectDirAbs,
214
+ configPathAbs: discovery.configPathAbs,
215
+ compilerOptions: discovery.compilerOptions,
216
+ resolutionMode: 'exact',
217
+ });
218
+ expect(k2).toBe(k1);
219
+ // The TS adapter prefixes with its id so cross-adapter collisions
220
+ // are impossible.
221
+ expect(k1.startsWith('ts-')).toBe(true);
222
+ });
223
+ it('I-6 — cacheKey changes when the tsconfig content changes', () => {
224
+ const { discovery } = buildPipeline(typescriptGraphAdapter, dir);
225
+ const before = typescriptGraphAdapter.cacheKey({
226
+ projectDirAbs: discovery.projectDirAbs,
227
+ configPathAbs: discovery.configPathAbs,
228
+ compilerOptions: discovery.compilerOptions,
229
+ resolutionMode: 'exact',
230
+ });
231
+ // Mutate the tsconfig (a meaningful semantic change).
232
+ if (discovery.configPathAbs) {
233
+ writeFileSync(discovery.configPathAbs, JSON.stringify({
234
+ compilerOptions: {
235
+ target: 'ES2020',
236
+ module: 'Node16',
237
+ moduleResolution: 'Node16',
238
+ strict: true,
239
+ },
240
+ include: ['**/*.ts'],
241
+ }), 'utf8');
242
+ }
243
+ const after = typescriptGraphAdapter.cacheKey({
244
+ projectDirAbs: discovery.projectDirAbs,
245
+ configPathAbs: discovery.configPathAbs,
246
+ compilerOptions: discovery.compilerOptions,
247
+ resolutionMode: 'exact',
248
+ });
249
+ expect(after).not.toBe(before);
250
+ });
251
+ // ── I-7: parseProject is total over `files` ──────────────────
252
+ it('I-7 — parseProject is total: every file is either parsed or in parseErrors', () => {
253
+ const discovery = typescriptGraphAdapter.discoverFiles({ cwd: dir });
254
+ const parsed = typescriptGraphAdapter.parseProject({
255
+ projectDirAbs: discovery.projectDirAbs,
256
+ files: discovery.files,
257
+ compilerOptions: discovery.compilerOptions,
258
+ resolutionMode: 'exact',
259
+ });
260
+ // For TypeScript: a clean fixture parses without errors.
261
+ // The contract: each file either parses (i.e. is reachable from
262
+ // the program) or is named in parseErrors.
263
+ const tsProject = parsed.project;
264
+ const reachable = new Set(tsProject.program.getSourceFiles().map((sf) => sf.fileName));
265
+ const erroredFiles = new Set(parsed.parseErrors.map((e) => e.filePath));
266
+ for (const f of discovery.files) {
267
+ const seen = reachable.has(f) || erroredFiles.has(f);
268
+ // realpath quirks: try suffix match too.
269
+ const suffixMatch = [...reachable].some((r) => r.endsWith(f.replace(discovery.projectDirAbs, '')));
270
+ expect(seen || suffixMatch).toBe(true);
271
+ }
272
+ });
273
+ // ── I-8: adapter is single-language ──────────────────────────
274
+ it('I-8 — adapter id matches its handled language family', () => {
275
+ expect(typescriptGraphAdapter.id).toBe('typescript');
276
+ // The TS adapter's catalog cacheKey prefix encodes the language;
277
+ // a Python adapter (when it lands) MUST emit a different prefix.
278
+ const k = typescriptGraphAdapter.cacheKey({ projectDirAbs: dir, resolutionMode: 'exact' });
279
+ expect(k).toMatch(/^ts-/);
280
+ });
281
+ // ── I-9: adapter is referentially transparent ────────────────
282
+ it('I-9 — repeated discoverFiles calls return the same files list', () => {
283
+ const a = typescriptGraphAdapter.discoverFiles({ cwd: dir });
284
+ const b = typescriptGraphAdapter.discoverFiles({ cwd: dir });
285
+ expect(b.projectDirAbs).toBe(a.projectDirAbs);
286
+ expect([...b.files].sort()).toEqual([...a.files].sort());
287
+ expect(b.configPathAbs).toBe(a.configPathAbs);
288
+ });
289
+ // ── walkOutput shape ─────────────────────────────────────────
290
+ it('walkProject emits the expected occurrence kinds for the fixture', () => {
291
+ const { walk } = buildPipeline(typescriptGraphAdapter, dir);
292
+ const kinds = new Set();
293
+ for (const arr of Object.values(walk.occurrences)) {
294
+ for (const o of arr)
295
+ kinds.add(o.kind);
296
+ }
297
+ expect(kinds.has('module-init')).toBe(true);
298
+ expect(kinds.has('function-declaration')).toBe(true);
299
+ // The arrow `arrow` from util.ts.
300
+ expect(kinds.has('arrow')).toBe(true);
301
+ });
302
+ it('CallSiteRecord opaque handles round-trip through resolveCallSites', async () => {
303
+ const { walk, catalog, project } = buildPipeline(typescriptGraphAdapter, dir);
304
+ const records = [...walk.callSites];
305
+ expect(records.length).toBeGreaterThan(0);
306
+ // The opaque shape uses nodeRef/sourceFileRef; the adapter can
307
+ // cast back successfully (validated by the promise resolving, not rejecting).
308
+ await expect(typescriptGraphAdapter.resolveCallSites({
309
+ project,
310
+ catalog,
311
+ callSites: records,
312
+ projectDirAbs: dir,
313
+ resolutionMode: 'exact',
314
+ })).resolves.toBeDefined();
315
+ });
316
+ });
317
+ // ── Python adapter ──────────────────────────────────────────────────
318
+ const PY_FIXTURE_PYPROJECT = `[project]
319
+ name = "contract-fixture"
320
+ version = "0.1.0"
321
+ requires-python = ">=3.10"
322
+ `;
323
+ const PY_FIXTURE_FILES = {
324
+ 'main.py': `from util import helper, Greeter
325
+
326
+
327
+ def entry(x):
328
+ g = Greeter("hi")
329
+ msg = g.greet(x)
330
+ return helper(msg)
331
+
332
+
333
+ def unused():
334
+ print("orphan")
335
+
336
+
337
+ if __name__ == "__main__":
338
+ entry(7)
339
+ `,
340
+ 'util.py': `def helper(value):
341
+ return f"helper:{value}"
342
+
343
+
344
+ class Greeter:
345
+ def __init__(self, prefix):
346
+ self.prefix = prefix
347
+
348
+ def greet(self, who):
349
+ return f"{self.prefix} {who}"
350
+
351
+
352
+ add_one = lambda n: n + 1
353
+ `,
354
+ 'tests/test_sample.py': `from util import helper
355
+
356
+
357
+ def test_helper_returns_prefixed_value():
358
+ assert helper("ok") == "helper:ok"
359
+ `,
360
+ };
361
+ function setupPythonFixture(dir) {
362
+ mkdirSync(dir, { recursive: true });
363
+ writeFileSync(join(dir, 'pyproject.toml'), PY_FIXTURE_PYPROJECT, 'utf8');
364
+ for (const [rel, content] of Object.entries(PY_FIXTURE_FILES)) {
365
+ const p = join(dir, rel);
366
+ mkdirSync(p.slice(0, Math.max(0, p.lastIndexOf('/'))), { recursive: true });
367
+ writeFileSync(p, content, 'utf8');
368
+ }
369
+ }
370
+ function buildPythonPipeline(adapter, dir) {
371
+ const discovery = adapter.discoverFiles({ cwd: dir });
372
+ const parsed = adapter.parseProject({
373
+ projectDirAbs: discovery.projectDirAbs,
374
+ files: discovery.files,
375
+ compilerOptions: discovery.compilerOptions,
376
+ resolutionMode: 'exact',
377
+ });
378
+ const walk = adapter.walkProject({
379
+ project: parsed.project,
380
+ projectDirAbs: discovery.projectDirAbs,
381
+ files: discovery.files,
382
+ });
383
+ const cacheKeyArgs = {
384
+ projectDirAbs: discovery.projectDirAbs,
385
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
386
+ ...(discovery.compilerOptions === undefined
387
+ ? {}
388
+ : { compilerOptions: discovery.compilerOptions }),
389
+ resolutionMode: 'exact',
390
+ };
391
+ const catalog = {
392
+ version: '3.0',
393
+ tool: 'graph',
394
+ language: adapter.id,
395
+ builtAt: '2026-05-18T00:00:00.000Z',
396
+ cacheKey: adapter.cacheKey(cacheKeyArgs),
397
+ functions: walk.occurrences,
398
+ };
399
+ return { project: parsed.project, walk, catalog, discovery };
400
+ }
401
+ describe('GraphLanguageAdapter contract — Python', () => {
402
+ let dir;
403
+ beforeEach(() => {
404
+ dir = mkdtempSync(join(tmpdir(), 'graph-contract-py-'));
405
+ setupPythonFixture(dir);
406
+ });
407
+ afterEach(() => {
408
+ rmSync(dir, { recursive: true, force: true });
409
+ });
410
+ it('exposes the six required adapter methods + identity fields', () => {
411
+ expect(pythonGraphAdapter.id).toBe('python');
412
+ expect(pythonGraphAdapter.fileExtensions).toContain('.py');
413
+ expect(pythonGraphAdapter.displayName).toBeDefined();
414
+ expect(typeof pythonGraphAdapter.discoverFiles).toBe('function');
415
+ expect(typeof pythonGraphAdapter.parseProject).toBe('function');
416
+ expect(typeof pythonGraphAdapter.walkProject).toBe('function');
417
+ expect(typeof pythonGraphAdapter.resolveCallSites).toBe('function');
418
+ expect(typeof pythonGraphAdapter.cacheKey).toBe('function');
419
+ });
420
+ it('I-1 — walkProject is deterministic across two runs over the same project', () => {
421
+ const a = buildPythonPipeline(pythonGraphAdapter, dir);
422
+ const b = buildPythonPipeline(pythonGraphAdapter, dir);
423
+ const ca = canonicalizeWalkOutput(a.walk);
424
+ const cb = canonicalizeWalkOutput(b.walk);
425
+ expect(cb.occurrences).toEqual(ca.occurrences);
426
+ expect(cb.callSiteSummary).toEqual(ca.callSiteSummary);
427
+ });
428
+ it('I-2 — different function bodies produce different bodyHashes', () => {
429
+ const { walk } = buildPythonPipeline(pythonGraphAdapter, dir);
430
+ const allOccs = [];
431
+ for (const arr of Object.values(walk.occurrences))
432
+ allOccs.push(...arr);
433
+ const byHash = new Map();
434
+ for (const o of allOccs)
435
+ byHash.set(o.bodyHash, (byHash.get(o.bodyHash) ?? 0) + 1);
436
+ const collisions = [...byHash.values()].filter((n) => n > 1);
437
+ expect(collisions).toHaveLength(0);
438
+ });
439
+ it('I-3 — every CallSiteRecord.ownerHash maps to a known occurrence', () => {
440
+ const { walk } = buildPythonPipeline(pythonGraphAdapter, dir);
441
+ const knownHashes = new Set();
442
+ for (const arr of Object.values(walk.occurrences)) {
443
+ for (const o of arr)
444
+ knownHashes.add(o.bodyHash);
445
+ }
446
+ for (const r of walk.callSites) {
447
+ expect(knownHashes.has(r.ownerHash)).toBe(true);
448
+ }
449
+ });
450
+ it('I-4 — resolveCallSites does not mutate the input catalog', () => {
451
+ const { walk, catalog, project } = buildPythonPipeline(pythonGraphAdapter, dir);
452
+ const before = JSON.stringify(catalog);
453
+ pythonGraphAdapter.resolveCallSites({
454
+ project,
455
+ catalog,
456
+ callSites: walk.callSites,
457
+ projectDirAbs: dir,
458
+ resolutionMode: 'exact',
459
+ });
460
+ const after = JSON.stringify(catalog);
461
+ expect(after).toBe(before);
462
+ });
463
+ it('I-5 — every CallEdge.to references a catalog bodyHash or is empty', () => {
464
+ const { walk, catalog, project } = buildPythonPipeline(pythonGraphAdapter, dir);
465
+ const knownHashes = new Set();
466
+ for (const arr of Object.values(catalog.functions)) {
467
+ for (const o of arr)
468
+ knownHashes.add(o.bodyHash);
469
+ }
470
+ const resolved = pythonGraphAdapter.resolveCallSites({
471
+ project,
472
+ catalog,
473
+ callSites: walk.callSites,
474
+ projectDirAbs: dir,
475
+ resolutionMode: 'exact',
476
+ });
477
+ for (const edges of resolved.edgesByOwner.values()) {
478
+ for (const e of edges) {
479
+ if (e.to.length === 0)
480
+ continue;
481
+ for (const target of e.to) {
482
+ expect(knownHashes.has(target)).toBe(true);
483
+ }
484
+ }
485
+ }
486
+ });
487
+ it('I-6 — cacheKey is stable for the same projectDir / configPath input', () => {
488
+ const { discovery } = buildPythonPipeline(pythonGraphAdapter, dir);
489
+ const k1 = pythonGraphAdapter.cacheKey({
490
+ projectDirAbs: discovery.projectDirAbs,
491
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
492
+ resolutionMode: 'exact',
493
+ });
494
+ const k2 = pythonGraphAdapter.cacheKey({
495
+ projectDirAbs: discovery.projectDirAbs,
496
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
497
+ resolutionMode: 'exact',
498
+ });
499
+ expect(k2).toBe(k1);
500
+ expect(k1.startsWith('py-')).toBe(true);
501
+ });
502
+ it('I-6 — cacheKey changes when the pyproject content changes', () => {
503
+ const { discovery } = buildPythonPipeline(pythonGraphAdapter, dir);
504
+ const before = pythonGraphAdapter.cacheKey({
505
+ projectDirAbs: discovery.projectDirAbs,
506
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
507
+ resolutionMode: 'exact',
508
+ });
509
+ if (discovery.configPathAbs) {
510
+ writeFileSync(discovery.configPathAbs, `[project]
511
+ name = "contract-fixture"
512
+ version = "0.2.0"
513
+ requires-python = ">=3.11"
514
+ `, 'utf8');
515
+ }
516
+ const after = pythonGraphAdapter.cacheKey({
517
+ projectDirAbs: discovery.projectDirAbs,
518
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
519
+ resolutionMode: 'exact',
520
+ });
521
+ expect(after).not.toBe(before);
522
+ });
523
+ it('I-7 — parseProject is total: every file is either parsed or in parseErrors', () => {
524
+ const discovery = pythonGraphAdapter.discoverFiles({ cwd: dir });
525
+ const parsed = pythonGraphAdapter.parseProject({
526
+ projectDirAbs: discovery.projectDirAbs,
527
+ files: discovery.files,
528
+ resolutionMode: 'exact',
529
+ });
530
+ const erroredFiles = new Set(parsed.parseErrors.map((e) => e.filePath));
531
+ for (const f of discovery.files) {
532
+ const inProject = parsed.project.files.has(f);
533
+ const rel = f.startsWith(discovery.projectDirAbs)
534
+ ? f.slice(discovery.projectDirAbs.length + 1)
535
+ : f;
536
+ const inErrors = erroredFiles.has(rel);
537
+ expect(inProject || inErrors).toBe(true);
538
+ }
539
+ });
540
+ it('I-8 — adapter id matches its handled language family', () => {
541
+ expect(pythonGraphAdapter.id).toBe('python');
542
+ const k = pythonGraphAdapter.cacheKey({ projectDirAbs: dir, resolutionMode: 'exact' });
543
+ expect(k).toMatch(/^py-/);
544
+ // Cross-adapter prefix isolation: the TS adapter must NEVER produce
545
+ // a key starting with `py-` and vice-versa. (I-8 backstop.)
546
+ expect(k.startsWith('ts-')).toBe(false);
547
+ });
548
+ it('I-9 — repeated discoverFiles calls return the same files list', () => {
549
+ const a = pythonGraphAdapter.discoverFiles({ cwd: dir });
550
+ const b = pythonGraphAdapter.discoverFiles({ cwd: dir });
551
+ expect(b.projectDirAbs).toBe(a.projectDirAbs);
552
+ expect([...b.files].sort()).toEqual([...a.files].sort());
553
+ expect(b.configPathAbs).toBe(a.configPathAbs);
554
+ });
555
+ it('walkProject emits the expected occurrence kinds for the fixture', () => {
556
+ const { walk } = buildPythonPipeline(pythonGraphAdapter, dir);
557
+ const kinds = new Set();
558
+ for (const arr of Object.values(walk.occurrences)) {
559
+ for (const o of arr)
560
+ kinds.add(o.kind);
561
+ }
562
+ expect(kinds.has('module-init')).toBe(true);
563
+ expect(kinds.has('function-declaration')).toBe(true);
564
+ expect(kinds.has('method')).toBe(true);
565
+ expect(kinds.has('constructor')).toBe(true);
566
+ expect(kinds.has('arrow')).toBe(true);
567
+ });
568
+ it('resolveCallSites produces non-empty edges for the fixture', () => {
569
+ const { walk, catalog, project } = buildPythonPipeline(pythonGraphAdapter, dir);
570
+ expect(walk.callSites.length).toBeGreaterThan(0);
571
+ const resolved = pythonGraphAdapter.resolveCallSites({
572
+ project,
573
+ catalog,
574
+ callSites: walk.callSites,
575
+ projectDirAbs: dir,
576
+ resolutionMode: 'exact',
577
+ });
578
+ let resolvedEdges = 0;
579
+ for (const edges of resolved.edgesByOwner.values()) {
580
+ for (const e of edges) {
581
+ if (e.to.length > 0)
582
+ resolvedEdges++;
583
+ }
584
+ }
585
+ expect(resolvedEdges).toBeGreaterThan(0);
586
+ // The Python adapter's name-based resolution produces medium/low —
587
+ // never high — for ordinary call edges. Creation edges (lambda) are
588
+ // the only `'high'` source.
589
+ const allConfidences = new Set();
590
+ for (const edges of resolved.edgesByOwner.values()) {
591
+ for (const e of edges)
592
+ allConfidences.add(e.confidence);
593
+ }
594
+ expect(allConfidences.has('high') || allConfidences.has('medium')).toBe(true);
595
+ });
596
+ });
597
+ // ── Rust adapter ────────────────────────────────────────────────────
598
+ const RS_FIXTURE_CARGO_TOML = `[package]
599
+ name = "contract-fixture"
600
+ version = "0.1.0"
601
+ edition = "2021"
602
+ `;
603
+ const RS_FIXTURE_FILES = {
604
+ 'src/main.rs': `mod util;
605
+
606
+ use util::{Greeter, helper};
607
+
608
+ fn entry(x: i32) -> String {
609
+ let g = Greeter::new("hello");
610
+ let msg = g.greet(x);
611
+ helper(&msg)
612
+ }
613
+
614
+ fn unused() {
615
+ println!("orphan");
616
+ }
617
+
618
+ fn main() {
619
+ let result = entry(7);
620
+ println!("{}", result);
621
+ }
622
+ `,
623
+ 'src/util.rs': `pub fn helper(value: &str) -> String {
624
+ format!("helper:{}", value)
625
+ }
626
+
627
+ pub struct Greeter {
628
+ prefix: String,
629
+ }
630
+
631
+ impl Greeter {
632
+ pub fn new(prefix: &str) -> Self {
633
+ Greeter { prefix: prefix.to_string() }
634
+ }
635
+
636
+ pub fn greet(&self, who: i32) -> String {
637
+ format!("{} {}", self.prefix, who)
638
+ }
639
+ }
640
+
641
+ pub fn make_adder() -> impl Fn(i32) -> i32 {
642
+ let inc = |n: i32| n + 1;
643
+ inc
644
+ }
645
+ `,
646
+ 'tests/integration_test.rs': `#[test]
647
+ fn helper_prepends_prefix() {
648
+ assert_eq!(1 + 1, 2);
649
+ }
650
+ `,
651
+ };
652
+ function setupRustFixture(dir) {
653
+ mkdirSync(dir, { recursive: true });
654
+ writeFileSync(join(dir, 'Cargo.toml'), RS_FIXTURE_CARGO_TOML, 'utf8');
655
+ for (const [rel, content] of Object.entries(RS_FIXTURE_FILES)) {
656
+ const p = join(dir, rel);
657
+ mkdirSync(p.slice(0, Math.max(0, p.lastIndexOf('/'))), { recursive: true });
658
+ writeFileSync(p, content, 'utf8');
659
+ }
660
+ }
661
+ function buildRustPipeline(adapter, dir) {
662
+ const discovery = adapter.discoverFiles({ cwd: dir });
663
+ const parsed = adapter.parseProject({
664
+ projectDirAbs: discovery.projectDirAbs,
665
+ files: discovery.files,
666
+ compilerOptions: discovery.compilerOptions,
667
+ resolutionMode: 'exact',
668
+ });
669
+ const walk = adapter.walkProject({
670
+ project: parsed.project,
671
+ projectDirAbs: discovery.projectDirAbs,
672
+ files: discovery.files,
673
+ });
674
+ const cacheKeyArgs = {
675
+ projectDirAbs: discovery.projectDirAbs,
676
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
677
+ resolutionMode: 'exact',
678
+ };
679
+ const catalog = {
680
+ version: '3.0',
681
+ tool: 'graph',
682
+ language: adapter.id,
683
+ builtAt: '2026-05-18T00:00:00.000Z',
684
+ cacheKey: adapter.cacheKey(cacheKeyArgs),
685
+ functions: walk.occurrences,
686
+ };
687
+ return { project: parsed.project, walk, catalog, discovery };
688
+ }
689
+ describe('GraphLanguageAdapter contract — Rust', () => {
690
+ let dir;
691
+ beforeEach(() => {
692
+ dir = mkdtempSync(join(tmpdir(), 'graph-contract-rs-'));
693
+ setupRustFixture(dir);
694
+ });
695
+ afterEach(() => {
696
+ rmSync(dir, { recursive: true, force: true });
697
+ });
698
+ it('exposes the six required adapter methods + identity fields', () => {
699
+ expect(rustGraphAdapter.id).toBe('rust');
700
+ expect(rustGraphAdapter.fileExtensions).toContain('.rs');
701
+ expect(rustGraphAdapter.displayName).toBeDefined();
702
+ expect(typeof rustGraphAdapter.discoverFiles).toBe('function');
703
+ expect(typeof rustGraphAdapter.parseProject).toBe('function');
704
+ expect(typeof rustGraphAdapter.walkProject).toBe('function');
705
+ expect(typeof rustGraphAdapter.resolveCallSites).toBe('function');
706
+ expect(typeof rustGraphAdapter.cacheKey).toBe('function');
707
+ });
708
+ it('I-1 — walkProject is deterministic across two runs over the same project', () => {
709
+ const a = buildRustPipeline(rustGraphAdapter, dir);
710
+ const b = buildRustPipeline(rustGraphAdapter, dir);
711
+ const ca = canonicalizeWalkOutput(a.walk);
712
+ const cb = canonicalizeWalkOutput(b.walk);
713
+ expect(cb.occurrences).toEqual(ca.occurrences);
714
+ expect(cb.callSiteSummary).toEqual(ca.callSiteSummary);
715
+ });
716
+ it('I-2 — different function bodies produce different bodyHashes', () => {
717
+ const { walk } = buildRustPipeline(rustGraphAdapter, dir);
718
+ const allOccs = [];
719
+ for (const arr of Object.values(walk.occurrences))
720
+ allOccs.push(...arr);
721
+ const byHash = new Map();
722
+ for (const o of allOccs)
723
+ byHash.set(o.bodyHash, (byHash.get(o.bodyHash) ?? 0) + 1);
724
+ const collisions = [...byHash.values()].filter((n) => n > 1);
725
+ expect(collisions).toHaveLength(0);
726
+ });
727
+ it('I-3 — every CallSiteRecord.ownerHash maps to a known occurrence', () => {
728
+ const { walk } = buildRustPipeline(rustGraphAdapter, dir);
729
+ const knownHashes = new Set();
730
+ for (const arr of Object.values(walk.occurrences)) {
731
+ for (const o of arr)
732
+ knownHashes.add(o.bodyHash);
733
+ }
734
+ for (const r of walk.callSites) {
735
+ expect(knownHashes.has(r.ownerHash)).toBe(true);
736
+ }
737
+ });
738
+ it('I-4 — resolveCallSites does not mutate the input catalog', () => {
739
+ const { walk, catalog, project } = buildRustPipeline(rustGraphAdapter, dir);
740
+ const before = JSON.stringify(catalog);
741
+ rustGraphAdapter.resolveCallSites({
742
+ project,
743
+ catalog,
744
+ callSites: walk.callSites,
745
+ projectDirAbs: dir,
746
+ resolutionMode: 'exact',
747
+ });
748
+ const after = JSON.stringify(catalog);
749
+ expect(after).toBe(before);
750
+ });
751
+ it('I-5 — every CallEdge.to references a catalog bodyHash or is empty', () => {
752
+ const { walk, catalog, project } = buildRustPipeline(rustGraphAdapter, dir);
753
+ const knownHashes = new Set();
754
+ for (const arr of Object.values(catalog.functions)) {
755
+ for (const o of arr)
756
+ knownHashes.add(o.bodyHash);
757
+ }
758
+ const resolved = rustGraphAdapter.resolveCallSites({
759
+ project,
760
+ catalog,
761
+ callSites: walk.callSites,
762
+ projectDirAbs: dir,
763
+ resolutionMode: 'exact',
764
+ });
765
+ for (const edges of resolved.edgesByOwner.values()) {
766
+ for (const e of edges) {
767
+ if (e.to.length === 0)
768
+ continue;
769
+ for (const target of e.to) {
770
+ expect(knownHashes.has(target)).toBe(true);
771
+ }
772
+ }
773
+ }
774
+ });
775
+ it('I-6 — cacheKey is stable for the same projectDir / configPath input', () => {
776
+ const { discovery } = buildRustPipeline(rustGraphAdapter, dir);
777
+ const k1 = rustGraphAdapter.cacheKey({
778
+ projectDirAbs: discovery.projectDirAbs,
779
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
780
+ resolutionMode: 'exact',
781
+ });
782
+ const k2 = rustGraphAdapter.cacheKey({
783
+ projectDirAbs: discovery.projectDirAbs,
784
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
785
+ resolutionMode: 'exact',
786
+ });
787
+ expect(k2).toBe(k1);
788
+ expect(k1.startsWith('rs-')).toBe(true);
789
+ });
790
+ it('I-6 — cacheKey changes when the Cargo manifest content changes', () => {
791
+ const { discovery } = buildRustPipeline(rustGraphAdapter, dir);
792
+ const before = rustGraphAdapter.cacheKey({
793
+ projectDirAbs: discovery.projectDirAbs,
794
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
795
+ resolutionMode: 'exact',
796
+ });
797
+ if (discovery.configPathAbs) {
798
+ writeFileSync(discovery.configPathAbs, `[package]
799
+ name = "contract-fixture"
800
+ version = "0.2.0"
801
+ edition = "2021"
802
+ `, 'utf8');
803
+ }
804
+ const after = rustGraphAdapter.cacheKey({
805
+ projectDirAbs: discovery.projectDirAbs,
806
+ ...(discovery.configPathAbs === undefined ? {} : { configPathAbs: discovery.configPathAbs }),
807
+ resolutionMode: 'exact',
808
+ });
809
+ expect(after).not.toBe(before);
810
+ });
811
+ it('I-7 — parseProject is total: every file is either parsed or in parseErrors', () => {
812
+ const discovery = rustGraphAdapter.discoverFiles({ cwd: dir });
813
+ const parsed = rustGraphAdapter.parseProject({
814
+ projectDirAbs: discovery.projectDirAbs,
815
+ files: discovery.files,
816
+ resolutionMode: 'exact',
817
+ });
818
+ const erroredFiles = new Set(parsed.parseErrors.map((e) => e.filePath));
819
+ for (const f of discovery.files) {
820
+ const inProject = parsed.project.files.has(f);
821
+ const rel = f.startsWith(discovery.projectDirAbs)
822
+ ? f.slice(discovery.projectDirAbs.length + 1)
823
+ : f;
824
+ const inErrors = erroredFiles.has(rel);
825
+ expect(inProject || inErrors).toBe(true);
826
+ }
827
+ });
828
+ it('I-8 — adapter id matches its handled language family', () => {
829
+ expect(rustGraphAdapter.id).toBe('rust');
830
+ const k = rustGraphAdapter.cacheKey({ projectDirAbs: dir, resolutionMode: 'exact' });
831
+ expect(k).toMatch(/^rs-/);
832
+ // Cross-adapter prefix isolation. (I-8 backstop.)
833
+ expect(k.startsWith('ts-')).toBe(false);
834
+ expect(k.startsWith('py-')).toBe(false);
835
+ });
836
+ it('I-9 — repeated discoverFiles calls return the same files list', () => {
837
+ const a = rustGraphAdapter.discoverFiles({ cwd: dir });
838
+ const b = rustGraphAdapter.discoverFiles({ cwd: dir });
839
+ expect(b.projectDirAbs).toBe(a.projectDirAbs);
840
+ expect([...b.files].sort()).toEqual([...a.files].sort());
841
+ expect(b.configPathAbs).toBe(a.configPathAbs);
842
+ });
843
+ it('walkProject emits the expected occurrence kinds for the fixture', () => {
844
+ const { walk } = buildRustPipeline(rustGraphAdapter, dir);
845
+ const kinds = new Set();
846
+ const enclosingClasses = new Set();
847
+ for (const arr of Object.values(walk.occurrences)) {
848
+ for (const o of arr) {
849
+ kinds.add(o.kind);
850
+ enclosingClasses.add(o.enclosingClass);
851
+ }
852
+ }
853
+ expect(kinds.has('module-init')).toBe(true);
854
+ expect(kinds.has('function-declaration')).toBe(true);
855
+ expect(kinds.has('method')).toBe(true);
856
+ expect(kinds.has('arrow')).toBe(true);
857
+ // The Greeter impl propagates an enclosingClass for the methods.
858
+ expect(enclosingClasses.has('Greeter')).toBe(true);
859
+ });
860
+ it('resolveCallSites produces non-empty edges for the fixture', () => {
861
+ const { walk, catalog, project } = buildRustPipeline(rustGraphAdapter, dir);
862
+ expect(walk.callSites.length).toBeGreaterThan(0);
863
+ const resolved = rustGraphAdapter.resolveCallSites({
864
+ project,
865
+ catalog,
866
+ callSites: walk.callSites,
867
+ projectDirAbs: dir,
868
+ resolutionMode: 'exact',
869
+ });
870
+ let resolvedEdges = 0;
871
+ for (const edges of resolved.edgesByOwner.values()) {
872
+ for (const e of edges) {
873
+ if (e.to.length > 0)
874
+ resolvedEdges++;
875
+ }
876
+ }
877
+ expect(resolvedEdges).toBeGreaterThan(0);
878
+ });
879
+ });
880
+ //# sourceMappingURL=lang-adapter-contract.test.js.map