@js-recon/js-recon 1.4.3 → 2.0.1-alpha.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 (461) hide show
  1. package/.claude-sync/sync.sh +1 -1
  2. package/.github/workflows/build-and-prettify.yaml +55 -25
  3. package/.github/workflows/codeql.yml +33 -0
  4. package/.github/workflows/exploit-smoke-test.yaml +160 -0
  5. package/.github/workflows/framework-detection-smoke-test.yaml +135 -0
  6. package/.github/workflows/plumber.yml +20 -0
  7. package/.github/workflows/promote-js-recon.yml +5 -3
  8. package/.github/workflows/publish-js-recon.yml +6 -1
  9. package/.github/workflows/rules-smoke-test.yaml +15 -7
  10. package/.nvmrc +1 -0
  11. package/.plumber.yaml +151 -0
  12. package/CHANGELOG.md +54 -0
  13. package/CONTRIBUTING.md +32 -0
  14. package/Dockerfile +1 -0
  15. package/README.md +55 -2
  16. package/SECURITY.md +1 -1
  17. package/build/analyze/engine/astEngine.js +10 -9
  18. package/build/analyze/engine/astEngine.js.map +1 -1
  19. package/build/analyze/engine/csMastSEngine.js +6 -6
  20. package/build/analyze/engine/csMastSEngine.js.map +1 -1
  21. package/build/analyze/engine/index.js +1 -1
  22. package/build/analyze/engine/index.js.map +1 -1
  23. package/build/analyze/engine/requestEngine.js +6 -6
  24. package/build/analyze/engine/requestEngine.js.map +1 -1
  25. package/build/analyze/helpers/determineCompatibleVersion.js +70 -0
  26. package/build/analyze/helpers/determineCompatibleVersion.js.map +1 -0
  27. package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js +9 -3
  28. package/build/analyze/helpers/engineHelpers/findMemberExpressionAssignment.js.map +1 -1
  29. package/build/analyze/helpers/initRules.js +42 -24
  30. package/build/analyze/helpers/initRules.js.map +1 -1
  31. package/build/analyze/helpers/outputHelper.js +3 -3
  32. package/build/analyze/helpers/outputHelper.js.map +1 -1
  33. package/build/analyze/helpers/schemas.js +1 -0
  34. package/build/analyze/helpers/schemas.js.map +1 -1
  35. package/build/analyze/helpers/validate.js +9 -5
  36. package/build/analyze/helpers/validate.js.map +1 -1
  37. package/build/analyze/index.js +26 -16
  38. package/build/analyze/index.js.map +1 -1
  39. package/build/cliProgram.js +710 -0
  40. package/build/cliProgram.js.map +1 -0
  41. package/build/completion/index.js +245 -339
  42. package/build/completion/index.js.map +1 -1
  43. package/build/completion/introspect.js +17 -0
  44. package/build/completion/introspect.js.map +1 -0
  45. package/build/config/applicationConfig.js +494 -0
  46. package/build/config/applicationConfig.js.map +1 -0
  47. package/build/cs_mast/index.js +18 -17
  48. package/build/cs_mast/index.js.map +1 -1
  49. package/build/endpoints/gen_report/gen_json.js +2 -2
  50. package/build/endpoints/gen_report/gen_json.js.map +1 -1
  51. package/build/endpoints/gen_report/utility/iterate_n_store.js +1 -1
  52. package/build/endpoints/gen_report/utility/iterate_n_store.js.map +1 -1
  53. package/build/endpoints/index.js +12 -12
  54. package/build/endpoints/index.js.map +1 -1
  55. package/build/endpoints/next_js/client_jsFilesHref.js +3 -3
  56. package/build/endpoints/next_js/client_jsFilesHref.js.map +1 -1
  57. package/build/endpoints/next_js/client_jsonParse.js +4 -4
  58. package/build/endpoints/next_js/client_jsonParse.js.map +1 -1
  59. package/build/endpoints/next_js/client_mappedJsonFile.js +6 -6
  60. package/build/endpoints/next_js/client_mappedJsonFile.js.map +1 -1
  61. package/build/endpoints/next_js/client_subsequentRequests.js +5 -5
  62. package/build/endpoints/next_js/client_subsequentRequests.js.map +1 -1
  63. package/build/exploit/angular/cve-2026-68945.js +61 -0
  64. package/build/exploit/angular/cve-2026-68945.js.map +1 -0
  65. package/build/exploit/index.js +60 -0
  66. package/build/exploit/index.js.map +1 -0
  67. package/build/exploit/next/cve-2025-29927.js +65 -0
  68. package/build/exploit/next/cve-2025-29927.js.map +1 -0
  69. package/build/exploit/next/cve-2026-64645.js +51 -0
  70. package/build/exploit/next/cve-2026-64645.js.map +1 -0
  71. package/build/exploit/nuxt/cve-2026-71315.js +47 -0
  72. package/build/exploit/nuxt/cve-2026-71315.js.map +1 -0
  73. package/build/exploit/nuxt/cve-2026-71320.js +114 -0
  74. package/build/exploit/nuxt/cve-2026-71320.js.map +1 -0
  75. package/build/exploit/react/cve-2025-55182.js +116 -0
  76. package/build/exploit/react/cve-2025-55182.js.map +1 -0
  77. package/build/exploit/registry/cveRegistry.js +115 -0
  78. package/build/exploit/registry/cveRegistry.js.map +1 -0
  79. package/build/exploit/types.js +2 -0
  80. package/build/exploit/types.js.map +1 -0
  81. package/build/exploit/utility/compareResponses.js +29 -0
  82. package/build/exploit/utility/compareResponses.js.map +1 -0
  83. package/build/exploit/utility/outputHelper.js +22 -0
  84. package/build/exploit/utility/outputHelper.js.map +1 -0
  85. package/build/exploit/utility/rawRequest.js +54 -0
  86. package/build/exploit/utility/rawRequest.js.map +1 -0
  87. package/build/exploit/utility/sandboxRce.js +49 -0
  88. package/build/exploit/utility/sandboxRce.js.map +1 -0
  89. package/build/exploit/utility/toEngineOutput.js +28 -0
  90. package/build/exploit/utility/toEngineOutput.js.map +1 -0
  91. package/build/exploit/utility/wsClient.js +78 -0
  92. package/build/exploit/utility/wsClient.js.map +1 -0
  93. package/build/exploit/vite/cve-2025-30208.js +44 -0
  94. package/build/exploit/vite/cve-2025-30208.js.map +1 -0
  95. package/build/exploit/vite/cve-2026-39363.js +60 -0
  96. package/build/exploit/vite/cve-2026-39363.js.map +1 -0
  97. package/build/fingerprint/index.js +11 -21
  98. package/build/fingerprint/index.js.map +1 -1
  99. package/build/globalConfig.js +2 -2
  100. package/build/globalConfig.js.map +1 -1
  101. package/build/index.js +16 -555
  102. package/build/index.js.map +1 -1
  103. package/build/lazyLoad/angular/angular_getFromPageSource.js +3 -3
  104. package/build/lazyLoad/angular/angular_getFromPageSource.js.map +1 -1
  105. package/build/lazyLoad/downloadFilesUtil.js +51 -40
  106. package/build/lazyLoad/downloadFilesUtil.js.map +1 -1
  107. package/build/lazyLoad/downloadQueue.js +154 -50
  108. package/build/lazyLoad/downloadQueue.js.map +1 -1
  109. package/build/lazyLoad/downloadResponse.js +113 -0
  110. package/build/lazyLoad/downloadResponse.js.map +1 -0
  111. package/build/lazyLoad/generic/generic_crawlPages.js +186 -0
  112. package/build/lazyLoad/generic/generic_crawlPages.js.map +1 -0
  113. package/build/lazyLoad/generic/generic_downloadFiles.js +81 -0
  114. package/build/lazyLoad/generic/generic_downloadFiles.js.map +1 -0
  115. package/build/lazyLoad/generic/generic_extractLinks.js +79 -0
  116. package/build/lazyLoad/generic/generic_extractLinks.js.map +1 -0
  117. package/build/lazyLoad/generic/generic_getScriptTags.js +152 -0
  118. package/build/lazyLoad/generic/generic_getScriptTags.js.map +1 -0
  119. package/build/lazyLoad/generic/generic_importMapDiscovery.js +87 -0
  120. package/build/lazyLoad/generic/generic_importMapDiscovery.js.map +1 -0
  121. package/build/lazyLoad/generic/generic_resolveScope.js +61 -0
  122. package/build/lazyLoad/generic/generic_resolveScope.js.map +1 -0
  123. package/build/lazyLoad/generic/generic_scanAttributesForJs.js +122 -0
  124. package/build/lazyLoad/generic/generic_scanAttributesForJs.js.map +1 -0
  125. package/build/lazyLoad/generic/generic_stringsDiscovery.js +103 -0
  126. package/build/lazyLoad/generic/generic_stringsDiscovery.js.map +1 -0
  127. package/build/lazyLoad/generic/generic_webpackChunkPaths.js +56 -0
  128. package/build/lazyLoad/generic/generic_webpackChunkPaths.js.map +1 -0
  129. package/build/lazyLoad/globals.js +35 -0
  130. package/build/lazyLoad/globals.js.map +1 -1
  131. package/build/lazyLoad/index.js +311 -175
  132. package/build/lazyLoad/index.js.map +1 -1
  133. package/build/lazyLoad/methodFilter.js +2 -0
  134. package/build/lazyLoad/methodFilter.js.map +1 -1
  135. package/build/lazyLoad/next_js/NextJsCrawler.js +39 -14
  136. package/build/lazyLoad/next_js/NextJsCrawler.js.map +1 -1
  137. package/build/lazyLoad/next_js/next_GetJSScript.js +3 -3
  138. package/build/lazyLoad/next_js/next_GetJSScript.js.map +1 -1
  139. package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js +4 -4
  140. package/build/lazyLoad/next_js/next_GetLazyResourcesBuildManifestJs.js.map +1 -1
  141. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js +21 -20
  142. package/build/lazyLoad/next_js/next_GetLazyResourcesWebpackJs.js.map +1 -1
  143. package/build/lazyLoad/next_js/next_SubsequentRequests.js +100 -99
  144. package/build/lazyLoad/next_js/next_SubsequentRequests.js.map +1 -1
  145. package/build/lazyLoad/next_js/next_bruteForceJsFiles.js +1 -1
  146. package/build/lazyLoad/next_js/next_bruteForceJsFiles.js.map +1 -1
  147. package/build/lazyLoad/next_js/next_buildId.js +2 -2
  148. package/build/lazyLoad/next_js/next_buildId.js.map +1 -1
  149. package/build/lazyLoad/next_js/next_getClientSidePaths.js +2 -2
  150. package/build/lazyLoad/next_js/next_getClientSidePaths.js.map +1 -1
  151. package/build/lazyLoad/next_js/next_parseLayoutJs.js +6 -6
  152. package/build/lazyLoad/next_js/next_parseLayoutJs.js.map +1 -1
  153. package/build/lazyLoad/next_js/next_promiseResolve.js +1 -1
  154. package/build/lazyLoad/next_js/next_promiseResolve.js.map +1 -1
  155. package/build/lazyLoad/next_js/next_routerStateForge.js +341 -0
  156. package/build/lazyLoad/next_js/next_routerStateForge.js.map +1 -0
  157. package/build/lazyLoad/next_js/next_scriptTagsSubsequentRequests.js +1 -1
  158. package/build/lazyLoad/next_js/next_scriptTagsSubsequentRequests.js.map +1 -1
  159. package/build/lazyLoad/next_js/next_serverActionIdScan.js +77 -0
  160. package/build/lazyLoad/next_js/next_serverActionIdScan.js.map +1 -0
  161. package/build/lazyLoad/nuxt_js/nuxt_astParse.js +15 -13
  162. package/build/lazyLoad/nuxt_js/nuxt_astParse.js.map +1 -1
  163. package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js +2 -2
  164. package/build/lazyLoad/nuxt_js/nuxt_getBuildsManifest.js.map +1 -1
  165. package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js +3 -3
  166. package/build/lazyLoad/nuxt_js/nuxt_getFromPageSource.js.map +1 -1
  167. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js +2 -2
  168. package/build/lazyLoad/nuxt_js/nuxt_stringAnalysisJSFiles.js.map +1 -1
  169. package/build/lazyLoad/outputPath.js +92 -0
  170. package/build/lazyLoad/outputPath.js.map +1 -0
  171. package/build/lazyLoad/react/react_followImports.js +15 -5
  172. package/build/lazyLoad/react/react_followImports.js.map +1 -1
  173. package/build/lazyLoad/react/react_getScriptTags.js +11 -6
  174. package/build/lazyLoad/react/react_getScriptTags.js.map +1 -1
  175. package/build/lazyLoad/react/react_sourcemapUrls.js +3 -2
  176. package/build/lazyLoad/react/react_sourcemapUrls.js.map +1 -1
  177. package/build/lazyLoad/react/react_webpackChunkPaths.js +12 -203
  178. package/build/lazyLoad/react/react_webpackChunkPaths.js.map +1 -1
  179. package/build/lazyLoad/researchUtils.js +15 -0
  180. package/build/lazyLoad/researchUtils.js.map +1 -0
  181. package/build/lazyLoad/shared/webpackChunkParsers.js +218 -0
  182. package/build/lazyLoad/shared/webpackChunkParsers.js.map +1 -0
  183. package/build/lazyLoad/stagnation/stagnationMonitor.js +63 -0
  184. package/build/lazyLoad/stagnation/stagnationMonitor.js.map +1 -0
  185. package/build/lazyLoad/svelte/svelte_discoverPagesFromJs.js +1 -0
  186. package/build/lazyLoad/svelte/svelte_discoverPagesFromJs.js.map +1 -1
  187. package/build/lazyLoad/svelte/svelte_getFromPageSource.js +5 -5
  188. package/build/lazyLoad/svelte/svelte_getFromPageSource.js.map +1 -1
  189. package/build/lazyLoad/svelte/svelte_getVersionJson.js +2 -2
  190. package/build/lazyLoad/svelte/svelte_getVersionJson.js.map +1 -1
  191. package/build/lazyLoad/svelte/svelte_recursivePageCrawl.js +6 -6
  192. package/build/lazyLoad/svelte/svelte_recursivePageCrawl.js.map +1 -1
  193. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js +2 -2
  194. package/build/lazyLoad/svelte/svelte_stringAnalysisJSFiles.js.map +1 -1
  195. package/build/lazyLoad/techDetect/checkAngularJS.js +1 -1
  196. package/build/lazyLoad/techDetect/checkAngularJS.js.map +1 -1
  197. package/build/lazyLoad/techDetect/checkNuxtJS.js +3 -3
  198. package/build/lazyLoad/techDetect/checkNuxtJS.js.map +1 -1
  199. package/build/lazyLoad/techDetect/checkSvelte.js +0 -1
  200. package/build/lazyLoad/techDetect/checkSvelte.js.map +1 -1
  201. package/build/lazyLoad/techDetect/checkVueJS.js +6 -6
  202. package/build/lazyLoad/techDetect/checkVueJS.js.map +1 -1
  203. package/build/lazyLoad/techDetect/index.js +106 -60
  204. package/build/lazyLoad/techDetect/index.js.map +1 -1
  205. package/build/lazyLoad/vue/vue_RuntimeJs.js +20 -15
  206. package/build/lazyLoad/vue/vue_RuntimeJs.js.map +1 -1
  207. package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js +10 -5
  208. package/build/lazyLoad/vue/vue_SingleJsFileOnHome.js.map +1 -1
  209. package/build/lazyLoad/vue/vue_discoverJsFiles.js +20 -17
  210. package/build/lazyLoad/vue/vue_discoverJsFiles.js.map +1 -1
  211. package/build/lazyLoad/vue/vue_getClientSidePaths.js +70 -46
  212. package/build/lazyLoad/vue/vue_getClientSidePaths.js.map +1 -1
  213. package/build/lazyLoad/vue/vue_jsImports.js +3 -2
  214. package/build/lazyLoad/vue/vue_jsImports.js.map +1 -1
  215. package/build/lazyLoad/vue/vue_pageSrc.js +3 -2
  216. package/build/lazyLoad/vue/vue_pageSrc.js.map +1 -1
  217. package/build/lazyLoad/vue/vue_reconstructSourceMaps.js +6 -5
  218. package/build/lazyLoad/vue/vue_reconstructSourceMaps.js.map +1 -1
  219. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js +10 -21
  220. package/build/lazyLoad/vue/vue_recursiveClientSidePathDownload.js.map +1 -1
  221. package/build/lazyLoad/vue/vue_severalJsFilesHome.js +5 -4
  222. package/build/lazyLoad/vue/vue_severalJsFilesHome.js.map +1 -1
  223. package/build/lazyLoad/vue/vue_stringJsFiles.js +28 -25
  224. package/build/lazyLoad/vue/vue_stringJsFiles.js.map +1 -1
  225. package/build/lazyLoad/vue/vue_viteMapDeps.js +8 -2
  226. package/build/lazyLoad/vue/vue_viteMapDeps.js.map +1 -1
  227. package/build/load/index.js +8 -7
  228. package/build/load/index.js.map +1 -1
  229. package/build/map/angular_js/getAngularConnections.js +6 -6
  230. package/build/map/angular_js/getAngularConnections.js.map +1 -1
  231. package/build/map/graphql/resolveGraphql.js +6 -6
  232. package/build/map/graphql/resolveGraphql.js.map +1 -1
  233. package/build/map/index.js +17 -17
  234. package/build/map/index.js.map +1 -1
  235. package/build/map/interactive-mode/commandHandler.js +278 -0
  236. package/build/map/interactive-mode/commandHandler.js.map +1 -0
  237. package/build/map/interactive-mode/commandHelpers.js +189 -0
  238. package/build/map/interactive-mode/commandHelpers.js.map +1 -0
  239. package/build/map/interactive-mode/esqueryGen.js.map +1 -0
  240. package/build/map/interactive-mode/helpMenu.js +11 -0
  241. package/build/map/interactive-mode/helpMenu.js.map +1 -0
  242. package/build/map/interactive-mode/inputPatch.js.map +1 -0
  243. package/build/map/interactive-mode/interactive.js +71 -0
  244. package/build/map/interactive-mode/interactive.js.map +1 -0
  245. package/build/map/interactive-mode/keybindings.js.map +1 -0
  246. package/build/map/interactive-mode/printer.js.map +1 -0
  247. package/build/map/interactive-mode/ui.js.map +1 -0
  248. package/build/map/next_js/getAxiosInstances.js +5 -5
  249. package/build/map/next_js/getAxiosInstances.js.map +1 -1
  250. package/build/map/next_js/getExports.js +4 -4
  251. package/build/map/next_js/getExports.js.map +1 -1
  252. package/build/map/next_js/getFetchInstances.js +10 -10
  253. package/build/map/next_js/getFetchInstances.js.map +1 -1
  254. package/build/map/next_js/getTurbopackConnections.js +8 -8
  255. package/build/map/next_js/getTurbopackConnections.js.map +1 -1
  256. package/build/map/next_js/getWebpackConnections.js +27 -16
  257. package/build/map/next_js/getWebpackConnections.js.map +1 -1
  258. package/build/map/next_js/interactive.js +3 -65
  259. package/build/map/next_js/interactive.js.map +1 -1
  260. package/build/map/next_js/interactive_helpers/commandHandler.js +38 -290
  261. package/build/map/next_js/interactive_helpers/commandHandler.js.map +1 -1
  262. package/build/map/next_js/interactive_helpers/commandHelpers.js +0 -184
  263. package/build/map/next_js/interactive_helpers/commandHelpers.js.map +1 -1
  264. package/build/map/next_js/interactive_helpers/helpMenu.js +2 -10
  265. package/build/map/next_js/interactive_helpers/helpMenu.js.map +1 -1
  266. package/build/map/next_js/resolveAxios.js +6 -6
  267. package/build/map/next_js/resolveAxios.js.map +1 -1
  268. package/build/map/next_js/resolveAxiosHelpers/findAxiosClients.js +4 -4
  269. package/build/map/next_js/resolveAxiosHelpers/findAxiosClients.js.map +1 -1
  270. package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js +2 -2
  271. package/build/map/next_js/resolveAxiosHelpers/findAxiosInstance.js.map +1 -1
  272. package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js +5 -5
  273. package/build/map/next_js/resolveAxiosHelpers/findCrossChunkParams.js.map +1 -1
  274. package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js +10 -10
  275. package/build/map/next_js/resolveAxiosHelpers/handleAxiosCreate.js.map +1 -1
  276. package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js +13 -9
  277. package/build/map/next_js/resolveAxiosHelpers/handleZDotCreate.js.map +1 -1
  278. package/build/map/next_js/resolveAxiosHelpers/processAxiosCall.js +12 -8
  279. package/build/map/next_js/resolveAxiosHelpers/processAxiosCall.js.map +1 -1
  280. package/build/map/next_js/resolveAxiosHelpers/processDirectAxiosCall.js +12 -8
  281. package/build/map/next_js/resolveAxiosHelpers/processDirectAxiosCall.js.map +1 -1
  282. package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js +11 -11
  283. package/build/map/next_js/resolveAxiosHelpers/processExportedEndpoints.js.map +1 -1
  284. package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js +16 -16
  285. package/build/map/next_js/resolveAxiosHelpers/traceAxiosInstanceExports.js.map +1 -1
  286. package/build/map/next_js/resolveFetch.js +23 -23
  287. package/build/map/next_js/resolveFetch.js.map +1 -1
  288. package/build/map/next_js/resolveNewRequest.js +10 -10
  289. package/build/map/next_js/resolveNewRequest.js.map +1 -1
  290. package/build/map/next_js/resolveServerActions.js +4 -4
  291. package/build/map/next_js/resolveServerActions.js.map +1 -1
  292. package/build/map/next_js/utils.js +8 -2
  293. package/build/map/next_js/utils.js.map +1 -1
  294. package/build/map/react_js/getReactConnections.js +21 -14
  295. package/build/map/react_js/getReactConnections.js.map +1 -1
  296. package/build/map/svelte_js/interactive.js +3 -54
  297. package/build/map/svelte_js/interactive.js.map +1 -1
  298. package/build/map/vue_js/getViteConnections.js +23 -16
  299. package/build/map/vue_js/getViteConnections.js.map +1 -1
  300. package/build/map/vue_js/interactive.js +3 -63
  301. package/build/map/vue_js/interactive.js.map +1 -1
  302. package/build/map/vue_js/interactive_helpers/commandHandler.js +34 -283
  303. package/build/map/vue_js/interactive_helpers/commandHandler.js.map +1 -1
  304. package/build/map/vue_js/interactive_helpers/helpMenu.js +2 -10
  305. package/build/map/vue_js/interactive_helpers/helpMenu.js.map +1 -1
  306. package/build/map/vue_js/taint_utils.js +1 -1
  307. package/build/map/vue_js/taint_utils.js.map +1 -1
  308. package/build/map/vue_js/vue_resolveFetch.js +12 -12
  309. package/build/map/vue_js/vue_resolveFetch.js.map +1 -1
  310. package/build/map/vue_js/vue_resolveHttpClient.js +18 -18
  311. package/build/map/vue_js/vue_resolveHttpClient.js.map +1 -1
  312. package/build/map/vue_js/vue_resolveXhr.js +11 -11
  313. package/build/map/vue_js/vue_resolveXhr.js.map +1 -1
  314. package/build/mcp/chatOneShot.js +7 -7
  315. package/build/mcp/chatOneShot.js.map +1 -1
  316. package/build/mcp/claudeCodeCreds.js +5 -5
  317. package/build/mcp/claudeCodeCreds.js.map +1 -1
  318. package/build/mcp/cli.js +36 -35
  319. package/build/mcp/cli.js.map +1 -1
  320. package/build/mcp/config.js +4 -4
  321. package/build/mcp/config.js.map +1 -1
  322. package/build/mcp/index.js +7 -7
  323. package/build/mcp/index.js.map +1 -1
  324. package/build/mcp/mcpServer.js +3 -2
  325. package/build/mcp/mcpServer.js.map +1 -1
  326. package/build/mcp/skills.js +1 -0
  327. package/build/mcp/skills.js.map +1 -1
  328. package/build/mcp/tools.js +5 -4
  329. package/build/mcp/tools.js.map +1 -1
  330. package/build/proxy/checkFeasibility.js +8 -4
  331. package/build/proxy/checkFeasibility.js.map +1 -1
  332. package/build/proxy/checkFireWallBlocking.js +23 -13
  333. package/build/proxy/checkFireWallBlocking.js.map +1 -1
  334. package/build/proxy/detectBlockedResponse.js +205 -0
  335. package/build/proxy/detectBlockedResponse.js.map +1 -0
  336. package/build/proxy/genReq.js +434 -127
  337. package/build/proxy/genReq.js.map +1 -1
  338. package/build/proxy/index.js +3 -3
  339. package/build/proxy/index.js.map +1 -1
  340. package/build/proxy/oxylabsFallback.js +110 -0
  341. package/build/proxy/oxylabsFallback.js.map +1 -0
  342. package/build/proxy/proxyFeasibilityRequest.js +11 -9
  343. package/build/proxy/proxyFeasibilityRequest.js.map +1 -1
  344. package/build/refactor/index.js +83 -79
  345. package/build/refactor/index.js.map +1 -1
  346. package/build/refactor/next/index.js +69 -26
  347. package/build/refactor/next/index.js.map +1 -1
  348. package/build/refactor/next/transform.js +11 -4
  349. package/build/refactor/next/transform.js.map +1 -1
  350. package/build/refactor/next/validator.js +7 -7
  351. package/build/refactor/next/validator.js.map +1 -1
  352. package/build/refactor/react/index.js +13 -13
  353. package/build/refactor/react/index.js.map +1 -1
  354. package/build/refactor/react/validator.js +7 -7
  355. package/build/refactor/react/validator.js.map +1 -1
  356. package/build/refactor/react-vite/index.js +3 -3
  357. package/build/refactor/react-vite/index.js.map +1 -1
  358. package/build/refactor/remote/hf-client.js +1 -0
  359. package/build/refactor/remote/hf-client.js.map +1 -1
  360. package/build/refactor/remote/version-detect.js +12 -12
  361. package/build/refactor/remote/version-detect.js.map +1 -1
  362. package/build/refactor/vue/index.js +6 -6
  363. package/build/refactor/vue/index.js.map +1 -1
  364. package/build/refactor/vue/vite.js +6 -6
  365. package/build/refactor/vue/vite.js.map +1 -1
  366. package/build/report/index.js +30 -8
  367. package/build/report/index.js.map +1 -1
  368. package/build/report/swaggerJacker.js +63 -0
  369. package/build/report/swaggerJacker.js.map +1 -0
  370. package/build/report/utility/genHtml.js +7 -7
  371. package/build/report/utility/genHtml.js.map +1 -1
  372. package/build/report/utility/populateDb/populateAnalysisFindings.js +2 -2
  373. package/build/report/utility/populateDb/populateAnalysisFindings.js.map +1 -1
  374. package/build/report/utility/populateDb/populateEndpoints.js +3 -3
  375. package/build/report/utility/populateDb/populateEndpoints.js.map +1 -1
  376. package/build/report/utility/populateDb/populateMappedJson.js +2 -2
  377. package/build/report/utility/populateDb/populateMappedJson.js.map +1 -1
  378. package/build/report/utility/populateDb/populateMappedOpenapi.js +3 -3
  379. package/build/report/utility/populateDb/populateMappedOpenapi.js.map +1 -1
  380. package/build/run/bundler-detect.js +45 -9
  381. package/build/run/bundler-detect.js.map +1 -1
  382. package/build/run/cancellableStep.js +27 -0
  383. package/build/run/cancellableStep.js.map +1 -0
  384. package/build/run/dashboard/dashboardHtml.js +150 -0
  385. package/build/run/dashboard/dashboardHtml.js.map +1 -0
  386. package/build/run/dashboard/fsBrowser.js +43 -0
  387. package/build/run/dashboard/fsBrowser.js.map +1 -0
  388. package/build/run/dashboard/server.js +123 -0
  389. package/build/run/dashboard/server.js.map +1 -0
  390. package/build/run/dashboard/state.js +97 -0
  391. package/build/run/dashboard/state.js.map +1 -0
  392. package/build/run/index.js +239 -204
  393. package/build/run/index.js.map +1 -1
  394. package/build/run/interruptHandler.js +18 -0
  395. package/build/run/interruptHandler.js.map +1 -1
  396. package/build/run/outputDirectory.js +377 -0
  397. package/build/run/outputDirectory.js.map +1 -0
  398. package/build/sourcemaps/index.js +116 -29
  399. package/build/sourcemaps/index.js.map +1 -1
  400. package/build/strings/index.js +38 -28
  401. package/build/strings/index.js.map +1 -1
  402. package/build/strings/openapi.js +4 -4
  403. package/build/strings/openapi.js.map +1 -1
  404. package/build/strings/permutate.js +4 -4
  405. package/build/strings/permutate.js.map +1 -1
  406. package/build/strings/secrets.js +1 -0
  407. package/build/strings/secrets.js.map +1 -1
  408. package/build/strings/trufflehog.js +66 -19
  409. package/build/strings/trufflehog.js.map +1 -1
  410. package/build/strings/trufflehogConfig.js +36 -0
  411. package/build/strings/trufflehogConfig.js.map +1 -0
  412. package/build/strings/trufflehogDownload.js +115 -0
  413. package/build/strings/trufflehogDownload.js.map +1 -0
  414. package/build/utility/ai.js +33 -4
  415. package/build/utility/ai.js.map +1 -1
  416. package/build/utility/banner.js +4 -3
  417. package/build/utility/banner.js.map +1 -1
  418. package/build/utility/configureSandbox.js +2 -2
  419. package/build/utility/configureSandbox.js.map +1 -1
  420. package/build/utility/downloadProgress.js +99 -0
  421. package/build/utility/downloadProgress.js.map +1 -0
  422. package/build/utility/globals.js +19 -11
  423. package/build/utility/globals.js.map +1 -1
  424. package/build/utility/heap.js +2 -1
  425. package/build/utility/heap.js.map +1 -1
  426. package/build/utility/makeReq.js +490 -257
  427. package/build/utility/makeReq.js.map +1 -1
  428. package/build/utility/openapiGenerator.js +2 -2
  429. package/build/utility/openapiGenerator.js.map +1 -1
  430. package/build/utility/printMsg.js +53 -0
  431. package/build/utility/printMsg.js.map +1 -0
  432. package/build/utility/progressLog.js +68 -7
  433. package/build/utility/progressLog.js.map +1 -1
  434. package/build/utility/resolvePath.js +2 -1
  435. package/build/utility/resolvePath.js.map +1 -1
  436. package/build/utility/ruleVersionMap.js +73 -0
  437. package/build/utility/ruleVersionMap.js.map +1 -0
  438. package/build/utility/targetInputs.js +177 -0
  439. package/build/utility/targetInputs.js.map +1 -0
  440. package/build/utility/urlUtils.js +37 -1
  441. package/build/utility/urlUtils.js.map +1 -1
  442. package/config.dist.yaml +251 -0
  443. package/contributing/testing.md +106 -0
  444. package/eslint.config.js +24 -0
  445. package/package.json +22 -5
  446. package/scripts/exploit-smoke-test.js +82 -0
  447. package/scripts/framework-detect-smoke-test.js +64 -0
  448. package/scripts/postinstall-completion.mjs +40 -0
  449. package/scripts/smoke-test.js +12 -0
  450. package/build/lazyLoad/downloadLoadedJsUtil.js +0 -98
  451. package/build/lazyLoad/downloadLoadedJsUtil.js.map +0 -1
  452. package/build/map/next_js/interactive_helpers/esqueryGen.js.map +0 -1
  453. package/build/map/next_js/interactive_helpers/inputPatch.js.map +0 -1
  454. package/build/map/next_js/interactive_helpers/keybindings.js.map +0 -1
  455. package/build/map/next_js/interactive_helpers/printer.js.map +0 -1
  456. package/build/map/next_js/interactive_helpers/ui.js.map +0 -1
  457. /package/build/map/{next_js/interactive_helpers → interactive-mode}/esqueryGen.js +0 -0
  458. /package/build/map/{next_js/interactive_helpers → interactive-mode}/inputPatch.js +0 -0
  459. /package/build/map/{next_js/interactive_helpers → interactive-mode}/keybindings.js +0 -0
  460. /package/build/map/{next_js/interactive_helpers → interactive-mode}/printer.js +0 -0
  461. /package/build/map/{next_js/interactive_helpers → interactive-mode}/ui.js +0 -0
@@ -10,13 +10,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  var _a;
11
11
  import chalk from "chalk";
12
12
  import fs from "fs";
13
- import frameworkDetect from "./techDetect/index.js";
14
- import CONFIG from "../globalConfig.js";
13
+ import frameworkDetect, { getLastInterceptedUrls } from "./techDetect/index.js";
15
14
  import _traverse from "@babel/traverse";
16
15
  const traverse = ((_a = _traverse.default) !== null && _a !== void 0 ? _a : _traverse);
17
16
  import { URL } from "url";
18
17
  // Next.js
19
18
  import NextJsCrawler from "./next_js/NextJsCrawler.js";
19
+ import next_serverActionIdScan from "./next_js/next_serverActionIdScan.js";
20
20
  import { next_buildId_RSC } from "./next_js/next_buildId.js";
21
21
  // Nuxt.js
22
22
  import nuxt_getFromPageSource from "./nuxt_js/nuxt_getFromPageSource.js";
@@ -41,8 +41,9 @@ import react_getScriptTags from "./react/react_getScriptTags.js";
41
41
  import react_webpackChunkPaths from "./react/react_webpackChunkPaths.js";
42
42
  import react_sourcemapUrls from "./react/react_sourcemapUrls.js";
43
43
  import react_followImports from "./react/react_followImports.js";
44
- import downloadLoadedJs from "./downloadLoadedJsUtil.js";
45
44
  import { DownloadQueue } from "./downloadQueue.js";
45
+ import generic_crawlPages from "./generic/generic_crawlPages.js";
46
+ import resolveGenericScope from "./generic/generic_resolveScope.js";
46
47
  import path from "path";
47
48
  import { join } from "path";
48
49
  import { extractSourceMaps } from "../sourcemaps/index.js";
@@ -50,6 +51,13 @@ import { extractSourceMaps } from "../sourcemaps/index.js";
50
51
  import * as lazyLoadGlobals from "./globals.js";
51
52
  import * as globals from "../utility/globals.js";
52
53
  import { shouldRunMethod } from "./methodFilter.js";
54
+ import DownloadProgress from "../utility/downloadProgress.js";
55
+ import { progressLog, progressWarn } from "../utility/progressLog.js";
56
+ import { resolveHostOutputDirectory } from "./outputPath.js";
57
+ import { withRequestSignal } from "../utility/makeReq.js";
58
+ import { resolveTargetInputs } from "../utility/targetInputs.js";
59
+ import { accumulateTechnique, createTechniqueRecorder } from "./researchUtils.js";
60
+ import { printMsg, MSG } from "../utility/printMsg.js";
53
61
  /**
54
62
  * Downloads the required JavaScript files for a given URL
55
63
  * @param {string} url The URL to download the JS files from
@@ -62,19 +70,46 @@ import { shouldRunMethod } from "./methodFilter.js";
62
70
  * @param {boolean} insecure If true, then disable SSL certificate verification
63
71
  * @returns {Promise<void>} A Promise that resolves when the download is complete
64
72
  */
65
- const lazyLoad = (url_1, output_1, strictScope_1, inputScope_1, threads_1, subsequentRequestsFlag_1, urlsFile_1, insecure_1, buildId_1, sourcemapDir_1, research_1, researchOutput_1, maxIterations_1, ...args_1) => __awaiter(void 0, [url_1, output_1, strictScope_1, inputScope_1, threads_1, subsequentRequestsFlag_1, urlsFile_1, insecure_1, buildId_1, sourcemapDir_1, research_1, researchOutput_1, maxIterations_1, ...args_1], void 0, function* (url, output, strictScope, inputScope, threads, subsequentRequestsFlag, urlsFile, insecure, buildId, sourcemapDir, research, researchOutput, maxIterations, maxJsSizeMb = 2, hardTimeoutMs = 30 * 60 * 1000, maxPageVisits = 200, includeMethods = [], excludeMethods = [], detectionTimeoutMs = 30 * 1000) {
73
+ const lazyLoad = (url_1, output_1, strictScope_1, inputScope_1, threads_1, subsequentRequestsFlag_1, urlsFile_1, insecure_1, buildId_1, sourcemapDir_1, research_1, researchOutput_1, maxIterations_1, ...args_1) => __awaiter(void 0, [url_1, output_1, strictScope_1, inputScope_1, threads_1, subsequentRequestsFlag_1, urlsFile_1, insecure_1, buildId_1, sourcemapDir_1, research_1, researchOutput_1, maxIterations_1, ...args_1], void 0, function* (url, output, strictScope, inputScope, threads, subsequentRequestsFlag, urlsFile, insecure, buildId, sourcemapDir, research, researchOutput, maxIterations, maxJsSizeMb = 2, hardTimeoutMs = 30 * 60 * 1000, maxPageVisits = 200, includeMethods = [], excludeMethods = [], detectionTimeoutMs = 30 * 1000, cancellationSignal, isBatch = false, maxRedirects = 20, stringsEnabled = false, stringsMaxIterations = 5, stagnationTimeinMs = 0, stagnationPercentage = 80, stagnationMonitorMs = 60 * 1000, rscParamBruteforceLimit = 20) {
74
+ const resolvedTargets = resolveTargetInputs(url);
66
75
  // Hoisted so the timeout handler can stop discovery and drain downloads.
67
76
  let activeCrawler = null;
68
77
  let activeQueue = null;
78
+ let activeDownloadProgress = null;
79
+ let activeDetectionController = null;
80
+ let hardTimeoutReached = false;
81
+ const workController = new AbortController();
82
+ const requestCancellation = () => {
83
+ hardTimeoutReached = true;
84
+ workController.abort();
85
+ activeDetectionController === null || activeDetectionController === void 0 ? void 0 : activeDetectionController.abort();
86
+ activeCrawler === null || activeCrawler === void 0 ? void 0 : activeCrawler.stop();
87
+ activeDownloadProgress === null || activeDownloadProgress === void 0 ? void 0 : activeDownloadProgress.stop();
88
+ activeDownloadProgress = null;
89
+ };
90
+ const enqueue = (queue, urls) => {
91
+ if (!hardTimeoutReached)
92
+ queue.push(urls);
93
+ };
94
+ const handleExternalCancellation = () => requestCancellation();
95
+ const cleanupActiveWork = () => __awaiter(void 0, void 0, void 0, function* () {
96
+ activeDownloadProgress === null || activeDownloadProgress === void 0 ? void 0 : activeDownloadProgress.stop();
97
+ activeDownloadProgress = null;
98
+ if (hardTimeoutReached)
99
+ yield (activeQueue === null || activeQueue === void 0 ? void 0 : activeQueue.drain().catch(() => undefined));
100
+ });
101
+ cancellationSignal === null || cancellationSignal === void 0 ? void 0 : cancellationSignal.addEventListener("abort", handleExternalCancellation, { once: true });
102
+ if (cancellationSignal === null || cancellationSignal === void 0 ? void 0 : cancellationSignal.aborted)
103
+ requestCancellation();
69
104
  const work = () => __awaiter(void 0, void 0, void 0, function* () {
70
- console.log(chalk.cyan("[i] Loading 'Lazy Load' module"));
105
+ printMsg(MSG.Header, "[i] Loading 'Lazy Load' module");
71
106
  if (globals.getDisableSandbox()) {
72
- console.error(chalk.yellow("[!] Browser sandbox disabled"));
107
+ printMsg(MSG.Warn, "[!] Browser sandbox disabled");
73
108
  }
74
109
  globals.setInsecure(insecure);
75
110
  if (insecure) {
76
111
  process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
77
- console.error(chalk.yellow("[!] Running in insecure mode. SSL certificate verification disabled"));
112
+ printMsg(MSG.Warn, "[!] Running in insecure mode. SSL certificate verification disabled");
78
113
  }
79
114
  // if cache enabled, check if the cache file exists or not. If no, then create a new one
80
115
  if (!globals.getDisableCache()) {
@@ -82,52 +117,51 @@ const lazyLoad = (url_1, output_1, strictScope_1, inputScope_1, threads_1, subse
82
117
  fs.writeFileSync(globals.getRespCacheFile(), "{}");
83
118
  }
84
119
  }
85
- let urls;
86
- // check if the url is file or a URL
87
- if (fs.existsSync(url)) {
88
- urls = fs.readFileSync(url, "utf8").split("\n");
89
- // remove the empty lines
90
- urls = urls.filter((url) => url.trim() !== "");
91
- }
92
- else if (url.match(/https?:\/\/[a-zA-Z0-9\-_\.:]+/)) {
93
- urls = [url];
94
- }
95
- else {
96
- console.error(chalk.red("[!] Invalid URL or file path"));
97
- process.exit(3);
98
- }
99
- for (const url of urls) {
100
- console.log(chalk.cyan(`[i] Processing ${url}`));
120
+ for (const url of resolvedTargets.targets) {
121
+ if (hardTimeoutReached)
122
+ break;
123
+ printMsg(MSG.Header, `[i] Processing ${url}`);
124
+ lazyLoadGlobals.clearJsUrls();
125
+ lazyLoadGlobals.clearJsonUrls();
126
+ lazyLoadGlobals.clearCrawledUrls();
101
127
  if (strictScope) {
102
- lazyLoadGlobals.pushToScope(new URL(url).host);
128
+ lazyLoadGlobals.setScope([new URL(url).host]);
103
129
  }
104
130
  else {
105
- lazyLoadGlobals.setScope(inputScope);
131
+ lazyLoadGlobals.setScope([...inputScope]);
106
132
  }
107
133
  lazyLoadGlobals.setMaxReqQueue(threads);
134
+ globals.setTech("");
108
135
  let tech;
109
- if (detectionTimeoutMs > 0) {
110
- let timedOut = false;
111
- tech = yield Promise.race([
112
- frameworkDetect(url),
113
- new Promise((resolve) => setTimeout(() => {
114
- timedOut = true;
115
- resolve(null);
116
- }, detectionTimeoutMs)),
117
- ]);
118
- if (timedOut) {
119
- console.error(chalk.yellow(`[!] Framework detection timed out after ${detectionTimeoutMs}ms; treating as not detected`));
120
- }
136
+ const detectionController = new AbortController();
137
+ activeDetectionController = detectionController;
138
+ let detectionTimedOut = false;
139
+ const detectionTimeoutHandle = detectionTimeoutMs > 0
140
+ ? setTimeout(() => {
141
+ detectionTimedOut = true;
142
+ detectionController.abort();
143
+ }, detectionTimeoutMs)
144
+ : undefined;
145
+ try {
146
+ tech = yield frameworkDetect(url, detectionController.signal);
121
147
  }
122
- else {
123
- tech = yield frameworkDetect(url);
148
+ finally {
149
+ if (detectionTimeoutHandle !== undefined)
150
+ clearTimeout(detectionTimeoutHandle);
151
+ if (activeDetectionController === detectionController)
152
+ activeDetectionController = null;
153
+ }
154
+ if (detectionTimedOut) {
155
+ printMsg(MSG.Warn, `[!] Framework detection timed out after ${detectionTimeoutMs}ms; treating as not detected`);
124
156
  }
157
+ if (hardTimeoutReached)
158
+ break;
125
159
  globals.setTech(tech ? tech.name : "");
126
160
  if (tech) {
127
161
  if (tech.name === "next") {
128
- console.log(chalk.green("[✓] Next.js detected"));
129
- console.log(chalk.yellow(`Evidence: ${tech.evidence}`));
130
- activeQueue = new DownloadQueue(output, threads);
162
+ printMsg(MSG.Run, "[✓] Next.js detected");
163
+ printMsg(MSG.Warn, `Evidence: ${tech.evidence}`);
164
+ activeQueue = new DownloadQueue(output, threads, { alreadyBatchTargetRoot: isBatch });
131
165
  const crawler = new NextJsCrawler({
132
166
  url,
133
167
  output,
@@ -137,91 +171,142 @@ const lazyLoad = (url_1, output_1, strictScope_1, inputScope_1, threads_1, subse
137
171
  research,
138
172
  maxIterations,
139
173
  maxPageVisits,
140
- onUrlsDiscovered: (urls) => activeQueue.push(urls),
174
+ rscParamBruteforceLimit,
175
+ onUrlsDiscovered: (urls) => enqueue(activeQueue, urls),
141
176
  includeMethods,
142
177
  excludeMethods,
178
+ isBatch,
143
179
  });
144
180
  activeCrawler = crawler;
145
- yield crawler.crawl();
146
- activeCrawler = null; // done — prevent timeout handler from calling stop()
181
+ try {
182
+ yield crawler.crawl();
183
+ }
184
+ finally {
185
+ activeCrawler = null; // done — prevent timeout handler from calling stop()
186
+ }
187
+ if (hardTimeoutReached)
188
+ return;
147
189
  yield activeQueue.drain();
190
+ if (hardTimeoutReached)
191
+ return;
148
192
  activeQueue.printSummary();
149
193
  activeQueue = null;
194
+ if (shouldRunMethod("next_serverActionIdScan", includeMethods, excludeMethods)) {
195
+ const scanDir = resolveHostOutputDirectory(output, new URL(url).host, isBatch);
196
+ const scanResult = yield next_serverActionIdScan(scanDir);
197
+ if (scanResult.allActionIds.length > 0) {
198
+ printMsg(MSG.Run, `[✓] Found ${scanResult.allActionIds.length} Server Action ID(s) referenced in downloaded chunks (disclosure only — not invoked)`);
199
+ fs.writeFileSync(path.join(scanDir, "server_action_ids.json"), JSON.stringify(scanResult, null, 4));
200
+ }
201
+ }
150
202
  if (buildId) {
151
203
  // get the buildId
152
204
  // the directory is the output <output>/<host.replace(":", "_")>/___subsequent_requests
153
- const buildId = yield next_buildId_RSC(output + "/" + new URL(url).host.replace(":", "_") + "/___subsequent_requests");
205
+ const targetOutputDir = resolveHostOutputDirectory(output, new URL(url).host, isBatch);
206
+ const buildId = yield next_buildId_RSC(path.join(targetOutputDir, "___subsequent_requests"));
207
+ if (hardTimeoutReached)
208
+ return;
154
209
  if (buildId) {
155
- console.log(chalk.cyan("[+] Found buildId: " + buildId));
210
+ printMsg(MSG.Header, "[+] Found buildId: " + buildId);
156
211
  // now, write it to a file
157
- fs.writeFileSync(path.join(output, new URL(url).host.replace(":", "_") + "/BUILD_ID"), buildId);
212
+ fs.writeFileSync(path.join(targetOutputDir, "BUILD_ID"), buildId);
158
213
  }
159
214
  }
160
215
  // if the research mode is enabled, then write the technique efficiency to a file
161
216
  if (research) {
162
217
  // prettify the JSON and write
163
218
  fs.writeFileSync(researchOutput, JSON.stringify(crawler.techniqueEfficiencyMapping, null, 4));
164
- console.log(chalk.green("[✓] Research mode enabled. Technique efficiency written to " + researchOutput));
219
+ printMsg(MSG.Run, "[✓] Research mode enabled. Technique efficiency written to " + researchOutput);
165
220
  }
166
221
  // extract the source maps
167
222
  yield extractSourceMaps(output, join(output, sourcemapDir));
168
223
  }
169
224
  else if (tech.name === "vue") {
170
- console.log(chalk.green("[✓] Vue.js detected"));
171
- console.log(chalk.yellow(`Evidence: ${tech.evidence}`));
172
- activeQueue = new DownloadQueue(output, threads);
225
+ printMsg(MSG.Run, "[✓] Vue.js detected");
226
+ printMsg(MSG.Warn, `Evidence: ${tech.evidence}`);
227
+ activeQueue = new DownloadQueue(output, threads, { alreadyBatchTargetRoot: isBatch });
173
228
  const queue = activeQueue;
174
- const onFilesDiscovered = (files) => queue.push(files);
229
+ const onFilesDiscovered = (files) => enqueue(queue, files);
230
+ const vueResearchMap = {};
231
+ const vueOnTechnique = research ? createTechniqueRecorder(vueResearchMap) : undefined;
175
232
  // run the full discovery pipeline against the entry URL
176
- const { clientSidePaths } = yield vue_discoverJsFiles(url, maxJsSizeMb, onFilesDiscovered, includeMethods, excludeMethods, threads);
233
+ const { clientSidePaths } = yield vue_discoverJsFiles(url, maxJsSizeMb, onFilesDiscovered, includeMethods, excludeMethods, threads, true, true, vueOnTechnique);
234
+ if (hardTimeoutReached)
235
+ return;
177
236
  // recurse the same pipeline through every client-side path we found
178
- yield vue_recursiveClientSidePathDownload(clientSidePaths, threads, maxJsSizeMb, onFilesDiscovered, includeMethods, excludeMethods);
237
+ yield vue_recursiveClientSidePathDownload(clientSidePaths, threads, maxJsSizeMb, onFilesDiscovered, includeMethods, excludeMethods, vueOnTechnique);
238
+ if (hardTimeoutReached)
239
+ return;
179
240
  yield queue.drain();
241
+ if (hardTimeoutReached)
242
+ return;
180
243
  queue.printSummary();
244
+ if (research) {
245
+ fs.writeFileSync(researchOutput, JSON.stringify(vueResearchMap, null, 4));
246
+ printMsg(MSG.Run, "[✓] Research mode enabled. Technique efficiency written to " + researchOutput);
247
+ }
181
248
  // extract the source maps
182
249
  yield extractSourceMaps(output, join(output, sourcemapDir));
183
250
  }
184
251
  else if (tech.name === "nuxt") {
185
- console.log(chalk.green("[✓] Nuxt.js detected"));
186
- console.log(chalk.yellow(`Evidence: ${tech.evidence}`));
187
- const queue = new DownloadQueue(output, threads);
252
+ printMsg(MSG.Run, "[✓] Nuxt.js detected");
253
+ printMsg(MSG.Warn, `Evidence: ${tech.evidence}`);
254
+ const queue = new DownloadQueue(output, threads, { alreadyBatchTargetRoot: isBatch });
188
255
  activeQueue = queue;
256
+ const nuxtResearchMap = {};
189
257
  // find the files from the page source
190
258
  const jsFilesFromPageSource = shouldRunMethod("nuxt_getFromPageSource", includeMethods, excludeMethods)
191
259
  ? yield nuxt_getFromPageSource(url)
192
260
  : [];
193
- queue.push(jsFilesFromPageSource);
261
+ enqueue(queue, jsFilesFromPageSource);
262
+ if (research)
263
+ accumulateTechnique(nuxtResearchMap, "nuxt_getFromPageSource", jsFilesFromPageSource);
194
264
  const jsFilesFromStringAnalysis = shouldRunMethod("nuxt_stringAnalysisJSFiles", includeMethods, excludeMethods)
195
265
  ? yield nuxt_stringAnalysisJSFiles(url, threads)
196
266
  : [];
197
- queue.push(jsFilesFromStringAnalysis);
267
+ enqueue(queue, jsFilesFromStringAnalysis);
268
+ if (research)
269
+ accumulateTechnique(nuxtResearchMap, "nuxt_stringAnalysisJSFiles", jsFilesFromStringAnalysis);
198
270
  const firstBatch = [...new Set([...jsFilesFromPageSource, ...jsFilesFromStringAnalysis])];
199
- let jsFilesFromAST = [];
271
+ const jsFilesFromAST = [];
200
272
  if (shouldRunMethod("nuxt_astParse", includeMethods, excludeMethods)) {
201
- console.log(chalk.cyan("[i] Analyzing functions in the files found"));
273
+ printMsg(MSG.Header, "[i] Analyzing functions in the files found");
202
274
  for (const jsFile of firstBatch) {
203
275
  jsFilesFromAST.push(...(yield nuxt_astParse(jsFile)));
204
276
  }
205
277
  }
206
- queue.push(jsFilesFromAST);
207
- queue.push(lazyLoadGlobals.getJsUrls());
278
+ enqueue(queue, jsFilesFromAST);
279
+ if (research)
280
+ accumulateTechnique(nuxtResearchMap, "nuxt_astParse", jsFilesFromAST);
281
+ enqueue(queue, lazyLoadGlobals.getJsUrls());
208
282
  const buildsManifestFiles = shouldRunMethod("nuxt_getBuildsManifest", includeMethods, excludeMethods)
209
283
  ? yield nuxt_getBuildsManifest(url)
210
284
  : [];
211
- queue.push(buildsManifestFiles);
285
+ enqueue(queue, buildsManifestFiles);
286
+ if (research)
287
+ accumulateTechnique(nuxtResearchMap, "nuxt_getBuildsManifest", buildsManifestFiles);
212
288
  yield queue.drain();
289
+ if (hardTimeoutReached)
290
+ return;
213
291
  queue.printSummary();
292
+ if (research) {
293
+ fs.writeFileSync(researchOutput, JSON.stringify(nuxtResearchMap, null, 4));
294
+ printMsg(MSG.Run, "[✓] Research mode enabled. Technique efficiency written to " + researchOutput);
295
+ }
214
296
  }
215
297
  else if (tech.name === "svelte") {
216
- console.log(chalk.green("[✓] Svelte detected"));
217
- console.log(chalk.yellow(`Evidence: ${tech.evidence}`));
218
- const queue = new DownloadQueue(output, threads);
298
+ printMsg(MSG.Run, "[✓] Svelte detected");
299
+ printMsg(MSG.Warn, `Evidence: ${tech.evidence}`);
300
+ const queue = new DownloadQueue(output, threads, { alreadyBatchTargetRoot: isBatch });
219
301
  activeQueue = queue;
302
+ const svelteResearchMap = {};
220
303
  // find the files from the page source
221
304
  const jsFilesFromPageSource = shouldRunMethod("svelte_getFromPageSource", includeMethods, excludeMethods)
222
305
  ? yield svelte_getFromPageSource(url)
223
306
  : [];
224
- queue.push(jsFilesFromPageSource);
307
+ enqueue(queue, jsFilesFromPageSource);
308
+ if (research)
309
+ accumulateTechnique(svelteResearchMap, "svelte_getFromPageSource", jsFilesFromPageSource);
225
310
  // probe /<appDir>/version.json — SvelteKit serves this for the `updated` store
226
311
  // but never references it from HTML or JS, so it is invisible to all other steps
227
312
  const appDir = (() => {
@@ -239,8 +324,10 @@ const lazyLoad = (url_1, output_1, strictScope_1, inputScope_1, threads_1, subse
239
324
  ? yield svelte_getVersionJson(url, appDir)
240
325
  : [];
241
326
  if (versionJsonFiles.length > 0) {
242
- queue.push(versionJsonFiles);
327
+ enqueue(queue, versionJsonFiles);
243
328
  }
329
+ if (research)
330
+ accumulateTechnique(svelteResearchMap, "svelte_getVersionJson", versionJsonFiles);
244
331
  // analyze the strings now
245
332
  let jsFilesFromStringAnalysis = [];
246
333
  let mapFilesFromStringAnalysis = [];
@@ -248,27 +335,36 @@ const lazyLoad = (url_1, output_1, strictScope_1, inputScope_1, threads_1, subse
248
335
  const result = yield svelte_stringAnalysisJSFiles(url, threads);
249
336
  jsFilesFromStringAnalysis = result.jsFiles;
250
337
  mapFilesFromStringAnalysis = result.mapFiles;
251
- queue.push(jsFilesFromStringAnalysis);
338
+ enqueue(queue, jsFilesFromStringAnalysis);
252
339
  if (mapFilesFromStringAnalysis.length > 0) {
253
- queue.push(mapFilesFromStringAnalysis);
340
+ enqueue(queue, mapFilesFromStringAnalysis);
341
+ }
342
+ if (research) {
343
+ accumulateTechnique(svelteResearchMap, "svelte_stringAnalysisJSFiles", jsFilesFromStringAnalysis);
344
+ accumulateTechnique(svelteResearchMap, "svelte_stringAnalysisJSFiles", mapFilesFromStringAnalysis);
254
345
  }
255
346
  }
256
347
  // recursively follow ESM static imports (import ... from "./chunk.js")
348
+ // svelte_followImports reuses react_followImports — tracked under react_followImports key
257
349
  const visited = new Set();
258
350
  let toFollow = [...new Set([...jsFilesFromPageSource, ...jsFilesFromStringAnalysis])];
259
351
  while (toFollow.length > 0) {
260
352
  const newFiles = yield react_followImports(toFollow, maxJsSizeMb, url, visited, threads);
261
353
  if (newFiles.length === 0)
262
354
  break;
263
- console.log(chalk.green(`[✓] Discovered ${newFiles.length} more JS file(s) via imports`));
264
- queue.push(newFiles);
355
+ printMsg(MSG.Run, `[✓] Discovered ${newFiles.length} more JS file(s) via imports`);
356
+ enqueue(queue, newFiles);
357
+ if (research)
358
+ accumulateTechnique(svelteResearchMap, "react_followImports", newFiles);
265
359
  toFollow = newFiles;
266
360
  }
267
361
  // crawl same-origin HTML pages found via <a href> and <link href>,
268
362
  // running the full JS-discovery pipeline on each
269
363
  const jsFilesFromPageCrawl = shouldRunMethod("svelte_recursivePageCrawl", includeMethods, excludeMethods)
270
- ? yield svelte_recursivePageCrawl(url, maxJsSizeMb, (files) => queue.push(files))
364
+ ? yield svelte_recursivePageCrawl(url, maxJsSizeMb, (files) => enqueue(queue, files))
271
365
  : [];
366
+ if (research)
367
+ accumulateTechnique(svelteResearchMap, "svelte_recursivePageCrawl", jsFilesFromPageCrawl);
272
368
  // Svelte/Astro apps use client-side routing — the home page rarely has
273
369
  // <a href> links in its server-rendered HTML. Scan downloaded JS for
274
370
  // embedded page path strings (e.g. "/admin", "/debug") and visit each
@@ -278,31 +374,46 @@ const lazyLoad = (url_1, output_1, strictScope_1, inputScope_1, threads_1, subse
278
374
  ? yield svelte_discoverPagesFromJs(url, threads)
279
375
  : [];
280
376
  if (jsFilesFromPathScan.length > 0) {
281
- queue.push(jsFilesFromPathScan);
377
+ enqueue(queue, jsFilesFromPathScan);
282
378
  }
379
+ if (research)
380
+ accumulateTechnique(svelteResearchMap, "svelte_discoverPagesFromJs", jsFilesFromPathScan);
283
381
  // run string analysis once more to catch JS files discovered during page crawl
284
382
  if ((jsFilesFromPageCrawl.length > 0 || jsFilesFromPathScan.length > 0) &&
285
383
  shouldRunMethod("svelte_stringAnalysisJSFiles", includeMethods, excludeMethods)) {
286
384
  const { jsFiles: jsFilesFromStringAnalysis2, mapFiles: mapFilesFromStringAnalysis2 } = yield svelte_stringAnalysisJSFiles(url, threads);
287
- queue.push(jsFilesFromStringAnalysis2);
385
+ enqueue(queue, jsFilesFromStringAnalysis2);
288
386
  if (mapFilesFromStringAnalysis2.length > 0) {
289
- queue.push(mapFilesFromStringAnalysis2);
387
+ enqueue(queue, mapFilesFromStringAnalysis2);
388
+ }
389
+ if (research) {
390
+ accumulateTechnique(svelteResearchMap, "svelte_stringAnalysisJSFiles", jsFilesFromStringAnalysis2);
391
+ accumulateTechnique(svelteResearchMap, "svelte_stringAnalysisJSFiles", mapFilesFromStringAnalysis2);
290
392
  }
291
393
  }
292
394
  yield queue.drain();
395
+ if (hardTimeoutReached)
396
+ return;
293
397
  queue.printSummary();
398
+ if (research) {
399
+ fs.writeFileSync(researchOutput, JSON.stringify(svelteResearchMap, null, 4));
400
+ printMsg(MSG.Run, "[✓] Research mode enabled. Technique efficiency written to " + researchOutput);
401
+ }
294
402
  yield extractSourceMaps(output, join(output, sourcemapDir));
295
403
  }
296
404
  else if (tech.name === "angular") {
297
- console.log(chalk.green("[✓] Angular detected"));
298
- console.log(chalk.yellow(`Evidence: ${tech.evidence}`));
299
- const queue = new DownloadQueue(output, threads);
405
+ printMsg(MSG.Run, "[✓] Angular detected");
406
+ printMsg(MSG.Warn, `Evidence: ${tech.evidence}`);
407
+ const queue = new DownloadQueue(output, threads, { alreadyBatchTargetRoot: isBatch });
300
408
  activeQueue = queue;
409
+ const angularResearchMap = {};
301
410
  // find the files from the page source
302
411
  const jsFilesFromPageSource = shouldRunMethod("angular_getFromPageSource", includeMethods, excludeMethods)
303
412
  ? yield angular_getFromPageSource(url)
304
413
  : [];
305
- queue.push(jsFilesFromPageSource);
414
+ enqueue(queue, jsFilesFromPageSource);
415
+ if (research)
416
+ accumulateTechnique(angularResearchMap, "angular_getFromPageSource", jsFilesFromPageSource);
306
417
  // files using the main.js
307
418
  if (shouldRunMethod("angular_getFromMainJs", includeMethods, excludeMethods)) {
308
419
  let mainJsUrl;
@@ -314,130 +425,155 @@ const lazyLoad = (url_1, output_1, strictScope_1, inputScope_1, threads_1, subse
314
425
  }
315
426
  if (mainJsUrl) {
316
427
  const jsFilesFromMainJs = yield angular_getFromMainJs(mainJsUrl);
317
- queue.push(jsFilesFromMainJs);
428
+ enqueue(queue, jsFilesFromMainJs);
429
+ if (research)
430
+ accumulateTechnique(angularResearchMap, "angular_getFromMainJs", jsFilesFromMainJs);
318
431
  // recursively follow chunk-to-chunk imports beyond the first hop
319
432
  if (shouldRunMethod("angular_recursiveChunkImports", includeMethods, excludeMethods)) {
320
433
  const transitiveChunks = yield angular_recursiveChunkImports(jsFilesFromMainJs, threads);
321
- queue.push(transitiveChunks);
434
+ enqueue(queue, transitiveChunks);
435
+ if (research)
436
+ accumulateTechnique(angularResearchMap, "angular_recursiveChunkImports", transitiveChunks);
322
437
  }
323
438
  }
324
439
  }
325
440
  yield queue.drain();
441
+ if (hardTimeoutReached)
442
+ return;
326
443
  queue.printSummary();
444
+ if (research) {
445
+ fs.writeFileSync(researchOutput, JSON.stringify(angularResearchMap, null, 4));
446
+ printMsg(MSG.Run, "[✓] Research mode enabled. Technique efficiency written to " + researchOutput);
447
+ }
327
448
  }
328
449
  else if (tech.name === "react") {
329
- console.log(chalk.green("[✓] React detected"));
330
- console.log(chalk.yellow(`Evidence: ${tech.evidence}`));
331
- const queue = new DownloadQueue(output, threads);
450
+ printMsg(MSG.Run, "[✓] React detected");
451
+ printMsg(MSG.Warn, `Evidence: ${tech.evidence}`);
452
+ const queue = new DownloadQueue(output, threads, {
453
+ compactOutput: true,
454
+ onProgress: (progress) => activeDownloadProgress === null || activeDownloadProgress === void 0 ? void 0 : activeDownloadProgress.update(progress),
455
+ alreadyBatchTargetRoot: isBatch,
456
+ });
332
457
  activeQueue = queue;
458
+ const reactResearchMap = {};
333
459
  // Seed: <script src> tags + <link rel="modulepreload"> (Vite vendor chunks)
334
460
  const jsFilesFromPageSource = shouldRunMethod("react_getScriptTags", includeMethods, excludeMethods)
335
- ? yield react_getScriptTags(url, maxJsSizeMb, output)
461
+ ? yield react_getScriptTags(url, maxJsSizeMb, output, isBatch)
336
462
  : [];
337
- queue.push(jsFilesFromPageSource);
463
+ if (hardTimeoutReached)
464
+ return;
465
+ enqueue(queue, jsFilesFromPageSource);
466
+ if (research)
467
+ accumulateTechnique(reactResearchMap, "react_getScriptTags", jsFilesFromPageSource);
338
468
  // webpack-style chunk path builders (CRA / custom webpack configs)
339
469
  const webpackChunkPaths = shouldRunMethod("react_webpackChunkPaths", includeMethods, excludeMethods)
340
470
  ? yield react_webpackChunkPaths(url, maxJsSizeMb, jsFilesFromPageSource, threads)
341
471
  : [];
342
- queue.push(webpackChunkPaths);
343
- // Recursively follow ESM imports and Vite __vite_mapDeps references.
344
- // visited starts empty so the seed files are fetched and parsed in the first round.
345
- const visited = new Set();
346
- if (shouldRunMethod("react_followImports", includeMethods, excludeMethods)) {
347
- let toFollow = [...new Set([...jsFilesFromPageSource, ...webpackChunkPaths])];
348
- while (toFollow.length > 0) {
349
- const newFiles = yield react_followImports(toFollow, maxJsSizeMb, url, visited, threads);
350
- if (newFiles.length === 0)
351
- break;
352
- console.log(chalk.green(`[✓] Discovered ${newFiles.length} more JS file(s) via imports`));
353
- queue.push(newFiles);
354
- toFollow = newFiles;
472
+ if (hardTimeoutReached)
473
+ return;
474
+ activeDownloadProgress = new DownloadProgress();
475
+ activeDownloadProgress.update(queue.progress);
476
+ try {
477
+ enqueue(queue, webpackChunkPaths);
478
+ if (research)
479
+ accumulateTechnique(reactResearchMap, "react_webpackChunkPaths", webpackChunkPaths);
480
+ // Recursively follow ESM imports and Vite __vite_mapDeps references.
481
+ // visited starts empty so the seed files are fetched and parsed in the first round.
482
+ const visited = new Set();
483
+ if (shouldRunMethod("react_followImports", includeMethods, excludeMethods)) {
484
+ let toFollow = [...new Set([...jsFilesFromPageSource, ...webpackChunkPaths])];
485
+ while (toFollow.length > 0 && !hardTimeoutReached) {
486
+ const newFiles = yield react_followImports(toFollow, maxJsSizeMb, url, visited, threads, true);
487
+ if (hardTimeoutReached)
488
+ break;
489
+ if (newFiles.length === 0)
490
+ break;
491
+ progressLog(chalk.green(`[✓] Discovered ${newFiles.length} more JS file(s) via imports`));
492
+ enqueue(queue, newFiles);
493
+ if (research)
494
+ accumulateTechnique(reactResearchMap, "react_followImports", newFiles);
495
+ toFollow = newFiles;
496
+ }
497
+ }
498
+ // Sourcemaps for everything discovered
499
+ if (!hardTimeoutReached &&
500
+ shouldRunMethod("react_sourcemapUrls", includeMethods, excludeMethods)) {
501
+ const sourcemapUrls = yield react_sourcemapUrls([...visited], threads);
502
+ enqueue(queue, sourcemapUrls);
503
+ if (research)
504
+ accumulateTechnique(reactResearchMap, "react_sourcemapUrls", sourcemapUrls);
355
505
  }
506
+ yield queue.drain();
356
507
  }
357
- // Sourcemaps for everything discovered
358
- if (shouldRunMethod("react_sourcemapUrls", includeMethods, excludeMethods)) {
359
- const sourcemapUrls = yield react_sourcemapUrls([...visited], threads);
360
- queue.push(sourcemapUrls);
508
+ finally {
509
+ activeDownloadProgress === null || activeDownloadProgress === void 0 ? void 0 : activeDownloadProgress.stop();
510
+ activeDownloadProgress = null;
361
511
  }
362
- yield queue.drain();
512
+ if (hardTimeoutReached)
513
+ return;
363
514
  queue.printSummary();
364
- extractSourceMaps(output, join(output, sourcemapDir));
515
+ if (research) {
516
+ fs.writeFileSync(researchOutput, JSON.stringify(reactResearchMap, null, 4));
517
+ printMsg(MSG.Run, "[✓] Research mode enabled. Technique efficiency written to " + researchOutput);
518
+ }
519
+ yield extractSourceMaps(output, join(output, sourcemapDir));
365
520
  }
366
521
  }
367
522
  else {
368
- console.error(chalk.red("[!] Framework not detected :("));
369
- console.log(chalk.magenta(CONFIG.notFoundMessage));
370
- console.log(chalk.yellow("[i] Trying to download loaded JS files"));
371
- const js_urls = yield downloadLoadedJs(url);
372
- if (js_urls && js_urls.length > 0) {
373
- console.log(chalk.green(`[✓] Found ${js_urls.length} JS chunks`));
374
- // Second-chance tech detection: scan downloaded URL paths for
375
- // framework signatures that Puppeteer may have missed on timeout
376
- // (e.g. Next.js served at a non-root basePath).
377
- let secondChanceTech = null;
378
- let secondChanceEvidence = "";
379
- for (const u of js_urls) {
380
- if (u.includes("/_next/")) {
381
- secondChanceTech = "next";
382
- secondChanceEvidence = u;
383
- break;
384
- }
385
- if (u.includes("/_nuxt/")) {
386
- secondChanceTech = "nuxt";
387
- secondChanceEvidence = u;
388
- break;
389
- }
390
- if (u.includes("/_app/immutable/")) {
391
- secondChanceTech = "svelte";
392
- secondChanceEvidence = u;
393
- break;
394
- }
395
- }
396
- if (secondChanceTech) {
397
- console.log(chalk.green(`[✓] Detected ${secondChanceTech} from downloaded file paths (evidence: ${secondChanceEvidence})`));
398
- globals.setTech(secondChanceTech);
399
- }
400
- const queue = new DownloadQueue(output, threads);
401
- queue.push(js_urls);
402
- yield queue.drain();
403
- queue.printSummary();
523
+ printMsg(MSG.Warn, "[i] Framework not detected — falling back to generic JS extraction");
524
+ globals.setTech("generic");
525
+ // If the caller didn't customize scope (still the "*" default) and isn't
526
+ // using --strict-scope, default the generic crawl's scope to the origin
527
+ // reached after following redirects, rather than crawling the whole web.
528
+ if (!strictScope && inputScope.length === 1 && inputScope[0] === "*") {
529
+ const resolvedScope = yield resolveGenericScope(url, maxRedirects);
530
+ lazyLoadGlobals.setScope(resolvedScope);
531
+ }
532
+ // Downloads JS incrementally as pages are crawled (see generic_crawlPages.ts) —
533
+ // the returned list is only used for the final summary count.
534
+ const genericResearchMap = {};
535
+ const allUrls = yield generic_crawlPages(url, maxJsSizeMb, output, maxPageVisits, threads, research ? genericResearchMap : undefined, stringsEnabled, stringsMaxIterations, getLastInterceptedUrls(), stagnationTimeinMs, stagnationPercentage, stagnationMonitorMs);
536
+ if (hardTimeoutReached)
537
+ return;
538
+ if (allUrls.length > 0) {
539
+ printMsg(MSG.Run, `[✓] Found ${allUrls.length} JS file(s)`);
540
+ }
541
+ else {
542
+ printMsg(MSG.Warn, "[i] No JS files discovered");
543
+ }
544
+ if (research) {
545
+ fs.writeFileSync(researchOutput, JSON.stringify(genericResearchMap, null, 4));
546
+ printMsg(MSG.Run, "[✓] Research mode enabled. Technique efficiency written to " + researchOutput);
404
547
  }
405
548
  }
406
549
  }
407
550
  });
408
551
  if (hardTimeoutMs === 0) {
409
- yield work();
410
- return;
552
+ try {
553
+ yield withRequestSignal(workController.signal, work);
554
+ return;
555
+ }
556
+ finally {
557
+ cancellationSignal === null || cancellationSignal === void 0 ? void 0 : cancellationSignal.removeEventListener("abort", handleExternalCancellation);
558
+ yield cleanupActiveWork();
559
+ }
560
+ }
561
+ const timeoutHandle = setTimeout(() => {
562
+ requestCancellation();
563
+ const timeoutLabel = hardTimeoutMs >= 60000 ? `${hardTimeoutMs / 60000} min` : `${hardTimeoutMs}ms`;
564
+ progressWarn(chalk.yellow(`[!] Lazyload hard timeout reached (${timeoutLabel}). Stopping discovery and waiting for in-flight work...`));
565
+ }, hardTimeoutMs);
566
+ try {
567
+ // Never return while work() can still mutate files, globals, or terminal
568
+ // state. The timeout requests cooperative cancellation, then this await
569
+ // provides the lifecycle boundary for all in-flight framework work.
570
+ yield withRequestSignal(workController.signal, work);
571
+ }
572
+ finally {
573
+ clearTimeout(timeoutHandle);
574
+ cancellationSignal === null || cancellationSignal === void 0 ? void 0 : cancellationSignal.removeEventListener("abort", handleExternalCancellation);
575
+ yield cleanupActiveWork();
411
576
  }
412
- let timeoutHandle;
413
- yield Promise.race([
414
- work().finally(() => {
415
- // work() finished before the timeout — cancel the timer so it never
416
- // fires orphaned and doesn't hold the event loop open.
417
- if (timeoutHandle !== undefined)
418
- clearTimeout(timeoutHandle);
419
- }),
420
- new Promise((resolve) => {
421
- timeoutHandle = setTimeout(() => {
422
- console.error(chalk.yellow(`[!] Lazyload hard timeout reached (${hardTimeoutMs / 60000} min). Draining discovered files before moving on...`));
423
- // Signal the crawler to stop at its next iteration boundary.
424
- activeCrawler === null || activeCrawler === void 0 ? void 0 : activeCrawler.stop();
425
- const q = activeQueue;
426
- if (q) {
427
- // Wait for already-queued downloads to finish, then move on.
428
- q.drain()
429
- .then(() => {
430
- q.printSummary();
431
- resolve();
432
- })
433
- .catch(() => resolve());
434
- }
435
- else {
436
- resolve();
437
- }
438
- }, hardTimeoutMs);
439
- }),
440
- ]);
441
577
  });
442
578
  export default lazyLoad;
443
579
  //# sourceMappingURL=index.js.map