heaplens-cli 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +43 -2
  3. package/THIRD_PARTY_NOTICES.json +2199 -0
  4. package/bin/heaplens.cjs +72 -0
  5. package/distribution.json +1754 -0
  6. package/licenses/d3-LICENSE.txt +12 -0
  7. package/licenses/rust/aho-corasick-1.1.4/COPYING +3 -0
  8. package/licenses/rust/aho-corasick-1.1.4/LICENSE-MIT +21 -0
  9. package/licenses/rust/anstream-0.6.21/LICENSE-APACHE +202 -0
  10. package/licenses/rust/anstream-0.6.21/LICENSE-MIT +19 -0
  11. package/licenses/rust/anstyle-1.0.13/LICENSE-APACHE +202 -0
  12. package/licenses/rust/anstyle-1.0.13/LICENSE-MIT +19 -0
  13. package/licenses/rust/anstyle-parse-0.2.7/LICENSE-APACHE +202 -0
  14. package/licenses/rust/anstyle-parse-0.2.7/LICENSE-MIT +19 -0
  15. package/licenses/rust/anstyle-query-1.1.5/LICENSE-APACHE +202 -0
  16. package/licenses/rust/anstyle-query-1.1.5/LICENSE-MIT +19 -0
  17. package/licenses/rust/anyhow-1.0.100/LICENSE-APACHE +176 -0
  18. package/licenses/rust/anyhow-1.0.100/LICENSE-MIT +23 -0
  19. package/licenses/rust/arrayvec-0.5.2/LICENSE-APACHE +201 -0
  20. package/licenses/rust/arrayvec-0.5.2/LICENSE-MIT +25 -0
  21. package/licenses/rust/arrayvec-0.7.8/LICENSE-APACHE +201 -0
  22. package/licenses/rust/arrayvec-0.7.8/LICENSE-MIT +25 -0
  23. package/licenses/rust/atomic-waker-1.1.2/LICENSE-APACHE +201 -0
  24. package/licenses/rust/atomic-waker-1.1.2/LICENSE-MIT +23 -0
  25. package/licenses/rust/atomic-waker-1.1.2/LICENSE-THIRD-PARTY +45 -0
  26. package/licenses/rust/autocfg-1.5.1/LICENSE-APACHE +201 -0
  27. package/licenses/rust/autocfg-1.5.1/LICENSE-MIT +25 -0
  28. package/licenses/rust/base64-0.22.1/LICENSE-APACHE +201 -0
  29. package/licenses/rust/base64-0.22.1/LICENSE-MIT +21 -0
  30. package/licenses/rust/base64-0.23.1/LICENSE-APACHE +201 -0
  31. package/licenses/rust/base64-0.23.1/LICENSE-MIT +21 -0
  32. package/licenses/rust/bitflags-1.3.2/LICENSE-APACHE +201 -0
  33. package/licenses/rust/bitflags-1.3.2/LICENSE-MIT +25 -0
  34. package/licenses/rust/bitflags-2.10.0/LICENSE-APACHE +201 -0
  35. package/licenses/rust/bitflags-2.10.0/LICENSE-MIT +25 -0
  36. package/licenses/rust/blake3-1.8.7/LICENSE_A2 +202 -0
  37. package/licenses/rust/blake3-1.8.7/LICENSE_A2LLVM +219 -0
  38. package/licenses/rust/blake3-1.8.7/LICENSE_CC0 +121 -0
  39. package/licenses/rust/bytes-1.11.0/LICENSE +25 -0
  40. package/licenses/rust/cc-1.6.0/LICENSE-APACHE +201 -0
  41. package/licenses/rust/cc-1.6.0/LICENSE-MIT +25 -0
  42. package/licenses/rust/cfg-if-1.0.4/LICENSE-APACHE +201 -0
  43. package/licenses/rust/cfg-if-1.0.4/LICENSE-MIT +25 -0
  44. package/licenses/rust/cfg_aliases-0.2.2/LICENSE +9 -0
  45. package/licenses/rust/cfg_aliases-0.2.2/NOTICES.md +26 -0
  46. package/licenses/rust/chacha20-0.10.2/LICENSE-APACHE +201 -0
  47. package/licenses/rust/chacha20-0.10.2/LICENSE-MIT +25 -0
  48. package/licenses/rust/chrono-0.4.45/LICENSE.txt +240 -0
  49. package/licenses/rust/clap-4.5.60/LICENSE-APACHE +202 -0
  50. package/licenses/rust/clap-4.5.60/LICENSE-MIT +19 -0
  51. package/licenses/rust/clap_builder-4.5.60/LICENSE-APACHE +202 -0
  52. package/licenses/rust/clap_builder-4.5.60/LICENSE-MIT +19 -0
  53. package/licenses/rust/clap_derive-4.5.55/LICENSE-APACHE +202 -0
  54. package/licenses/rust/clap_derive-4.5.55/LICENSE-MIT +19 -0
  55. package/licenses/rust/clap_lex-1.0.0/LICENSE-APACHE +202 -0
  56. package/licenses/rust/clap_lex-1.0.0/LICENSE-MIT +19 -0
  57. package/licenses/rust/colorchoice-1.0.4/LICENSE-APACHE +202 -0
  58. package/licenses/rust/colorchoice-1.0.4/LICENSE-MIT +19 -0
  59. package/licenses/rust/constant_time_eq-0.4.2/LICENSE-APACHE +176 -0
  60. package/licenses/rust/constant_time_eq-0.4.2/LICENSE-CC0 +121 -0
  61. package/licenses/rust/constant_time_eq-0.4.2/LICENSE-MIT0 +14 -0
  62. package/licenses/rust/core-foundation-sys-0.8.7/LICENSE-APACHE +201 -0
  63. package/licenses/rust/core-foundation-sys-0.8.7/LICENSE-MIT +25 -0
  64. package/licenses/rust/crossbeam-deque-0.8.6/LICENSE-APACHE +201 -0
  65. package/licenses/rust/crossbeam-deque-0.8.6/LICENSE-MIT +27 -0
  66. package/licenses/rust/crossbeam-epoch-0.9.18/LICENSE-APACHE +201 -0
  67. package/licenses/rust/crossbeam-epoch-0.9.18/LICENSE-MIT +27 -0
  68. package/licenses/rust/crossbeam-utils-0.8.21/LICENSE-APACHE +201 -0
  69. package/licenses/rust/crossbeam-utils-0.8.21/LICENSE-MIT +27 -0
  70. package/licenses/rust/displaydoc-0.2.7/LICENSE-APACHE +201 -0
  71. package/licenses/rust/displaydoc-0.2.7/LICENSE-MIT +23 -0
  72. package/licenses/rust/either-1.15.0/LICENSE-APACHE +201 -0
  73. package/licenses/rust/either-1.15.0/LICENSE-MIT +25 -0
  74. package/licenses/rust/env_filter-0.1.4/LICENSE-APACHE +202 -0
  75. package/licenses/rust/env_filter-0.1.4/LICENSE-MIT +19 -0
  76. package/licenses/rust/env_logger-0.11.8/LICENSE-APACHE +202 -0
  77. package/licenses/rust/env_logger-0.11.8/LICENSE-MIT +19 -0
  78. package/licenses/rust/equivalent-1.0.2/LICENSE-APACHE +201 -0
  79. package/licenses/rust/equivalent-1.0.2/LICENSE-MIT +25 -0
  80. package/licenses/rust/errno-0.3.14/LICENSE-APACHE +201 -0
  81. package/licenses/rust/errno-0.3.14/LICENSE-MIT +25 -0
  82. package/licenses/rust/fastrand-2.3.0/LICENSE-APACHE +201 -0
  83. package/licenses/rust/fastrand-2.3.0/LICENSE-MIT +23 -0
  84. package/licenses/rust/find-msvc-tools-0.1.14/LICENSE-APACHE +201 -0
  85. package/licenses/rust/find-msvc-tools-0.1.14/LICENSE-MIT +25 -0
  86. package/licenses/rust/fixedbitset-0.4.2/LICENSE-APACHE +201 -0
  87. package/licenses/rust/fixedbitset-0.4.2/LICENSE-MIT +25 -0
  88. package/licenses/rust/form_urlencoded-1.2.2/LICENSE-APACHE +201 -0
  89. package/licenses/rust/form_urlencoded-1.2.2/LICENSE-MIT +25 -0
  90. package/licenses/rust/futures-channel-0.3.34/LICENSE-APACHE +202 -0
  91. package/licenses/rust/futures-channel-0.3.34/LICENSE-MIT +26 -0
  92. package/licenses/rust/futures-core-0.3.34/LICENSE-APACHE +202 -0
  93. package/licenses/rust/futures-core-0.3.34/LICENSE-MIT +26 -0
  94. package/licenses/rust/futures-io-0.3.34/LICENSE-APACHE +202 -0
  95. package/licenses/rust/futures-io-0.3.34/LICENSE-MIT +26 -0
  96. package/licenses/rust/futures-sink-0.3.34/LICENSE-APACHE +202 -0
  97. package/licenses/rust/futures-sink-0.3.34/LICENSE-MIT +26 -0
  98. package/licenses/rust/futures-task-0.3.34/LICENSE-APACHE +202 -0
  99. package/licenses/rust/futures-task-0.3.34/LICENSE-MIT +26 -0
  100. package/licenses/rust/futures-util-0.3.34/LICENSE-APACHE +202 -0
  101. package/licenses/rust/futures-util-0.3.34/LICENSE-MIT +26 -0
  102. package/licenses/rust/getrandom-0.2.17/LICENSE-APACHE +201 -0
  103. package/licenses/rust/getrandom-0.2.17/LICENSE-MIT +26 -0
  104. package/licenses/rust/getrandom-0.3.4/LICENSE-APACHE +201 -0
  105. package/licenses/rust/getrandom-0.3.4/LICENSE-MIT +26 -0
  106. package/licenses/rust/getrandom-0.4.3/LICENSE-APACHE +201 -0
  107. package/licenses/rust/getrandom-0.4.3/LICENSE-MIT +26 -0
  108. package/licenses/rust/getset-0.0.9/LICENSE +21 -0
  109. package/licenses/rust/hashbrown-0.16.1/LICENSE-APACHE +201 -0
  110. package/licenses/rust/hashbrown-0.16.1/LICENSE-MIT +25 -0
  111. package/licenses/rust/heck-0.3.3/LICENSE-APACHE +201 -0
  112. package/licenses/rust/heck-0.3.3/LICENSE-MIT +25 -0
  113. package/licenses/rust/heck-0.5.0/LICENSE-APACHE +201 -0
  114. package/licenses/rust/heck-0.5.0/LICENSE-MIT +25 -0
  115. package/licenses/rust/http-1.5.0/LICENSE-APACHE +201 -0
  116. package/licenses/rust/http-1.5.0/LICENSE-MIT +25 -0
  117. package/licenses/rust/http-body-1.1.0/LICENSE +25 -0
  118. package/licenses/rust/http-body-util-0.1.5/LICENSE +25 -0
  119. package/licenses/rust/httparse-1.10.1/LICENSE-APACHE +201 -0
  120. package/licenses/rust/httparse-1.10.1/LICENSE-MIT +20 -0
  121. package/licenses/rust/hyper-1.12.0/LICENSE +19 -0
  122. package/licenses/rust/hyper-rustls-0.27.10/LICENSE-APACHE +201 -0
  123. package/licenses/rust/hyper-rustls-0.27.10/LICENSE-ISC +15 -0
  124. package/licenses/rust/hyper-rustls-0.27.10/LICENSE-MIT +25 -0
  125. package/licenses/rust/hyper-util-0.1.21/LICENSE +19 -0
  126. package/licenses/rust/iana-time-zone-0.1.65/LICENSE-APACHE +201 -0
  127. package/licenses/rust/iana-time-zone-0.1.65/LICENSE-MIT +25 -0
  128. package/licenses/rust/icu_collections-2.3.0/LICENSE +46 -0
  129. package/licenses/rust/icu_locale_core-2.3.0/LICENSE +46 -0
  130. package/licenses/rust/icu_normalizer-2.3.0/LICENSE +46 -0
  131. package/licenses/rust/icu_normalizer_data-2.3.0/LICENSE +46 -0
  132. package/licenses/rust/icu_properties-2.3.0/LICENSE +46 -0
  133. package/licenses/rust/icu_properties_data-2.3.0/LICENSE +46 -0
  134. package/licenses/rust/icu_provider-2.3.1/LICENSE +46 -0
  135. package/licenses/rust/idna-1.1.0/LICENSE-APACHE +201 -0
  136. package/licenses/rust/idna-1.1.0/LICENSE-MIT +25 -0
  137. package/licenses/rust/idna_adapter-1.2.2/LICENSE-APACHE +201 -0
  138. package/licenses/rust/idna_adapter-1.2.2/LICENSE-MIT +25 -0
  139. package/licenses/rust/indexmap-2.13.0/LICENSE-APACHE +201 -0
  140. package/licenses/rust/indexmap-2.13.0/LICENSE-MIT +25 -0
  141. package/licenses/rust/ipnet-2.12.2/LICENSE-APACHE +201 -0
  142. package/licenses/rust/ipnet-2.12.2/LICENSE-MIT +7 -0
  143. package/licenses/rust/is_terminal_polyfill-1.70.2/LICENSE-APACHE +202 -0
  144. package/licenses/rust/is_terminal_polyfill-1.70.2/LICENSE-MIT +19 -0
  145. package/licenses/rust/itoa-1.0.17/LICENSE-APACHE +176 -0
  146. package/licenses/rust/itoa-1.0.17/LICENSE-MIT +23 -0
  147. package/licenses/rust/jiff-0.2.18/COPYING +3 -0
  148. package/licenses/rust/jiff-0.2.18/LICENSE-MIT +21 -0
  149. package/licenses/rust/jvm-hprof-0.1.0/LICENSE.txt +36 -0
  150. package/licenses/rust/lexical-core-0.7.6/LICENSE-APACHE +201 -0
  151. package/licenses/rust/lexical-core-0.7.6/LICENSE-MIT +23 -0
  152. package/licenses/rust/libc-0.2.180/LICENSE-APACHE +176 -0
  153. package/licenses/rust/libc-0.2.180/LICENSE-MIT +25 -0
  154. package/licenses/rust/litemap-0.8.3/LICENSE +46 -0
  155. package/licenses/rust/lock_api-0.4.14/LICENSE-APACHE +201 -0
  156. package/licenses/rust/lock_api-0.4.14/LICENSE-MIT +25 -0
  157. package/licenses/rust/log-0.4.29/LICENSE-APACHE +201 -0
  158. package/licenses/rust/log-0.4.29/LICENSE-MIT +25 -0
  159. package/licenses/rust/lru-slab-0.1.3/LICENSE-APACHE +202 -0
  160. package/licenses/rust/lru-slab-0.1.3/LICENSE-MIT +7 -0
  161. package/licenses/rust/lru-slab-0.1.3/LICENSE-ZLIB +19 -0
  162. package/licenses/rust/memchr-2.7.6/COPYING +3 -0
  163. package/licenses/rust/memchr-2.7.6/LICENSE-MIT +21 -0
  164. package/licenses/rust/memmap2-0.9.9/LICENSE-APACHE +201 -0
  165. package/licenses/rust/memmap2-0.9.9/LICENSE-MIT +26 -0
  166. package/licenses/rust/mio-1.1.1/LICENSE +19 -0
  167. package/licenses/rust/nom-5.1.3/LICENSE +20 -0
  168. package/licenses/rust/num-traits-0.2.19/LICENSE-APACHE +201 -0
  169. package/licenses/rust/num-traits-0.2.19/LICENSE-MIT +25 -0
  170. package/licenses/rust/once_cell-1.21.3/LICENSE-APACHE +201 -0
  171. package/licenses/rust/once_cell-1.21.3/LICENSE-MIT +23 -0
  172. package/licenses/rust/parking_lot-0.12.5/LICENSE-APACHE +201 -0
  173. package/licenses/rust/parking_lot-0.12.5/LICENSE-MIT +25 -0
  174. package/licenses/rust/parking_lot_core-0.9.12/LICENSE-APACHE +201 -0
  175. package/licenses/rust/parking_lot_core-0.9.12/LICENSE-MIT +25 -0
  176. package/licenses/rust/percent-encoding-2.3.2/LICENSE-APACHE +201 -0
  177. package/licenses/rust/percent-encoding-2.3.2/LICENSE-MIT +25 -0
  178. package/licenses/rust/petgraph-0.6.5/LICENSE-APACHE +201 -0
  179. package/licenses/rust/petgraph-0.6.5/LICENSE-MIT +25 -0
  180. package/licenses/rust/pin-project-lite-0.2.16/LICENSE-APACHE +177 -0
  181. package/licenses/rust/pin-project-lite-0.2.16/LICENSE-MIT +23 -0
  182. package/licenses/rust/potential_utf-0.1.6/LICENSE +46 -0
  183. package/licenses/rust/proc-macro2-1.0.105/LICENSE-APACHE +176 -0
  184. package/licenses/rust/proc-macro2-1.0.105/LICENSE-MIT +23 -0
  185. package/licenses/rust/quinn-0.11.12/LICENSE-APACHE +201 -0
  186. package/licenses/rust/quinn-0.11.12/LICENSE-MIT +7 -0
  187. package/licenses/rust/quinn-proto-0.11.19/LICENSE-APACHE +201 -0
  188. package/licenses/rust/quinn-proto-0.11.19/LICENSE-MIT +7 -0
  189. package/licenses/rust/quinn-udp-0.5.16/LICENSE-APACHE +201 -0
  190. package/licenses/rust/quinn-udp-0.5.16/LICENSE-MIT +7 -0
  191. package/licenses/rust/quote-1.0.47/LICENSE-APACHE +176 -0
  192. package/licenses/rust/quote-1.0.47/LICENSE-MIT +23 -0
  193. package/licenses/rust/rand-0.10.3/COPYRIGHT +12 -0
  194. package/licenses/rust/rand-0.10.3/LICENSE-APACHE +176 -0
  195. package/licenses/rust/rand-0.10.3/LICENSE-MIT +26 -0
  196. package/licenses/rust/rand_core-0.10.1/COPYRIGHT +9 -0
  197. package/licenses/rust/rand_core-0.10.1/LICENSE-APACHE +187 -0
  198. package/licenses/rust/rand_core-0.10.1/LICENSE-MIT +25 -0
  199. package/licenses/rust/rand_pcg-0.10.2/COPYRIGHT +12 -0
  200. package/licenses/rust/rand_pcg-0.10.2/LICENSE-APACHE +187 -0
  201. package/licenses/rust/rand_pcg-0.10.2/LICENSE-MIT +26 -0
  202. package/licenses/rust/rayon-1.11.0/LICENSE-APACHE +201 -0
  203. package/licenses/rust/rayon-1.11.0/LICENSE-MIT +25 -0
  204. package/licenses/rust/rayon-core-1.13.0/LICENSE-APACHE +201 -0
  205. package/licenses/rust/rayon-core-1.13.0/LICENSE-MIT +25 -0
  206. package/licenses/rust/regex-1.12.2/LICENSE-APACHE +201 -0
  207. package/licenses/rust/regex-1.12.2/LICENSE-MIT +25 -0
  208. package/licenses/rust/regex-automata-0.4.13/LICENSE-APACHE +201 -0
  209. package/licenses/rust/regex-automata-0.4.13/LICENSE-MIT +25 -0
  210. package/licenses/rust/regex-syntax-0.8.8/LICENSE-APACHE +201 -0
  211. package/licenses/rust/regex-syntax-0.8.8/LICENSE-MIT +25 -0
  212. package/licenses/rust/reqwest-0.12.28/LICENSE-APACHE +201 -0
  213. package/licenses/rust/reqwest-0.12.28/LICENSE-MIT +20 -0
  214. package/licenses/rust/ring-0.17.14/LICENSE +9 -0
  215. package/licenses/rust/ring-0.17.14/LICENSE-BoringSSL +272 -0
  216. package/licenses/rust/ring-0.17.14/LICENSE-other-bits +13 -0
  217. package/licenses/rust/rustc-hash-2.1.3/LICENSE-APACHE +176 -0
  218. package/licenses/rust/rustc-hash-2.1.3/LICENSE-MIT +23 -0
  219. package/licenses/rust/rustix-1.1.3/COPYRIGHT +29 -0
  220. package/licenses/rust/rustix-1.1.3/LICENSE-APACHE +201 -0
  221. package/licenses/rust/rustix-1.1.3/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  222. package/licenses/rust/rustix-1.1.3/LICENSE-MIT +23 -0
  223. package/licenses/rust/rustls-0.23.45/LICENSE-APACHE +201 -0
  224. package/licenses/rust/rustls-0.23.45/LICENSE-ISC +15 -0
  225. package/licenses/rust/rustls-0.23.45/LICENSE-MIT +25 -0
  226. package/licenses/rust/rustls-pki-types-1.15.1/LICENSE-APACHE +201 -0
  227. package/licenses/rust/rustls-pki-types-1.15.1/LICENSE-MIT +25 -0
  228. package/licenses/rust/rustls-webpki-0.103.15/LICENSE +19 -0
  229. package/licenses/rust/ryu-1.0.22/LICENSE-APACHE +176 -0
  230. package/licenses/rust/ryu-1.0.22/LICENSE-BOOST +23 -0
  231. package/licenses/rust/scopeguard-1.2.0/LICENSE-APACHE +201 -0
  232. package/licenses/rust/scopeguard-1.2.0/LICENSE-MIT +25 -0
  233. package/licenses/rust/serde-1.0.228/LICENSE-APACHE +176 -0
  234. package/licenses/rust/serde-1.0.228/LICENSE-MIT +23 -0
  235. package/licenses/rust/serde_core-1.0.228/LICENSE-APACHE +176 -0
  236. package/licenses/rust/serde_core-1.0.228/LICENSE-MIT +23 -0
  237. package/licenses/rust/serde_derive-1.0.228/LICENSE-APACHE +176 -0
  238. package/licenses/rust/serde_derive-1.0.228/LICENSE-MIT +23 -0
  239. package/licenses/rust/serde_json-1.0.149/LICENSE-APACHE +176 -0
  240. package/licenses/rust/serde_json-1.0.149/LICENSE-MIT +23 -0
  241. package/licenses/rust/serde_urlencoded-0.7.1/LICENSE-APACHE +176 -0
  242. package/licenses/rust/serde_urlencoded-0.7.1/LICENSE-MIT +25 -0
  243. package/licenses/rust/shlex-2.0.1/LICENSE-APACHE +13 -0
  244. package/licenses/rust/shlex-2.0.1/LICENSE-MIT +21 -0
  245. package/licenses/rust/signal-hook-registry-1.4.8/LICENSE-APACHE +201 -0
  246. package/licenses/rust/signal-hook-registry-1.4.8/LICENSE-MIT +25 -0
  247. package/licenses/rust/slab-0.4.12/LICENSE +25 -0
  248. package/licenses/rust/smallvec-1.15.1/LICENSE-APACHE +201 -0
  249. package/licenses/rust/smallvec-1.15.1/LICENSE-MIT +25 -0
  250. package/licenses/rust/socket2-0.6.1/LICENSE-APACHE +201 -0
  251. package/licenses/rust/socket2-0.6.1/LICENSE-MIT +25 -0
  252. package/licenses/rust/stable_deref_trait-1.2.1/LICENSE-APACHE +201 -0
  253. package/licenses/rust/stable_deref_trait-1.2.1/LICENSE-MIT +25 -0
  254. package/licenses/rust/static_assertions-1.1.0/LICENSE-APACHE +202 -0
  255. package/licenses/rust/static_assertions-1.1.0/LICENSE-MIT +21 -0
  256. package/licenses/rust/strsim-0.11.1/LICENSE +23 -0
  257. package/licenses/rust/strum-0.20.0/LICENSE +21 -0
  258. package/licenses/rust/strum_macros-0.20.1/LICENSE +21 -0
  259. package/licenses/rust/subtle-2.6.1/LICENSE +29 -0
  260. package/licenses/rust/syn-1.0.109/LICENSE-APACHE +201 -0
  261. package/licenses/rust/syn-1.0.109/LICENSE-MIT +23 -0
  262. package/licenses/rust/syn-2.0.114/LICENSE-APACHE +176 -0
  263. package/licenses/rust/syn-2.0.114/LICENSE-MIT +23 -0
  264. package/licenses/rust/syn-3.0.6/LICENSE-APACHE +176 -0
  265. package/licenses/rust/syn-3.0.6/LICENSE-MIT +23 -0
  266. package/licenses/rust/sync_wrapper-1.0.2/LICENSE +177 -0
  267. package/licenses/rust/synstructure-0.14.0/LICENSE +7 -0
  268. package/licenses/rust/tempfile-3.24.0/LICENSE-APACHE +201 -0
  269. package/licenses/rust/tempfile-3.24.0/LICENSE-MIT +25 -0
  270. package/licenses/rust/thiserror-1.0.69/LICENSE-APACHE +176 -0
  271. package/licenses/rust/thiserror-1.0.69/LICENSE-MIT +23 -0
  272. package/licenses/rust/thiserror-2.0.21/LICENSE-APACHE +176 -0
  273. package/licenses/rust/thiserror-2.0.21/LICENSE-MIT +23 -0
  274. package/licenses/rust/thiserror-impl-1.0.69/LICENSE-APACHE +176 -0
  275. package/licenses/rust/thiserror-impl-1.0.69/LICENSE-MIT +23 -0
  276. package/licenses/rust/thiserror-impl-2.0.21/LICENSE-APACHE +176 -0
  277. package/licenses/rust/thiserror-impl-2.0.21/LICENSE-MIT +23 -0
  278. package/licenses/rust/tinystr-0.8.4/LICENSE +46 -0
  279. package/licenses/rust/tinyvec-1.13.3/LICENSE-APACHE.md +202 -0
  280. package/licenses/rust/tinyvec-1.13.3/LICENSE-MIT.md +5 -0
  281. package/licenses/rust/tinyvec-1.13.3/LICENSE-ZLIB.md +11 -0
  282. package/licenses/rust/tokio-1.49.0/LICENSE +21 -0
  283. package/licenses/rust/tokio-macros-2.6.0/LICENSE +22 -0
  284. package/licenses/rust/tokio-rustls-0.26.6/LICENSE-APACHE +201 -0
  285. package/licenses/rust/tokio-rustls-0.26.6/LICENSE-MIT +25 -0
  286. package/licenses/rust/tower-0.5.3/LICENSE +25 -0
  287. package/licenses/rust/tower-http-0.6.11/LICENSE +25 -0
  288. package/licenses/rust/tower-layer-0.3.3/LICENSE +25 -0
  289. package/licenses/rust/tower-service-0.3.3/LICENSE +25 -0
  290. package/licenses/rust/tracing-0.1.44/LICENSE +25 -0
  291. package/licenses/rust/tracing-core-0.1.36/LICENSE +25 -0
  292. package/licenses/rust/try-lock-0.2.5/LICENSE +21 -0
  293. package/licenses/rust/unicode-ident-1.0.22/LICENSE-APACHE +176 -0
  294. package/licenses/rust/unicode-ident-1.0.22/LICENSE-MIT +23 -0
  295. package/licenses/rust/unicode-ident-1.0.22/LICENSE-UNICODE +39 -0
  296. package/licenses/rust/unicode-segmentation-1.12.0/COPYRIGHT +7 -0
  297. package/licenses/rust/unicode-segmentation-1.12.0/LICENSE-APACHE +201 -0
  298. package/licenses/rust/unicode-segmentation-1.12.0/LICENSE-MIT +25 -0
  299. package/licenses/rust/untrusted-0.9.0/LICENSE.txt +13 -0
  300. package/licenses/rust/url-2.5.8/LICENSE-APACHE +201 -0
  301. package/licenses/rust/url-2.5.8/LICENSE-MIT +25 -0
  302. package/licenses/rust/utf8_iter-1.0.4/COPYRIGHT +42 -0
  303. package/licenses/rust/utf8_iter-1.0.4/LICENSE-APACHE +202 -0
  304. package/licenses/rust/utf8_iter-1.0.4/LICENSE-MIT +25 -0
  305. package/licenses/rust/utf8parse-0.2.2/LICENSE-APACHE +176 -0
  306. package/licenses/rust/utf8parse-0.2.2/LICENSE-MIT +25 -0
  307. package/licenses/rust/version_check-0.9.5/LICENSE-APACHE +201 -0
  308. package/licenses/rust/version_check-0.9.5/LICENSE-MIT +19 -0
  309. package/licenses/rust/want-0.3.2/LICENSE +20 -0
  310. package/licenses/rust/webpki-roots-1.0.9/LICENSE +61 -0
  311. package/licenses/rust/writeable-0.6.4/LICENSE +46 -0
  312. package/licenses/rust/yoke-0.8.3/LICENSE +46 -0
  313. package/licenses/rust/yoke-derive-0.8.4/LICENSE +46 -0
  314. package/licenses/rust/zerofrom-0.1.8/LICENSE +46 -0
  315. package/licenses/rust/zerofrom-derive-0.1.8/LICENSE +46 -0
  316. package/licenses/rust/zeroize-1.9.1/LICENSE-APACHE +202 -0
  317. package/licenses/rust/zeroize-1.9.1/LICENSE-MIT +25 -0
  318. package/licenses/rust/zerotrie-0.2.5/LICENSE +46 -0
  319. package/licenses/rust/zerovec-0.11.8/LICENSE +46 -0
  320. package/licenses/rust/zerovec-derive-0.11.6/LICENSE +46 -0
  321. package/licenses/rust/zmij-1.0.14/LICENSE-MIT +23 -0
  322. package/package.json +33 -4
  323. package/runtime/bin/heaplens +0 -0
  324. package/runtime/bin/hprof-server +0 -0
  325. package/runtime/browser-ui/ai-actions.cjs +126 -0
  326. package/runtime/browser-ui/ai.cjs +121 -0
  327. package/runtime/browser-ui/build/fix-system-prompt.txt +17 -0
  328. package/runtime/browser-ui/build/index.html +7605 -0
  329. package/runtime/browser-ui/build/prompts.cjs +188 -0
  330. package/runtime/browser-ui/build/providers.json +65 -0
  331. package/runtime/browser-ui/build/system-prompt.txt +41 -0
  332. package/runtime/browser-ui/consent-actions.cjs +25 -0
  333. package/runtime/browser-ui/export-actions.cjs +47 -0
  334. package/runtime/browser-ui/guards.cjs +8 -0
  335. package/runtime/browser-ui/monitor-actions.cjs +37 -0
  336. package/runtime/browser-ui/monitor.cjs +73 -0
  337. package/runtime/browser-ui/object-actions.cjs +60 -0
  338. package/runtime/browser-ui/progressive-actions.cjs +6 -0
  339. package/runtime/browser-ui/rpc.cjs +115 -0
  340. package/runtime/browser-ui/server.cjs +209 -0
  341. package/runtime/browser-ui/session.cjs +184 -0
  342. package/runtime/browser-ui/snapshot-actions.cjs +38 -0
  343. package/runtime/browser-ui/source-actions.cjs +38 -0
  344. package/runtime/browser-ui/sources.cjs +72 -0
  345. package/runtime/browser-ui/telemetry-actions.cjs +39 -0
  346. package/runtime/browser-ui/waste-actions.cjs +20 -0
  347. package/runtime/package.json +1 -0
  348. package/runtime/telemetry/contract.cjs +31 -0
  349. package/runtime/telemetry/contract.json +60 -0
  350. package/runtime/telemetry/node.cjs +62 -0
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+ const { spawn } = require('node:child_process');
3
+ const { EventEmitter } = require('node:events');
4
+ // Preserve wide integers as decimal strings. Existing renderer actions reject
5
+ // unsafe IDs rather than selecting an adjacent object after JSON rounding.
6
+ function parseExact(text) {
7
+ let out = '', quoted = false, escaped = false;
8
+ for (let i = 0; i < text.length;) {
9
+ const c = text[i];
10
+ if (quoted) {
11
+ out += c;
12
+ i++;
13
+ if (escaped)
14
+ escaped = false;
15
+ else if (c === '\\')
16
+ escaped = true;
17
+ else if (c === '"')
18
+ quoted = false;
19
+ continue;
20
+ }
21
+ if (c === '"') {
22
+ quoted = true;
23
+ out += c;
24
+ i++;
25
+ continue;
26
+ }
27
+ if (c === '-' || /[0-9]/.test(c)) {
28
+ const match = text.slice(i).match(/^-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/);
29
+ if (!match)
30
+ throw Error('Invalid JSON');
31
+ const n = match[0];
32
+ out += !/[.eE]/.test(n) && (BigInt(n) > 9007199254740991n || BigInt(n) < -9007199254740991n) ? JSON.stringify(n) : n;
33
+ i += n.length;
34
+ }
35
+ else {
36
+ out += c;
37
+ i++;
38
+ }
39
+ }
40
+ return JSON.parse(out);
41
+ }
42
+ class Rpc extends EventEmitter {
43
+ constructor(executable, legacy = false) {
44
+ super();
45
+ this.pending = new Map();
46
+ this.seq = 0;
47
+ this.buffer = '';
48
+ this.closed = false;
49
+ this.child = spawn(executable, legacy ? ['--legacy'] : [], { stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
50
+ this.child.stderr.on('data', () => { }); // Never forward raw heap paths/parser bytes.
51
+ this.child.stdin.on('error', () => this.fail());
52
+ this.child.on('error', () => this.fail());
53
+ this.child.on('exit', () => this.fail());
54
+ this.child.stdout.setEncoding('utf8');
55
+ this.child.stdout.on('data', text => {
56
+ if (this.closed)
57
+ return;
58
+ this.buffer += text;
59
+ if (Buffer.byteLength(this.buffer) > 64 * 1024 * 1024)
60
+ return this.fail();
61
+ let end;
62
+ while ((end = this.buffer.indexOf('\n')) !== -1) {
63
+ const line = this.buffer.slice(0, end);
64
+ this.buffer = this.buffer.slice(end + 1);
65
+ try {
66
+ const message = parseExact(line);
67
+ if (message.id !== undefined) {
68
+ const p = this.pending.get(message.id);
69
+ if (!p)
70
+ continue;
71
+ this.pending.delete(message.id);
72
+ clearTimeout(p.timer);
73
+ message.error ? p.reject(Error('Engine request failed. Check syntax or retry.')) : p.resolve(message.result);
74
+ }
75
+ else if (message.method)
76
+ this.emit('notification', message);
77
+ }
78
+ catch {
79
+ return this.fail();
80
+ }
81
+ }
82
+ });
83
+ }
84
+ request(method, params, timeout = 30000) {
85
+ if (this.closed)
86
+ return Promise.reject(Error('Server unavailable. Retry.'));
87
+ const id = ++this.seq;
88
+ const promise = new Promise((resolve, reject) => {
89
+ const timer = setTimeout(() => { this.pending.delete(id); reject(Error('Engine request did not reply. Retry or simplify the query.')); }, timeout);
90
+ this.pending.set(id, { resolve, reject, timer });
91
+ this.child.stdin.write(serialize({ jsonrpc: '2.0', id, method, params }, method === 'compare_snapshots') + '\n');
92
+ });
93
+ return { id, promise };
94
+ }
95
+ call(method, params, timeout) { const r = this.request(method, params, timeout); return r.promise || r; }
96
+ fail() { if (this.closed)
97
+ return; this.closed = true; for (const p of this.pending.values()) {
98
+ clearTimeout(p.timer);
99
+ p.reject(Error('Server unavailable. Retry.'));
100
+ } this.pending.clear(); this.child.kill(); this.emit('unavailable'); }
101
+ close() { this.removeAllListeners(); this.fail(); }
102
+ }
103
+ // Only typed engine numeric fields are emitted as exact unquoted decimals.
104
+ // Ordinary heap/user strings (including digits) remain JSON strings.
105
+ const integerFields = new Set(['object_id', 'total_heap_size', 'reachable_heap_size', 'total_instances', 'total_classes', 'total_arrays', 'total_gc_roots', 'instance_count', 'shallow_size', 'retained_size', 'total_wasted_bytes', 'duplicate_string_wasted_bytes', 'empty_collection_wasted_bytes', 'over_allocated_wasted_bytes', 'boxed_primitive_wasted_bytes']);
106
+ function serialize(value, exact = false, key = '') {
107
+ if (exact && integerFields.has(key) && typeof value === 'string' && /^(0|[1-9]\d*)$/.test(value) && BigInt(value) <= 18446744073709551615n)
108
+ return value;
109
+ if (Array.isArray(value))
110
+ return '[' + value.map(x => serialize(x, exact)).join(',') + ']';
111
+ if (value && typeof value === 'object')
112
+ return '{' + Object.entries(value).filter(([, v]) => v !== undefined).map(([k, v]) => JSON.stringify(k) + ':' + serialize(v, exact, k)).join(',') + '}';
113
+ return JSON.stringify(value);
114
+ }
115
+ module.exports = { Rpc, parseExact, serialize };
@@ -0,0 +1,209 @@
1
+ 'use strict';
2
+ const http = require('node:http'), fs = require('node:fs/promises'), sync = require('node:fs'), path = require('node:path'), crypto = require('node:crypto');
3
+ const { spawn } = require('node:child_process');
4
+ const { Sources } = require('./sources.cjs'), { Session } = require('./session.cjs');
5
+ const {Telemetry}=require('../telemetry/node.cjs');
6
+ const {platform,architecture}=require('../telemetry/contract.cjs');
7
+ class BrowserHost {
8
+ constructor(options) {
9
+ this.server = options.server;
10
+ this.telemetry=new Telemetry({context:{host:'browser',version:require('../package.json').version,os:platform(process.platform),arch:architecture(process.arch)},
11
+ level:options.telemetry??'all',disabled:!!process.env.CI || process.env.DO_NOT_TRACK==='1',...(options.telemetrySend?{send:options.telemetrySend}:{})});
12
+ this.legacy = options.legacy;
13
+ this.quickScan = !!options.quickScan;
14
+ this.sourceRoots = options.roots || [];
15
+ this.sources = new Sources(this.sourceRoots);
16
+ this.sessions = new Map();
17
+ this.queue = Promise.resolve();
18
+ this.token = crypto.randomBytes(32).toString('base64url');
19
+ this.stopped = false;
20
+ this.lastSeen = Date.now();
21
+ this.seen = false;
22
+ const assets = options.assets || path.join(__dirname, 'build');
23
+ this.html = sync.readFileSync(path.join(assets, 'index.html'), 'utf8');
24
+ this.providers = JSON.parse(sync.readFileSync(path.join(assets, 'providers.json'), 'utf8'));
25
+ this.prompts = require(path.join(assets, 'prompts.cjs'));
26
+ options.files.forEach(file => { const session = new Session(this, file); this.sessions.set(session.id, session); });
27
+ }
28
+ available() { return [...this.sessions.values()].filter(s => s.state === 'ready' && s.snapshot); }
29
+ changed() { for (const s of this.sessions.values())
30
+ if (s.state !== 'closed')
31
+ s.emit('snapshotsChanged'); }
32
+ slot(work) { const result = this.queue.then(() => this.stopped ? undefined : work()); this.queue = result.catch(() => { }); return result; }
33
+ async listen(port = 0) {
34
+ this.http = http.createServer((req, res) => this.handle(req, res));
35
+ this.http.requestTimeout = 15000;
36
+ this.http.headersTimeout = 10000;
37
+ this.http.maxHeadersCount = 32;
38
+ await new Promise((resolve, reject) => { this.http.once('error', reject); this.http.listen(port, '127.0.0.1', resolve); });
39
+ this.port = this.http.address().port;
40
+ this.origin = 'http://127.0.0.1:' + this.port;
41
+ this.idle = setInterval(() => { if (Date.now() - this.lastSeen > (this.seen ? 90000 : 300000))
42
+ this.shutdown(); }, 5000);
43
+ this.idle.unref();
44
+ return this;
45
+ }
46
+ url(id = [...this.sessions.keys()][0]) { return this.origin + '/dump/' + id + '#token=' + this.token; }
47
+ headers(res) { res.setHeader('Cache-Control', 'no-store'); res.setHeader('Referrer-Policy', 'no-referrer'); res.setHeader('X-Content-Type-Options', 'nosniff'); res.setHeader('X-Frame-Options', 'DENY'); res.setHeader('Cross-Origin-Resource-Policy', 'same-origin'); res.setHeader('Content-Security-Policy', "frame-ancestors 'none'; base-uri 'none'; form-action 'none'"); }
48
+ reply(res, status, data) { if (res.destroyed || res.writableEnded)
49
+ return; this.headers(res); res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' }); res.end(JSON.stringify(data)); }
50
+ authenticated(req) { const raw = req.headers['x-heaplens-token']; if (typeof raw !== 'string' || raw.length !== this.token.length)
51
+ return false; return crypto.timingSafeEqual(Buffer.from(raw), Buffer.from(this.token)); }
52
+ async handle(req, res) {
53
+ try {
54
+ if (req.headers.host !== '127.0.0.1:' + this.port)
55
+ return this.reply(res, 403, { error: 'Invalid host' });
56
+ const url = new URL(req.url, this.origin);
57
+ if (url.origin !== this.origin || url.searchParams.has('token'))
58
+ return this.reply(res, 403, { error: 'Invalid request' });
59
+ if (req.method === 'GET' && /^\/dump\/[a-f0-9-]{36}$/.test(url.pathname) && this.sessions.get(url.pathname.split('/').at(-1))?.state !== 'closed' && this.sessions.has(url.pathname.split('/').at(-1))) {
60
+ const nonce = crypto.randomBytes(24).toString('base64');
61
+ this.headers(res);
62
+ res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
63
+ res.end(this.html.replaceAll('__NONCE__', nonce));
64
+ return;
65
+ }
66
+ if (!url.pathname.startsWith('/api/') || !this.authenticated(req) || req.headers.origin && req.headers.origin !== this.origin || req.headers['sec-fetch-site'] && req.headers['sec-fetch-site'] !== 'same-origin' && req.headers['sec-fetch-site'] !== 'none' || req.method === 'POST' && req.headers.origin !== this.origin)
67
+ return this.reply(res, 403, { error: 'Unauthorized local request' });
68
+ this.lastSeen = Date.now();
69
+ this.seen = true;
70
+ if (req.method === 'GET' && url.pathname === '/api/catalog')
71
+ return this.reply(res, 200, { dumps: [...this.sessions.values()].filter(s => s.state !== 'closed').map(s => ({ id: s.id, label: s.file.label })) });
72
+ if (req.method === 'GET' && url.pathname === '/api/events') {
73
+ const session = this.sessions.get(url.searchParams.get('session')), after = Number(url.searchParams.get('after'));
74
+ if (!session || session.view !== req.headers['x-heaplens-view'] || !Number.isSafeInteger(after) || after < 0 || after > session.seq)
75
+ return this.reply(res, 400, { error: 'Invalid event cursor or retired page' });
76
+ while (session.events.length && session.events[0].seq <= after)
77
+ session.eventBytes -= session.events.shift().bytes;
78
+ const reset = after > 0 && session.events.length && after < session.events[0].seq - 1;
79
+ return this.reply(res, 200, { reset: !!reset, cursor: session.seq, events: reset ? [] : session.events.map(e => ({ seq: e.seq, message: e.message })) });
80
+ }
81
+ if (req.method === 'POST' && url.pathname === '/api/command') {
82
+ const body = await this.body(req);
83
+ const session = this.sessions.get(body.session);
84
+ if (!session || !body.message || !session.commands.has(body.message.command))
85
+ return this.reply(res, 400, { error: 'Unsupported action' });
86
+ // Do not keep an HTTP request open for an analysis, approval or stream.
87
+ if (body.message.command === 'ready')
88
+ session.attach(req.headers['x-heaplens-view']);
89
+ else if (session.view !== req.headers['x-heaplens-view'])
90
+ return this.reply(res, 403, { error: 'Retired page' });
91
+ const generation = session.generation, view = session.view;
92
+ // Acknowledge before running a side effect: shutdown must not
93
+ // destroy its own HTTP response or strand the launcher's client.
94
+ res.once('finish', () => setImmediate(() => {
95
+ if (this.stopped || session.view !== view)
96
+ return;
97
+ session.dispatch(body.message).catch(() => {
98
+ if (session.state !== 'closed' && session.generation === generation && session.view === view)
99
+ session.emit('localActionStatus', { message: 'Action rejected or failed. Check limits and analysis status.' });
100
+ });
101
+ }));
102
+ this.reply(res, 202, { accepted: true });
103
+ return;
104
+ }
105
+ this.reply(res, 404, { error: 'Not found' });
106
+ }
107
+ catch {
108
+ this.reply(res, 400, { error: 'Invalid local request' });
109
+ }
110
+ }
111
+ async body(req) { if (req.headers['content-type'] !== 'application/json')
112
+ throw Error('Expected JSON'); let bytes = 0; const chunks = []; for await (const chunk of req) {
113
+ bytes += chunk.length;
114
+ if (bytes > 9 * 1024 * 1024)
115
+ throw Error('Request exceeds limit');
116
+ chunks.push(chunk);
117
+ } return JSON.parse(Buffer.concat(chunks).toString('utf8')); }
118
+ async shutdown() {
119
+ if (this.stopped)
120
+ return;
121
+ this.stopped = true;
122
+ this.telemetry.dispose();
123
+ clearInterval(this.idle);
124
+ for (const session of this.sessions.values()) {
125
+ session.emit('browserStopped');
126
+ session.close();
127
+ }
128
+ await new Promise(resolve => { if (!this.http?.listening)
129
+ return resolve(); this.http.close(resolve); this.http.closeAllConnections(); });
130
+ }
131
+ }
132
+ function options(argv) {
133
+ const out = { roots: [], files: [], legacy: false, port: 0, browser: true, telemetry:'all' };
134
+ for (let i = 0; i < argv.length; i++) {
135
+ const flag = argv[i];
136
+ if (flag === '--legacy')
137
+ out.legacy = true;
138
+ else if (flag === '--quick-scan')
139
+ out.quickScan = true;
140
+ else if (flag === '--no-browser')
141
+ out.browser = false;
142
+ else if (['--server', '--source-root', '--port', '--dump', '--telemetry'].includes(flag)) {
143
+ const value = argv[++i];
144
+ if (!value)
145
+ throw Error('Missing argument');
146
+ if(flag==='--telemetry') {
147
+ if(!['off','error','all'].includes(value))throw Error('Invalid telemetry choice');
148
+ out.telemetry=value;
149
+ } else if (flag === '--server')
150
+ out.server = value;
151
+ else if (flag === '--source-root')
152
+ out.roots.push(value);
153
+ else if (flag === '--port')
154
+ out.port = Number(value);
155
+ else
156
+ out.files.push(value);
157
+ }
158
+ else
159
+ throw Error('Unknown argument');
160
+ }
161
+ if (!out.server || out.files.length < 1 || out.files.length > 8 || out.roots.length > 16 || !Number.isInteger(out.port) || out.port < 0 || out.port > 65535)
162
+ throw Error('Invalid arguments');
163
+ return out;
164
+ }
165
+ async function main(argv) {
166
+ let host;
167
+ try {
168
+ const args = options(argv), files = [];
169
+ for (const input of args.files) {
170
+ const file = await fs.realpath(input), stat = await fs.stat(file);
171
+ if (!stat.isFile() || !stat.size || files.some(f => f.path === file))
172
+ throw Error('Invalid input');
173
+ files.push({ path: file, label: path.basename(file), size: stat.size, mtime: stat.mtimeMs });
174
+ }
175
+ args.server = await fs.realpath(args.server);
176
+ if (!(await fs.stat(args.server)).isFile())
177
+ throw Error('Invalid server');
178
+ args.roots = await Promise.all(args.roots.map(async (root) => { const real = await fs.realpath(root); if (!(await fs.stat(real)).isDirectory())
179
+ throw Error('Invalid source root'); return real; }));
180
+ host = await new BrowserHost({ ...args, files }).listen(args.port);
181
+ const launch = host.url();
182
+ process.stdout.write('HeapLens local browser (keep this terminal running):\n' + launch + '\nThis launch URL is a private session capability. Do not share it. Ctrl+C stops owned servers.\n');
183
+ if (args.browser) {
184
+ const launcher = process.platform === 'darwin' ? ['open', [launch]] : process.platform === 'win32' ? ['rundll32', ['url.dll,FileProtocolHandler', launch]] : ['xdg-open', [launch]];
185
+ const browser = spawn(launcher[0], launcher[1], { stdio: 'ignore', windowsHide: true });
186
+ browser.on('error', () => process.stderr.write('Could not open a browser. Paste the private launch URL above.\n'));
187
+ }
188
+ for (const signal of ['SIGINT', 'SIGTERM'])
189
+ process.once(signal, () => host.shutdown());
190
+ // Retire the service if its foreground CLI parent exits unexpectedly.
191
+ const parent = process.ppid;
192
+ const check = setInterval(() => { try {
193
+ process.kill(parent, 0);
194
+ }
195
+ catch {
196
+ clearInterval(check);
197
+ host.shutdown();
198
+ } }, 3000);
199
+ check.unref();
200
+ }
201
+ catch {
202
+ await host?.shutdown();
203
+ process.stderr.write('Cannot start local browser. Check Node runtime, built browser assets, server binary, dump/source paths and port.\n');
204
+ process.exitCode = 3;
205
+ }
206
+ }
207
+ if (require.main === module)
208
+ main(process.argv.slice(2));
209
+ module.exports = { BrowserHost, options, main };
@@ -0,0 +1,184 @@
1
+ 'use strict';
2
+ const crypto = require('node:crypto');
3
+ const { AsyncLocalStorage } = require('node:async_hooks');
4
+ const { Rpc } = require('./rpc.cjs');
5
+ const { requestId, objectId } = require('./guards.cjs');
6
+ const capabilities = ["object-actions", "consent-actions", "source-actions", "ai-actions", "snapshot-actions", "export-actions", "monitor-actions", "telemetry-actions", "progressive-actions", "waste-actions"].map(name => require('./' + name + '.cjs'));
7
+ const rename = { summary: 'summary', top_objects: 'topObjects', top_layers: 'topLayers', class_histogram: 'classHistogram', leak_suspects: 'leakSuspects', object_leak_suspects: 'objectLeakSuspects', waste_analysis: 'wasteAnalysis', storage: 'storage' };
8
+ // Immutable compact snapshots, not a second heap graph or comparator.
9
+ function snapshot(raw) {
10
+ const summary = Object.fromEntries(['total_heap_size', 'reachable_heap_size', 'total_instances', 'total_classes', 'total_arrays', 'total_gc_roots'].map(k => [k, raw.summary[k]]));
11
+ const waste = raw.waste_analysis ? Object.fromEntries(['total_wasted_bytes', 'waste_percentage', 'duplicate_string_wasted_bytes', 'empty_collection_wasted_bytes', 'over_allocated_wasted_bytes', 'boxed_primitive_wasted_bytes'].map(k => [k, raw.waste_analysis[k]])) : null;
12
+ const value = { summary, class_histogram: raw.class_histogram, leak_suspects: raw.leak_suspects, waste_analysis: waste };
13
+ if (Buffer.byteLength(JSON.stringify(value)) > 8 * 1024 * 1024)
14
+ throw Error('Comparison metadata exceeds 8 MiB');
15
+ return value;
16
+ }
17
+ class Session {
18
+ constructor(host, file) {
19
+ this.host = host;
20
+ this.file = file;
21
+ this.id = crypto.randomUUID();
22
+ this.generation = 0;
23
+ this.events = [];
24
+ this.seq = 0;
25
+ this.eventBytes = 0;
26
+ this.state = 'idle';
27
+ this.raw = null;
28
+ this.mode = host.quickScan ? 'shallow' : 'full';
29
+ this.history = [];
30
+ this.config = null;
31
+ this.chatGrant = null;
32
+ this.pendingConsent = null;
33
+ this.activeAi = null;
34
+ this.actionContext = new AsyncLocalStorage();
35
+ // New host capabilities extend this registry instead of changing analysis classes.
36
+ this.commands = new Map(Object.entries({ ready: m => this.ready(), retryAnalysis: m => this.analyze(), cancelAnalysis: m => this.cancel(), closeDump: m => this.close(), shutdown: m => this.host.shutdown() }));
37
+ for (const capability of capabilities)
38
+ capability.install(this);
39
+ }
40
+ purge() { this.events = []; this.eventBytes = 0; }
41
+ attach(view) { if (typeof view !== 'string' || !/^[a-f0-9-]{36}$/.test(view))
42
+ throw Error('Invalid page session'); if (this.view !== view) {
43
+ this.view = view;
44
+ this.stopAi();
45
+ this.monitor?.close();
46
+ this.monitor = null;
47
+ this.chatGrant = null;
48
+ this.purge();
49
+ } }
50
+ emit(command, data = {}) {
51
+ const action = this.actionContext.getStore();
52
+ if (action && (action.view !== this.view || action.generation !== this.generation))
53
+ return;
54
+ const message = { command, ...data };
55
+ try { require('./telemetry-actions.cjs').observe(this,command,data); } catch { /* Diagnostics cannot fail UI delivery. */ }
56
+ const bytes = Buffer.byteLength(JSON.stringify(message));
57
+ if (bytes > 64 * 1024 * 1024)
58
+ throw Error('UI response exceeds 64 MiB');
59
+ this.events.push({ seq: ++this.seq, message, bytes });
60
+ this.eventBytes += bytes;
61
+ while (this.events.length > 256 || this.eventBytes > 64 * 1024 * 1024) {
62
+ this.eventBytes -= this.events.shift().bytes;
63
+ }
64
+ }
65
+ async dispatch(m) {
66
+ if (!m || typeof m !== 'object' || Array.isArray(m) || !this.commands.has(m.command) || this.state === 'closed' && !['ready', 'shutdown'].includes(m.command))
67
+ throw Error('Unsupported action');
68
+ const action = this.commands.get(m.command);
69
+ if (['ready', 'retryAnalysis', 'quickScan', 'analyzeRetained', 'cancelAnalysis', 'closeDump', 'shutdown'].includes(m.command))
70
+ await this.actionContext.run(undefined, () => action(m));
71
+ else
72
+ await this.actionContext.run({ view: this.view, generation: this.generation }, () => action(m));
73
+ }
74
+ status(message) { this.emit('browserStatus', { message }); }
75
+ ready() {
76
+ if (this.raw) {
77
+ if(this.mode === 'shallow') this.emit('shallowScanComplete', {summary:this.raw.summary,classHistogram:this.raw.class_histogram,readiness:this.raw.readiness});
78
+ else this.emit('analysisComplete', { ...Object.fromEntries(Object.entries(rename).map(([a, b]) => [b, this.raw[a]])), displayName: this.file.label });
79
+ this.status('READY | server PID ' + this.rpc?.child.pid);
80
+ this.emit('aiHistory', { messages: this.history });
81
+ }
82
+ else if (this.state === 'idle')
83
+ this.analyze();
84
+ else
85
+ this.status(this.state === 'closed' ? 'Dump closed. Start a new CLI session to reopen.' : this.state.toUpperCase() + ' | server PID ' + (this.rpc?.child.pid || 'pending'));
86
+ }
87
+ async analyze(mode = this.mode) {
88
+ if (['loading', 'queued'].includes(this.state))
89
+ return;
90
+ const generation = ++this.generation;
91
+ this.mode = mode;
92
+ const shallow = mode === 'shallow';
93
+ this.stopAi();
94
+ this.chatGrant = null;
95
+ this.history = [];
96
+ this.monitor?.close();
97
+ this.raw = null;
98
+ this.snapshot = null;
99
+ this.rpc?.close();
100
+ this.purge();
101
+ this.state = 'queued';
102
+ this.host.changed();
103
+ this.emit('analysisStarting',{analysisScope:mode});
104
+ this.emit('analysisProgress', { stage: 'loading', phase: 1, totalPhases: 4, fileMetadata: { file_size: this.file.size } });
105
+ this.status('Waiting for analysis slot. Other dumps remain available.');
106
+ await this.host.slot(async () => {
107
+ if (generation !== this.generation || this.state === 'closed')
108
+ return;
109
+ this.state = 'loading';
110
+ this.rpc = new Rpc(this.host.server, this.host.legacy);
111
+ const rpc = this.rpc;
112
+ this.status('ANALYZING | server PID ' + rpc.child.pid);
113
+ rpc.on('unavailable', () => { if (generation !== this.generation)
114
+ return; this.state = 'failed'; this.raw = null; this.snapshot = null; this.stopAi(); this.host.changed(); this.emit('serverCrashed', { message: 'Analysis server unavailable. Retry starts a new server.' }); this.status('SERVER UNAVAILABLE | Retry'); this.finishAnalysis?.(); });
115
+ let finish;
116
+ const completed = new Promise(resolve => { finish = resolve; });
117
+ this.finishAnalysis = finish;
118
+ rpc.on('notification', message => {
119
+ if (generation !== this.generation || this.state !== 'loading' || message.params?.request_id !== this.analysisId)
120
+ return;
121
+ const p = message.params;
122
+ if (message.method === (shallow ? 'heap_scan_progress' : 'heap_analysis_progress'))
123
+ this.emit('analysisProgress', { stage: p.stage, phase: p.phase, totalPhases: p.total_phases, summary: p.summary, snapshot: p.snapshot, fileMetadata: p.file_metadata });
124
+ if (message.method === (shallow ? 'heap_scan_complete' : 'heap_analysis_complete')) {
125
+ if (p.status === 'completed') {
126
+ this.raw = p;
127
+ this.state = shallow ? 'shallow-ready' : 'ready';
128
+ try {
129
+ this.snapshot = shallow ? null : snapshot(p);
130
+ }
131
+ catch {
132
+ this.snapshot = null;
133
+ this.status('READY. Comparison metadata exceeds its limit.');
134
+ }
135
+ this.ready();
136
+ this.host.changed();
137
+ }
138
+ else {
139
+ this.state = p.status === 'cancelled' ? 'cancelled' : 'failed';
140
+ this.raw = null;
141
+ this.snapshot = null;
142
+ this.emit(p.status === 'cancelled' ? 'analysisCancelled' : 'serverCrashed', { message: 'Analysis failed or was cancelled. Retry.' });
143
+ this.status(this.state.toUpperCase() + ' | Retry');
144
+ }
145
+ finish();
146
+ }
147
+ });
148
+ try {
149
+ const started = rpc.request(shallow ? 'scan_heap' : 'analyze_heap', { path: this.file.path }, 30000);
150
+ this.analysisId = started.id;
151
+ await started.promise;
152
+ await completed;
153
+ }
154
+ catch {
155
+ if (generation === this.generation) {
156
+ this.state = 'failed';
157
+ this.emit('serverCrashed', { message: 'Server did not acknowledge analysis. Retry.' });
158
+ rpc.close();
159
+ }
160
+ finish();
161
+ }
162
+ finally {
163
+ if (this.finishAnalysis === finish)
164
+ this.finishAnalysis = null;
165
+ }
166
+ });
167
+ }
168
+ async cancel() {
169
+ if (this.state === 'queued') {
170
+ this.generation++;
171
+ this.state = 'cancelled';
172
+ this.emit('analysisCancelled');
173
+ this.status('CANCELLED | Retry');
174
+ return;
175
+ }
176
+ if (this.state === 'loading')
177
+ await this.rpc.call('cancel_analysis', { path: this.file.path, analysis_request_id: this.analysisId }).catch(() => { });
178
+ }
179
+ async engine(method, params) { if (this.state !== 'ready' || !this.rpc)
180
+ throw Error('Wait for analysis or Retry.'); const gen = this.generation; const result = await this.rpc.call(method, { ...params, path: this.file.path }); if (gen !== this.generation || this.state !== 'ready')
181
+ throw Error('Analysis changed. Repeat the action.'); return result; }
182
+ close() { this.generation++; this.state = 'closed'; this.stopAi(); this.config = null; this.history = []; this.raw = null; this.snapshot = null; this.rpc?.close(); this.monitor?.close(); this.finishAnalysis?.(); this.purge(); this.host.changed(); this.emit('analysisCancelled'); this.status('Dump closed; server stopped. Other dumps are unaffected.'); }
183
+ }
184
+ module.exports = { Session, snapshot, requestId, objectId };
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+ const { requestId } = require('./guards.cjs');
3
+ const actions = {
4
+ list(all) { const rows = this.host.available().filter(s => all || s !== this); this.emit(all ? 'allAnalyzedFiles' : 'analyzedFiles', { files: rows.map(s => s.id), labels: Object.fromEntries(rows.map(s => [s.id, s.file.label])) }); },
5
+ async compare(m) {
6
+ const id = requestId(m);
7
+ try {
8
+ const baseline = this.host.sessions.get(m.baselinePath);
9
+ if (!baseline?.snapshot || !this.snapshot || baseline === this)
10
+ throw Error();
11
+ const a = baseline.snapshot, b = this.snapshot;
12
+ const result = await this.engine('compare_snapshots', { baseline: a, current: b, baseline_label: baseline.file.label, current_label: this.file.label });
13
+ if (a !== baseline.snapshot || b !== this.snapshot)
14
+ throw Error();
15
+ this.emit('compareResult', { requestId: id, result, sizeModelsDiffer: JSON.stringify(baseline.raw.summary.size_model) !== JSON.stringify(this.raw.summary.size_model) });
16
+ }
17
+ catch {
18
+ this.emit('compareError', { requestId: id, error: 'Choose two currently analyzed dumps with comparison metadata below 8 MiB.' });
19
+ }
20
+ },
21
+ timeline(m) {
22
+ const id = requestId(m);
23
+ try {
24
+ if (!Array.isArray(m.paths) || m.paths.length < 2 || m.paths.length > 8 || new Set(m.paths).size !== m.paths.length)
25
+ throw Error();
26
+ const selected = m.paths.map(id => { const s = this.host.sessions.get(id); if (!s?.snapshot)
27
+ throw Error(); return s; }).sort((a, b) => a.file.mtime - b.file.mtime);
28
+ const tracked = new Set(selected.flatMap(s => s.snapshot.class_histogram.slice(0, 10).map(x => x.class_name)));
29
+ this.emit('timelineDataResponse', { requestId: id, sizeModelsDiffer: new Set(selected.map(s => JSON.stringify(s.raw.summary.size_model))).size > 1, result: { snapshots: selected.map(s => ({ path: s.file.label, timestamp: s.file.mtime / 1000, summary: s.snapshot.summary, size_model: s.raw.summary.size_model, top_classes: s.snapshot.class_histogram.filter(x => tracked.has(x.class_name)) })) } });
30
+ }
31
+ catch {
32
+ this.emit('timelineError', { requestId: id, error: 'Select 2–8 analyzed dumps. Time order is file modification time, not necessarily capture time.' });
33
+ }
34
+ }
35
+ };
36
+ module.exports.install = session => { for (const [name, action] of Object.entries(actions))
37
+ session[name] = action.bind(session); for (const [command, handler] of Object.entries({ listAnalyzedFiles: m => session.list(false), listAllAnalyzedFiles: m => session.list(true), compareHeaps: m => session.compare(m), getTimelineData: m => session.timeline(m) }))
38
+ session.commands.set(command, handler); };
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+ const { requestId } = require('./guards.cjs');
3
+ const actions = {
4
+ async selectSource(name, signal) {
5
+ const matches = await this.host.sources.find(name);
6
+ if (!matches.length)
7
+ throw Error('No matching Java file under --source-root. Add the source folder when launching.');
8
+ if (matches.length === 1)
9
+ return matches[0];
10
+ const selected = await this.consent('Choose source', 'Several files match. Select a local file; no network request is made.', matches.map((f, i) => [f.label + ' (root ' + f.rootIndex + ')', String(i)]).concat([['Cancel', null]]), signal);
11
+ return selected === null ? null : matches[Number(selected)];
12
+ },
13
+ async source(m) {
14
+ const id = requestId(m), gen = this.generation;
15
+ try {
16
+ if (this.state !== 'ready')
17
+ throw Error();
18
+ const file = await this.selectSource(m.className);
19
+ if (gen !== this.generation)
20
+ return;
21
+ if (!file) {
22
+ this.emit('sourceNavigationResult', { requestId: id, className: m.className, status: 'cancelled' });
23
+ return;
24
+ }
25
+ const text = await this.host.sources.read(file);
26
+ if (gen !== this.generation)
27
+ return;
28
+ this.emit('browserSource', { label: file.label, text });
29
+ this.emit('sourceNavigationResult', { requestId: id, className: m.className, status: 'opened' });
30
+ }
31
+ catch {
32
+ this.emit('sourceNavigationResult', { requestId: id, className: m.className, status: 'not-found' });
33
+ this.emit('localActionStatus', { message: 'Source unavailable. Supply --source-root. Dependency-JAR lookup/decompilation is not bundled.' });
34
+ }
35
+ }
36
+ };
37
+ module.exports.install = session => { for (const [name, action] of Object.entries(actions))
38
+ session[name] = action.bind(session); session.commands.set('openProjectSource', m => session.source(m)); };