explorbot 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. package/README.md +80 -26
  2. package/bin/explorbot-cli.ts +680 -0
  3. package/boat/api-tester/src/ai/chief/styles.ts +15 -0
  4. package/boat/api-tester/src/ai/chief.ts +335 -0
  5. package/boat/api-tester/src/ai/curler-tools.ts +278 -0
  6. package/boat/api-tester/src/ai/curler.ts +306 -0
  7. package/boat/api-tester/src/api-client.ts +28 -0
  8. package/boat/api-tester/src/apibot.ts +203 -0
  9. package/boat/api-tester/src/cli.ts +301 -0
  10. package/boat/api-tester/src/config.ts +190 -0
  11. package/dist/bin/explorbot-cli.js +23 -101
  12. package/dist/boat/api-tester/bin/apibot-cli.js +0 -1
  13. package/dist/boat/api-tester/src/ai/chief/styles.js +0 -1
  14. package/dist/boat/api-tester/src/ai/chief.js +0 -1
  15. package/dist/boat/api-tester/src/ai/curler-tools.js +0 -1
  16. package/dist/boat/api-tester/src/ai/curler.js +0 -1
  17. package/dist/boat/api-tester/src/api-client.js +0 -1
  18. package/dist/boat/api-tester/src/apibot.js +0 -1
  19. package/dist/boat/api-tester/src/cli.js +0 -1
  20. package/dist/boat/api-tester/src/config.js +0 -1
  21. package/dist/src/action-result.js +0 -1
  22. package/dist/src/action.js +14 -12
  23. package/dist/src/activity.js +0 -1
  24. package/dist/src/ai/agent.js +0 -1
  25. package/dist/src/ai/bosun.js +0 -1
  26. package/dist/src/ai/captain/idle-mode.js +0 -1
  27. package/dist/src/ai/captain/mixin.js +0 -1
  28. package/dist/src/ai/captain/test-mode.js +0 -1
  29. package/dist/src/ai/captain/web-mode.js +0 -1
  30. package/dist/src/ai/captain.js +0 -1
  31. package/dist/src/ai/conversation.js +0 -1
  32. package/dist/src/ai/experience-compactor.js +0 -1
  33. package/dist/src/ai/fisherman-tools.js +0 -1
  34. package/dist/src/ai/fisherman.js +0 -1
  35. package/dist/src/ai/historian.js +0 -1
  36. package/dist/src/ai/navigator.js +0 -1
  37. package/dist/src/ai/pilot.js +0 -1
  38. package/dist/src/ai/planner/session-dedup.js +0 -1
  39. package/dist/src/ai/planner/styles.js +0 -1
  40. package/dist/src/ai/planner/subpages.js +42 -7
  41. package/dist/src/ai/planner.js +15 -4
  42. package/dist/src/ai/provider.js +0 -1
  43. package/dist/src/ai/quartermaster.js +0 -1
  44. package/dist/src/ai/researcher/cache.js +13 -9
  45. package/dist/src/ai/researcher/coordinates.js +4 -3
  46. package/dist/src/ai/researcher/deep-analysis.js +16 -20
  47. package/dist/src/ai/researcher/fingerprint-worker.js +0 -1
  48. package/dist/src/ai/researcher/focus.js +0 -1
  49. package/dist/src/ai/researcher/locators.js +1 -2
  50. package/dist/src/ai/researcher/mixin.js +0 -1
  51. package/dist/src/ai/researcher/parser.js +4 -4
  52. package/dist/src/ai/researcher/research-result.js +2 -1
  53. package/dist/src/ai/researcher.js +6 -6
  54. package/dist/src/ai/rules.js +0 -1
  55. package/dist/src/ai/task-agent.js +0 -1
  56. package/dist/src/ai/tester.js +0 -1
  57. package/dist/src/ai/tools.js +4 -1
  58. package/dist/src/api/api-client.js +0 -1
  59. package/dist/src/api/request-result.js +0 -1
  60. package/dist/src/api/request-store.js +0 -1
  61. package/dist/src/api/spec-reader.js +0 -1
  62. package/dist/src/api/xhr-capture.js +0 -1
  63. package/dist/src/browser-server.js +0 -1
  64. package/dist/src/command-handler.js +0 -1
  65. package/dist/src/commands/add-rule-command.js +0 -1
  66. package/dist/src/commands/base-command.js +0 -1
  67. package/dist/src/commands/clean-command.js +0 -1
  68. package/dist/src/commands/context-aria-command.js +0 -1
  69. package/dist/src/commands/context-command.js +2 -3
  70. package/dist/src/commands/context-data-command.js +0 -1
  71. package/dist/src/commands/context-experience-command.js +0 -1
  72. package/dist/src/commands/context-html-command.js +0 -1
  73. package/dist/src/commands/context-knowledge-command.js +0 -1
  74. package/dist/src/commands/debug-command.js +0 -1
  75. package/dist/src/commands/drill-command.js +0 -1
  76. package/dist/src/commands/exit-command.js +0 -1
  77. package/dist/src/commands/explore-command.js +3 -3
  78. package/dist/src/commands/freesail-command.js +0 -1
  79. package/dist/src/commands/help-command.js +0 -1
  80. package/dist/src/commands/index.js +0 -1
  81. package/dist/src/commands/init-command.js +117 -0
  82. package/dist/src/commands/knows-command.js +0 -1
  83. package/dist/src/commands/learn-command.js +0 -1
  84. package/dist/src/commands/navigate-command.js +0 -1
  85. package/dist/src/commands/path-command.js +0 -1
  86. package/dist/src/commands/plan-clear-command.js +0 -1
  87. package/dist/src/commands/plan-command.js +6 -2
  88. package/dist/src/commands/plan-edit-command.js +0 -1
  89. package/dist/src/commands/plan-load-command.js +0 -1
  90. package/dist/src/commands/plan-reload-command.js +0 -1
  91. package/dist/src/commands/plan-save-command.js +0 -1
  92. package/dist/src/commands/research-command.js +0 -1
  93. package/dist/src/commands/start-command.js +0 -1
  94. package/dist/src/commands/status-command.js +0 -1
  95. package/dist/src/commands/test-command.js +0 -1
  96. package/dist/src/components/ActivityPane.js +0 -1
  97. package/dist/src/components/AddKnowledge.js +0 -1
  98. package/dist/src/components/AddRule.js +0 -1
  99. package/dist/src/components/App.js +0 -1
  100. package/dist/src/components/Autocomplete.js +0 -1
  101. package/dist/src/components/InputPane.js +0 -1
  102. package/dist/src/components/InputReadline.js +0 -1
  103. package/dist/src/components/LogPane.js +0 -1
  104. package/dist/src/components/PlanEditor.js +0 -1
  105. package/dist/src/components/PlanPane.js +0 -1
  106. package/dist/src/components/SessionTimer.js +0 -1
  107. package/dist/src/components/StateTransitionPane.js +0 -1
  108. package/dist/src/components/StatusPane.js +0 -1
  109. package/dist/src/components/TaskPane.js +0 -1
  110. package/dist/src/components/Welcome.js +0 -1
  111. package/dist/src/components/WelcomeChecklist.js +0 -1
  112. package/dist/src/components/WelcomeCommands.js +0 -1
  113. package/dist/src/components/autocomplete-store.js +0 -1
  114. package/dist/src/components/parse-keypress.js +0 -1
  115. package/dist/src/config.js +0 -1
  116. package/dist/src/execution-controller.js +0 -1
  117. package/dist/src/experience-tracker.js +0 -1
  118. package/dist/src/explorbot.js +1 -2
  119. package/dist/src/explorer.js +58 -17
  120. package/dist/src/index.js +0 -1
  121. package/dist/src/knowledge-tracker.js +2 -2
  122. package/dist/src/observability.js +0 -1
  123. package/dist/src/reporter.js +0 -1
  124. package/dist/src/state-manager.js +0 -1
  125. package/dist/src/stats.js +0 -1
  126. package/dist/src/test-plan.js +0 -1
  127. package/dist/src/utils/aria.js +0 -1
  128. package/dist/src/utils/cli-name.js +16 -0
  129. package/dist/src/utils/code-extractor.js +0 -1
  130. package/dist/src/utils/context-formatter.js +0 -1
  131. package/dist/src/utils/error-page.js +0 -1
  132. package/dist/src/utils/expandable.js +0 -1
  133. package/dist/src/utils/hooks-runner.js +0 -1
  134. package/dist/src/utils/html-diff.js +0 -1
  135. package/dist/src/utils/html.js +0 -1
  136. package/dist/src/utils/logger.js +0 -1
  137. package/dist/src/utils/loop.js +0 -1
  138. package/dist/src/utils/markdown-parser.js +0 -1
  139. package/dist/src/utils/markdown-query.js +0 -1
  140. package/dist/src/utils/markdown-terminal.js +0 -1
  141. package/dist/src/utils/research-parser.js +0 -1
  142. package/dist/src/utils/retry.js +0 -1
  143. package/dist/src/utils/rules-loader.js +0 -1
  144. package/dist/src/utils/strings.js +0 -1
  145. package/dist/src/utils/test-plan-markdown.js +0 -1
  146. package/dist/src/utils/throttle.js +0 -1
  147. package/dist/src/utils/unique-names.js +0 -1
  148. package/dist/src/utils/url-matcher.js +0 -1
  149. package/dist/src/utils/web-element.js +6 -5
  150. package/dist/src/utils/xpath.js +0 -1
  151. package/package.json +28 -4
  152. package/src/action-result.ts +694 -0
  153. package/src/action.ts +449 -0
  154. package/src/activity.ts +111 -0
  155. package/src/ai/agent.ts +3 -0
  156. package/src/ai/bosun.ts +557 -0
  157. package/src/ai/captain/idle-mode.ts +116 -0
  158. package/src/ai/captain/mixin.ts +22 -0
  159. package/src/ai/captain/test-mode.ts +262 -0
  160. package/src/ai/captain/web-mode.ts +136 -0
  161. package/src/ai/captain.ts +504 -0
  162. package/src/ai/conversation.ts +205 -0
  163. package/src/ai/experience-compactor.ts +284 -0
  164. package/src/ai/fisherman-tools.ts +181 -0
  165. package/src/ai/fisherman.ts +223 -0
  166. package/src/ai/historian.ts +457 -0
  167. package/src/ai/navigator.ts +572 -0
  168. package/src/ai/pilot.ts +776 -0
  169. package/src/ai/planner/session-dedup.ts +35 -0
  170. package/src/ai/planner/styles.ts +17 -0
  171. package/src/ai/planner/subpages.ts +171 -0
  172. package/src/ai/planner.ts +549 -0
  173. package/src/ai/provider.ts +613 -0
  174. package/src/ai/quartermaster.ts +286 -0
  175. package/src/ai/researcher/cache.ts +109 -0
  176. package/src/ai/researcher/coordinates.ts +239 -0
  177. package/src/ai/researcher/deep-analysis.ts +412 -0
  178. package/src/ai/researcher/fingerprint-worker.ts +59 -0
  179. package/src/ai/researcher/focus.ts +42 -0
  180. package/src/ai/researcher/locators.ts +282 -0
  181. package/src/ai/researcher/mixin.ts +4 -0
  182. package/src/ai/researcher/parser.ts +186 -0
  183. package/src/ai/researcher/research-result.ts +116 -0
  184. package/src/ai/researcher.ts +858 -0
  185. package/src/ai/rules.ts +376 -0
  186. package/src/ai/task-agent.ts +141 -0
  187. package/src/ai/tester.ts +939 -0
  188. package/src/ai/tools.ts +1122 -0
  189. package/src/api/api-client.ts +109 -0
  190. package/src/api/request-result.ts +212 -0
  191. package/src/api/request-store.ts +130 -0
  192. package/src/api/spec-reader.ts +174 -0
  193. package/src/api/xhr-capture.ts +100 -0
  194. package/src/browser-server.ts +74 -0
  195. package/src/command-handler.ts +454 -0
  196. package/src/commands/add-rule-command.ts +63 -0
  197. package/src/commands/base-command.ts +27 -0
  198. package/src/commands/clean-command.ts +73 -0
  199. package/src/commands/context-aria-command.ts +22 -0
  200. package/src/commands/context-command.ts +67 -0
  201. package/src/commands/context-data-command.ts +30 -0
  202. package/src/commands/context-experience-command.ts +48 -0
  203. package/src/commands/context-html-command.ts +33 -0
  204. package/src/commands/context-knowledge-command.ts +43 -0
  205. package/src/commands/debug-command.ts +13 -0
  206. package/src/commands/drill-command.ts +34 -0
  207. package/src/commands/exit-command.ts +32 -0
  208. package/src/commands/explore-command.ts +129 -0
  209. package/src/commands/freesail-command.ts +95 -0
  210. package/src/commands/help-command.ts +8 -0
  211. package/src/commands/index.ts +69 -0
  212. package/src/commands/init-command.ts +131 -0
  213. package/src/commands/knows-command.ts +68 -0
  214. package/src/commands/learn-command.ts +44 -0
  215. package/src/commands/navigate-command.ts +18 -0
  216. package/src/commands/path-command.ts +83 -0
  217. package/src/commands/plan-clear-command.ts +14 -0
  218. package/src/commands/plan-command.ts +46 -0
  219. package/src/commands/plan-edit-command.ts +9 -0
  220. package/src/commands/plan-load-command.ts +18 -0
  221. package/src/commands/plan-reload-command.ts +28 -0
  222. package/src/commands/plan-save-command.ts +25 -0
  223. package/src/commands/research-command.ts +45 -0
  224. package/src/commands/start-command.ts +13 -0
  225. package/src/commands/status-command.tsx +23 -0
  226. package/src/commands/test-command.ts +84 -0
  227. package/src/components/ActivityPane.tsx +80 -0
  228. package/src/components/AddKnowledge.tsx +169 -0
  229. package/src/components/AddRule.tsx +174 -0
  230. package/src/components/App.tsx +377 -0
  231. package/src/components/Autocomplete.tsx +63 -0
  232. package/src/components/InputPane.tsx +259 -0
  233. package/src/components/InputReadline.tsx +704 -0
  234. package/src/components/LogPane.tsx +187 -0
  235. package/src/components/PlanEditor.tsx +150 -0
  236. package/src/components/PlanPane.tsx +71 -0
  237. package/src/components/SessionTimer.tsx +35 -0
  238. package/src/components/StateTransitionPane.tsx +149 -0
  239. package/src/components/StatusPane.tsx +62 -0
  240. package/src/components/TaskPane.tsx +119 -0
  241. package/src/components/Welcome.tsx +83 -0
  242. package/src/components/WelcomeChecklist.tsx +118 -0
  243. package/src/components/WelcomeCommands.tsx +102 -0
  244. package/src/components/autocomplete-store.ts +35 -0
  245. package/src/components/parse-keypress.ts +170 -0
  246. package/src/config.ts +491 -0
  247. package/src/execution-controller.ts +109 -0
  248. package/src/experience-tracker.ts +350 -0
  249. package/src/explorbot.ts +405 -0
  250. package/src/explorer.ts +760 -0
  251. package/src/index.tsx +62 -0
  252. package/src/knowledge-tracker.ts +230 -0
  253. package/src/observability.ts +150 -0
  254. package/src/reporter.ts +224 -0
  255. package/src/state-manager.ts +556 -0
  256. package/src/stats.ts +53 -0
  257. package/src/test-plan.ts +432 -0
  258. package/src/utils/aria.ts +629 -0
  259. package/src/utils/cli-name.ts +13 -0
  260. package/src/utils/code-extractor.ts +22 -0
  261. package/src/utils/context-formatter.ts +239 -0
  262. package/src/utils/error-page.ts +23 -0
  263. package/src/utils/expandable.ts +38 -0
  264. package/src/utils/hooks-runner.ts +79 -0
  265. package/src/utils/html-diff.ts +918 -0
  266. package/src/utils/html.ts +1316 -0
  267. package/src/utils/logger.ts +534 -0
  268. package/src/utils/loop.ts +176 -0
  269. package/src/utils/markdown-parser.ts +127 -0
  270. package/src/utils/markdown-query.ts +466 -0
  271. package/src/utils/markdown-terminal.ts +43 -0
  272. package/src/utils/research-parser.ts +11 -0
  273. package/src/utils/retry.ts +73 -0
  274. package/src/utils/rules-loader.ts +118 -0
  275. package/src/utils/strings.ts +13 -0
  276. package/src/utils/test-plan-markdown.ts +332 -0
  277. package/src/utils/throttle.ts +18 -0
  278. package/src/utils/unique-names.ts +14 -0
  279. package/src/utils/url-matcher.ts +45 -0
  280. package/src/utils/web-element.ts +147 -0
  281. package/src/utils/xpath.ts +129 -0
  282. package/dist/bin/explorbot-cli.js.map +0 -1
  283. package/dist/boat/api-tester/bin/apibot-cli.js.map +0 -1
  284. package/dist/boat/api-tester/example/apibot.config.js +0 -31
  285. package/dist/boat/api-tester/example/apibot.config.js.map +0 -1
  286. package/dist/boat/api-tester/src/ai/chief/styles.js.map +0 -1
  287. package/dist/boat/api-tester/src/ai/chief.js.map +0 -1
  288. package/dist/boat/api-tester/src/ai/curler-tools.js.map +0 -1
  289. package/dist/boat/api-tester/src/ai/curler.js.map +0 -1
  290. package/dist/boat/api-tester/src/api-client.js.map +0 -1
  291. package/dist/boat/api-tester/src/apibot.js.map +0 -1
  292. package/dist/boat/api-tester/src/cli.js.map +0 -1
  293. package/dist/boat/api-tester/src/config.js.map +0 -1
  294. package/dist/prompts/audit-rules.md +0 -124
  295. package/dist/src/action-result.js.map +0 -1
  296. package/dist/src/action.js.map +0 -1
  297. package/dist/src/activity.js.map +0 -1
  298. package/dist/src/ai/agent.js.map +0 -1
  299. package/dist/src/ai/bosun.js.map +0 -1
  300. package/dist/src/ai/captain/idle-mode.js.map +0 -1
  301. package/dist/src/ai/captain/mixin.js.map +0 -1
  302. package/dist/src/ai/captain/test-mode.js.map +0 -1
  303. package/dist/src/ai/captain/web-mode.js.map +0 -1
  304. package/dist/src/ai/captain.js.map +0 -1
  305. package/dist/src/ai/conversation.js.map +0 -1
  306. package/dist/src/ai/experience-compactor.js.map +0 -1
  307. package/dist/src/ai/fisherman-tools.js.map +0 -1
  308. package/dist/src/ai/fisherman.js.map +0 -1
  309. package/dist/src/ai/historian.js.map +0 -1
  310. package/dist/src/ai/navigator.js.map +0 -1
  311. package/dist/src/ai/pilot.js.map +0 -1
  312. package/dist/src/ai/planner/session-dedup.js.map +0 -1
  313. package/dist/src/ai/planner/styles.js.map +0 -1
  314. package/dist/src/ai/planner/subpages.js.map +0 -1
  315. package/dist/src/ai/planner.js.map +0 -1
  316. package/dist/src/ai/provider.js.map +0 -1
  317. package/dist/src/ai/quartermaster.js.map +0 -1
  318. package/dist/src/ai/researcher/cache.js.map +0 -1
  319. package/dist/src/ai/researcher/coordinates.js.map +0 -1
  320. package/dist/src/ai/researcher/deep-analysis.js.map +0 -1
  321. package/dist/src/ai/researcher/fingerprint-worker.js.map +0 -1
  322. package/dist/src/ai/researcher/focus.js.map +0 -1
  323. package/dist/src/ai/researcher/locators.js.map +0 -1
  324. package/dist/src/ai/researcher/mixin.js.map +0 -1
  325. package/dist/src/ai/researcher/parser.js.map +0 -1
  326. package/dist/src/ai/researcher/research-result.js.map +0 -1
  327. package/dist/src/ai/researcher.js.map +0 -1
  328. package/dist/src/ai/rules.js.map +0 -1
  329. package/dist/src/ai/task-agent.js.map +0 -1
  330. package/dist/src/ai/tester.js.map +0 -1
  331. package/dist/src/ai/tools.js.map +0 -1
  332. package/dist/src/api/api-client.js.map +0 -1
  333. package/dist/src/api/request-result.js.map +0 -1
  334. package/dist/src/api/request-store.js.map +0 -1
  335. package/dist/src/api/spec-reader.js.map +0 -1
  336. package/dist/src/api/xhr-capture.js.map +0 -1
  337. package/dist/src/browser-server.js.map +0 -1
  338. package/dist/src/command-handler.js.map +0 -1
  339. package/dist/src/commands/add-rule-command.js.map +0 -1
  340. package/dist/src/commands/base-command.js.map +0 -1
  341. package/dist/src/commands/clean-command.js.map +0 -1
  342. package/dist/src/commands/context-aria-command.js.map +0 -1
  343. package/dist/src/commands/context-command.js.map +0 -1
  344. package/dist/src/commands/context-data-command.js.map +0 -1
  345. package/dist/src/commands/context-experience-command.js.map +0 -1
  346. package/dist/src/commands/context-html-command.js.map +0 -1
  347. package/dist/src/commands/context-knowledge-command.js.map +0 -1
  348. package/dist/src/commands/debug-command.js.map +0 -1
  349. package/dist/src/commands/drill-command.js.map +0 -1
  350. package/dist/src/commands/exit-command.js.map +0 -1
  351. package/dist/src/commands/explore-command.js.map +0 -1
  352. package/dist/src/commands/freesail-command.js.map +0 -1
  353. package/dist/src/commands/help-command.js.map +0 -1
  354. package/dist/src/commands/index.js.map +0 -1
  355. package/dist/src/commands/knows-command.js.map +0 -1
  356. package/dist/src/commands/learn-command.js.map +0 -1
  357. package/dist/src/commands/navigate-command.js.map +0 -1
  358. package/dist/src/commands/path-command.js.map +0 -1
  359. package/dist/src/commands/plan-clear-command.js.map +0 -1
  360. package/dist/src/commands/plan-command.js.map +0 -1
  361. package/dist/src/commands/plan-edit-command.js.map +0 -1
  362. package/dist/src/commands/plan-load-command.js.map +0 -1
  363. package/dist/src/commands/plan-reload-command.js.map +0 -1
  364. package/dist/src/commands/plan-save-command.js.map +0 -1
  365. package/dist/src/commands/research-command.js.map +0 -1
  366. package/dist/src/commands/start-command.js.map +0 -1
  367. package/dist/src/commands/status-command.js.map +0 -1
  368. package/dist/src/commands/test-command.js.map +0 -1
  369. package/dist/src/components/ActivityPane.js.map +0 -1
  370. package/dist/src/components/AddKnowledge.js.map +0 -1
  371. package/dist/src/components/AddRule.js.map +0 -1
  372. package/dist/src/components/App.js.map +0 -1
  373. package/dist/src/components/Autocomplete.js.map +0 -1
  374. package/dist/src/components/InputPane.js.map +0 -1
  375. package/dist/src/components/InputReadline.js.map +0 -1
  376. package/dist/src/components/LogPane.js.map +0 -1
  377. package/dist/src/components/PlanEditor.js.map +0 -1
  378. package/dist/src/components/PlanPane.js.map +0 -1
  379. package/dist/src/components/SessionTimer.js.map +0 -1
  380. package/dist/src/components/StateTransitionPane.js.map +0 -1
  381. package/dist/src/components/StatusPane.js.map +0 -1
  382. package/dist/src/components/TaskPane.js.map +0 -1
  383. package/dist/src/components/Welcome.js.map +0 -1
  384. package/dist/src/components/WelcomeChecklist.js.map +0 -1
  385. package/dist/src/components/WelcomeCommands.js.map +0 -1
  386. package/dist/src/components/autocomplete-store.js.map +0 -1
  387. package/dist/src/components/parse-keypress.js.map +0 -1
  388. package/dist/src/config.js.map +0 -1
  389. package/dist/src/execution-controller.js.map +0 -1
  390. package/dist/src/experience-tracker.js.map +0 -1
  391. package/dist/src/explorbot.js.map +0 -1
  392. package/dist/src/explorer.js.map +0 -1
  393. package/dist/src/index.js.map +0 -1
  394. package/dist/src/knowledge-tracker.js.map +0 -1
  395. package/dist/src/observability.js.map +0 -1
  396. package/dist/src/reporter.js.map +0 -1
  397. package/dist/src/state-manager.js.map +0 -1
  398. package/dist/src/stats.js.map +0 -1
  399. package/dist/src/test-plan.js.map +0 -1
  400. package/dist/src/utils/aria.js.map +0 -1
  401. package/dist/src/utils/code-extractor.js.map +0 -1
  402. package/dist/src/utils/context-formatter.js.map +0 -1
  403. package/dist/src/utils/error-page.js.map +0 -1
  404. package/dist/src/utils/expandable.js.map +0 -1
  405. package/dist/src/utils/hooks-runner.js.map +0 -1
  406. package/dist/src/utils/html-diff.js.map +0 -1
  407. package/dist/src/utils/html.js.map +0 -1
  408. package/dist/src/utils/logger.js.map +0 -1
  409. package/dist/src/utils/loop.js.map +0 -1
  410. package/dist/src/utils/markdown-parser.js.map +0 -1
  411. package/dist/src/utils/markdown-query.js.map +0 -1
  412. package/dist/src/utils/markdown-terminal.js.map +0 -1
  413. package/dist/src/utils/research-parser.js.map +0 -1
  414. package/dist/src/utils/retry.js.map +0 -1
  415. package/dist/src/utils/rules-loader.js.map +0 -1
  416. package/dist/src/utils/strings.js.map +0 -1
  417. package/dist/src/utils/test-plan-markdown.js.map +0 -1
  418. package/dist/src/utils/throttle.js.map +0 -1
  419. package/dist/src/utils/unique-names.js.map +0 -1
  420. package/dist/src/utils/url-matcher.js.map +0 -1
  421. package/dist/src/utils/web-element.js.map +0 -1
  422. package/dist/src/utils/xpath.js.map +0 -1
  423. package/prompts/audit-rules.md +0 -124
@@ -0,0 +1,694 @@
1
+ import fs from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { ConfigParser, type HtmlConfig, outputPath } from './config.ts';
4
+ import type { Link, WebPageState } from './state-manager.ts';
5
+ import { compactAriaSnapshot, diffAriaSnapshots } from './utils/aria.ts';
6
+ import { type HtmlDiffPart, type HtmlDiffResult, htmlDiff } from './utils/html-diff.ts';
7
+ import { extractHeadings, extractLinks, extractTargetedHtml, htmlCombinedSnapshot, htmlMinimalUISnapshot, htmlTextSnapshot, minifyHtml } from './utils/html.ts';
8
+ import { createDebug } from './utils/logger.ts';
9
+ import { extractStatePath, matchesUrl } from './utils/url-matcher.ts';
10
+
11
+ const debugLog = createDebug('explorbot:state');
12
+
13
+ interface ActionResultData extends WebPageState {
14
+ html?: string;
15
+ fullUrl?: string | undefined;
16
+ screenshot?: Buffer;
17
+ screenshotFile?: string;
18
+ logFile?: string;
19
+ htmlFile?: string;
20
+ title?: string;
21
+ timestamp?: Date;
22
+ error?: string | null;
23
+ h1?: string | undefined;
24
+ h2?: string | undefined;
25
+ h3?: string | undefined;
26
+ h4?: string | undefined;
27
+ browserLogs?: any[];
28
+ iframeSnapshots?: Array<{ src: string; html: string; id?: string }>;
29
+ ariaSnapshot?: string | null;
30
+ ariaSnapshotFile?: string;
31
+ iframeURL?: string;
32
+ links?: Link[];
33
+ }
34
+
35
+ export interface PageDiff {
36
+ urlChanged: boolean;
37
+ previousUrl?: string;
38
+ currentUrl: string;
39
+ ariaChanges?: string | null;
40
+ htmlParts?: HtmlDiffPart[];
41
+ iframes?: string;
42
+ }
43
+
44
+ export interface ToolResultMetadata {
45
+ url: string;
46
+ locator: string;
47
+ targetedHtml: string;
48
+ pageDiff: PageDiff | null;
49
+ iframeURL?: string;
50
+ }
51
+
52
+ export class ActionResult implements ActionResultData {
53
+ public id?: number;
54
+ public title = '';
55
+ public error: string | null = null;
56
+ public timestamp: Date = new Date();
57
+ public h1: string | undefined = undefined;
58
+ public h2: string | undefined = undefined;
59
+ public h3: string | undefined = undefined;
60
+ public h4: string | undefined = undefined;
61
+ public url = '';
62
+ public fullUrl: string | undefined = undefined;
63
+ public browserLogs: any[] = [];
64
+ public iframeSnapshots: Array<{ src: string; html: string; id?: string }> = [];
65
+ public iframeURL: string | undefined = undefined;
66
+ readonly screenshotFile: string | undefined = undefined;
67
+ private _screenshot: Buffer | undefined = undefined;
68
+ readonly htmlFile: string | undefined = undefined;
69
+ private _html: string | undefined = undefined;
70
+ readonly logFile: string | undefined = undefined;
71
+ private _browserLogs: any[] | undefined = undefined;
72
+ readonly ariaSnapshotFile: string | undefined = undefined;
73
+ private _ariaSnapshot: string | null | undefined = undefined;
74
+ private _lastExtractedHtml: string | undefined = undefined;
75
+ notes: any;
76
+ public links: Link[] = [];
77
+ public verifications?: Record<string, boolean>;
78
+
79
+ constructor(data: ActionResultData) {
80
+ this.id = data.id;
81
+ this.timestamp = data.timestamp ?? new Date();
82
+ this.url = data.url ?? '';
83
+ this.fullUrl = data.fullUrl;
84
+ this.title = data.title ?? '';
85
+ this.error = data.error ?? null;
86
+ this.browserLogs = data.browserLogs ?? [];
87
+ this.iframeSnapshots = data.iframeSnapshots ?? [];
88
+ this.iframeURL = data.iframeURL;
89
+ this.notes = data.notes ?? [];
90
+ this.verifications = data.verifications;
91
+
92
+ // Set readonly properties
93
+ if (data.screenshotFile !== undefined) {
94
+ this.screenshotFile = data.screenshotFile;
95
+ }
96
+ if (data.htmlFile !== undefined) {
97
+ this.htmlFile = data.htmlFile;
98
+ }
99
+ if (data.logFile !== undefined) {
100
+ this.logFile = data.logFile;
101
+ }
102
+ if (data.ariaSnapshotFile !== undefined) {
103
+ this.ariaSnapshotFile = data.ariaSnapshotFile;
104
+ }
105
+
106
+ // Store HTML and browser logs in private properties if provided
107
+ if (data.html !== undefined) {
108
+ this._html = data.html;
109
+ }
110
+
111
+ if (data.browserLogs !== undefined) {
112
+ this._browserLogs = data.browserLogs;
113
+ }
114
+ if (data.screenshot !== undefined) {
115
+ this._screenshot = data.screenshot;
116
+ }
117
+ if (data.ariaSnapshot !== undefined) {
118
+ this._ariaSnapshot = data.ariaSnapshot;
119
+ }
120
+
121
+ if (!this.fullUrl && this.url && this.url !== '') {
122
+ this.fullUrl = this.url;
123
+ }
124
+
125
+ this.extractHeadings(this.html);
126
+
127
+ if (data.links) {
128
+ this.links = data.links;
129
+ } else if (this._html) {
130
+ this.links = extractLinks(this._html);
131
+ }
132
+
133
+ if (this.url && this.url !== '') {
134
+ this.url = extractStatePath(this.url);
135
+ }
136
+ }
137
+
138
+ get hash() {
139
+ return this.getStateHash();
140
+ }
141
+
142
+ get html(): string {
143
+ if (this._html) return this._html;
144
+ if (this.htmlFile) {
145
+ return (this._html = ActionResult.loadHtmlFromFile(this.htmlFile) || '');
146
+ }
147
+ return '';
148
+ }
149
+
150
+ set html(value: string) {
151
+ this._html = value;
152
+ }
153
+
154
+ get screenshot(): Buffer | undefined {
155
+ if (this._screenshot) {
156
+ return this._screenshot;
157
+ }
158
+ if (this.screenshotFile) {
159
+ return ActionResult.loadScreenshotFromFile(this.screenshotFile);
160
+ }
161
+ return undefined;
162
+ }
163
+
164
+ get browserLogsContent(): any[] {
165
+ if (this._browserLogs !== undefined) {
166
+ return this._browserLogs;
167
+ }
168
+ if (this.logFile) {
169
+ return ActionResult.loadBrowserLogsFromFile(this.logFile);
170
+ }
171
+ return [];
172
+ }
173
+
174
+ get ariaSnapshot(): string | null {
175
+ if (this._ariaSnapshot !== undefined) {
176
+ return this._ariaSnapshot;
177
+ }
178
+ if (!this.ariaSnapshotFile) {
179
+ this._ariaSnapshot = null;
180
+ return null;
181
+ }
182
+ this._ariaSnapshot = ActionResult.loadAriaSnapshotFromFile(this.ariaSnapshotFile);
183
+ return this._ariaSnapshot;
184
+ }
185
+
186
+ set ariaSnapshot(value: string | null) {
187
+ this._ariaSnapshot = value;
188
+ }
189
+
190
+ private extractHeadings(html: string): void {
191
+ if (!html) return;
192
+
193
+ if (this._lastExtractedHtml === html) return;
194
+
195
+ const extracted = extractHeadings(html);
196
+
197
+ if (!this.h1 && extracted.h1) this.h1 = extracted.h1;
198
+ if (!this.h2 && extracted.h2) this.h2 = extracted.h2;
199
+ if (!this.h3 && extracted.h3) this.h3 = extracted.h3;
200
+ if (!this.h4 && extracted.h4) this.h4 = extracted.h4;
201
+
202
+ this._lastExtractedHtml = html;
203
+ }
204
+
205
+ addVerification(assertion: string, passed: boolean): void {
206
+ this.verifications ??= {};
207
+ this.verifications[assertion] = passed;
208
+ }
209
+
210
+ isSameUrl(state: WebPageState): boolean {
211
+ if (!this.url || this.url === '') {
212
+ return false;
213
+ }
214
+ return extractStatePath(state.url) === extractStatePath(this.url);
215
+ }
216
+
217
+ isMatchedBy(state: WebPageState): boolean {
218
+ if (!this.url || this.url === '') {
219
+ return false;
220
+ }
221
+
222
+ const isRelevant = matchesUrl(extractStatePath(state.url), extractStatePath(this.url));
223
+ if (!isRelevant) {
224
+ return false;
225
+ }
226
+
227
+ // If headings are provided in state, they must match
228
+ if (state.h1 && this.h1 && !matchesUrl(this.h1, state.h1)) {
229
+ return false;
230
+ }
231
+ if (state.h2 && this.h2 && !matchesUrl(this.h2, state.h2)) {
232
+ return false;
233
+ }
234
+ if (state.h3 && this.h3 && !matchesUrl(this.h3, state.h3)) {
235
+ return false;
236
+ }
237
+
238
+ return true;
239
+ }
240
+
241
+ isRelevantExperienceRecord(record: WebPageState, options?: { includeDescendantExperience?: boolean }): boolean {
242
+ if (!record.url || !this.url) return false;
243
+ if (this.isMatchedBy(record)) return true;
244
+ if (!options?.includeDescendantExperience) return false;
245
+ const cur = extractStatePath(this.url);
246
+ const exp = extractStatePath(record.url);
247
+ if (!cur || !exp) return false;
248
+ return matchesUrl(`${cur}/*`, exp);
249
+ }
250
+
251
+ async simplifiedHtml(htmlConfig?: HtmlConfig): Promise<string> {
252
+ const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
253
+ return await minifyHtml(htmlMinimalUISnapshot(this.html ?? '', normalizedConfig?.minimal));
254
+ }
255
+
256
+ async combinedHtml(htmlConfig?: HtmlConfig & { keepPositions?: boolean }): Promise<string> {
257
+ const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
258
+ const combinedHtml = await minifyHtml(htmlCombinedSnapshot(this.html ?? '', normalizedConfig?.combined, { keepPositions: htmlConfig?.keepPositions }));
259
+ debugLog(`----${this.url}----`);
260
+ debugLog(`Combined HTML: \n${combinedHtml}`);
261
+ debugLog('----');
262
+ return combinedHtml;
263
+ }
264
+
265
+ async textHtml(htmlConfig?: HtmlConfig): Promise<string> {
266
+ const normalizedConfig = this.normalizeHtmlConfig(htmlConfig);
267
+ return await minifyHtml(htmlTextSnapshot(this.html ?? '', normalizedConfig?.text));
268
+ }
269
+
270
+ getInteractiveARIA(): string {
271
+ return compactAriaSnapshot(this.ariaSnapshot, false);
272
+ }
273
+
274
+ getCompactARIA(): string {
275
+ return compactAriaSnapshot(this.ariaSnapshot, true);
276
+ }
277
+
278
+ private normalizeHtmlConfig(htmlConfig?: HtmlConfig): HtmlConfig | undefined {
279
+ if (htmlConfig) {
280
+ return htmlConfig;
281
+ }
282
+ const parser = ConfigParser.getInstance();
283
+ return parser.getConfig().html;
284
+ }
285
+
286
+ static fromState(state: WebPageState): ActionResult {
287
+ let html: string | undefined = undefined;
288
+ let screenshot: Buffer | undefined = undefined;
289
+ let browserLogs: any[] | undefined = undefined;
290
+
291
+ // Only load from files if the data isn't already provided
292
+ if (state.htmlFile && !state.html) {
293
+ html = ActionResult.loadHtmlFromFile(state.htmlFile) || '';
294
+ } else if (state.html) {
295
+ html = state.html;
296
+ }
297
+
298
+ if (state.logFile) {
299
+ browserLogs = ActionResult.loadBrowserLogsFromFile(state.logFile);
300
+ }
301
+
302
+ if (state.screenshotFile) {
303
+ screenshot = ActionResult.loadScreenshotFromFile(state.screenshotFile);
304
+ }
305
+
306
+ const actionState = state as ActionResultData;
307
+ let ariaSnapshot = state.ariaSnapshot ?? null;
308
+
309
+ if (!ariaSnapshot && state.ariaSnapshotFile) {
310
+ ariaSnapshot = ActionResult.loadAriaSnapshotFromFile(state.ariaSnapshotFile);
311
+ }
312
+
313
+ const actionResultData: any = {
314
+ ...state,
315
+ html,
316
+ url: state.fullUrl || state.url || '',
317
+ title: state.title,
318
+ browserLogs,
319
+ screenshot,
320
+ ariaSnapshot,
321
+ iframeURL: actionState.iframeURL,
322
+ };
323
+
324
+ if (state.timestamp) {
325
+ actionResultData.timestamp = state.timestamp;
326
+ }
327
+
328
+ return new ActionResult(actionResultData);
329
+ }
330
+
331
+ private static loadHtmlFromFile(htmlFile: string): string | null {
332
+ try {
333
+ const filePath = outputPath('states', htmlFile);
334
+ if (fs.existsSync(filePath)) {
335
+ return fs.readFileSync(filePath, 'utf8');
336
+ }
337
+ return null;
338
+ } catch (error) {
339
+ console.error('Failed to load HTML from file:', error);
340
+ return null;
341
+ }
342
+ }
343
+
344
+ private static loadScreenshotFromFile(screenshotFile: string): Buffer | undefined {
345
+ try {
346
+ const filePath = outputPath('states', screenshotFile);
347
+ if (fs.existsSync(filePath)) {
348
+ return fs.readFileSync(filePath);
349
+ }
350
+ return undefined;
351
+ } catch (error) {
352
+ console.error('Failed to load screenshot from file:', error);
353
+ return undefined;
354
+ }
355
+ }
356
+
357
+ private static loadBrowserLogsFromFile(logFile: string): any[] {
358
+ try {
359
+ const filePath = outputPath('states', logFile);
360
+ if (fs.existsSync(filePath)) {
361
+ const logContent = fs.readFileSync(filePath, 'utf8');
362
+ return logContent
363
+ .split('\n')
364
+ .filter((line) => line.trim())
365
+ .map((line) => {
366
+ const match = line.match(/\[([^\]]+)\] (\w+): (.+)/);
367
+ if (match) {
368
+ return {
369
+ timestamp: match[1],
370
+ type: match[2].toLowerCase(),
371
+ text: match[3],
372
+ level: match[2].toLowerCase(),
373
+ message: match[3],
374
+ };
375
+ }
376
+ return { text: line, type: 'log', level: 'log', message: line };
377
+ });
378
+ }
379
+ return [];
380
+ } catch (error) {
381
+ console.error('Failed to load browser logs from file:', error);
382
+ return [];
383
+ }
384
+ }
385
+
386
+ private static loadAriaSnapshotFromFile(ariaSnapshotFile: string): string | null {
387
+ try {
388
+ const filePath = outputPath('states', ariaSnapshotFile);
389
+ if (!fs.existsSync(filePath)) {
390
+ return null;
391
+ }
392
+
393
+ const content = fs.readFileSync(filePath, 'utf8');
394
+ const trimmed = content.trim();
395
+ if (!trimmed) {
396
+ return null;
397
+ }
398
+
399
+ return trimmed.endsWith('\n') ? trimmed : `${trimmed}\n`;
400
+ } catch (error) {
401
+ console.error('Failed to load aria snapshot from file:', error);
402
+ return null;
403
+ }
404
+ }
405
+
406
+ toAiContext(): string {
407
+ const parts: string[] = [];
408
+
409
+ if (this.url && this.url !== '') {
410
+ parts.push(`<url>${this.url}</url>`);
411
+ }
412
+
413
+ if (this.title) {
414
+ parts.push(`<title>${this.title}</title>`);
415
+ }
416
+
417
+ if (this.h1) {
418
+ parts.push(`<h1>${this.h1}</h1>`);
419
+ }
420
+
421
+ if (this.h2) {
422
+ parts.push(`<h2>${this.h2}</h2>`);
423
+ }
424
+
425
+ if (this.h3) {
426
+ parts.push(`<h3>${this.h3}</h3>`);
427
+ }
428
+
429
+ if (this.h4) {
430
+ parts.push(`<h4>${this.h4}</h4>`);
431
+ }
432
+
433
+ if (this.notes.length > 0) {
434
+ parts.push(`<notes>${this.notes.join('\n')}</notes>`);
435
+ }
436
+
437
+ const ariaSummary = this.getInteractiveARIA();
438
+ if (ariaSummary) {
439
+ parts.push(`<aria>\n${ariaSummary}</aria>`);
440
+ }
441
+
442
+ if (this.links.length > 0) {
443
+ const linksToShow = this.links.slice(0, 20);
444
+ const linksList = linksToShow.map((link) => `- [${link.title}](${link.url})`).join('\n');
445
+ const suffix = this.links.length > 20 ? `\n... and ${this.links.length - 20} more` : '';
446
+ parts.push(`<links>\n${linksList}${suffix}</links>`);
447
+ }
448
+
449
+ debugLog(`AI context: \n${parts.join('\n')}`);
450
+
451
+ return parts.join('\n');
452
+ }
453
+
454
+ get relativeUrl(): string | null {
455
+ if (!this.url || this.url === '') return null;
456
+
457
+ try {
458
+ const urlObj = new URL(this.url);
459
+ const path = urlObj.pathname.replace(/\/$/, '') || '/';
460
+ const hash = urlObj.hash || '';
461
+ return path + hash;
462
+ } catch {
463
+ // If URL parsing fails, assume it's already a relative URL
464
+ return this.url;
465
+ }
466
+ }
467
+
468
+ get isInsideIframe(): boolean {
469
+ return !!this.iframeURL;
470
+ }
471
+
472
+ getStateHash(): string {
473
+ const parts: string[] = [];
474
+
475
+ parts.push(this.relativeUrl || this.url || '/');
476
+
477
+ this.extractHeadings(this.html);
478
+
479
+ const headings = ['h1', 'h2'];
480
+
481
+ for (const heading of headings) {
482
+ const value = this[heading as keyof this] as string;
483
+ if (value) {
484
+ parts.push(`${heading}_${value}`);
485
+ }
486
+ }
487
+
488
+ let stateString = parts
489
+ .map((part) => part.substring(0, 100))
490
+ .join('_')
491
+ .replace(/[^a-zA-Z0-9_]/g, '_')
492
+ .replace(/_+/g, '_')
493
+ .replace(/^_|_$/g, '')
494
+ .toLowerCase();
495
+
496
+ if (stateString.length > 200) {
497
+ stateString = stateString.substring(0, 200);
498
+ if (stateString.endsWith('_')) {
499
+ stateString = stateString.slice(0, -1);
500
+ }
501
+ }
502
+
503
+ return stateString;
504
+ }
505
+
506
+ private saveBrowserLogs(): void {
507
+ const logs = this.browserLogsContent;
508
+ if (!logs || logs.length === 0) return;
509
+
510
+ try {
511
+ const outputDir = 'output';
512
+ const stateHash = this.getStateHash();
513
+ const filename = `${stateHash}.log`;
514
+ const filePath = join(outputDir, filename);
515
+
516
+ // Ensure output directory exists
517
+ if (!fs.existsSync(outputDir)) {
518
+ fs.mkdirSync(outputDir, { recursive: true });
519
+ }
520
+
521
+ // Format logs for saving
522
+ const formattedLogs = logs.map((log: any) => {
523
+ const timestamp = new Date().toISOString();
524
+ const level = (log.type || log.level || 'LOG').toUpperCase();
525
+ const message = log.text || log.message || String(log);
526
+ return `[${timestamp}] ${level}: ${message}`;
527
+ });
528
+
529
+ // Save log content to file
530
+ const logContent = `${formattedLogs.join('\n')}\n`;
531
+ fs.writeFileSync(filePath, logContent, 'utf8');
532
+ } catch (error) {
533
+ // Silently fail to avoid breaking the main flow
534
+ console.error('Failed to save browser logs:', error);
535
+ }
536
+ }
537
+
538
+ private saveHtmlOutput(): void {
539
+ try {
540
+ const outputDir = 'output';
541
+ const stateHash = this.getStateHash();
542
+ const filename = `${stateHash}.html`;
543
+ const filePath = join(outputDir, filename);
544
+
545
+ // Ensure output directory exists
546
+ if (!fs.existsSync(outputDir)) {
547
+ fs.mkdirSync(outputDir, { recursive: true });
548
+ }
549
+
550
+ // Save HTML content to file
551
+ fs.writeFileSync(filePath, this.html, 'utf8');
552
+ } catch (error) {
553
+ // Silently fail to avoid breaking the main flow
554
+ console.error('Failed to save HTML output:', error);
555
+ }
556
+ }
557
+
558
+ async diff(previousState: ActionResult | null): Promise<Diff> {
559
+ return new Diff(this, previousState);
560
+ }
561
+
562
+ async toToolResult(previousState: ActionResult | null, locator: string): Promise<ToolResultMetadata> {
563
+ const result: ToolResultMetadata = {
564
+ url: previousState?.url || '',
565
+ locator,
566
+ targetedHtml: '',
567
+ pageDiff: null,
568
+ iframeURL: this.iframeURL,
569
+ };
570
+
571
+ if (previousState) {
572
+ const html = await previousState.simplifiedHtml();
573
+ result.targetedHtml = extractTargetedHtml(html, locator);
574
+ }
575
+
576
+ if (previousState?.id !== undefined && this.id === previousState.id) {
577
+ return result;
578
+ }
579
+
580
+ const urlChanged = previousState ? !this.isSameUrl({ url: previousState.url }) : true;
581
+
582
+ if (!previousState) {
583
+ result.pageDiff = {
584
+ urlChanged: true,
585
+ currentUrl: this.url,
586
+ };
587
+ return result;
588
+ }
589
+
590
+ const diff = await this.diff(previousState);
591
+ await diff.calculate();
592
+
593
+ const pageDiff: PageDiff = {
594
+ urlChanged,
595
+ previousUrl: previousState.url,
596
+ currentUrl: this.url,
597
+ };
598
+
599
+ if (diff.ariaChanged) {
600
+ pageDiff.ariaChanges = diff.ariaChanged;
601
+ }
602
+
603
+ if (diff.htmlParts.length > 0) {
604
+ const htmlConfig = this.normalizeHtmlConfig();
605
+ const processedParts: HtmlDiffPart[] = [];
606
+ for (const part of diff.htmlParts) {
607
+ const filteredHtml = htmlCombinedSnapshot(part.subtree, htmlConfig?.combined);
608
+ const minified = await minifyHtml(filteredHtml);
609
+ if (minified) {
610
+ processedParts.push({ ...part, subtree: minified });
611
+ }
612
+ }
613
+ if (processedParts.length > 0) {
614
+ pageDiff.htmlParts = processedParts;
615
+ }
616
+ }
617
+
618
+ if (pageDiff.ariaChanges && this.iframeSnapshots.length > 0) {
619
+ const addedCount = (pageDiff.ariaChanges.match(/\n {4}- /g) || []).length;
620
+ const removedMatch = pageDiff.ariaChanges.match(/removed: (\d+) interactive/);
621
+ const removedCount = removedMatch ? Number.parseInt(removedMatch[1]) : 0;
622
+ if (addedCount + removedCount >= 50) {
623
+ pageDiff.iframes = this.iframeSnapshots.map((snap) => `iframe src="${snap.src}":\n${snap.html}`).join('\n\n');
624
+ }
625
+ }
626
+
627
+ result.pageDiff = pageDiff;
628
+ return result;
629
+ }
630
+ }
631
+
632
+ export class Diff {
633
+ private _htmlDiffResult: HtmlDiffResult | null = null;
634
+ private _ariaDiffResult: string | null = null;
635
+ private _isSameUrl: boolean;
636
+ private _urlChanged: boolean;
637
+
638
+ constructor(
639
+ private current: ActionResult,
640
+ private previous: ActionResult | null
641
+ ) {
642
+ this._isSameUrl = previous ? current.isSameUrl({ url: previous.url }) : false;
643
+ this._urlChanged = !this._isSameUrl;
644
+ }
645
+
646
+ hasChanges(): boolean {
647
+ if (!this.previous) return false;
648
+ if (this._urlChanged) return true;
649
+
650
+ const hasHtmlChanges = this._htmlDiffResult && (this._htmlDiffResult.parts.length > 0 || this._htmlDiffResult.added.length > 0 || this._htmlDiffResult.removed.length > 0);
651
+ const hasAriaChanges = this._ariaDiffResult !== null;
652
+
653
+ return hasHtmlChanges || hasAriaChanges;
654
+ }
655
+
656
+ isSameUrl(): boolean {
657
+ return this._isSameUrl;
658
+ }
659
+
660
+ urlHasChanged(): boolean {
661
+ return this._urlChanged;
662
+ }
663
+
664
+ get htmlParts(): HtmlDiffPart[] {
665
+ if (!this._htmlDiffResult) return [];
666
+ return this._htmlDiffResult.parts;
667
+ }
668
+
669
+ get ariaChanged(): string | null {
670
+ return this._ariaDiffResult;
671
+ }
672
+
673
+ get ariaRemoved(): string | null {
674
+ return this._ariaDiffResult;
675
+ }
676
+
677
+ get htmlDiff(): HtmlDiffResult | null {
678
+ return this._htmlDiffResult;
679
+ }
680
+
681
+ get ariaDiff(): string | null {
682
+ return this._ariaDiffResult;
683
+ }
684
+
685
+ async calculate(): Promise<void> {
686
+ if (!this.previous) return;
687
+
688
+ if (this._isSameUrl) {
689
+ this._htmlDiffResult = await htmlDiff(this.previous.html, this.current.html, ConfigParser.getInstance().getConfig().html);
690
+ }
691
+
692
+ this._ariaDiffResult = diffAriaSnapshots(this.previous.ariaSnapshot, this.current.ariaSnapshot);
693
+ }
694
+ }