@midscene/core 1.12.10-beta-20260920031616.0 → 1.13.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 (319) hide show
  1. package/dist/es/agent/agent.mjs +103 -6
  2. package/dist/es/agent/agent.mjs.map +1 -1
  3. package/dist/es/agent/index.mjs +4 -3
  4. package/dist/es/agent/tasks.mjs +2 -2
  5. package/dist/es/agent/tasks.mjs.map +1 -1
  6. package/dist/es/agent/test-runner-nodes.mjs +291 -40
  7. package/dist/es/agent/test-runner-nodes.mjs.map +1 -1
  8. package/dist/es/agent/utils.mjs +1 -1
  9. package/dist/es/device/index.mjs +15 -2
  10. package/dist/es/device/index.mjs.map +1 -1
  11. package/dist/es/dump/html-utils.mjs +37 -4
  12. package/dist/es/dump/html-utils.mjs.map +1 -1
  13. package/dist/es/dump/screenshot-store.mjs +8 -1
  14. package/dist/es/dump/screenshot-store.mjs.map +1 -1
  15. package/dist/es/report-cli.mjs +9 -4
  16. package/dist/es/report-cli.mjs.map +1 -1
  17. package/dist/es/report-generator.mjs +117 -4
  18. package/dist/es/report-generator.mjs.map +1 -1
  19. package/dist/es/report-html-template.mjs +1 -1
  20. package/dist/es/report.mjs +190 -41
  21. package/dist/es/report.mjs.map +1 -1
  22. package/dist/es/test-run-report.mjs.map +1 -1
  23. package/dist/es/test-runner/engine/case-runner.mjs +52 -0
  24. package/dist/es/test-runner/engine/case-runner.mjs.map +1 -0
  25. package/dist/es/test-runner/engine/document-runtime.mjs +273 -0
  26. package/dist/es/test-runner/engine/document-runtime.mjs.map +1 -0
  27. package/dist/es/test-runner/engine/execute-step.mjs +177 -0
  28. package/dist/es/test-runner/engine/execute-step.mjs.map +1 -0
  29. package/dist/es/test-runner/engine/execution-failure.mjs +39 -0
  30. package/dist/es/test-runner/engine/execution-failure.mjs.map +1 -0
  31. package/dist/es/test-runner/engine/index.mjs +12 -0
  32. package/dist/es/test-runner/engine/project-runtime.mjs +142 -0
  33. package/dist/es/test-runner/engine/project-runtime.mjs.map +1 -0
  34. package/dist/es/test-runner/engine/registry.mjs +61 -0
  35. package/dist/es/test-runner/engine/registry.mjs.map +1 -0
  36. package/dist/es/test-runner/engine/resource-operations.mjs +211 -0
  37. package/dist/es/test-runner/engine/resource-operations.mjs.map +1 -0
  38. package/dist/es/test-runner/engine/run-collected-case.mjs +193 -0
  39. package/dist/es/test-runner/engine/run-collected-case.mjs.map +1 -0
  40. package/dist/es/test-runner/engine/run-workflow-document.mjs +155 -0
  41. package/dist/es/test-runner/engine/run-workflow-document.mjs.map +1 -0
  42. package/dist/es/test-runner/engine/scheduler.mjs +27 -0
  43. package/dist/es/test-runner/engine/scheduler.mjs.map +1 -0
  44. package/dist/es/test-runner/engine/scope-teardown.mjs +16 -0
  45. package/dist/es/test-runner/engine/scope-teardown.mjs.map +1 -0
  46. package/dist/es/test-runner/engine/types.mjs +0 -0
  47. package/dist/es/test-runner/errors.mjs +201 -0
  48. package/dist/es/test-runner/errors.mjs.map +1 -0
  49. package/dist/es/test-runner/execution-record.mjs +83 -0
  50. package/dist/es/test-runner/execution-record.mjs.map +1 -0
  51. package/dist/es/test-runner/index.mjs +12 -0
  52. package/dist/es/test-runner/node/define-node.mjs +44 -0
  53. package/dist/es/test-runner/node/define-node.mjs.map +1 -0
  54. package/dist/es/test-runner/node/index.mjs +2 -0
  55. package/dist/es/test-runner/node/types.mjs +0 -0
  56. package/dist/es/test-runner/parser/document-format.mjs +21 -0
  57. package/dist/es/test-runner/parser/document-format.mjs.map +1 -0
  58. package/dist/es/test-runner/parser/normalize.mjs +87 -0
  59. package/dist/es/test-runner/parser/normalize.mjs.map +1 -0
  60. package/dist/es/test-runner/parser/types.mjs +0 -0
  61. package/dist/es/test-runner/project.mjs +0 -0
  62. package/dist/es/test-runner/reporting/agent-report.mjs +23 -0
  63. package/dist/es/test-runner/reporting/agent-report.mjs.map +1 -0
  64. package/dist/es/test-runner/reporting/execution-record.mjs +73 -0
  65. package/dist/es/test-runner/reporting/execution-record.mjs.map +1 -0
  66. package/dist/es/test-runner/reporting/merge-report.mjs +231 -0
  67. package/dist/es/test-runner/reporting/merge-report.mjs.map +1 -0
  68. package/dist/es/test-runner/reporting/sanitize.mjs +162 -0
  69. package/dist/es/test-runner/reporting/sanitize.mjs.map +1 -0
  70. package/dist/es/test-runner/reporting/test-run-report.mjs +327 -0
  71. package/dist/es/test-runner/reporting/test-run-report.mjs.map +1 -0
  72. package/dist/es/test-runner/reporting/types.mjs +0 -0
  73. package/dist/es/utils.mjs +1 -1
  74. package/dist/es/yaml/execution-context.node.mjs +6 -0
  75. package/dist/es/yaml/execution-context.node.mjs.map +1 -0
  76. package/dist/es/yaml/execution-context.stub.mjs +8 -0
  77. package/dist/es/yaml/execution-context.stub.mjs.map +1 -0
  78. package/dist/es/yaml/execution-session.mjs +90 -0
  79. package/dist/es/yaml/execution-session.mjs.map +1 -0
  80. package/dist/es/yaml/index.mjs +2 -1
  81. package/dist/es/yaml/legacy-yaml-execution-record.mjs +47 -0
  82. package/dist/es/yaml/legacy-yaml-execution-record.mjs.map +1 -0
  83. package/dist/es/yaml/legacy-yaml-report.mjs +52 -0
  84. package/dist/es/yaml/legacy-yaml-report.mjs.map +1 -0
  85. package/dist/es/yaml/legacy-yaml-runtime.mjs +119 -0
  86. package/dist/es/yaml/legacy-yaml-runtime.mjs.map +1 -0
  87. package/dist/es/yaml/player-state.mjs +15 -0
  88. package/dist/es/yaml/player-state.mjs.map +1 -0
  89. package/dist/es/yaml/player.mjs +214 -348
  90. package/dist/es/yaml/player.mjs.map +1 -1
  91. package/dist/es/yaml/report-policy.mjs +7 -0
  92. package/dist/es/yaml/report-policy.mjs.map +1 -0
  93. package/dist/es/yaml/test-runner-compat.mjs +301 -0
  94. package/dist/es/yaml/test-runner-compat.mjs.map +1 -0
  95. package/dist/es/yaml/test-runner-nodes.mjs +147 -0
  96. package/dist/es/yaml/test-runner-nodes.mjs.map +1 -0
  97. package/dist/es/yaml/utils.mjs +12 -1
  98. package/dist/es/yaml/utils.mjs.map +1 -1
  99. package/dist/es/yaml-runtime.mjs +7 -0
  100. package/dist/es/yaml.mjs.map +1 -1
  101. package/dist/lib/agent/agent.js +103 -6
  102. package/dist/lib/agent/agent.js.map +1 -1
  103. package/dist/lib/agent/index.js +37 -3
  104. package/dist/lib/agent/tasks.js +2 -2
  105. package/dist/lib/agent/tasks.js.map +1 -1
  106. package/dist/lib/agent/test-runner-nodes.js +329 -45
  107. package/dist/lib/agent/test-runner-nodes.js.map +1 -1
  108. package/dist/lib/agent/utils.js +1 -1
  109. package/dist/lib/device/index.js +15 -2
  110. package/dist/lib/device/index.js.map +1 -1
  111. package/dist/lib/dump/html-utils.js +43 -4
  112. package/dist/lib/dump/html-utils.js.map +1 -1
  113. package/dist/lib/dump/screenshot-store.js +8 -1
  114. package/dist/lib/dump/screenshot-store.js.map +1 -1
  115. package/dist/lib/report-cli.js +8 -3
  116. package/dist/lib/report-cli.js.map +1 -1
  117. package/dist/lib/report-generator.js +114 -1
  118. package/dist/lib/report-generator.js.map +1 -1
  119. package/dist/lib/report-html-template.js +1 -1
  120. package/dist/lib/report.js +187 -38
  121. package/dist/lib/report.js.map +1 -1
  122. package/dist/lib/test-run-report.js.map +1 -1
  123. package/dist/lib/test-runner/engine/case-runner.js +89 -0
  124. package/dist/lib/test-runner/engine/case-runner.js.map +1 -0
  125. package/dist/lib/test-runner/engine/document-runtime.js +307 -0
  126. package/dist/lib/test-runner/engine/document-runtime.js.map +1 -0
  127. package/dist/lib/test-runner/engine/execute-step.js +211 -0
  128. package/dist/lib/test-runner/engine/execute-step.js.map +1 -0
  129. package/dist/lib/test-runner/engine/execution-failure.js +79 -0
  130. package/dist/lib/test-runner/engine/execution-failure.js.map +1 -0
  131. package/dist/lib/test-runner/engine/index.js +137 -0
  132. package/dist/lib/test-runner/engine/index.js.map +1 -0
  133. package/dist/lib/test-runner/engine/project-runtime.js +176 -0
  134. package/dist/lib/test-runner/engine/project-runtime.js.map +1 -0
  135. package/dist/lib/test-runner/engine/registry.js +95 -0
  136. package/dist/lib/test-runner/engine/registry.js.map +1 -0
  137. package/dist/lib/test-runner/engine/resource-operations.js +272 -0
  138. package/dist/lib/test-runner/engine/resource-operations.js.map +1 -0
  139. package/dist/lib/test-runner/engine/run-collected-case.js +227 -0
  140. package/dist/lib/test-runner/engine/run-collected-case.js.map +1 -0
  141. package/dist/lib/test-runner/engine/run-workflow-document.js +189 -0
  142. package/dist/lib/test-runner/engine/run-workflow-document.js.map +1 -0
  143. package/dist/lib/test-runner/engine/scheduler.js +64 -0
  144. package/dist/lib/test-runner/engine/scheduler.js.map +1 -0
  145. package/dist/lib/test-runner/engine/scope-teardown.js +50 -0
  146. package/dist/lib/test-runner/engine/scope-teardown.js.map +1 -0
  147. package/dist/lib/test-runner/engine/types.js +20 -0
  148. package/dist/lib/test-runner/engine/types.js.map +1 -0
  149. package/dist/lib/test-runner/errors.js +283 -0
  150. package/dist/lib/test-runner/errors.js.map +1 -0
  151. package/dist/lib/test-runner/execution-record.js +123 -0
  152. package/dist/lib/test-runner/execution-record.js.map +1 -0
  153. package/dist/lib/test-runner/index.js +137 -0
  154. package/dist/lib/test-runner/index.js.map +1 -0
  155. package/dist/lib/test-runner/node/define-node.js +78 -0
  156. package/dist/lib/test-runner/node/define-node.js.map +1 -0
  157. package/dist/lib/test-runner/node/index.js +67 -0
  158. package/dist/lib/test-runner/node/index.js.map +1 -0
  159. package/dist/lib/test-runner/node/types.js +20 -0
  160. package/dist/lib/test-runner/node/types.js.map +1 -0
  161. package/dist/lib/test-runner/parser/document-format.js +58 -0
  162. package/dist/lib/test-runner/parser/document-format.js.map +1 -0
  163. package/dist/lib/test-runner/parser/normalize.js +124 -0
  164. package/dist/lib/test-runner/parser/normalize.js.map +1 -0
  165. package/dist/lib/test-runner/parser/types.js +20 -0
  166. package/dist/lib/test-runner/parser/types.js.map +1 -0
  167. package/dist/lib/test-runner/project.js +20 -0
  168. package/dist/lib/test-runner/project.js.map +1 -0
  169. package/dist/lib/test-runner/reporting/agent-report.js +57 -0
  170. package/dist/lib/test-runner/reporting/agent-report.js.map +1 -0
  171. package/dist/lib/test-runner/reporting/execution-record.js +107 -0
  172. package/dist/lib/test-runner/reporting/execution-record.js.map +1 -0
  173. package/dist/lib/test-runner/reporting/merge-report.js +265 -0
  174. package/dist/lib/test-runner/reporting/merge-report.js.map +1 -0
  175. package/dist/lib/test-runner/reporting/sanitize.js +202 -0
  176. package/dist/lib/test-runner/reporting/sanitize.js.map +1 -0
  177. package/dist/lib/test-runner/reporting/test-run-report.js +364 -0
  178. package/dist/lib/test-runner/reporting/test-run-report.js.map +1 -0
  179. package/dist/lib/test-runner/reporting/types.js +20 -0
  180. package/dist/lib/test-runner/reporting/types.js.map +1 -0
  181. package/dist/lib/utils.js +1 -1
  182. package/dist/lib/yaml/execution-context.node.js +43 -0
  183. package/dist/lib/yaml/execution-context.node.js.map +1 -0
  184. package/dist/lib/yaml/execution-context.stub.js +45 -0
  185. package/dist/lib/yaml/execution-context.stub.js.map +1 -0
  186. package/dist/lib/yaml/execution-session.js +136 -0
  187. package/dist/lib/yaml/execution-session.js.map +1 -0
  188. package/dist/lib/yaml/index.js +49 -7
  189. package/dist/lib/yaml/legacy-yaml-execution-record.js +81 -0
  190. package/dist/lib/yaml/legacy-yaml-execution-record.js.map +1 -0
  191. package/dist/lib/yaml/legacy-yaml-report.js +86 -0
  192. package/dist/lib/yaml/legacy-yaml-report.js.map +1 -0
  193. package/dist/lib/yaml/legacy-yaml-runtime.js +156 -0
  194. package/dist/lib/yaml/legacy-yaml-runtime.js.map +1 -0
  195. package/dist/lib/yaml/player-state.js +52 -0
  196. package/dist/lib/yaml/player-state.js.map +1 -0
  197. package/dist/lib/yaml/player.js +213 -347
  198. package/dist/lib/yaml/player.js.map +1 -1
  199. package/dist/lib/yaml/report-policy.js +41 -0
  200. package/dist/lib/yaml/report-policy.js.map +1 -0
  201. package/dist/lib/yaml/test-runner-compat.js +344 -0
  202. package/dist/lib/yaml/test-runner-compat.js.map +1 -0
  203. package/dist/lib/yaml/test-runner-nodes.js +184 -0
  204. package/dist/lib/yaml/test-runner-nodes.js.map +1 -0
  205. package/dist/lib/yaml/utils.js +18 -1
  206. package/dist/lib/yaml/utils.js.map +1 -1
  207. package/dist/lib/yaml-runtime.js +67 -0
  208. package/dist/lib/yaml-runtime.js.map +1 -0
  209. package/dist/lib/yaml.js.map +1 -1
  210. package/dist/types/agent/agent.d.ts +14 -1
  211. package/dist/types/agent/agent.d.ts.map +1 -1
  212. package/dist/types/agent/index.d.ts +4 -3
  213. package/dist/types/agent/index.d.ts.map +1 -1
  214. package/dist/types/agent/tasks.d.ts +1 -1
  215. package/dist/types/agent/tasks.d.ts.map +1 -1
  216. package/dist/types/agent/test-runner-nodes.d.ts +226 -0
  217. package/dist/types/agent/test-runner-nodes.d.ts.map +1 -1
  218. package/dist/types/device/index.d.ts +1 -1
  219. package/dist/types/device/index.d.ts.map +1 -1
  220. package/dist/types/dump/html-utils.d.ts +4 -0
  221. package/dist/types/dump/html-utils.d.ts.map +1 -1
  222. package/dist/types/dump/screenshot-store.d.ts +2 -1
  223. package/dist/types/dump/screenshot-store.d.ts.map +1 -1
  224. package/dist/types/report-cli.d.ts.map +1 -1
  225. package/dist/types/report-generator.d.ts +10 -0
  226. package/dist/types/report-generator.d.ts.map +1 -1
  227. package/dist/types/report.d.ts +3 -1
  228. package/dist/types/report.d.ts.map +1 -1
  229. package/dist/types/test-run-report.d.ts +18 -2
  230. package/dist/types/test-run-report.d.ts.map +1 -1
  231. package/dist/types/test-runner/engine/case-runner.d.ts +13 -0
  232. package/dist/types/test-runner/engine/case-runner.d.ts.map +1 -0
  233. package/dist/types/test-runner/engine/document-runtime.d.ts +4 -0
  234. package/dist/types/test-runner/engine/document-runtime.d.ts.map +1 -0
  235. package/dist/types/test-runner/engine/execute-step.d.ts +17 -0
  236. package/dist/types/test-runner/engine/execute-step.d.ts.map +1 -0
  237. package/dist/types/test-runner/engine/execution-failure.d.ts +13 -0
  238. package/dist/types/test-runner/engine/execution-failure.d.ts.map +1 -0
  239. package/dist/types/test-runner/engine/index.d.ts +13 -0
  240. package/dist/types/test-runner/engine/index.d.ts.map +1 -0
  241. package/dist/types/test-runner/engine/project-runtime.d.ts +3 -0
  242. package/dist/types/test-runner/engine/project-runtime.d.ts.map +1 -0
  243. package/dist/types/test-runner/engine/registry.d.ts +12 -0
  244. package/dist/types/test-runner/engine/registry.d.ts.map +1 -0
  245. package/dist/types/test-runner/engine/resource-operations.d.ts +32 -0
  246. package/dist/types/test-runner/engine/resource-operations.d.ts.map +1 -0
  247. package/dist/types/test-runner/engine/run-collected-case.d.ts +4 -0
  248. package/dist/types/test-runner/engine/run-collected-case.d.ts.map +1 -0
  249. package/dist/types/test-runner/engine/run-workflow-document.d.ts +4 -0
  250. package/dist/types/test-runner/engine/run-workflow-document.d.ts.map +1 -0
  251. package/dist/types/test-runner/engine/scheduler.d.ts +12 -0
  252. package/dist/types/test-runner/engine/scheduler.d.ts.map +1 -0
  253. package/dist/types/test-runner/engine/scope-teardown.d.ts +3 -0
  254. package/dist/types/test-runner/engine/scope-teardown.d.ts.map +1 -0
  255. package/dist/types/test-runner/engine/types.d.ts +226 -0
  256. package/dist/types/test-runner/engine/types.d.ts.map +1 -0
  257. package/dist/types/test-runner/errors.d.ts +79 -0
  258. package/dist/types/test-runner/errors.d.ts.map +1 -0
  259. package/dist/types/test-runner/execution-record.d.ts +43 -0
  260. package/dist/types/test-runner/execution-record.d.ts.map +1 -0
  261. package/dist/types/test-runner/index.d.ts +20 -0
  262. package/dist/types/test-runner/index.d.ts.map +1 -0
  263. package/dist/types/test-runner/node/define-node.d.ts +4 -0
  264. package/dist/types/test-runner/node/define-node.d.ts.map +1 -0
  265. package/dist/types/test-runner/node/index.d.ts +3 -0
  266. package/dist/types/test-runner/node/index.d.ts.map +1 -0
  267. package/dist/types/test-runner/node/types.d.ts +66 -0
  268. package/dist/types/test-runner/node/types.d.ts.map +1 -0
  269. package/dist/types/test-runner/parser/document-format.d.ts +8 -0
  270. package/dist/types/test-runner/parser/document-format.d.ts.map +1 -0
  271. package/dist/types/test-runner/parser/normalize.d.ts +9 -0
  272. package/dist/types/test-runner/parser/normalize.d.ts.map +1 -0
  273. package/dist/types/test-runner/parser/types.d.ts +69 -0
  274. package/dist/types/test-runner/parser/types.d.ts.map +1 -0
  275. package/dist/types/test-runner/project.d.ts +75 -0
  276. package/dist/types/test-runner/project.d.ts.map +1 -0
  277. package/dist/types/test-runner/reporting/agent-report.d.ts +9 -0
  278. package/dist/types/test-runner/reporting/agent-report.d.ts.map +1 -0
  279. package/dist/types/test-runner/reporting/execution-record.d.ts +9 -0
  280. package/dist/types/test-runner/reporting/execution-record.d.ts.map +1 -0
  281. package/dist/types/test-runner/reporting/merge-report.d.ts +15 -0
  282. package/dist/types/test-runner/reporting/merge-report.d.ts.map +1 -0
  283. package/dist/types/test-runner/reporting/sanitize.d.ts +6 -0
  284. package/dist/types/test-runner/reporting/sanitize.d.ts.map +1 -0
  285. package/dist/types/test-runner/reporting/test-run-report.d.ts +5 -0
  286. package/dist/types/test-runner/reporting/test-run-report.d.ts.map +1 -0
  287. package/dist/types/test-runner/reporting/types.d.ts +34 -0
  288. package/dist/types/test-runner/reporting/types.d.ts.map +1 -0
  289. package/dist/types/yaml/execution-context.node.d.ts +4 -0
  290. package/dist/types/yaml/execution-context.node.d.ts.map +1 -0
  291. package/dist/types/yaml/execution-context.stub.d.ts +6 -0
  292. package/dist/types/yaml/execution-context.stub.d.ts.map +1 -0
  293. package/dist/types/yaml/execution-session.d.ts +17 -0
  294. package/dist/types/yaml/execution-session.d.ts.map +1 -0
  295. package/dist/types/yaml/index.d.ts +1 -1
  296. package/dist/types/yaml/index.d.ts.map +1 -1
  297. package/dist/types/yaml/legacy-yaml-execution-record.d.ts +24 -0
  298. package/dist/types/yaml/legacy-yaml-execution-record.d.ts.map +1 -0
  299. package/dist/types/yaml/legacy-yaml-report.d.ts +19 -0
  300. package/dist/types/yaml/legacy-yaml-report.d.ts.map +1 -0
  301. package/dist/types/yaml/legacy-yaml-runtime.d.ts +23 -0
  302. package/dist/types/yaml/legacy-yaml-runtime.d.ts.map +1 -0
  303. package/dist/types/yaml/player-state.d.ts +11 -0
  304. package/dist/types/yaml/player-state.d.ts.map +1 -0
  305. package/dist/types/yaml/player.d.ts +11 -3
  306. package/dist/types/yaml/player.d.ts.map +1 -1
  307. package/dist/types/yaml/report-policy.d.ts +4 -0
  308. package/dist/types/yaml/report-policy.d.ts.map +1 -0
  309. package/dist/types/yaml/test-runner-compat.d.ts +17 -0
  310. package/dist/types/yaml/test-runner-compat.d.ts.map +1 -0
  311. package/dist/types/yaml/test-runner-nodes.d.ts +6 -0
  312. package/dist/types/yaml/test-runner-nodes.d.ts.map +1 -0
  313. package/dist/types/yaml/utils.d.ts +9 -0
  314. package/dist/types/yaml/utils.d.ts.map +1 -1
  315. package/dist/types/yaml-runtime.d.ts +13 -0
  316. package/dist/types/yaml-runtime.d.ts.map +1 -0
  317. package/dist/types/yaml.d.ts +25 -7
  318. package/dist/types/yaml.d.ts.map +1 -1
  319. package/package.json +28 -2
@@ -48,9 +48,11 @@ var __webpack_exports__ = {};
48
48
  escapeContent: ()=>escapeContent,
49
49
  DATA_SCREENSHOT_MODE_ATTR: ()=>DATA_SCREENSHOT_MODE_ATTR,
50
50
  generateDumpScriptTag: ()=>generateDumpScriptTag,
51
- streamDumpScriptsSync: ()=>streamDumpScriptsSync,
51
+ extractTestRunReportDumpSync: ()=>extractTestRunReportDumpSync,
52
52
  STREAMING_CHUNK_SIZE: ()=>STREAMING_CHUNK_SIZE,
53
+ streamDumpScriptsSync: ()=>streamDumpScriptsSync,
53
54
  extractLastDumpScriptSync: ()=>extractLastDumpScriptSync,
55
+ generateTestRunReportScriptTag: ()=>generateTestRunReportScriptTag,
54
56
  parseDumpScript: ()=>parseDumpScript,
55
57
  streamImageScriptsToFile: ()=>streamImageScriptsToFile,
56
58
  streamScanTags: ()=>streamScanTags,
@@ -61,7 +63,9 @@ var __webpack_exports__ = {};
61
63
  });
62
64
  var external_node_fs_ = __webpack_require__("node:fs");
63
65
  const promises_namespaceObject = require("node:fs/promises");
66
+ const external_node_string_decoder_namespaceObject = require("node:string_decoder");
64
67
  const utils_namespaceObject = require("@midscene/shared/utils");
68
+ const external_test_run_report_js_namespaceObject = require("../test-run-report.js");
65
69
  function _define_property(obj, key, value) {
66
70
  if (key in obj) Object.defineProperty(obj, key, {
67
71
  value: value,
@@ -139,6 +143,34 @@ var __webpack_exports__ = {};
139
143
  function htmlScriptCloseTag() {
140
144
  return String.fromCharCode(60) + "/script>";
141
145
  }
146
+ function generateTestRunReportScriptTag(dump) {
147
+ return [
148
+ String.fromCharCode(60),
149
+ 'script type="',
150
+ external_test_run_report_js_namespaceObject.TEST_RUN_REPORT_SCRIPT_TYPE,
151
+ '">\n',
152
+ (0, utils_namespaceObject.escapeScriptTag)(JSON.stringify(dump)),
153
+ '\n',
154
+ htmlScriptCloseTag(),
155
+ '\n'
156
+ ].join('');
157
+ }
158
+ function extractTestRunReportDumpSync(filePath) {
159
+ let result;
160
+ const openTag = `${String.fromCharCode(60)}script`;
161
+ streamScanTags(filePath, openTag, htmlScriptCloseTag(), (content)=>{
162
+ const tagEnd = content.indexOf('>');
163
+ const attributes = content.slice(0, tagEnd);
164
+ const type = attributes.match(/(?:^|\s)type\s*=\s*["']([^"']+)["']/i)?.[1];
165
+ if (type !== external_test_run_report_js_namespaceObject.TEST_RUN_REPORT_SCRIPT_TYPE) return false;
166
+ if (result) throw new Error(`Report contains multiple Midscene Test dumps: ${filePath}`);
167
+ const parsed = JSON.parse(unescapeContent(content.slice(tagEnd + 1)));
168
+ if (parsed?.schemaVersion !== 1 || parsed?.kind !== 'test-runner' || !Array.isArray(parsed.projects)) throw new Error(`Invalid Midscene Test report dump: ${filePath}`);
169
+ result = parsed;
170
+ return false;
171
+ });
172
+ return result;
173
+ }
142
174
  const STREAMING_CHUNK_SIZE = 65536;
143
175
  class StreamingTagMatcher {
144
176
  push(nextChunk) {
@@ -192,14 +224,16 @@ var __webpack_exports__ = {};
192
224
  const fd = (0, external_node_fs_.openSync)(filePath, 'r');
193
225
  const fileSize = (0, external_node_fs_.statSync)(filePath).size;
194
226
  const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);
227
+ const decoder = new external_node_string_decoder_namespaceObject.StringDecoder('utf8');
195
228
  const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);
196
229
  let position = 0;
197
230
  try {
198
231
  while(position < fileSize){
199
232
  const bytesRead = (0, external_node_fs_.readSync)(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);
200
233
  position += bytesRead;
201
- if (matcher.push(buffer.toString('utf-8', 0, bytesRead))) return;
234
+ if (matcher.push(decoder.write(buffer.subarray(0, bytesRead)))) return;
202
235
  }
236
+ matcher.push(decoder.end());
203
237
  } finally{
204
238
  (0, external_node_fs_.closeSync)(fd);
205
239
  }
@@ -207,14 +241,15 @@ var __webpack_exports__ = {};
207
241
  async function streamScanTagsAsync(filePath, openTag, closeTag, onMatch) {
208
242
  const handle = await (0, promises_namespaceObject.open)(filePath, 'r');
209
243
  const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);
244
+ const decoder = new external_node_string_decoder_namespaceObject.StringDecoder('utf8');
210
245
  let position = 0;
211
246
  const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);
212
247
  try {
213
248
  while(true){
214
249
  const { bytesRead } = await handle.read(buffer, 0, STREAMING_CHUNK_SIZE, position);
215
- if (0 === bytesRead) return;
250
+ if (0 === bytesRead) return void matcher.push(decoder.end());
216
251
  position += bytesRead;
217
- if (matcher.push(buffer.toString('utf-8', 0, bytesRead))) return;
252
+ if (matcher.push(decoder.write(buffer.subarray(0, bytesRead)))) return;
218
253
  }
219
254
  } finally{
220
255
  await handle.close();
@@ -462,9 +497,11 @@ exports.escapeContent = __webpack_exports__.escapeContent;
462
497
  exports.extractAllDumpScriptsSync = __webpack_exports__.extractAllDumpScriptsSync;
463
498
  exports.extractImageByIdSync = __webpack_exports__.extractImageByIdSync;
464
499
  exports.extractLastDumpScriptSync = __webpack_exports__.extractLastDumpScriptSync;
500
+ exports.extractTestRunReportDumpSync = __webpack_exports__.extractTestRunReportDumpSync;
465
501
  exports.generateAgentReportComment = __webpack_exports__.generateAgentReportComment;
466
502
  exports.generateDumpScriptTag = __webpack_exports__.generateDumpScriptTag;
467
503
  exports.generateImageScriptTag = __webpack_exports__.generateImageScriptTag;
504
+ exports.generateTestRunReportScriptTag = __webpack_exports__.generateTestRunReportScriptTag;
468
505
  exports.getBaseUrlFixScript = __webpack_exports__.getBaseUrlFixScript;
469
506
  exports.parseDumpScript = __webpack_exports__.parseDumpScript;
470
507
  exports.parseDumpScriptAttributes = __webpack_exports__.parseDumpScriptAttributes;
@@ -482,9 +519,11 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
482
519
  "extractAllDumpScriptsSync",
483
520
  "extractImageByIdSync",
484
521
  "extractLastDumpScriptSync",
522
+ "extractTestRunReportDumpSync",
485
523
  "generateAgentReportComment",
486
524
  "generateDumpScriptTag",
487
525
  "generateImageScriptTag",
526
+ "generateTestRunReportScriptTag",
488
527
  "getBaseUrlFixScript",
489
528
  "parseDumpScript",
490
529
  "parseDumpScriptAttributes",
@@ -1 +1 @@
1
- {"version":3,"file":"dump/html-utils.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/html-utils.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { closeSync, openSync, readSync, statSync } from 'node:fs';\nimport { open as openAsync } from 'node:fs/promises';\nimport { antiEscapeScriptTag, escapeScriptTag } from '@midscene/shared/utils';\nimport type { AIUsageInfo, IReportActionDump, ModelBrief } from '../types';\n\nexport const escapeContent = escapeScriptTag;\nexport const unescapeContent = antiEscapeScriptTag;\n\nfunction cleanHtmlCommentValue(value: unknown): string {\n return String(value ?? 'N/A')\n .replace(/\\0/g, '')\n .replace(/--/g, '- -');\n}\n\nfunction formatModelBriefForAgent(brief: ModelBrief): string {\n const intent = brief.intent || 'default';\n const model = brief.name || 'unknown';\n const description = brief.modelDescription\n ? ` (${brief.modelDescription})`\n : '';\n return `${intent}: ${model}${description}`;\n}\n\nfunction hasUsageModelInfo(usage?: AIUsageInfo): usage is AIUsageInfo {\n return Boolean(\n usage &&\n (usage.model_name ||\n usage.response_model_name ||\n usage.model_description ||\n usage.intent),\n );\n}\n\nfunction formatUsageModelForAgent(source: string, usage: AIUsageInfo): string {\n const intent = usage.intent || source;\n const model = usage.model_name || usage.response_model_name || 'unknown';\n const description = usage.model_description\n ? ` (${usage.model_description})`\n : '';\n return `${intent}: ${model}${description}`;\n}\n\nfunction collectUsageModelsForAgent(report: IReportActionDump): string {\n const models = new Map<string, string>();\n const executions = Array.isArray(report.executions) ? report.executions : [];\n for (const execution of executions) {\n const tasks = Array.isArray(execution.tasks) ? execution.tasks : [];\n for (const task of tasks) {\n const taskWithUsage = task as {\n usage?: AIUsageInfo;\n searchAreaUsage?: AIUsageInfo;\n };\n const usages: Array<[string, AIUsageInfo | undefined]> = [\n ['main', taskWithUsage.usage],\n ['searchArea', taskWithUsage.searchAreaUsage],\n ];\n\n for (const [source, usage] of usages) {\n if (!hasUsageModelInfo(usage)) {\n continue;\n }\n const formatted = formatUsageModelForAgent(source, usage);\n models.set(formatted, formatted);\n }\n }\n }\n\n return Array.from(models.values()).join('; ');\n}\n\nexport function generateAgentReportComment(report: IReportActionDump): string {\n const executions = Array.isArray(report.executions) ? report.executions : [];\n const taskCount = executions.reduce(\n (sum, execution) =>\n sum + (Array.isArray(execution.tasks) ? execution.tasks.length : 0),\n 0,\n );\n const modelBriefs = report.modelBriefs?.length\n ? report.modelBriefs.map(formatModelBriefForAgent).join('; ')\n : collectUsageModelsForAgent(report) || 'No model metadata recorded';\n\n const lines = [\n 'For Agent Analysis:',\n `Report: ${cleanHtmlCommentValue(report.groupName)}`,\n `SDK: ${cleanHtmlCommentValue(report.sdkVersion)}`,\n `Device: ${cleanHtmlCommentValue(report.deviceType)}`,\n `Executions: ${executions.length}; Tasks: ${taskCount}`,\n `Models: ${cleanHtmlCommentValue(modelBriefs)}`,\n 'Structured report JSON is stored in script[type=\"midscene_web_dump\"] tags near this comment.',\n 'Screenshots are stored as script[type=\"midscene-image\"] tags or files referenced by screenshot refs.',\n 'For AI analysis, inspect each execution task for type, status, timing, param, output, usage, searchAreaUsage, recorder, and screenshot refs.',\n 'Use the Markdown export when available; it contains the same report context plus image links for agent review.',\n ];\n\n return `\\n<!--\\n${lines.join('\\n')}\\n-->\\n`;\n}\n\nfunction htmlScriptCloseTag(): string {\n // biome-ignore lint/style/useTemplate: keep this token runtime-built for inline report bundles\n return String.fromCharCode(60) + '/script>';\n}\n\n/** Chunk size for streaming file operations (64KB) */\nexport const STREAMING_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Callback for processing matched tags during streaming.\n * @param content - The content between open and close tags\n * @returns true to stop scanning, false to continue\n */\ntype TagMatchCallback = (content: string) => boolean;\n\nclass StreamingTagMatcher {\n private leftover = '';\n private capturing = false;\n private currentContent = '';\n\n constructor(\n private readonly openTag: string,\n private readonly closeTag: string,\n private readonly onMatch: TagMatchCallback,\n ) {}\n\n push(nextChunk: string): boolean {\n const chunk = this.leftover + nextChunk;\n this.leftover = '';\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!this.capturing) {\n const startIndex = chunk.indexOf(this.openTag, searchStart);\n if (startIndex === -1) {\n const retainedSuffixLength = Math.max(0, this.openTag.length - 1);\n this.leftover = retainedSuffixLength\n ? chunk.slice(-retainedSuffixLength)\n : '';\n return false;\n }\n this.capturing = true;\n searchStart = startIndex + this.openTag.length;\n }\n\n const endIndex = chunk.indexOf(this.closeTag, searchStart);\n if (endIndex !== -1) {\n this.currentContent += chunk.slice(searchStart, endIndex);\n if (this.onMatch(this.currentContent)) return true;\n this.capturing = false;\n this.currentContent = '';\n searchStart = endIndex + this.closeTag.length;\n continue;\n }\n\n const retainedSuffixLength = Math.max(0, this.closeTag.length - 1);\n const contentEnd = Math.max(\n searchStart,\n chunk.length - retainedSuffixLength,\n );\n this.currentContent += chunk.slice(searchStart, contentEnd);\n this.leftover = chunk.slice(contentEnd);\n return false;\n }\n\n return false;\n }\n}\n\n/**\n * Stream through a file and find tags matching the pattern.\n * Memory usage: O(chunk_size + tag_size), not O(file_size).\n *\n * @param filePath - Absolute path to the file\n * @param openTag - Opening tag to search for\n * @param closeTag - Closing tag\n * @param onMatch - Callback for each matched tag content\n */\nexport function streamScanTags(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): void {\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);\n let position = 0;\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n position += bytesRead;\n if (matcher.push(buffer.toString('utf-8', 0, bytesRead))) return;\n }\n } finally {\n closeSync(fd);\n }\n}\n\n/** Asynchronously stream tags without blocking the Node.js event loop. */\nexport async function streamScanTagsAsync(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): Promise<void> {\n const handle = await openAsync(filePath, 'r');\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n let position = 0;\n const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);\n\n try {\n while (true) {\n const { bytesRead } = await handle.read(\n buffer,\n 0,\n STREAMING_CHUNK_SIZE,\n position,\n );\n if (bytesRead === 0) return;\n position += bytesRead;\n if (matcher.push(buffer.toString('utf-8', 0, bytesRead))) return;\n }\n } finally {\n await handle.close();\n }\n}\n\n/**\n * Synchronously extract a specific image's base64 data from an HTML file by its id.\n * Uses streaming to avoid loading the entire file into memory.\n *\n * @param htmlPath - Absolute path to the HTML file\n * @param imageId - The id of the image to extract\n * @returns The base64 data string, or null if not found\n */\nexport function extractImageByIdSync(\n htmlPath: string,\n imageId: string,\n): string | null {\n const targetTag = `<script type=\"midscene-image\" data-id=\"${imageId}\">`;\n const closeTag = htmlScriptCloseTag();\n\n let result: string | null = null;\n\n streamScanTags(htmlPath, targetTag, closeTag, (content) => {\n result = unescapeContent(content);\n return true; // Stop after first match\n });\n\n return result;\n}\n\n/**\n * Stream image script tags from source file directly to output file.\n * Memory usage: O(single_image_size), not O(all_images_size).\n *\n * @param srcFilePath - Source HTML file path\n * @param destFilePath - Destination file path to append to\n * @param writtenImageIds - IDs already written to the destination\n */\nexport function streamImageScriptsToFile(\n srcFilePath: string,\n destFilePath: string,\n writtenImageIds: Set<string> = new Set(),\n): void {\n const { appendFileSync } = require('node:fs');\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = htmlScriptCloseTag();\n\n streamScanTags(srcFilePath, openTag, closeTag, (content) => {\n const imageId = parseImageScriptId(content);\n if (!imageId || writtenImageIds.has(imageId)) {\n return false;\n }\n // Write complete tag immediately to destination, don't accumulate\n appendFileSync(destFilePath, `${openTag}${content}${closeTag}\\n`);\n writtenImageIds.add(imageId);\n return false; // Continue scanning for more tags\n });\n}\n\nfunction parseImageScriptId(contentAfterType: string): string | null {\n const match = /^ data-id=\"([a-z0-9_-]{1,128})\">/i.exec(contentAfterType);\n return match?.[1] ?? null;\n}\n\n/** Collect IDs of real inline image script tags without blocking on a large report. */\nexport async function collectImageScriptIds(\n filePath: string,\n): Promise<Set<string>> {\n const imageIds = new Set<string>();\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = htmlScriptCloseTag();\n\n await streamScanTagsAsync(filePath, openTag, closeTag, (content) => {\n const imageId = parseImageScriptId(content);\n if (imageId) imageIds.add(imageId);\n return false;\n });\n return imageIds;\n}\n\n/**\n * Extract the LAST dump script content from HTML file using streaming.\n * Memory usage: O(dump_size), not O(file_size).\n *\n * @param filePath - Absolute path to the HTML file\n * @returns The dump script content (trimmed), or empty string if not found\n */\nexport function extractLastDumpScriptSync(filePath: string): string {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n let lastContent = '';\n\n // Custom streaming to handle the special case where open tag has variable attributes\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n // Find the end of the opening tag (the '>' character)\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n lastContent = currentContent.slice(0, endIdx).trim();\n capturing = false;\n currentContent = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n lastContent = currentContent.trim();\n capturing = false;\n currentContent = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n\n return lastContent;\n}\n\n/**\n * Extract ALL dump script contents from an HTML file using streaming.\n * Each entry includes the full opening tag (for attribute extraction) and the content.\n *\n * @param filePath - Absolute path to the HTML file\n * @returns Array of { openTag, content } for each dump script found\n */\nexport function extractAllDumpScriptsSync(\n filePath: string,\n): { openTag: string; content: string }[] {\n const results: { openTag: string; content: string }[] = [];\n streamDumpScriptsSync(filePath, (dumpScript) => {\n results.push(dumpScript);\n return false;\n });\n return results;\n}\n\n/**\n * Stream ALL dump scripts from an HTML file.\n * Calls onMatch for each dump script and keeps memory bounded to a single\n * dump script payload instead of accumulating every dump in memory.\n *\n * @param filePath - Absolute path to the HTML file\n * @param onMatch - Callback for each dump script; return true to stop early\n */\nexport function streamDumpScriptsSync(\n filePath: string,\n onMatch: (dumpScript: { openTag: string; content: string }) => boolean,\n): void {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n let currentOpenTag = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentOpenTag = chunk.slice(startIdx, tagEndIdx + 1);\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n const shouldStop = onMatch({\n openTag: currentOpenTag,\n content: currentContent.slice(0, endIdx).trim(),\n });\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n const shouldStop = onMatch({\n openTag: currentOpenTag,\n content: currentContent.trim(),\n });\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n}\n\nexport function parseImageScripts(html: string): Record<string, string> {\n const imageMap: Record<string, string> = {};\n const regex =\n /<script type=\"midscene-image\" data-id=\"([^\"]+)\">([\\s\\S]*?)<\\/script>/g;\n\n for (const match of html.matchAll(regex)) {\n const [, id, content] = match;\n imageMap[id] = unescapeContent(content);\n }\n\n return imageMap;\n}\n\nexport function parseDumpScript(html: string): string {\n // Use string search instead of regex to avoid ReDoS vulnerability\n // Find the LAST dump script tag (template may contain similar patterns in bundled JS)\n const scriptOpenTag = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n // Find the last occurrence of the opening tag\n const lastOpenIndex = html.lastIndexOf(scriptOpenTag);\n if (lastOpenIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the end of the opening tag (the '>' character)\n const tagEndIndex = html.indexOf('>', lastOpenIndex);\n if (tagEndIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the closing tag after the opening tag\n const closeIndex = html.indexOf(closeTag, tagEndIndex);\n if (closeIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Extract content between opening and closing tags\n const content = html.substring(tagEndIndex + 1, closeIndex);\n return unescapeContent(content);\n}\n\nexport function parseDumpScriptAttributes(\n html: string,\n): Record<string, string> {\n const regex = /<script type=\"midscene_web_dump\"([^>]*)>/;\n const match = regex.exec(html);\n\n if (!match) {\n return {};\n }\n\n const attrString = match[1];\n const attributes: Record<string, string> = {};\n const attrRegex = /(\\w+)=\"([^\"]*)\"/g;\n\n for (const attrMatch of attrString.matchAll(attrRegex)) {\n const [, key, value] = attrMatch;\n if (key !== 'type') {\n attributes[key] = decodeURIComponent(value);\n }\n }\n\n return attributes;\n}\n\nexport function generateImageScriptTag(id: string, data: string): string {\n // Do not use template string here, will cause bundle error with <script\n const closeTag = htmlScriptCloseTag();\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene-image\" data-id=\"' +\n id +\n '\">' +\n escapeContent(data) +\n closeTag\n );\n}\n\n/**\n * Inline script that fixes relative URL resolution for directory-mode reports.\n *\n * Problem: when a static server (e.g. `npx serve`) serves `name/index.html`\n * at URL `/name` (without trailing slash), relative paths like\n * `./screenshots/xxx.png` resolve to `/screenshots/xxx.png` instead of\n * `/name/screenshots/xxx.png`.\n *\n * Fix: dynamically insert a <base> tag so relative URLs resolve correctly.\n */\n// Do not use template string here, will cause bundle error with <script\n//\n// The closing script tag is built at runtime so bundlers cannot inline the\n// token that would prematurely close the report template's inline app bundle.\n//\n// Do NOT replace this with a string constant, hex escape (\\x3c), or literal\n// string concatenation. Bundlers may optimise those forms back to the unsafe\n// token.\nlet _baseUrlFixScript: string;\nexport function getBaseUrlFixScript(): string {\n if (!_baseUrlFixScript) {\n const close = htmlScriptCloseTag();\n _baseUrlFixScript =\n // biome-ignore lint/style/useTemplate: see above\n '\\n<script>(function(){' +\n 'var p=window.location.pathname;' +\n 'if(p.endsWith(\"/\")||/\\\\.\\\\w+$/.test(p))return;' +\n 'var b=document.createElement(\"base\");' +\n 'b.href=p+\"/\";' +\n 'document.head.insertBefore(b,document.head.firstChild)' +\n '})()' +\n close +\n '\\n';\n }\n return _baseUrlFixScript;\n}\n\n/**\n * Dump-script attribute that records how the report file stores screenshots.\n * Written by the report generator and the merger, read back when deciding\n * whether a report is in directory mode. Kept here, next to dump-tag\n * generation, so the writer and reader share one source of truth.\n */\nexport const DATA_SCREENSHOT_MODE_ATTR = 'data-screenshot-mode';\n\nexport function generateDumpScriptTag(\n json: string,\n attributes?: Record<string, string | number | boolean>,\n): string {\n const closeTag = htmlScriptCloseTag();\n let attrString = '';\n if (attributes && Object.keys(attributes).length > 0) {\n // Do not use template string here, will cause bundle error with <script\n attrString =\n // biome-ignore lint/style/useTemplate: <explanation>\n ' ' +\n Object.entries(attributes)\n // biome-ignore lint/style/useTemplate: <explanation>\n .map(([k, v]) => k + '=\"' + encodeURIComponent(v) + '\"')\n .join(' ');\n }\n\n // Do not use template string here, will cause bundle error with <script\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\"' +\n attrString +\n '>' +\n escapeContent(json) +\n closeTag\n );\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","escapeContent","escapeScriptTag","unescapeContent","antiEscapeScriptTag","cleanHtmlCommentValue","value","String","formatModelBriefForAgent","brief","intent","model","description","hasUsageModelInfo","usage","Boolean","formatUsageModelForAgent","source","collectUsageModelsForAgent","report","models","Map","executions","Array","execution","tasks","task","taskWithUsage","usages","formatted","generateAgentReportComment","taskCount","sum","modelBriefs","lines","htmlScriptCloseTag","STREAMING_CHUNK_SIZE","StreamingTagMatcher","nextChunk","chunk","searchStart","startIndex","retainedSuffixLength","Math","endIndex","contentEnd","openTag","closeTag","onMatch","streamScanTags","filePath","fd","openSync","fileSize","statSync","buffer","Buffer","matcher","position","bytesRead","readSync","closeSync","streamScanTagsAsync","handle","openAsync","extractImageByIdSync","htmlPath","imageId","targetTag","result","content","streamImageScriptsToFile","srcFilePath","destFilePath","writtenImageIds","Set","appendFileSync","require","parseImageScriptId","contentAfterType","match","collectImageScriptIds","imageIds","extractLastDumpScriptSync","openTagPrefix","lastContent","leftover","capturing","currentContent","endIdx","startIdx","tagEndIdx","extractAllDumpScriptsSync","results","streamDumpScriptsSync","dumpScript","currentOpenTag","shouldStop","parseImageScripts","html","imageMap","regex","id","parseDumpScript","scriptOpenTag","lastOpenIndex","Error","tagEndIndex","closeIndex","parseDumpScriptAttributes","attrString","attributes","attrRegex","attrMatch","decodeURIComponent","generateImageScriptTag","data","_baseUrlFixScript","getBaseUrlFixScript","close","DATA_SCREENSHOT_MODE_ATTR","generateDumpScriptTag","json","k","v","encodeURIComponent"],"mappings":"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"}
1
+ {"version":3,"file":"dump/html-utils.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/html-utils.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { closeSync, openSync, readSync, statSync } from 'node:fs';\nimport { open as openAsync } from 'node:fs/promises';\nimport { StringDecoder } from 'node:string_decoder';\nimport { antiEscapeScriptTag, escapeScriptTag } from '@midscene/shared/utils';\nimport {\n TEST_RUN_REPORT_SCRIPT_TYPE,\n type TestRunReportDump,\n} from '../test-run-report';\nimport type { AIUsageInfo, IReportActionDump, ModelBrief } from '../types';\n\nexport const escapeContent = escapeScriptTag;\nexport const unescapeContent = antiEscapeScriptTag;\n\nfunction cleanHtmlCommentValue(value: unknown): string {\n return String(value ?? 'N/A')\n .replace(/\\0/g, '')\n .replace(/--/g, '- -');\n}\n\nfunction formatModelBriefForAgent(brief: ModelBrief): string {\n const intent = brief.intent || 'default';\n const model = brief.name || 'unknown';\n const description = brief.modelDescription\n ? ` (${brief.modelDescription})`\n : '';\n return `${intent}: ${model}${description}`;\n}\n\nfunction hasUsageModelInfo(usage?: AIUsageInfo): usage is AIUsageInfo {\n return Boolean(\n usage &&\n (usage.model_name ||\n usage.response_model_name ||\n usage.model_description ||\n usage.intent),\n );\n}\n\nfunction formatUsageModelForAgent(source: string, usage: AIUsageInfo): string {\n const intent = usage.intent || source;\n const model = usage.model_name || usage.response_model_name || 'unknown';\n const description = usage.model_description\n ? ` (${usage.model_description})`\n : '';\n return `${intent}: ${model}${description}`;\n}\n\nfunction collectUsageModelsForAgent(report: IReportActionDump): string {\n const models = new Map<string, string>();\n const executions = Array.isArray(report.executions) ? report.executions : [];\n for (const execution of executions) {\n const tasks = Array.isArray(execution.tasks) ? execution.tasks : [];\n for (const task of tasks) {\n const taskWithUsage = task as {\n usage?: AIUsageInfo;\n searchAreaUsage?: AIUsageInfo;\n };\n const usages: Array<[string, AIUsageInfo | undefined]> = [\n ['main', taskWithUsage.usage],\n ['searchArea', taskWithUsage.searchAreaUsage],\n ];\n\n for (const [source, usage] of usages) {\n if (!hasUsageModelInfo(usage)) {\n continue;\n }\n const formatted = formatUsageModelForAgent(source, usage);\n models.set(formatted, formatted);\n }\n }\n }\n\n return Array.from(models.values()).join('; ');\n}\n\nexport function generateAgentReportComment(report: IReportActionDump): string {\n const executions = Array.isArray(report.executions) ? report.executions : [];\n const taskCount = executions.reduce(\n (sum, execution) =>\n sum + (Array.isArray(execution.tasks) ? execution.tasks.length : 0),\n 0,\n );\n const modelBriefs = report.modelBriefs?.length\n ? report.modelBriefs.map(formatModelBriefForAgent).join('; ')\n : collectUsageModelsForAgent(report) || 'No model metadata recorded';\n\n const lines = [\n 'For Agent Analysis:',\n `Report: ${cleanHtmlCommentValue(report.groupName)}`,\n `SDK: ${cleanHtmlCommentValue(report.sdkVersion)}`,\n `Device: ${cleanHtmlCommentValue(report.deviceType)}`,\n `Executions: ${executions.length}; Tasks: ${taskCount}`,\n `Models: ${cleanHtmlCommentValue(modelBriefs)}`,\n 'Structured report JSON is stored in script[type=\"midscene_web_dump\"] tags near this comment.',\n 'Screenshots are stored as script[type=\"midscene-image\"] tags or files referenced by screenshot refs.',\n 'For AI analysis, inspect each execution task for type, status, timing, param, output, usage, searchAreaUsage, recorder, and screenshot refs.',\n 'Use the Markdown export when available; it contains the same report context plus image links for agent review.',\n ];\n\n return `\\n<!--\\n${lines.join('\\n')}\\n-->\\n`;\n}\n\nfunction htmlScriptCloseTag(): string {\n // biome-ignore lint/style/useTemplate: keep this token runtime-built for inline report bundles\n return String.fromCharCode(60) + '/script>';\n}\n\n/** Avoid raw close tokens: this writer itself is bundled into report HTML. */\nexport function generateTestRunReportScriptTag(\n dump: TestRunReportDump,\n): string {\n return [\n String.fromCharCode(60),\n 'script type=\"',\n TEST_RUN_REPORT_SCRIPT_TYPE,\n '\">\\n',\n escapeScriptTag(JSON.stringify(dump)),\n '\\n',\n htmlScriptCloseTag(),\n '\\n',\n ].join('');\n}\n\nexport function extractTestRunReportDumpSync(\n filePath: string,\n): TestRunReportDump | undefined {\n let result: TestRunReportDump | undefined;\n // Consume executable scripts as whole elements: the embedded viewer itself\n // contains report-writer source with data-tag strings that are not payloads.\n const openTag = `${String.fromCharCode(60)}script`;\n streamScanTags(filePath, openTag, htmlScriptCloseTag(), (content) => {\n const tagEnd = content.indexOf('>');\n const attributes = content.slice(0, tagEnd);\n const type = attributes.match(/(?:^|\\s)type\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n if (type !== TEST_RUN_REPORT_SCRIPT_TYPE) return false;\n if (result)\n throw new Error(\n `Report contains multiple Midscene Test dumps: ${filePath}`,\n );\n const parsed = JSON.parse(unescapeContent(content.slice(tagEnd + 1)));\n if (\n parsed?.schemaVersion !== 1 ||\n parsed?.kind !== 'test-runner' ||\n !Array.isArray(parsed.projects)\n )\n throw new Error(`Invalid Midscene Test report dump: ${filePath}`);\n result = parsed;\n return false;\n });\n return result;\n}\n\n/** Chunk size for streaming file operations (64KB) */\nexport const STREAMING_CHUNK_SIZE = 64 * 1024;\n\n/**\n * Callback for processing matched tags during streaming.\n * @param content - The content between open and close tags\n * @returns true to stop scanning, false to continue\n */\ntype TagMatchCallback = (content: string) => boolean;\n\nclass StreamingTagMatcher {\n private leftover = '';\n private capturing = false;\n private currentContent = '';\n\n constructor(\n private readonly openTag: string,\n private readonly closeTag: string,\n private readonly onMatch: TagMatchCallback,\n ) {}\n\n push(nextChunk: string): boolean {\n const chunk = this.leftover + nextChunk;\n this.leftover = '';\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!this.capturing) {\n const startIndex = chunk.indexOf(this.openTag, searchStart);\n if (startIndex === -1) {\n const retainedSuffixLength = Math.max(0, this.openTag.length - 1);\n this.leftover = retainedSuffixLength\n ? chunk.slice(-retainedSuffixLength)\n : '';\n return false;\n }\n this.capturing = true;\n searchStart = startIndex + this.openTag.length;\n }\n\n const endIndex = chunk.indexOf(this.closeTag, searchStart);\n if (endIndex !== -1) {\n this.currentContent += chunk.slice(searchStart, endIndex);\n if (this.onMatch(this.currentContent)) return true;\n this.capturing = false;\n this.currentContent = '';\n searchStart = endIndex + this.closeTag.length;\n continue;\n }\n\n const retainedSuffixLength = Math.max(0, this.closeTag.length - 1);\n const contentEnd = Math.max(\n searchStart,\n chunk.length - retainedSuffixLength,\n );\n this.currentContent += chunk.slice(searchStart, contentEnd);\n this.leftover = chunk.slice(contentEnd);\n return false;\n }\n\n return false;\n }\n}\n\n/**\n * Stream through a file and find tags matching the pattern.\n * Memory usage: O(chunk_size + tag_size), not O(file_size).\n *\n * @param filePath - Absolute path to the file\n * @param openTag - Opening tag to search for\n * @param closeTag - Closing tag\n * @param onMatch - Callback for each matched tag content\n */\nexport function streamScanTags(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): void {\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n const decoder = new StringDecoder('utf8');\n const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);\n let position = 0;\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n position += bytesRead;\n if (matcher.push(decoder.write(buffer.subarray(0, bytesRead)))) return;\n }\n matcher.push(decoder.end());\n } finally {\n closeSync(fd);\n }\n}\n\n/** Asynchronously stream tags without blocking the Node.js event loop. */\nexport async function streamScanTagsAsync(\n filePath: string,\n openTag: string,\n closeTag: string,\n onMatch: TagMatchCallback,\n): Promise<void> {\n const handle = await openAsync(filePath, 'r');\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n const decoder = new StringDecoder('utf8');\n let position = 0;\n const matcher = new StreamingTagMatcher(openTag, closeTag, onMatch);\n\n try {\n while (true) {\n const { bytesRead } = await handle.read(\n buffer,\n 0,\n STREAMING_CHUNK_SIZE,\n position,\n );\n if (bytesRead === 0) {\n matcher.push(decoder.end());\n return;\n }\n position += bytesRead;\n if (matcher.push(decoder.write(buffer.subarray(0, bytesRead)))) return;\n }\n } finally {\n await handle.close();\n }\n}\n\n/**\n * Synchronously extract a specific image's base64 data from an HTML file by its id.\n * Uses streaming to avoid loading the entire file into memory.\n *\n * @param htmlPath - Absolute path to the HTML file\n * @param imageId - The id of the image to extract\n * @returns The base64 data string, or null if not found\n */\nexport function extractImageByIdSync(\n htmlPath: string,\n imageId: string,\n): string | null {\n const targetTag = `<script type=\"midscene-image\" data-id=\"${imageId}\">`;\n const closeTag = htmlScriptCloseTag();\n\n let result: string | null = null;\n\n streamScanTags(htmlPath, targetTag, closeTag, (content) => {\n result = unescapeContent(content);\n return true; // Stop after first match\n });\n\n return result;\n}\n\n/**\n * Stream image script tags from source file directly to output file.\n * Memory usage: O(single_image_size), not O(all_images_size).\n *\n * @param srcFilePath - Source HTML file path\n * @param destFilePath - Destination file path to append to\n * @param writtenImageIds - IDs already written to the destination\n */\nexport function streamImageScriptsToFile(\n srcFilePath: string,\n destFilePath: string,\n writtenImageIds: Set<string> = new Set(),\n): void {\n const { appendFileSync } = require('node:fs');\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = htmlScriptCloseTag();\n\n streamScanTags(srcFilePath, openTag, closeTag, (content) => {\n const imageId = parseImageScriptId(content);\n if (!imageId || writtenImageIds.has(imageId)) {\n return false;\n }\n // Write complete tag immediately to destination, don't accumulate\n appendFileSync(destFilePath, `${openTag}${content}${closeTag}\\n`);\n writtenImageIds.add(imageId);\n return false; // Continue scanning for more tags\n });\n}\n\nfunction parseImageScriptId(contentAfterType: string): string | null {\n const match = /^ data-id=\"([a-z0-9_-]{1,128})\">/i.exec(contentAfterType);\n return match?.[1] ?? null;\n}\n\n/** Collect IDs of real inline image script tags without blocking on a large report. */\nexport async function collectImageScriptIds(\n filePath: string,\n): Promise<Set<string>> {\n const imageIds = new Set<string>();\n const openTag = '<script type=\"midscene-image\"';\n const closeTag = htmlScriptCloseTag();\n\n await streamScanTagsAsync(filePath, openTag, closeTag, (content) => {\n const imageId = parseImageScriptId(content);\n if (imageId) imageIds.add(imageId);\n return false;\n });\n return imageIds;\n}\n\n/**\n * Extract the LAST dump script content from HTML file using streaming.\n * Memory usage: O(dump_size), not O(file_size).\n *\n * @param filePath - Absolute path to the HTML file\n * @returns The dump script content (trimmed), or empty string if not found\n */\nexport function extractLastDumpScriptSync(filePath: string): string {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n let lastContent = '';\n\n // Custom streaming to handle the special case where open tag has variable attributes\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n // Find the end of the opening tag (the '>' character)\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n lastContent = currentContent.slice(0, endIdx).trim();\n capturing = false;\n currentContent = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n lastContent = currentContent.trim();\n capturing = false;\n currentContent = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n\n return lastContent;\n}\n\n/**\n * Extract ALL dump script contents from an HTML file using streaming.\n * Each entry includes the full opening tag (for attribute extraction) and the content.\n *\n * @param filePath - Absolute path to the HTML file\n * @returns Array of { openTag, content } for each dump script found\n */\nexport function extractAllDumpScriptsSync(\n filePath: string,\n): { openTag: string; content: string }[] {\n const results: { openTag: string; content: string }[] = [];\n streamDumpScriptsSync(filePath, (dumpScript) => {\n results.push(dumpScript);\n return false;\n });\n return results;\n}\n\n/**\n * Stream ALL dump scripts from an HTML file.\n * Calls onMatch for each dump script and keeps memory bounded to a single\n * dump script payload instead of accumulating every dump in memory.\n *\n * @param filePath - Absolute path to the HTML file\n * @param onMatch - Callback for each dump script; return true to stop early\n */\nexport function streamDumpScriptsSync(\n filePath: string,\n onMatch: (dumpScript: { openTag: string; content: string }) => boolean,\n): void {\n const openTagPrefix = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n const fd = openSync(filePath, 'r');\n const fileSize = statSync(filePath).size;\n const buffer = Buffer.alloc(STREAMING_CHUNK_SIZE);\n\n let position = 0;\n let leftover = '';\n let capturing = false;\n let currentContent = '';\n let currentOpenTag = '';\n\n try {\n while (position < fileSize) {\n const bytesRead = readSync(fd, buffer, 0, STREAMING_CHUNK_SIZE, position);\n const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);\n position += bytesRead;\n\n let searchStart = 0;\n\n while (searchStart < chunk.length) {\n if (!capturing) {\n const startIdx = chunk.indexOf(openTagPrefix, searchStart);\n if (startIdx !== -1) {\n const tagEndIdx = chunk.indexOf('>', startIdx);\n if (tagEndIdx !== -1) {\n capturing = true;\n currentOpenTag = chunk.slice(startIdx, tagEndIdx + 1);\n currentContent = chunk.slice(tagEndIdx + 1);\n const endIdx = currentContent.indexOf(closeTag);\n if (endIdx !== -1) {\n const shouldStop = onMatch({\n openTag: currentOpenTag,\n content: currentContent.slice(0, endIdx).trim(),\n });\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = tagEndIdx + 1 + endIdx + closeTag.length;\n } else {\n leftover = currentContent.slice(-closeTag.length);\n currentContent = currentContent.slice(0, -closeTag.length);\n break;\n }\n } else {\n leftover = chunk.slice(startIdx);\n break;\n }\n } else {\n leftover = chunk.slice(-openTagPrefix.length);\n break;\n }\n } else {\n const endIdx = chunk.indexOf(closeTag, searchStart);\n if (endIdx !== -1) {\n currentContent += chunk.slice(searchStart, endIdx);\n const shouldStop = onMatch({\n openTag: currentOpenTag,\n content: currentContent.trim(),\n });\n if (shouldStop) return;\n capturing = false;\n currentContent = '';\n currentOpenTag = '';\n searchStart = endIdx + closeTag.length;\n } else {\n currentContent += chunk.slice(searchStart, -closeTag.length);\n leftover = chunk.slice(-closeTag.length);\n break;\n }\n }\n }\n }\n } finally {\n closeSync(fd);\n }\n}\n\nexport function parseImageScripts(html: string): Record<string, string> {\n const imageMap: Record<string, string> = {};\n const regex =\n /<script type=\"midscene-image\" data-id=\"([^\"]+)\">([\\s\\S]*?)<\\/script>/g;\n\n for (const match of html.matchAll(regex)) {\n const [, id, content] = match;\n imageMap[id] = unescapeContent(content);\n }\n\n return imageMap;\n}\n\nexport function parseDumpScript(html: string): string {\n // Use string search instead of regex to avoid ReDoS vulnerability\n // Find the LAST dump script tag (template may contain similar patterns in bundled JS)\n const scriptOpenTag = '<script type=\"midscene_web_dump\"';\n const closeTag = htmlScriptCloseTag();\n\n // Find the last occurrence of the opening tag\n const lastOpenIndex = html.lastIndexOf(scriptOpenTag);\n if (lastOpenIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the end of the opening tag (the '>' character)\n const tagEndIndex = html.indexOf('>', lastOpenIndex);\n if (tagEndIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Find the closing tag after the opening tag\n const closeIndex = html.indexOf(closeTag, tagEndIndex);\n if (closeIndex === -1) {\n throw new Error('No dump script found in HTML');\n }\n\n // Extract content between opening and closing tags\n const content = html.substring(tagEndIndex + 1, closeIndex);\n return unescapeContent(content);\n}\n\nexport function parseDumpScriptAttributes(\n html: string,\n): Record<string, string> {\n const regex = /<script type=\"midscene_web_dump\"([^>]*)>/;\n const match = regex.exec(html);\n\n if (!match) {\n return {};\n }\n\n const attrString = match[1];\n const attributes: Record<string, string> = {};\n const attrRegex = /(\\w+)=\"([^\"]*)\"/g;\n\n for (const attrMatch of attrString.matchAll(attrRegex)) {\n const [, key, value] = attrMatch;\n if (key !== 'type') {\n attributes[key] = decodeURIComponent(value);\n }\n }\n\n return attributes;\n}\n\nexport function generateImageScriptTag(id: string, data: string): string {\n // Do not use template string here, will cause bundle error with <script\n const closeTag = htmlScriptCloseTag();\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene-image\" data-id=\"' +\n id +\n '\">' +\n escapeContent(data) +\n closeTag\n );\n}\n\n/**\n * Inline script that fixes relative URL resolution for directory-mode reports.\n *\n * Problem: when a static server (e.g. `npx serve`) serves `name/index.html`\n * at URL `/name` (without trailing slash), relative paths like\n * `./screenshots/xxx.png` resolve to `/screenshots/xxx.png` instead of\n * `/name/screenshots/xxx.png`.\n *\n * Fix: dynamically insert a <base> tag so relative URLs resolve correctly.\n */\n// Do not use template string here, will cause bundle error with <script\n//\n// The closing script tag is built at runtime so bundlers cannot inline the\n// token that would prematurely close the report template's inline app bundle.\n//\n// Do NOT replace this with a string constant, hex escape (\\x3c), or literal\n// string concatenation. Bundlers may optimise those forms back to the unsafe\n// token.\nlet _baseUrlFixScript: string;\nexport function getBaseUrlFixScript(): string {\n if (!_baseUrlFixScript) {\n const close = htmlScriptCloseTag();\n _baseUrlFixScript =\n // biome-ignore lint/style/useTemplate: see above\n '\\n<script>(function(){' +\n 'var p=window.location.pathname;' +\n 'if(p.endsWith(\"/\")||/\\\\.\\\\w+$/.test(p))return;' +\n 'var b=document.createElement(\"base\");' +\n 'b.href=p+\"/\";' +\n 'document.head.insertBefore(b,document.head.firstChild)' +\n '})()' +\n close +\n '\\n';\n }\n return _baseUrlFixScript;\n}\n\n/**\n * Dump-script attribute that records how the report file stores screenshots.\n * Written by the report generator and the merger, read back when deciding\n * whether a report is in directory mode. Kept here, next to dump-tag\n * generation, so the writer and reader share one source of truth.\n */\nexport const DATA_SCREENSHOT_MODE_ATTR = 'data-screenshot-mode';\n\nexport function generateDumpScriptTag(\n json: string,\n attributes?: Record<string, string | number | boolean>,\n): string {\n const closeTag = htmlScriptCloseTag();\n let attrString = '';\n if (attributes && Object.keys(attributes).length > 0) {\n // Do not use template string here, will cause bundle error with <script\n attrString =\n // biome-ignore lint/style/useTemplate: <explanation>\n ' ' +\n Object.entries(attributes)\n // biome-ignore lint/style/useTemplate: <explanation>\n .map(([k, v]) => k + '=\"' + encodeURIComponent(v) + '\"')\n .join(' ');\n }\n\n // Do not use template string here, will cause bundle error with <script\n return (\n // biome-ignore lint/style/useTemplate: <explanation>\n '<script type=\"midscene_web_dump\"' +\n attrString +\n '>' +\n escapeContent(json) +\n closeTag\n );\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","escapeContent","escapeScriptTag","unescapeContent","antiEscapeScriptTag","cleanHtmlCommentValue","value","String","formatModelBriefForAgent","brief","intent","model","description","hasUsageModelInfo","usage","Boolean","formatUsageModelForAgent","source","collectUsageModelsForAgent","report","models","Map","executions","Array","execution","tasks","task","taskWithUsage","usages","formatted","generateAgentReportComment","taskCount","sum","modelBriefs","lines","htmlScriptCloseTag","generateTestRunReportScriptTag","dump","TEST_RUN_REPORT_SCRIPT_TYPE","JSON","extractTestRunReportDumpSync","filePath","result","openTag","streamScanTags","content","tagEnd","attributes","type","Error","parsed","STREAMING_CHUNK_SIZE","StreamingTagMatcher","nextChunk","chunk","searchStart","startIndex","retainedSuffixLength","Math","endIndex","contentEnd","closeTag","onMatch","fd","openSync","fileSize","statSync","buffer","Buffer","decoder","StringDecoder","matcher","position","bytesRead","readSync","closeSync","streamScanTagsAsync","handle","openAsync","extractImageByIdSync","htmlPath","imageId","targetTag","streamImageScriptsToFile","srcFilePath","destFilePath","writtenImageIds","Set","appendFileSync","require","parseImageScriptId","contentAfterType","match","collectImageScriptIds","imageIds","extractLastDumpScriptSync","openTagPrefix","lastContent","leftover","capturing","currentContent","endIdx","startIdx","tagEndIdx","extractAllDumpScriptsSync","results","streamDumpScriptsSync","dumpScript","currentOpenTag","shouldStop","parseImageScripts","html","imageMap","regex","id","parseDumpScript","scriptOpenTag","lastOpenIndex","tagEndIndex","closeIndex","parseDumpScriptAttributes","attrString","attrRegex","attrMatch","decodeURIComponent","generateImageScriptTag","data","_baseUrlFixScript","getBaseUrlFixScript","close","DATA_SCREENSHOT_MODE_ATTR","generateDumpScriptTag","json","k","v","encodeURIComponent"],"mappings":";;;;;;;;;;;;;;;;;IAAAA,oBAAoB,CAAC,GAAG,CAAC,UAASC;QACjC,IAAI,IAAIC,OAAOD,WACR,IAAGD,oBAAoB,CAAC,CAACC,YAAYC,QAAQ,CAACF,oBAAoB,CAAC,CAAC,UAASE,MACzEC,OAAO,cAAc,CAAC,UAASD,KAAK;YAAE,YAAY;YAAM,KAAKD,UAAU,CAACC,IAAI;QAAC;IAGzF;;;ICNAF,oBAAoB,CAAC,GAAG,CAACI,KAAKC,OAAUF,OAAO,SAAS,CAAC,cAAc,CAAC,IAAI,CAACC,KAAKC;;;ICClFL,oBAAoB,CAAC,GAAG,CAAC;QACxB,IAAG,AAAkB,eAAlB,OAAOM,UAA0BA,OAAO,WAAW,EACrDH,OAAO,cAAc,CAAC,UAASG,OAAO,WAAW,EAAE;YAAE,OAAO;QAAS;QAEtEH,OAAO,cAAc,CAAC,UAAS,cAAc;YAAE,OAAO;QAAK;IAC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ICIO,MAAMI,gBAAgBC,sBAAAA,eAAeA;IACrC,MAAMC,kBAAkBC,sBAAAA,mBAAmBA;IAElD,SAASC,sBAAsBC,KAAc;QAC3C,OAAOC,OAAOD,SAAS,OACpB,OAAO,CAAC,OAAO,IACf,OAAO,CAAC,OAAO;IACpB;IAEA,SAASE,yBAAyBC,KAAiB;QACjD,MAAMC,SAASD,MAAM,MAAM,IAAI;QAC/B,MAAME,QAAQF,MAAM,IAAI,IAAI;QAC5B,MAAMG,cAAcH,MAAM,gBAAgB,GACtC,CAAC,EAAE,EAAEA,MAAM,gBAAgB,CAAC,CAAC,CAAC,GAC9B;QACJ,OAAO,GAAGC,OAAO,EAAE,EAAEC,QAAQC,aAAa;IAC5C;IAEA,SAASC,kBAAkBC,KAAmB;QAC5C,OAAOC,QACLD,SACGA,CAAAA,MAAM,UAAU,IACfA,MAAM,mBAAmB,IACzBA,MAAM,iBAAiB,IACvBA,MAAM,MAAK;IAEnB;IAEA,SAASE,yBAAyBC,MAAc,EAAEH,KAAkB;QAClE,MAAMJ,SAASI,MAAM,MAAM,IAAIG;QAC/B,MAAMN,QAAQG,MAAM,UAAU,IAAIA,MAAM,mBAAmB,IAAI;QAC/D,MAAMF,cAAcE,MAAM,iBAAiB,GACvC,CAAC,EAAE,EAAEA,MAAM,iBAAiB,CAAC,CAAC,CAAC,GAC/B;QACJ,OAAO,GAAGJ,OAAO,EAAE,EAAEC,QAAQC,aAAa;IAC5C;IAEA,SAASM,2BAA2BC,MAAyB;QAC3D,MAAMC,SAAS,IAAIC;QACnB,MAAMC,aAAaC,MAAM,OAAO,CAACJ,OAAO,UAAU,IAAIA,OAAO,UAAU,GAAG,EAAE;QAC5E,KAAK,MAAMK,aAAaF,WAAY;YAClC,MAAMG,QAAQF,MAAM,OAAO,CAACC,UAAU,KAAK,IAAIA,UAAU,KAAK,GAAG,EAAE;YACnE,KAAK,MAAME,QAAQD,MAAO;gBACxB,MAAME,gBAAgBD;gBAItB,MAAME,SAAmD;oBACvD;wBAAC;wBAAQD,cAAc,KAAK;qBAAC;oBAC7B;wBAAC;wBAAcA,cAAc,eAAe;qBAAC;iBAC9C;gBAED,KAAK,MAAM,CAACV,QAAQH,MAAM,IAAIc,OAAQ;oBACpC,IAAI,CAACf,kBAAkBC,QACrB;oBAEF,MAAMe,YAAYb,yBAAyBC,QAAQH;oBACnDM,OAAO,GAAG,CAACS,WAAWA;gBACxB;YACF;QACF;QAEA,OAAON,MAAM,IAAI,CAACH,OAAO,MAAM,IAAI,IAAI,CAAC;IAC1C;IAEO,SAASU,2BAA2BX,MAAyB;QAClE,MAAMG,aAAaC,MAAM,OAAO,CAACJ,OAAO,UAAU,IAAIA,OAAO,UAAU,GAAG,EAAE;QAC5E,MAAMY,YAAYT,WAAW,MAAM,CACjC,CAACU,KAAKR,YACJQ,MAAOT,CAAAA,MAAM,OAAO,CAACC,UAAU,KAAK,IAAIA,UAAU,KAAK,CAAC,MAAM,GAAG,IACnE;QAEF,MAAMS,cAAcd,OAAO,WAAW,EAAE,SACpCA,OAAO,WAAW,CAAC,GAAG,CAACX,0BAA0B,IAAI,CAAC,QACtDU,2BAA2BC,WAAW;QAE1C,MAAMe,QAAQ;YACZ;YACA,CAAC,QAAQ,EAAE7B,sBAAsBc,OAAO,SAAS,GAAG;YACpD,CAAC,KAAK,EAAEd,sBAAsBc,OAAO,UAAU,GAAG;YAClD,CAAC,QAAQ,EAAEd,sBAAsBc,OAAO,UAAU,GAAG;YACrD,CAAC,YAAY,EAAEG,WAAW,MAAM,CAAC,SAAS,EAAES,WAAW;YACvD,CAAC,QAAQ,EAAE1B,sBAAsB4B,cAAc;YAC/C;YACA;YACA;YACA;SACD;QAED,OAAO,CAAC,QAAQ,EAAEC,MAAM,IAAI,CAAC,MAAM,OAAO,CAAC;IAC7C;IAEA,SAASC;QAEP,OAAO5B,OAAO,YAAY,CAAC,MAAM;IACnC;IAGO,SAAS6B,+BACdC,IAAuB;QAEvB,OAAO;YACL9B,OAAO,YAAY,CAAC;YACpB;YACA+B,4CAAAA,2BAA2BA;YAC3B;YACApC,IAAAA,sBAAAA,eAAAA,AAAAA,EAAgBqC,KAAK,SAAS,CAACF;YAC/B;YACAF;YACA;SACD,CAAC,IAAI,CAAC;IACT;IAEO,SAASK,6BACdC,QAAgB;QAEhB,IAAIC;QAGJ,MAAMC,UAAU,GAAGpC,OAAO,YAAY,CAAC,IAAI,MAAM,CAAC;QAClDqC,eAAeH,UAAUE,SAASR,sBAAsB,CAACU;YACvD,MAAMC,SAASD,QAAQ,OAAO,CAAC;YAC/B,MAAME,aAAaF,QAAQ,KAAK,CAAC,GAAGC;YACpC,MAAME,OAAOD,WAAW,KAAK,CAAC,yCAAyC,CAAC,EAAE;YAC1E,IAAIC,SAASV,4CAAAA,2BAA2BA,EAAE,OAAO;YACjD,IAAII,QACF,MAAM,IAAIO,MACR,CAAC,8CAA8C,EAAER,UAAU;YAE/D,MAAMS,SAASX,KAAK,KAAK,CAACpC,gBAAgB0C,QAAQ,KAAK,CAACC,SAAS;YACjE,IACEI,QAAQ,kBAAkB,KAC1BA,QAAQ,SAAS,iBACjB,CAAC3B,MAAM,OAAO,CAAC2B,OAAO,QAAQ,GAE9B,MAAM,IAAID,MAAM,CAAC,mCAAmC,EAAER,UAAU;YAClEC,SAASQ;YACT,OAAO;QACT;QACA,OAAOR;IACT;IAGO,MAAMS,uBAAuB;IASpC,MAAMC;QAWJ,KAAKC,SAAiB,EAAW;YAC/B,MAAMC,QAAQ,IAAI,CAAC,QAAQ,GAAGD;YAC9B,IAAI,CAAC,QAAQ,GAAG;YAChB,IAAIE,cAAc;YAElB,MAAOA,cAAcD,MAAM,MAAM,CAAE;gBACjC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;oBACnB,MAAME,aAAaF,MAAM,OAAO,CAAC,IAAI,CAAC,OAAO,EAAEC;oBAC/C,IAAIC,AAAe,OAAfA,YAAmB;wBACrB,MAAMC,uBAAuBC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG;wBAC/D,IAAI,CAAC,QAAQ,GAAGD,uBACZH,MAAM,KAAK,CAAC,CAACG,wBACb;wBACJ,OAAO;oBACT;oBACA,IAAI,CAAC,SAAS,GAAG;oBACjBF,cAAcC,aAAa,IAAI,CAAC,OAAO,CAAC,MAAM;gBAChD;gBAEA,MAAMG,WAAWL,MAAM,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAEC;gBAC9C,IAAII,AAAa,OAAbA,UAAiB;oBACnB,IAAI,CAAC,cAAc,IAAIL,MAAM,KAAK,CAACC,aAAaI;oBAChD,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,cAAc,GAAG,OAAO;oBAC9C,IAAI,CAAC,SAAS,GAAG;oBACjB,IAAI,CAAC,cAAc,GAAG;oBACtBJ,cAAcI,WAAW,IAAI,CAAC,QAAQ,CAAC,MAAM;oBAC7C;gBACF;gBAEA,MAAMF,uBAAuBC,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG;gBAChE,MAAME,aAAaF,KAAK,GAAG,CACzBH,aACAD,MAAM,MAAM,GAAGG;gBAEjB,IAAI,CAAC,cAAc,IAAIH,MAAM,KAAK,CAACC,aAAaK;gBAChD,IAAI,CAAC,QAAQ,GAAGN,MAAM,KAAK,CAACM;gBAC5B;YACF;YAEA,OAAO;QACT;QA9CA,YACmBjB,OAAe,EACfkB,QAAgB,EAChBC,OAAyB,CAC1C;;;;YARF,uBAAQ,YAAR;YACA,uBAAQ,aAAR;YACA,uBAAQ,kBAAR;iBAGmBnB,OAAO,GAAPA;iBACAkB,QAAQ,GAARA;iBACAC,OAAO,GAAPA;iBAPX,QAAQ,GAAG;iBACX,SAAS,GAAG;iBACZ,cAAc,GAAG;QAMtB;IA2CL;IAWO,SAASlB,eACdH,QAAgB,EAChBE,OAAe,EACfkB,QAAgB,EAChBC,OAAyB;QAEzB,MAAMC,KAAKC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASvB,UAAU;QAC9B,MAAMwB,WAAWC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASzB,UAAU,IAAI;QACxC,MAAM0B,SAASC,OAAO,KAAK,CAACjB;QAC5B,MAAMkB,UAAU,IAAIC,6CAAAA,aAAaA,CAAC;QAClC,MAAMC,UAAU,IAAInB,oBAAoBT,SAASkB,UAAUC;QAC3D,IAAIU,WAAW;QAEf,IAAI;YACF,MAAOA,WAAWP,SAAU;gBAC1B,MAAMQ,YAAYC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASX,IAAII,QAAQ,GAAGhB,sBAAsBqB;gBAChEA,YAAYC;gBACZ,IAAIF,QAAQ,IAAI,CAACF,QAAQ,KAAK,CAACF,OAAO,QAAQ,CAAC,GAAGM,cAAc;YAClE;YACAF,QAAQ,IAAI,CAACF,QAAQ,GAAG;QAC1B,SAAU;YACRM,IAAAA,kBAAAA,SAAAA,AAAAA,EAAUZ;QACZ;IACF;IAGO,eAAea,oBACpBnC,QAAgB,EAChBE,OAAe,EACfkB,QAAgB,EAChBC,OAAyB;QAEzB,MAAMe,SAAS,MAAMC,AAAAA,IAAAA,yBAAAA,IAAAA,AAAAA,EAAUrC,UAAU;QACzC,MAAM0B,SAASC,OAAO,KAAK,CAACjB;QAC5B,MAAMkB,UAAU,IAAIC,6CAAAA,aAAaA,CAAC;QAClC,IAAIE,WAAW;QACf,MAAMD,UAAU,IAAInB,oBAAoBT,SAASkB,UAAUC;QAE3D,IAAI;YACF,MAAO,KAAM;gBACX,MAAM,EAAEW,SAAS,EAAE,GAAG,MAAMI,OAAO,IAAI,CACrCV,QACA,GACAhB,sBACAqB;gBAEF,IAAIC,AAAc,MAAdA,WAAiB,YACnBF,QAAQ,IAAI,CAACF,QAAQ,GAAG;gBAG1BG,YAAYC;gBACZ,IAAIF,QAAQ,IAAI,CAACF,QAAQ,KAAK,CAACF,OAAO,QAAQ,CAAC,GAAGM,cAAc;YAClE;QACF,SAAU;YACR,MAAMI,OAAO,KAAK;QACpB;IACF;IAUO,SAASE,qBACdC,QAAgB,EAChBC,OAAe;QAEf,MAAMC,YAAY,CAAC,uCAAuC,EAAED,QAAQ,EAAE,CAAC;QACvE,MAAMpB,WAAW1B;QAEjB,IAAIO,SAAwB;QAE5BE,eAAeoC,UAAUE,WAAWrB,UAAU,CAAChB;YAC7CH,SAASvC,gBAAgB0C;YACzB,OAAO;QACT;QAEA,OAAOH;IACT;IAUO,SAASyC,yBACdC,WAAmB,EACnBC,YAAoB,EACpBC,kBAA+B,IAAIC,KAAK;QAExC,MAAM,EAAEC,cAAc,EAAE,GAAGC,oBAAQ;QACnC,MAAM9C,UAAU;QAChB,MAAMkB,WAAW1B;QAEjBS,eAAewC,aAAazC,SAASkB,UAAU,CAAChB;YAC9C,MAAMoC,UAAUS,mBAAmB7C;YACnC,IAAI,CAACoC,WAAWK,gBAAgB,GAAG,CAACL,UAClC,OAAO;YAGTO,eAAeH,cAAc,GAAG1C,UAAUE,UAAUgB,SAAS,EAAE,CAAC;YAChEyB,gBAAgB,GAAG,CAACL;YACpB,OAAO;QACT;IACF;IAEA,SAASS,mBAAmBC,gBAAwB;QAClD,MAAMC,QAAQ,oCAAoC,IAAI,CAACD;QACvD,OAAOC,OAAO,CAAC,EAAE,IAAI;IACvB;IAGO,eAAeC,sBACpBpD,QAAgB;QAEhB,MAAMqD,WAAW,IAAIP;QACrB,MAAM5C,UAAU;QAChB,MAAMkB,WAAW1B;QAEjB,MAAMyC,oBAAoBnC,UAAUE,SAASkB,UAAU,CAAChB;YACtD,MAAMoC,UAAUS,mBAAmB7C;YACnC,IAAIoC,SAASa,SAAS,GAAG,CAACb;YAC1B,OAAO;QACT;QACA,OAAOa;IACT;IASO,SAASC,0BAA0BtD,QAAgB;QACxD,MAAMuD,gBAAgB;QACtB,MAAMnC,WAAW1B;QAEjB,IAAI8D,cAAc;QAGlB,MAAMlC,KAAKC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASvB,UAAU;QAC9B,MAAMwB,WAAWC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASzB,UAAU,IAAI;QACxC,MAAM0B,SAASC,OAAO,KAAK,CAACjB;QAE5B,IAAIqB,WAAW;QACf,IAAI0B,WAAW;QACf,IAAIC,YAAY;QAChB,IAAIC,iBAAiB;QAErB,IAAI;YACF,MAAO5B,WAAWP,SAAU;gBAC1B,MAAMQ,YAAYC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASX,IAAII,QAAQ,GAAGhB,sBAAsBqB;gBAChE,MAAMlB,QAAQ4C,WAAW/B,OAAO,QAAQ,CAAC,SAAS,GAAGM;gBACrDD,YAAYC;gBAEZ,IAAIlB,cAAc;gBAElB,MAAOA,cAAcD,MAAM,MAAM,CAC/B,IAAK6C,WA2BE;oBACL,MAAME,SAAS/C,MAAM,OAAO,CAACO,UAAUN;oBACvC,IAAI8C,AAAW,OAAXA,QAAe;wBACjBD,kBAAkB9C,MAAM,KAAK,CAACC,aAAa8C;wBAC3CJ,cAAcG,eAAe,IAAI;wBACjCD,YAAY;wBACZC,iBAAiB;wBACjB7C,cAAc8C,SAASxC,SAAS,MAAM;oBACxC,OAAO;wBACLuC,kBAAkB9C,MAAM,KAAK,CAACC,aAAa,CAACM,SAAS,MAAM;wBAC3DqC,WAAW5C,MAAM,KAAK,CAAC,CAACO,SAAS,MAAM;wBACvC;oBACF;gBACF,OAxCgB;oBACd,MAAMyC,WAAWhD,MAAM,OAAO,CAAC0C,eAAezC;oBAC9C,IAAI+C,AAAa,OAAbA,UAAiB;wBAEnB,MAAMC,YAAYjD,MAAM,OAAO,CAAC,KAAKgD;wBACrC,IAAIC,AAAc,OAAdA,WAAkB;4BACpBJ,YAAY;4BACZC,iBAAiB9C,MAAM,KAAK,CAACiD,YAAY;4BACzC,MAAMF,SAASD,eAAe,OAAO,CAACvC;4BACtC,IAAIwC,AAAW,OAAXA,QAAe;gCACjBJ,cAAcG,eAAe,KAAK,CAAC,GAAGC,QAAQ,IAAI;gCAClDF,YAAY;gCACZC,iBAAiB;gCACjB7C,cAAcgD,YAAY,IAAIF,SAASxC,SAAS,MAAM;4BACxD,OAAO;gCACLqC,WAAWE,eAAe,KAAK,CAAC,CAACvC,SAAS,MAAM;gCAChDuC,iBAAiBA,eAAe,KAAK,CAAC,GAAG,CAACvC,SAAS,MAAM;gCACzD;4BACF;wBACF,OAAO;4BACLqC,WAAW5C,MAAM,KAAK,CAACgD;4BACvB;wBACF;oBACF,OAAO;wBACLJ,WAAW5C,MAAM,KAAK,CAAC,CAAC0C,cAAc,MAAM;wBAC5C;oBACF;gBACF;YAeJ;QACF,SAAU;YACRrB,IAAAA,kBAAAA,SAAAA,AAAAA,EAAUZ;QACZ;QAEA,OAAOkC;IACT;IASO,SAASO,0BACd/D,QAAgB;QAEhB,MAAMgE,UAAkD,EAAE;QAC1DC,sBAAsBjE,UAAU,CAACkE;YAC/BF,QAAQ,IAAI,CAACE;YACb,OAAO;QACT;QACA,OAAOF;IACT;IAUO,SAASC,sBACdjE,QAAgB,EAChBqB,OAAsE;QAEtE,MAAMkC,gBAAgB;QACtB,MAAMnC,WAAW1B;QAEjB,MAAM4B,KAAKC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASvB,UAAU;QAC9B,MAAMwB,WAAWC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASzB,UAAU,IAAI;QACxC,MAAM0B,SAASC,OAAO,KAAK,CAACjB;QAE5B,IAAIqB,WAAW;QACf,IAAI0B,WAAW;QACf,IAAIC,YAAY;QAChB,IAAIC,iBAAiB;QACrB,IAAIQ,iBAAiB;QAErB,IAAI;YACF,MAAOpC,WAAWP,SAAU;gBAC1B,MAAMQ,YAAYC,AAAAA,IAAAA,kBAAAA,QAAAA,AAAAA,EAASX,IAAII,QAAQ,GAAGhB,sBAAsBqB;gBAChE,MAAMlB,QAAQ4C,WAAW/B,OAAO,QAAQ,CAAC,SAAS,GAAGM;gBACrDD,YAAYC;gBAEZ,IAAIlB,cAAc;gBAElB,MAAOA,cAAcD,MAAM,MAAM,CAC/B,IAAK6C,WAgCE;oBACL,MAAME,SAAS/C,MAAM,OAAO,CAACO,UAAUN;oBACvC,IAAI8C,AAAW,OAAXA,QAAe;wBACjBD,kBAAkB9C,MAAM,KAAK,CAACC,aAAa8C;wBAC3C,MAAMQ,aAAa/C,QAAQ;4BACzB,SAAS8C;4BACT,SAASR,eAAe,IAAI;wBAC9B;wBACA,IAAIS,YAAY;wBAChBV,YAAY;wBACZC,iBAAiB;wBACjBQ,iBAAiB;wBACjBrD,cAAc8C,SAASxC,SAAS,MAAM;oBACxC,OAAO;wBACLuC,kBAAkB9C,MAAM,KAAK,CAACC,aAAa,CAACM,SAAS,MAAM;wBAC3DqC,WAAW5C,MAAM,KAAK,CAAC,CAACO,SAAS,MAAM;wBACvC;oBACF;gBACF,OAlDgB;oBACd,MAAMyC,WAAWhD,MAAM,OAAO,CAAC0C,eAAezC;oBAC9C,IAAI+C,AAAa,OAAbA,UAAiB;wBACnB,MAAMC,YAAYjD,MAAM,OAAO,CAAC,KAAKgD;wBACrC,IAAIC,AAAc,OAAdA,WAAkB;4BACpBJ,YAAY;4BACZS,iBAAiBtD,MAAM,KAAK,CAACgD,UAAUC,YAAY;4BACnDH,iBAAiB9C,MAAM,KAAK,CAACiD,YAAY;4BACzC,MAAMF,SAASD,eAAe,OAAO,CAACvC;4BACtC,IAAIwC,AAAW,OAAXA,QAAe;gCACjB,MAAMQ,aAAa/C,QAAQ;oCACzB,SAAS8C;oCACT,SAASR,eAAe,KAAK,CAAC,GAAGC,QAAQ,IAAI;gCAC/C;gCACA,IAAIQ,YAAY;gCAChBV,YAAY;gCACZC,iBAAiB;gCACjBQ,iBAAiB;gCACjBrD,cAAcgD,YAAY,IAAIF,SAASxC,SAAS,MAAM;4BACxD,OAAO;gCACLqC,WAAWE,eAAe,KAAK,CAAC,CAACvC,SAAS,MAAM;gCAChDuC,iBAAiBA,eAAe,KAAK,CAAC,GAAG,CAACvC,SAAS,MAAM;gCACzD;4BACF;wBACF,OAAO;4BACLqC,WAAW5C,MAAM,KAAK,CAACgD;4BACvB;wBACF;oBACF,OAAO;wBACLJ,WAAW5C,MAAM,KAAK,CAAC,CAAC0C,cAAc,MAAM;wBAC5C;oBACF;gBACF;YAoBJ;QACF,SAAU;YACRrB,IAAAA,kBAAAA,SAAAA,AAAAA,EAAUZ;QACZ;IACF;IAEO,SAAS+C,kBAAkBC,IAAY;QAC5C,MAAMC,WAAmC,CAAC;QAC1C,MAAMC,QACJ;QAEF,KAAK,MAAMrB,SAASmB,KAAK,QAAQ,CAACE,OAAQ;YACxC,MAAM,GAAGC,IAAIrE,QAAQ,GAAG+C;YACxBoB,QAAQ,CAACE,GAAG,GAAG/G,gBAAgB0C;QACjC;QAEA,OAAOmE;IACT;IAEO,SAASG,gBAAgBJ,IAAY;QAG1C,MAAMK,gBAAgB;QACtB,MAAMvD,WAAW1B;QAGjB,MAAMkF,gBAAgBN,KAAK,WAAW,CAACK;QACvC,IAAIC,AAAkB,OAAlBA,eACF,MAAM,IAAIpE,MAAM;QAIlB,MAAMqE,cAAcP,KAAK,OAAO,CAAC,KAAKM;QACtC,IAAIC,AAAgB,OAAhBA,aACF,MAAM,IAAIrE,MAAM;QAIlB,MAAMsE,aAAaR,KAAK,OAAO,CAAClD,UAAUyD;QAC1C,IAAIC,AAAe,OAAfA,YACF,MAAM,IAAItE,MAAM;QAIlB,MAAMJ,UAAUkE,KAAK,SAAS,CAACO,cAAc,GAAGC;QAChD,OAAOpH,gBAAgB0C;IACzB;IAEO,SAAS2E,0BACdT,IAAY;QAEZ,MAAME,QAAQ;QACd,MAAMrB,QAAQqB,MAAM,IAAI,CAACF;QAEzB,IAAI,CAACnB,OACH,OAAO,CAAC;QAGV,MAAM6B,aAAa7B,KAAK,CAAC,EAAE;QAC3B,MAAM7C,aAAqC,CAAC;QAC5C,MAAM2E,YAAY;QAElB,KAAK,MAAMC,aAAaF,WAAW,QAAQ,CAACC,WAAY;YACtD,MAAM,GAAG9H,KAAKU,MAAM,GAAGqH;YACvB,IAAI/H,AAAQ,WAARA,KACFmD,UAAU,CAACnD,IAAI,GAAGgI,mBAAmBtH;QAEzC;QAEA,OAAOyC;IACT;IAEO,SAAS8E,uBAAuBX,EAAU,EAAEY,IAAY;QAE7D,MAAMjE,WAAW1B;QACjB,OAEE,4CACA+E,KACA,OACAjH,cAAc6H,QACdjE;IAEJ;IAoBA,IAAIkE;IACG,SAASC;QACd,IAAI,CAACD,mBAAmB;YACtB,MAAME,QAAQ9F;YACd4F,oBAEE,oNAOAE,QACA;QACJ;QACA,OAAOF;IACT;IAQO,MAAMG,4BAA4B;IAElC,SAASC,sBACdC,IAAY,EACZrF,UAAsD;QAEtD,MAAMc,WAAW1B;QACjB,IAAIsF,aAAa;QACjB,IAAI1E,cAAclD,OAAO,IAAI,CAACkD,YAAY,MAAM,GAAG,GAEjD0E,aAEE,MACA5H,OAAO,OAAO,CAACkD,YAEZ,GAAG,CAAC,CAAC,CAACsF,GAAGC,EAAE,GAAKD,IAAI,OAAOE,mBAAmBD,KAAK,KACnD,IAAI,CAAC;QAIZ,OAEE,qCACAb,aACA,MACAxH,cAAcmI,QACdvE;IAEJ"}
@@ -112,6 +112,9 @@ function resolveScreenshotSource(refInput, options) {
112
112
  return resolveImageSource(ref, options);
113
113
  }
114
114
  class ReportImageStore {
115
+ enableFileCopies() {
116
+ this.alsoWriteFileCopy = true;
117
+ }
115
118
  async persist(screenshot) {
116
119
  await this.ensureExistingInlineImageIdsLoaded();
117
120
  const shouldWriteFileCopy = 'directory' === this.mode || this.alsoWriteFileCopy;
@@ -180,7 +183,11 @@ class ReportImageStore {
180
183
  await this.existingInlineImageIdsPromise;
181
184
  }
182
185
  async persistToSharedFileIfNeeded(screenshot, options) {
183
- const { relativePath, absolutePath } = await this.writeImageFileIfNeeded({
186
+ const fileName = `${screenshot.id}.${screenshot.extension}`;
187
+ const { relativePath, absolutePath } = this.writtenFileIds.has(screenshot.id) ? {
188
+ relativePath: `./screenshots/${fileName}`,
189
+ absolutePath: (0, external_node_path_namespaceObject.join)(this.screenshotsDir, fileName)
190
+ } : await this.writeImageFileIfNeeded({
184
191
  id: screenshot.id,
185
192
  extension: screenshot.extension,
186
193
  rawBase64: screenshot.rawBase64
@@ -1 +1 @@
1
- {"version":3,"file":"dump/screenshot-store.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/screenshot-store.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';\nimport { writeFile as writeFileAsync } from 'node:fs/promises';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport type { ScreenshotItem } from '../screenshot-item';\nimport { collectImageScriptIds, extractImageByIdSync } from './html-utils';\nimport {\n type ImageUrlRef,\n type ScreenshotRef,\n type StoredImageRef,\n imageFileExtensionForMimeType,\n imageRefFileExtension,\n normalizeImageUrlRef,\n normalizeScreenshotRef,\n normalizeStoredImageRef,\n} from './image-reference';\n\nexport type {\n ImageUrlRef,\n ScreenshotRef,\n StoredImageRef,\n} from './image-reference';\nexport {\n imageRefFileExtension,\n normalizeImageUrlRef,\n normalizeScreenshotRef,\n normalizeStoredImageRef,\n} from './image-reference';\n\nconst base64ImageDataUrlPattern =\n /^data:(image\\/[a-z0-9.+-]+)(?:;[^,]*)?;base64,([a-z0-9+/=\\s]+)$/i;\ntype ResolvedImageSource =\n | {\n type: 'data-uri';\n id: string;\n mimeType: StoredImageRef['mimeType'];\n dataUri: string;\n }\n | {\n type: 'file';\n id: string;\n mimeType: StoredImageRef['mimeType'];\n filePath: string;\n };\n\n/** Parse and normalize a base64 image data URL for report persistence. */\nexport function parseBase64ImageDataUrl(imageUrl: string): {\n mimeType: `image/${string}`;\n extension: string;\n rawBase64: string;\n} {\n const match = imageUrl.match(base64ImageDataUrlPattern);\n if (!match) {\n throw new Error(\n 'ReportImageStore: reference image must be a valid base64 image data URL',\n );\n }\n const mimeType = match[1].toLowerCase() as `image/${string}`;\n return {\n mimeType,\n extension: imageFileExtensionForMimeType(mimeType),\n rawBase64: match[2].replace(/\\s/g, ''),\n };\n}\n\nexport function isBase64ImageDataUrl(value: string): boolean {\n return base64ImageDataUrlPattern.test(value);\n}\n\nexport function resolveImageSource(\n refInput: unknown,\n options: {\n reportPath: string;\n imageDirectory?: string;\n fallbackId?: string;\n fallbackMimeType?: ScreenshotRef['mimeType'];\n },\n): ResolvedImageSource {\n const ref = normalizeStoredImageRef(refInput);\n const id = ref?.id ?? options.fallbackId;\n const mimeType = ref?.mimeType ?? options.fallbackMimeType;\n\n if (!id || !mimeType) {\n throw new Error(\n 'ReportImageStore: image id and mimeType are required to resolve an image',\n );\n }\n\n const resolveReportRelativePath = (filePath: string): string =>\n isAbsolute(filePath)\n ? filePath\n : join(dirname(options.reportPath), filePath);\n\n if (ref?.storage === 'file') {\n if (!ref.path) {\n throw new Error(\n `ReportImageStore: image ref \"${ref.id}\" missing file path`,\n );\n }\n\n const explicitFilePath = resolveReportRelativePath(ref.path);\n if (existsSync(explicitFilePath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: explicitFilePath,\n };\n }\n }\n\n const inlineDataUri = extractImageByIdSync(options.reportPath, id);\n if (inlineDataUri) {\n return {\n type: 'data-uri',\n id,\n mimeType,\n dataUri: inlineDataUri,\n };\n }\n\n const extension = ref\n ? imageRefFileExtension(ref)\n : imageFileExtensionForMimeType(options.fallbackMimeType!);\n if (options.imageDirectory) {\n const companionImagePath = join(\n options.imageDirectory,\n `${id}.${extension}`,\n );\n if (existsSync(companionImagePath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: companionImagePath,\n };\n }\n }\n const siblingImagePath = join(\n dirname(options.reportPath),\n 'screenshots',\n `${id}.${extension}`,\n );\n if (existsSync(siblingImagePath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: siblingImagePath,\n };\n }\n\n throw new Error(\n `ReportImageStore: cannot resolve image \"${id}\" from ${options.reportPath}`,\n );\n}\n\nexport function resolveScreenshotSource(\n refInput: unknown,\n options: {\n reportPath: string;\n imageDirectory?: string;\n fallbackId?: string;\n fallbackMimeType?: ScreenshotRef['mimeType'];\n },\n): ResolvedImageSource {\n const ref = normalizeScreenshotRef(refInput);\n return resolveImageSource(ref, options);\n}\n\n/** Persists and restores every image asset referenced by a report. */\nexport class ReportImageStore {\n private readonly mode: 'inline' | 'directory';\n private readonly reportPath: string;\n private readonly screenshotsDir?: string;\n private readonly writeInlineImage?: (\n id: string,\n base64: string,\n ) => void | Promise<void>;\n private readonly alsoWriteFileCopy: boolean;\n private readonly writtenInlineIds = new Set<string>();\n private readonly writtenFileIds = new Set<string>();\n private readonly shouldReuseExistingInlineReport: boolean;\n private existingInlineImageIdsPromise?: Promise<void>;\n\n constructor(options: {\n mode: 'inline' | 'directory';\n reportPath: string;\n screenshotsDir?: string;\n writeInlineImage?: (id: string, base64: string) => void | Promise<void>;\n alsoWriteFileCopy?: boolean;\n /** @deprecated Use alsoWriteFileCopy instead. */\n ensureFileCopy?: boolean;\n /** Reuse inline image assets already present in reportPath. */\n reuseExistingReport?: boolean;\n }) {\n this.mode = options.mode;\n this.reportPath = options.reportPath;\n this.screenshotsDir = options.screenshotsDir;\n this.writeInlineImage = options.writeInlineImage;\n this.alsoWriteFileCopy =\n options.alsoWriteFileCopy ?? options.ensureFileCopy ?? false;\n this.shouldReuseExistingInlineReport = Boolean(\n options.reuseExistingReport && this.mode === 'inline',\n );\n }\n\n async persist(screenshot: ScreenshotItem): Promise<ScreenshotRef> {\n await this.ensureExistingInlineImageIdsLoaded();\n const shouldWriteFileCopy =\n this.mode === 'directory' || this.alsoWriteFileCopy;\n const fileRef = shouldWriteFileCopy\n ? await this.persistToSharedFileIfNeeded(screenshot, {\n markAsPersisted: this.mode === 'directory',\n })\n : null;\n\n if (this.mode === 'inline') {\n if (!this.writeInlineImage) {\n throw new Error(\n 'ReportImageStore: writeInlineImage is required in inline mode',\n );\n }\n if (!this.writtenInlineIds.has(screenshot.id)) {\n await this.writeInlineImage(screenshot.id, screenshot.base64);\n this.writtenInlineIds.add(screenshot.id);\n }\n return screenshot.markPersistedInline(this.reportPath);\n }\n\n if (!fileRef) {\n throw new Error(\n 'ReportImageStore: file persistence is required in directory mode',\n );\n }\n return fileRef;\n }\n\n /** Persist a base64 multimodal prompt image and return its content-addressed ref. */\n async persistReferenceImage(imageUrl: string): Promise<ImageUrlRef> {\n await this.ensureExistingInlineImageIdsLoaded();\n const { mimeType, extension, rawBase64 } =\n parseBase64ImageDataUrl(imageUrl);\n const id = `reference-${createHash('sha256')\n .update(mimeType)\n .update('\\0')\n .update(Buffer.from(rawBase64, 'base64'))\n .digest('hex')}`;\n const shouldWriteFileCopy =\n this.mode === 'directory' || this.alsoWriteFileCopy;\n const fileLocation = shouldWriteFileCopy\n ? await this.writeImageFileIfNeeded({ id, extension, rawBase64 })\n : null;\n\n let ref: ImageUrlRef;\n if (this.mode === 'inline') {\n if (!this.writeInlineImage) {\n throw new Error(\n 'ReportImageStore: writeInlineImage is required in inline mode',\n );\n }\n if (!this.writtenInlineIds.has(id)) {\n await this.writeInlineImage(id, imageUrl);\n this.writtenInlineIds.add(id);\n }\n ref = {\n type: 'midscene_image_url_ref',\n id,\n mimeType,\n storage: 'inline',\n };\n } else {\n if (!fileLocation) {\n throw new Error(\n 'ReportImageStore: file persistence is required in directory mode',\n );\n }\n ref = {\n type: 'midscene_image_url_ref',\n id,\n mimeType,\n storage: 'file',\n path: fileLocation.relativePath,\n };\n }\n return ref;\n }\n\n private async ensureExistingInlineImageIdsLoaded(): Promise<void> {\n if (!this.shouldReuseExistingInlineReport) return;\n if (!this.existingInlineImageIdsPromise) {\n this.existingInlineImageIdsPromise = (async () => {\n try {\n const imageIds = await collectImageScriptIds(this.reportPath);\n for (const imageId of imageIds) this.writtenInlineIds.add(imageId);\n } catch (error) {\n if (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n error.code === 'ENOENT'\n ) {\n return;\n }\n throw new Error(\n `ReportImageStore: failed to index existing inline images from ${this.reportPath}`,\n { cause: error },\n );\n }\n })();\n }\n await this.existingInlineImageIdsPromise;\n }\n\n private async persistToSharedFileIfNeeded(\n screenshot: ScreenshotItem,\n options: {\n markAsPersisted: boolean;\n },\n ): Promise<ScreenshotRef> {\n const { relativePath, absolutePath } = await this.writeImageFileIfNeeded({\n id: screenshot.id,\n extension: screenshot.extension,\n rawBase64: screenshot.rawBase64,\n });\n\n if (options.markAsPersisted) {\n return screenshot.markPersistedToPath(relativePath, absolutePath);\n }\n\n return screenshot.registerPersistedFileCopy(relativePath, absolutePath);\n }\n\n private async writeImageFileIfNeeded(image: {\n id: string;\n extension: string;\n rawBase64: string;\n }): Promise<{ relativePath: string; absolutePath: string }> {\n const screenshotsDir = this.screenshotsDir;\n if (!screenshotsDir) {\n throw new Error(\n 'ReportImageStore: screenshotsDir is required when file persistence is enabled',\n );\n }\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n const fileName = `${image.id}.${image.extension}`;\n const relativePath = `./screenshots/${fileName}`;\n const absolutePath = join(screenshotsDir, fileName);\n if (!this.writtenFileIds.has(image.id)) {\n await writeFileAsync(\n absolutePath,\n Buffer.from(image.rawBase64, 'base64'),\n );\n this.writtenFileIds.add(image.id);\n }\n return { relativePath, absolutePath };\n }\n\n /** Resolve any stored report image reference to a data URI. */\n loadDataUri(refInput: unknown): string {\n const ref = normalizeStoredImageRef(refInput);\n if (!ref) {\n throw new Error('ReportImageStore: invalid image reference');\n }\n\n const resolved = resolveImageSource(ref, {\n reportPath: this.reportPath,\n imageDirectory: this.screenshotsDir,\n });\n\n if (resolved.type === 'data-uri') {\n return resolved.dataUri;\n }\n\n const data = readFileSync(resolved.filePath);\n return `data:${resolved.mimeType};base64,${data.toString('base64')}`;\n }\n\n /** @deprecated Use loadDataUri. */\n loadBase64(refInput: unknown): string {\n const screenshotRef = normalizeScreenshotRef(refInput);\n if (!screenshotRef) {\n throw new Error('ReportImageStore: invalid screenshot reference');\n }\n return this.loadDataUri(screenshotRef);\n }\n\n cleanup(): void {\n if (\n this.mode === 'directory' &&\n this.screenshotsDir &&\n existsSync(this.screenshotsDir)\n ) {\n rmSync(this.screenshotsDir, { recursive: true, force: true });\n }\n this.writtenInlineIds.clear();\n this.writtenFileIds.clear();\n this.existingInlineImageIdsPromise = undefined;\n }\n}\n\n/** @deprecated Use ReportImageStore. */\nexport { ReportImageStore as ScreenshotStore };\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","base64ImageDataUrlPattern","parseBase64ImageDataUrl","imageUrl","match","Error","mimeType","imageFileExtensionForMimeType","isBase64ImageDataUrl","value","resolveImageSource","refInput","options","ref","normalizeStoredImageRef","id","resolveReportRelativePath","filePath","isAbsolute","join","dirname","explicitFilePath","existsSync","inlineDataUri","extractImageByIdSync","extension","imageRefFileExtension","companionImagePath","siblingImagePath","resolveScreenshotSource","normalizeScreenshotRef","ReportImageStore","screenshot","shouldWriteFileCopy","fileRef","rawBase64","createHash","Buffer","fileLocation","imageIds","collectImageScriptIds","imageId","error","relativePath","absolutePath","image","screenshotsDir","mkdirSync","fileName","writeFileAsync","resolved","data","readFileSync","screenshotRef","rmSync","undefined","Set","Boolean"],"mappings":"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"}
1
+ {"version":3,"file":"dump/screenshot-store.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../../src/dump/screenshot-store.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import { createHash } from 'node:crypto';\nimport { existsSync, mkdirSync, readFileSync, rmSync } from 'node:fs';\nimport { writeFile as writeFileAsync } from 'node:fs/promises';\nimport { dirname, isAbsolute, join } from 'node:path';\nimport type { ScreenshotItem } from '../screenshot-item';\nimport { collectImageScriptIds, extractImageByIdSync } from './html-utils';\nimport {\n type ImageUrlRef,\n type ScreenshotRef,\n type StoredImageRef,\n imageFileExtensionForMimeType,\n imageRefFileExtension,\n normalizeImageUrlRef,\n normalizeScreenshotRef,\n normalizeStoredImageRef,\n} from './image-reference';\n\nexport type {\n ImageUrlRef,\n ScreenshotRef,\n StoredImageRef,\n} from './image-reference';\nexport {\n imageRefFileExtension,\n normalizeImageUrlRef,\n normalizeScreenshotRef,\n normalizeStoredImageRef,\n} from './image-reference';\n\nconst base64ImageDataUrlPattern =\n /^data:(image\\/[a-z0-9.+-]+)(?:;[^,]*)?;base64,([a-z0-9+/=\\s]+)$/i;\ntype ResolvedImageSource =\n | {\n type: 'data-uri';\n id: string;\n mimeType: StoredImageRef['mimeType'];\n dataUri: string;\n }\n | {\n type: 'file';\n id: string;\n mimeType: StoredImageRef['mimeType'];\n filePath: string;\n };\n\n/** Parse and normalize a base64 image data URL for report persistence. */\nexport function parseBase64ImageDataUrl(imageUrl: string): {\n mimeType: `image/${string}`;\n extension: string;\n rawBase64: string;\n} {\n const match = imageUrl.match(base64ImageDataUrlPattern);\n if (!match) {\n throw new Error(\n 'ReportImageStore: reference image must be a valid base64 image data URL',\n );\n }\n const mimeType = match[1].toLowerCase() as `image/${string}`;\n return {\n mimeType,\n extension: imageFileExtensionForMimeType(mimeType),\n rawBase64: match[2].replace(/\\s/g, ''),\n };\n}\n\nexport function isBase64ImageDataUrl(value: string): boolean {\n return base64ImageDataUrlPattern.test(value);\n}\n\nexport function resolveImageSource(\n refInput: unknown,\n options: {\n reportPath: string;\n imageDirectory?: string;\n fallbackId?: string;\n fallbackMimeType?: ScreenshotRef['mimeType'];\n },\n): ResolvedImageSource {\n const ref = normalizeStoredImageRef(refInput);\n const id = ref?.id ?? options.fallbackId;\n const mimeType = ref?.mimeType ?? options.fallbackMimeType;\n\n if (!id || !mimeType) {\n throw new Error(\n 'ReportImageStore: image id and mimeType are required to resolve an image',\n );\n }\n\n const resolveReportRelativePath = (filePath: string): string =>\n isAbsolute(filePath)\n ? filePath\n : join(dirname(options.reportPath), filePath);\n\n if (ref?.storage === 'file') {\n if (!ref.path) {\n throw new Error(\n `ReportImageStore: image ref \"${ref.id}\" missing file path`,\n );\n }\n\n const explicitFilePath = resolveReportRelativePath(ref.path);\n if (existsSync(explicitFilePath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: explicitFilePath,\n };\n }\n }\n\n const inlineDataUri = extractImageByIdSync(options.reportPath, id);\n if (inlineDataUri) {\n return {\n type: 'data-uri',\n id,\n mimeType,\n dataUri: inlineDataUri,\n };\n }\n\n const extension = ref\n ? imageRefFileExtension(ref)\n : imageFileExtensionForMimeType(options.fallbackMimeType!);\n if (options.imageDirectory) {\n const companionImagePath = join(\n options.imageDirectory,\n `${id}.${extension}`,\n );\n if (existsSync(companionImagePath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: companionImagePath,\n };\n }\n }\n const siblingImagePath = join(\n dirname(options.reportPath),\n 'screenshots',\n `${id}.${extension}`,\n );\n if (existsSync(siblingImagePath)) {\n return {\n type: 'file',\n id,\n mimeType,\n filePath: siblingImagePath,\n };\n }\n\n throw new Error(\n `ReportImageStore: cannot resolve image \"${id}\" from ${options.reportPath}`,\n );\n}\n\nexport function resolveScreenshotSource(\n refInput: unknown,\n options: {\n reportPath: string;\n imageDirectory?: string;\n fallbackId?: string;\n fallbackMimeType?: ScreenshotRef['mimeType'];\n },\n): ResolvedImageSource {\n const ref = normalizeScreenshotRef(refInput);\n return resolveImageSource(ref, options);\n}\n\n/** Persists and restores every image asset referenced by a report. */\nexport class ReportImageStore {\n private readonly mode: 'inline' | 'directory';\n private readonly reportPath: string;\n private readonly screenshotsDir?: string;\n private readonly writeInlineImage?: (\n id: string,\n base64: string,\n ) => void | Promise<void>;\n private alsoWriteFileCopy: boolean;\n private readonly writtenInlineIds = new Set<string>();\n private readonly writtenFileIds = new Set<string>();\n private readonly shouldReuseExistingInlineReport: boolean;\n private existingInlineImageIdsPromise?: Promise<void>;\n\n constructor(options: {\n mode: 'inline' | 'directory';\n reportPath: string;\n screenshotsDir?: string;\n writeInlineImage?: (id: string, base64: string) => void | Promise<void>;\n alsoWriteFileCopy?: boolean;\n /** @deprecated Use alsoWriteFileCopy instead. */\n ensureFileCopy?: boolean;\n /** Reuse inline image assets already present in reportPath. */\n reuseExistingReport?: boolean;\n }) {\n this.mode = options.mode;\n this.reportPath = options.reportPath;\n this.screenshotsDir = options.screenshotsDir;\n this.writeInlineImage = options.writeInlineImage;\n this.alsoWriteFileCopy =\n options.alsoWriteFileCopy ?? options.ensureFileCopy ?? false;\n this.shouldReuseExistingInlineReport = Boolean(\n options.reuseExistingReport && this.mode === 'inline',\n );\n }\n\n enableFileCopies(): void {\n this.alsoWriteFileCopy = true;\n }\n\n async persist(screenshot: ScreenshotItem): Promise<ScreenshotRef> {\n await this.ensureExistingInlineImageIdsLoaded();\n const shouldWriteFileCopy =\n this.mode === 'directory' || this.alsoWriteFileCopy;\n const fileRef = shouldWriteFileCopy\n ? await this.persistToSharedFileIfNeeded(screenshot, {\n markAsPersisted: this.mode === 'directory',\n })\n : null;\n\n if (this.mode === 'inline') {\n if (!this.writeInlineImage) {\n throw new Error(\n 'ReportImageStore: writeInlineImage is required in inline mode',\n );\n }\n if (!this.writtenInlineIds.has(screenshot.id)) {\n await this.writeInlineImage(screenshot.id, screenshot.base64);\n this.writtenInlineIds.add(screenshot.id);\n }\n return screenshot.markPersistedInline(this.reportPath);\n }\n\n if (!fileRef) {\n throw new Error(\n 'ReportImageStore: file persistence is required in directory mode',\n );\n }\n return fileRef;\n }\n\n /** Persist a base64 multimodal prompt image and return its content-addressed ref. */\n async persistReferenceImage(imageUrl: string): Promise<ImageUrlRef> {\n await this.ensureExistingInlineImageIdsLoaded();\n const { mimeType, extension, rawBase64 } =\n parseBase64ImageDataUrl(imageUrl);\n const id = `reference-${createHash('sha256')\n .update(mimeType)\n .update('\\0')\n .update(Buffer.from(rawBase64, 'base64'))\n .digest('hex')}`;\n const shouldWriteFileCopy =\n this.mode === 'directory' || this.alsoWriteFileCopy;\n const fileLocation = shouldWriteFileCopy\n ? await this.writeImageFileIfNeeded({ id, extension, rawBase64 })\n : null;\n\n let ref: ImageUrlRef;\n if (this.mode === 'inline') {\n if (!this.writeInlineImage) {\n throw new Error(\n 'ReportImageStore: writeInlineImage is required in inline mode',\n );\n }\n if (!this.writtenInlineIds.has(id)) {\n await this.writeInlineImage(id, imageUrl);\n this.writtenInlineIds.add(id);\n }\n ref = {\n type: 'midscene_image_url_ref',\n id,\n mimeType,\n storage: 'inline',\n };\n } else {\n if (!fileLocation) {\n throw new Error(\n 'ReportImageStore: file persistence is required in directory mode',\n );\n }\n ref = {\n type: 'midscene_image_url_ref',\n id,\n mimeType,\n storage: 'file',\n path: fileLocation.relativePath,\n };\n }\n return ref;\n }\n\n private async ensureExistingInlineImageIdsLoaded(): Promise<void> {\n if (!this.shouldReuseExistingInlineReport) return;\n if (!this.existingInlineImageIdsPromise) {\n this.existingInlineImageIdsPromise = (async () => {\n try {\n const imageIds = await collectImageScriptIds(this.reportPath);\n for (const imageId of imageIds) this.writtenInlineIds.add(imageId);\n } catch (error) {\n if (\n typeof error === 'object' &&\n error !== null &&\n 'code' in error &&\n error.code === 'ENOENT'\n ) {\n return;\n }\n throw new Error(\n `ReportImageStore: failed to index existing inline images from ${this.reportPath}`,\n { cause: error },\n );\n }\n })();\n }\n await this.existingInlineImageIdsPromise;\n }\n\n private async persistToSharedFileIfNeeded(\n screenshot: ScreenshotItem,\n options: {\n markAsPersisted: boolean;\n },\n ): Promise<ScreenshotRef> {\n const fileName = `${screenshot.id}.${screenshot.extension}`;\n const { relativePath, absolutePath } = this.writtenFileIds.has(\n screenshot.id,\n )\n ? {\n relativePath: `./screenshots/${fileName}`,\n absolutePath: join(this.screenshotsDir!, fileName),\n }\n : await this.writeImageFileIfNeeded({\n id: screenshot.id,\n extension: screenshot.extension,\n rawBase64: screenshot.rawBase64,\n });\n\n if (options.markAsPersisted) {\n return screenshot.markPersistedToPath(relativePath, absolutePath);\n }\n\n return screenshot.registerPersistedFileCopy(relativePath, absolutePath);\n }\n\n private async writeImageFileIfNeeded(image: {\n id: string;\n extension: string;\n rawBase64: string;\n }): Promise<{ relativePath: string; absolutePath: string }> {\n const screenshotsDir = this.screenshotsDir;\n if (!screenshotsDir) {\n throw new Error(\n 'ReportImageStore: screenshotsDir is required when file persistence is enabled',\n );\n }\n if (!existsSync(screenshotsDir)) {\n mkdirSync(screenshotsDir, { recursive: true });\n }\n\n const fileName = `${image.id}.${image.extension}`;\n const relativePath = `./screenshots/${fileName}`;\n const absolutePath = join(screenshotsDir, fileName);\n if (!this.writtenFileIds.has(image.id)) {\n await writeFileAsync(\n absolutePath,\n Buffer.from(image.rawBase64, 'base64'),\n );\n this.writtenFileIds.add(image.id);\n }\n return { relativePath, absolutePath };\n }\n\n /** Resolve any stored report image reference to a data URI. */\n loadDataUri(refInput: unknown): string {\n const ref = normalizeStoredImageRef(refInput);\n if (!ref) {\n throw new Error('ReportImageStore: invalid image reference');\n }\n\n const resolved = resolveImageSource(ref, {\n reportPath: this.reportPath,\n imageDirectory: this.screenshotsDir,\n });\n\n if (resolved.type === 'data-uri') {\n return resolved.dataUri;\n }\n\n const data = readFileSync(resolved.filePath);\n return `data:${resolved.mimeType};base64,${data.toString('base64')}`;\n }\n\n /** @deprecated Use loadDataUri. */\n loadBase64(refInput: unknown): string {\n const screenshotRef = normalizeScreenshotRef(refInput);\n if (!screenshotRef) {\n throw new Error('ReportImageStore: invalid screenshot reference');\n }\n return this.loadDataUri(screenshotRef);\n }\n\n cleanup(): void {\n if (\n this.mode === 'directory' &&\n this.screenshotsDir &&\n existsSync(this.screenshotsDir)\n ) {\n rmSync(this.screenshotsDir, { recursive: true, force: true });\n }\n this.writtenInlineIds.clear();\n this.writtenFileIds.clear();\n this.existingInlineImageIdsPromise = undefined;\n }\n}\n\n/** @deprecated Use ReportImageStore. */\nexport { ReportImageStore as ScreenshotStore };\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","base64ImageDataUrlPattern","parseBase64ImageDataUrl","imageUrl","match","Error","mimeType","imageFileExtensionForMimeType","isBase64ImageDataUrl","value","resolveImageSource","refInput","options","ref","normalizeStoredImageRef","id","resolveReportRelativePath","filePath","isAbsolute","join","dirname","explicitFilePath","existsSync","inlineDataUri","extractImageByIdSync","extension","imageRefFileExtension","companionImagePath","siblingImagePath","resolveScreenshotSource","normalizeScreenshotRef","ReportImageStore","screenshot","shouldWriteFileCopy","fileRef","rawBase64","createHash","Buffer","fileLocation","imageIds","collectImageScriptIds","imageId","error","fileName","relativePath","absolutePath","image","screenshotsDir","mkdirSync","writeFileAsync","resolved","data","readFileSync","screenshotRef","rmSync","undefined","Set","Boolean"],"mappings":"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"}
@@ -37,6 +37,7 @@ const screenshot_store_js_namespaceObject = require("./dump/screenshot-store.js"
37
37
  const task_status_js_namespaceObject = require("./dump/task-status.js");
38
38
  const external_report_js_namespaceObject = require("./report.js");
39
39
  const external_report_markdown_js_namespaceObject = require("./report-markdown.js");
40
+ const external_report_stats_js_namespaceObject = require("./report-stats.js");
40
41
  const external_types_js_namespaceObject = require("./types.js");
41
42
  function writeAttachmentFromReport(attachment, opts) {
42
43
  const { suggestedFileName, id, mimeType } = attachment;
@@ -141,14 +142,18 @@ function readSourceTestStatus(htmlPath) {
141
142
  }
142
143
  function deriveReportAttributesFromHtml(htmlPath, index) {
143
144
  const fallbackId = `${external_node_path_namespaceObject.basename(external_node_path_namespaceObject.dirname(htmlPath)) || external_node_path_namespaceObject.basename(htmlPath, external_node_path_namespaceObject.extname(htmlPath))}-${index + 1}`;
145
+ const runnerDump = (0, html_utils_js_namespaceObject.extractTestRunReportDumpSync)(htmlPath);
144
146
  try {
145
147
  const { baseDump, executions } = (0, external_report_js_namespaceObject.collectDedupedExecutions)(htmlPath);
146
- const testStatus = readSourceTestStatus(htmlPath) ?? (0, task_status_js_namespaceObject.deriveCaseStatus)(executions);
148
+ const recordedStatus = readSourceTestStatus(htmlPath);
149
+ const testStatus = recordedStatus && FAILING_TEST_STATUSES.has(recordedStatus) ? recordedStatus : runnerDump?.status === 'failed' ? 'failed' : recordedStatus ?? (runnerDump ? 'passed' : (0, task_status_js_namespaceObject.deriveCaseStatus)(executions));
147
150
  return {
148
151
  testId: fallbackId,
149
152
  testTitle: baseDump.groupName || fallbackId,
150
153
  testDescription: baseDump.groupDescription ?? '',
151
- testDuration: 0,
154
+ testDuration: runnerDump?.durationMs ?? (0, external_report_stats_js_namespaceObject.collectReportSummary)({
155
+ executions
156
+ }).timing.wallTimeMs ?? 0,
152
157
  testStatus
153
158
  };
154
159
  } catch {
@@ -157,7 +162,7 @@ function deriveReportAttributesFromHtml(htmlPath, index) {
157
162
  testTitle: fallbackId,
158
163
  testDescription: '',
159
164
  testDuration: 0,
160
- testStatus: 'passed'
165
+ testStatus: runnerDump?.status === 'failed' ? 'failed' : 'passed'
161
166
  };
162
167
  }
163
168
  }
@@ -1 +1 @@
1
- {"version":3,"file":"report-cli.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/report-cli.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n copyFileSync,\n existsSync,\n mkdirSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport * as path from 'node:path';\nimport { z } from 'zod';\nimport { extractAllDumpScriptsSync } from './dump/html-utils';\nimport { resolveScreenshotSource } from './dump/screenshot-store';\nimport { deriveCaseStatus } from './dump/task-status';\nimport {\n ReportMergingTool,\n collectDedupedExecutions,\n splitReportHtmlByExecution,\n} from './report';\nimport { reportToMarkdown } from './report-markdown';\nimport type { MarkdownAttachment } from './report-markdown';\nimport type { ReportFileAttributes, TestStatus } from './types';\nimport { ReportActionDump } from './types';\n\ntype ReportCliToolResult = {\n isError: boolean;\n content: Array<{\n type: 'text';\n text: string;\n }>;\n};\n\ntype ReportCliSchema = Record<string, z.ZodTypeAny>;\n\nexport interface ReportCliCommandDefinition {\n name: string;\n description: string;\n schema: ReportCliSchema;\n handler: (args: Record<string, unknown>) => Promise<ReportCliToolResult>;\n}\n\nexport interface ReportCliCommandEntry {\n name: string;\n def: ReportCliCommandDefinition;\n}\n\nexport type ConsumeReportFileAction = 'split' | 'to-markdown' | 'merge-html';\n\nexport interface ConsumeReportFileOptions {\n htmlPath: string;\n outputDir: string;\n}\n\nexport type SplitReportFileOptions = ConsumeReportFileOptions;\nexport type ReportFileToMarkdownOptions = ConsumeReportFileOptions;\n\nexport interface MergeReportFilesOptions {\n htmlPaths: string[];\n outputDir?: string;\n outputName?: string;\n overwrite?: boolean;\n}\n\nexport interface MergeReportFilesResult {\n mergedReportPath: string;\n}\n\nfunction writeAttachmentFromReport(\n attachment: MarkdownAttachment,\n opts: {\n htmlPath: string;\n screenshotsDir: string;\n writtenFiles: Set<string>;\n },\n): void {\n const { suggestedFileName, id, mimeType } = attachment;\n if (opts.writtenFiles.has(suggestedFileName)) return;\n\n const absolutePath = path.join(opts.screenshotsDir, suggestedFileName);\n\n const resolved = resolveScreenshotSource(attachment.sourceRef ?? null, {\n reportPath: opts.htmlPath,\n fallbackId: id,\n fallbackMimeType: (mimeType || 'image/png') as 'image/png' | 'image/jpeg',\n });\n\n if (resolved.type === 'data-uri') {\n const rawBase64 = resolved.dataUri.replace(\n /^data:image\\/[a-zA-Z+]+;base64,/,\n '',\n );\n writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));\n opts.writtenFiles.add(suggestedFileName);\n return;\n }\n\n if (!existsSync(resolved.filePath)) {\n throw new Error(\n `Cannot resolve screenshot \"${id}\" for markdown attachment from ${opts.htmlPath}`,\n );\n }\n\n copyFileSync(resolved.filePath, absolutePath);\n opts.writtenFiles.add(suggestedFileName);\n}\n\nasync function markdownFromReport(\n htmlPath: string,\n outputDir: string,\n): Promise<{ markdownFiles: string[]; screenshotFiles: string[] }> {\n const screenshotsDir = path.join(outputDir, 'screenshots');\n\n mkdirSync(outputDir, { recursive: true });\n mkdirSync(screenshotsDir, { recursive: true });\n\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n const mergedReport = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions,\n });\n\n const result = reportToMarkdown(mergedReport);\n\n const markdownFiles: string[] = [];\n const writtenScreenshots = new Set<string>();\n\n const mdPath = path.join(outputDir, 'report.md');\n writeFileSync(mdPath, result.markdown, 'utf-8');\n markdownFiles.push(mdPath);\n\n for (const attachment of result.attachments) {\n writeAttachmentFromReport(attachment, {\n htmlPath,\n screenshotsDir,\n writtenFiles: writtenScreenshots,\n });\n }\n\n return {\n markdownFiles,\n screenshotFiles: Array.from(writtenScreenshots)\n .sort()\n .map((f) => path.join(screenshotsDir, f)),\n };\n}\n\nfunction resolveReportHtmlPath(htmlPath: string): string {\n const normalizedPath = path.resolve(htmlPath);\n\n if (!existsSync(normalizedPath)) {\n throw new Error(`report-tool: --htmlPath does not exist: ${htmlPath}`);\n }\n\n const stats = statSync(normalizedPath);\n if (!stats.isDirectory()) {\n return normalizedPath;\n }\n\n const indexHtmlPath = path.join(normalizedPath, 'index.html');\n if (!existsSync(indexHtmlPath)) {\n throw new Error(\n `report-tool: \"${htmlPath}\" is not an HTML report file, and no index.html was found under this directory.`,\n );\n }\n\n return indexHtmlPath;\n}\n\nexport function splitReportFile(options: SplitReportFileOptions): {\n executionJsonFiles: string[];\n screenshotFiles: string[];\n} {\n const { htmlPath, outputDir } = options;\n if (!htmlPath) {\n throw new Error('splitReportFile: htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('splitReportFile: outputDir is required');\n }\n\n const resolvedHtmlPath = resolveReportHtmlPath(htmlPath);\n return splitReportHtmlByExecution({\n htmlPath: resolvedHtmlPath,\n outputDir,\n });\n}\n\nexport async function reportFileToMarkdown(\n options: ReportFileToMarkdownOptions,\n): Promise<{ markdownFiles: string[]; screenshotFiles: string[] }> {\n const { htmlPath, outputDir } = options;\n if (!htmlPath) {\n throw new Error('reportFileToMarkdown: htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('reportFileToMarkdown: outputDir is required');\n }\n\n const resolvedHtmlPath = resolveReportHtmlPath(htmlPath);\n return markdownFromReport(resolvedHtmlPath, outputDir);\n}\n\nconst TEST_STATUS_VALUES: ReadonlySet<TestStatus> = new Set<TestStatus>([\n 'passed',\n 'failed',\n 'timedOut',\n 'skipped',\n 'interrupted',\n]);\n\nconst FAILING_TEST_STATUSES: ReadonlySet<TestStatus> = new Set<TestStatus>([\n 'failed',\n 'timedOut',\n 'interrupted',\n]);\n\n/**\n * Reuse a `playwright_test_status` already recorded on the source report's dump\n * scripts (e.g. a Playwright-generated report carries the precise\n * timedOut/skipped/interrupted status). Returns undefined when the source has\n * no such attribute, so the caller can fall back to deriving from the dump.\n *\n * A single source report may bundle several executions (e.g. an already-merged\n * report), so surface a failing status if any execution failed — mirroring\n * `deriveCaseStatus`'s any-failure-wins semantics rather than taking the first\n * script's status and masking later failures.\n */\nfunction readSourceTestStatus(htmlPath: string): TestStatus | undefined {\n const recorded: TestStatus[] = [];\n for (const { openTag } of extractAllDumpScriptsSync(htmlPath)) {\n const match = openTag.match(/playwright_test_status=\"([^\"]*)\"/);\n if (!match) continue;\n const status = decodeURIComponent(match[1]) as TestStatus;\n if (TEST_STATUS_VALUES.has(status)) {\n recorded.push(status);\n }\n }\n if (recorded.length === 0) return undefined;\n return recorded.find((s) => FAILING_TEST_STATUSES.has(s)) ?? recorded[0];\n}\n\nfunction deriveReportAttributesFromHtml(\n htmlPath: string,\n index: number,\n): ReportFileAttributes {\n const fallbackId = `${path.basename(path.dirname(htmlPath)) || path.basename(htmlPath, path.extname(htmlPath))}-${index + 1}`;\n try {\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n // Prefer a status the source report already recorded (keeps Playwright's\n // precise timedOut/skipped/interrupted); otherwise infer it from the dump\n // so a failing case is not silently merged in as passed.\n const testStatus =\n readSourceTestStatus(htmlPath) ?? deriveCaseStatus(executions);\n return {\n testId: fallbackId,\n testTitle: baseDump.groupName || fallbackId,\n testDescription: baseDump.groupDescription ?? '',\n testDuration: 0,\n testStatus,\n };\n } catch {\n return {\n testId: fallbackId,\n testTitle: fallbackId,\n testDescription: '',\n testDuration: 0,\n testStatus: 'passed' as TestStatus,\n };\n }\n}\n\nexport function mergeReportFiles(\n options: MergeReportFilesOptions,\n): MergeReportFilesResult {\n const { htmlPaths, outputDir, outputName, overwrite = false } = options;\n if (!htmlPaths || htmlPaths.length === 0) {\n throw new Error('mergeReportFiles: htmlPaths is required');\n }\n\n const resolvedPaths = htmlPaths.map((p) => resolveReportHtmlPath(p));\n\n const tool = new ReportMergingTool();\n resolvedPaths.forEach((htmlPath, index) => {\n tool.append({\n reportFilePath: htmlPath,\n reportAttributes: deriveReportAttributesFromHtml(htmlPath, index),\n });\n });\n\n const mergedReportPath = tool.mergeReports(outputName ?? 'AUTO', {\n overwrite,\n outputDir,\n });\n\n if (!mergedReportPath) {\n throw new Error('mergeReportFiles: failed to produce a merged report');\n }\n\n return { mergedReportPath };\n}\n\nfunction normalizeHtmlReportArg(raw: unknown): string[] | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (Array.isArray(raw)) {\n return raw.filter(\n (p): p is string => typeof p === 'string' && p.length > 0,\n );\n }\n if (typeof raw === 'string') {\n const trimmed = raw.trim();\n return trimmed ? [trimmed] : [];\n }\n return undefined;\n}\n\nconst reportCommandDefinition: ReportCliCommandDefinition = {\n name: 'report-tool',\n description:\n 'Transform Midscene report artifacts, including splitting executions, converting to markdown, and merging multiple reports.',\n schema: {\n action: z\n .enum(['split', 'to-markdown', 'merge-html'])\n .optional()\n .describe(\n 'Report action to run. Supports: split, to-markdown, merge-html. Defaults to split.',\n ),\n htmlPath: z\n .string()\n .optional()\n .describe(\n 'Input report HTML path (e.g. ./report/index.html). Used by split and to-markdown.',\n ),\n htmlReport: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe(\n 'Input report HTML path for the merge action. Repeat the flag to merge multiple reports (e.g. --htmlReport ./a/index.html --htmlReport ./b.html).',\n ),\n outputDir: z\n .string()\n .optional()\n .describe(\n 'Output directory for generated report artifacts. For merge, defaults to the Midscene report directory.',\n ),\n outputName: z\n .string()\n .optional()\n .describe(\n 'Output report file/directory name (without .html) for the merge action. Defaults to an auto-generated name.',\n ),\n overwrite: z\n .union([z.boolean(), z.string()])\n .optional()\n .describe(\n 'Overwrite the existing merged report file if present (merge action only).',\n ),\n },\n handler: async (args) => {\n const {\n action = 'split',\n htmlPath,\n htmlReport,\n outputDir,\n outputName,\n overwrite,\n } = args as {\n action?: string;\n htmlPath?: string;\n htmlReport?: unknown;\n outputDir?: string;\n outputName?: string;\n overwrite?: unknown;\n };\n if (\n action !== 'split' &&\n action !== 'to-markdown' &&\n action !== 'merge-html'\n ) {\n throw new Error(\n `report-tool: unsupported --action value \"${action}\". Currently supported: split, to-markdown, merge-html`,\n );\n }\n\n if (action === 'merge-html') {\n const paths = normalizeHtmlReportArg(htmlReport);\n if (!paths || paths.length === 0) {\n throw new Error(\n 'report-tool: --htmlReport is required for action \"merge-html\". Repeat --htmlReport for each report (e.g. --htmlReport ./a/index.html --htmlReport ./b.html).',\n );\n }\n\n const overwriteFlag =\n overwrite === true || overwrite === 'true' || overwrite === '1';\n\n const result = mergeReportFiles({\n htmlPaths: paths,\n outputDir,\n outputName,\n overwrite: overwriteFlag,\n });\n\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Merged ${paths.length} report(s) into ${result.mergedReportPath}`,\n },\n ],\n };\n }\n\n if (!htmlPath) {\n throw new Error('report-tool: --htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('report-tool: --outputDir is required');\n }\n\n if (action === 'to-markdown') {\n const result = await reportFileToMarkdown({\n htmlPath,\n outputDir,\n });\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Markdown export completed. Generated ${result.markdownFiles.length} markdown file(s) and ${result.screenshotFiles.length} screenshot(s). Output path: ${outputDir}`,\n },\n ],\n };\n }\n\n const result = splitReportFile({\n htmlPath,\n outputDir,\n });\n\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Report split completed. Generated ${result.executionJsonFiles.length} execution JSON files and ${result.screenshotFiles.length} screenshots. Output path: ${outputDir}`,\n },\n ],\n };\n },\n};\n\nexport function createReportCliCommands(): ReportCliCommandEntry[] {\n return [\n {\n name: 'report-tool',\n def: reportCommandDefinition,\n },\n ];\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","writeAttachmentFromReport","attachment","opts","suggestedFileName","id","mimeType","absolutePath","path","resolved","resolveScreenshotSource","rawBase64","writeFileSync","Buffer","existsSync","Error","copyFileSync","markdownFromReport","htmlPath","outputDir","screenshotsDir","mkdirSync","baseDump","executions","collectDedupedExecutions","mergedReport","ReportActionDump","result","reportToMarkdown","markdownFiles","writtenScreenshots","Set","mdPath","Array","f","resolveReportHtmlPath","normalizedPath","stats","statSync","indexHtmlPath","splitReportFile","options","resolvedHtmlPath","splitReportHtmlByExecution","reportFileToMarkdown","TEST_STATUS_VALUES","FAILING_TEST_STATUSES","readSourceTestStatus","recorded","openTag","extractAllDumpScriptsSync","match","status","decodeURIComponent","s","deriveReportAttributesFromHtml","index","fallbackId","testStatus","deriveCaseStatus","mergeReportFiles","htmlPaths","outputName","overwrite","resolvedPaths","p","tool","ReportMergingTool","mergedReportPath","normalizeHtmlReportArg","raw","trimmed","reportCommandDefinition","z","args","action","htmlReport","paths","overwriteFlag","createReportCliCommands"],"mappings":"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"}
1
+ {"version":3,"file":"report-cli.js","sources":["webpack/runtime/define_property_getters","webpack/runtime/has_own_property","webpack/runtime/make_namespace_object","../../src/report-cli.ts"],"sourcesContent":["__webpack_require__.d = (exports, definition) => {\n\tfor(var key in definition) {\n if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {\n Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });\n }\n }\n};","__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))","// define __esModule on exports\n__webpack_require__.r = (exports) => {\n\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n\t}\n\tObject.defineProperty(exports, '__esModule', { value: true });\n};","import {\n copyFileSync,\n existsSync,\n mkdirSync,\n statSync,\n writeFileSync,\n} from 'node:fs';\nimport * as path from 'node:path';\nimport { z } from 'zod';\nimport {\n extractAllDumpScriptsSync,\n extractTestRunReportDumpSync,\n} from './dump/html-utils';\nimport { resolveScreenshotSource } from './dump/screenshot-store';\nimport { deriveCaseStatus } from './dump/task-status';\nimport {\n ReportMergingTool,\n collectDedupedExecutions,\n splitReportHtmlByExecution,\n} from './report';\nimport { reportToMarkdown } from './report-markdown';\nimport type { MarkdownAttachment } from './report-markdown';\nimport { collectReportSummary } from './report-stats';\nimport type { ReportFileAttributes, TestStatus } from './types';\nimport { ReportActionDump } from './types';\n\ntype ReportCliToolResult = {\n isError: boolean;\n content: Array<{\n type: 'text';\n text: string;\n }>;\n};\n\ntype ReportCliSchema = Record<string, z.ZodTypeAny>;\n\nexport interface ReportCliCommandDefinition {\n name: string;\n description: string;\n schema: ReportCliSchema;\n handler: (args: Record<string, unknown>) => Promise<ReportCliToolResult>;\n}\n\nexport interface ReportCliCommandEntry {\n name: string;\n def: ReportCliCommandDefinition;\n}\n\nexport type ConsumeReportFileAction = 'split' | 'to-markdown' | 'merge-html';\n\nexport interface ConsumeReportFileOptions {\n htmlPath: string;\n outputDir: string;\n}\n\nexport type SplitReportFileOptions = ConsumeReportFileOptions;\nexport type ReportFileToMarkdownOptions = ConsumeReportFileOptions;\n\nexport interface MergeReportFilesOptions {\n htmlPaths: string[];\n outputDir?: string;\n outputName?: string;\n overwrite?: boolean;\n}\n\nexport interface MergeReportFilesResult {\n mergedReportPath: string;\n}\n\nfunction writeAttachmentFromReport(\n attachment: MarkdownAttachment,\n opts: {\n htmlPath: string;\n screenshotsDir: string;\n writtenFiles: Set<string>;\n },\n): void {\n const { suggestedFileName, id, mimeType } = attachment;\n if (opts.writtenFiles.has(suggestedFileName)) return;\n\n const absolutePath = path.join(opts.screenshotsDir, suggestedFileName);\n\n const resolved = resolveScreenshotSource(attachment.sourceRef ?? null, {\n reportPath: opts.htmlPath,\n fallbackId: id,\n fallbackMimeType: (mimeType || 'image/png') as 'image/png' | 'image/jpeg',\n });\n\n if (resolved.type === 'data-uri') {\n const rawBase64 = resolved.dataUri.replace(\n /^data:image\\/[a-zA-Z+]+;base64,/,\n '',\n );\n writeFileSync(absolutePath, Buffer.from(rawBase64, 'base64'));\n opts.writtenFiles.add(suggestedFileName);\n return;\n }\n\n if (!existsSync(resolved.filePath)) {\n throw new Error(\n `Cannot resolve screenshot \"${id}\" for markdown attachment from ${opts.htmlPath}`,\n );\n }\n\n copyFileSync(resolved.filePath, absolutePath);\n opts.writtenFiles.add(suggestedFileName);\n}\n\nasync function markdownFromReport(\n htmlPath: string,\n outputDir: string,\n): Promise<{ markdownFiles: string[]; screenshotFiles: string[] }> {\n const screenshotsDir = path.join(outputDir, 'screenshots');\n\n mkdirSync(outputDir, { recursive: true });\n mkdirSync(screenshotsDir, { recursive: true });\n\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n\n const mergedReport = new ReportActionDump({\n sdkVersion: baseDump.sdkVersion,\n groupName: baseDump.groupName,\n groupDescription: baseDump.groupDescription,\n modelBriefs: baseDump.modelBriefs,\n deviceType: baseDump.deviceType,\n executions,\n });\n\n const result = reportToMarkdown(mergedReport);\n\n const markdownFiles: string[] = [];\n const writtenScreenshots = new Set<string>();\n\n const mdPath = path.join(outputDir, 'report.md');\n writeFileSync(mdPath, result.markdown, 'utf-8');\n markdownFiles.push(mdPath);\n\n for (const attachment of result.attachments) {\n writeAttachmentFromReport(attachment, {\n htmlPath,\n screenshotsDir,\n writtenFiles: writtenScreenshots,\n });\n }\n\n return {\n markdownFiles,\n screenshotFiles: Array.from(writtenScreenshots)\n .sort()\n .map((f) => path.join(screenshotsDir, f)),\n };\n}\n\nfunction resolveReportHtmlPath(htmlPath: string): string {\n const normalizedPath = path.resolve(htmlPath);\n\n if (!existsSync(normalizedPath)) {\n throw new Error(`report-tool: --htmlPath does not exist: ${htmlPath}`);\n }\n\n const stats = statSync(normalizedPath);\n if (!stats.isDirectory()) {\n return normalizedPath;\n }\n\n const indexHtmlPath = path.join(normalizedPath, 'index.html');\n if (!existsSync(indexHtmlPath)) {\n throw new Error(\n `report-tool: \"${htmlPath}\" is not an HTML report file, and no index.html was found under this directory.`,\n );\n }\n\n return indexHtmlPath;\n}\n\nexport function splitReportFile(options: SplitReportFileOptions): {\n executionJsonFiles: string[];\n screenshotFiles: string[];\n} {\n const { htmlPath, outputDir } = options;\n if (!htmlPath) {\n throw new Error('splitReportFile: htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('splitReportFile: outputDir is required');\n }\n\n const resolvedHtmlPath = resolveReportHtmlPath(htmlPath);\n return splitReportHtmlByExecution({\n htmlPath: resolvedHtmlPath,\n outputDir,\n });\n}\n\nexport async function reportFileToMarkdown(\n options: ReportFileToMarkdownOptions,\n): Promise<{ markdownFiles: string[]; screenshotFiles: string[] }> {\n const { htmlPath, outputDir } = options;\n if (!htmlPath) {\n throw new Error('reportFileToMarkdown: htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('reportFileToMarkdown: outputDir is required');\n }\n\n const resolvedHtmlPath = resolveReportHtmlPath(htmlPath);\n return markdownFromReport(resolvedHtmlPath, outputDir);\n}\n\nconst TEST_STATUS_VALUES: ReadonlySet<TestStatus> = new Set<TestStatus>([\n 'passed',\n 'failed',\n 'timedOut',\n 'skipped',\n 'interrupted',\n]);\n\nconst FAILING_TEST_STATUSES: ReadonlySet<TestStatus> = new Set<TestStatus>([\n 'failed',\n 'timedOut',\n 'interrupted',\n]);\n\n/**\n * Reuse a `playwright_test_status` already recorded on the source report's dump\n * scripts (e.g. a Playwright-generated report carries the precise\n * timedOut/skipped/interrupted status). Returns undefined when the source has\n * no such attribute, so the caller can fall back to deriving from the dump.\n *\n * A single source report may bundle several executions (e.g. an already-merged\n * report), so surface a failing status if any execution failed — mirroring\n * `deriveCaseStatus`'s any-failure-wins semantics rather than taking the first\n * script's status and masking later failures.\n */\nfunction readSourceTestStatus(htmlPath: string): TestStatus | undefined {\n const recorded: TestStatus[] = [];\n for (const { openTag } of extractAllDumpScriptsSync(htmlPath)) {\n const match = openTag.match(/playwright_test_status=\"([^\"]*)\"/);\n if (!match) continue;\n const status = decodeURIComponent(match[1]) as TestStatus;\n if (TEST_STATUS_VALUES.has(status)) {\n recorded.push(status);\n }\n }\n if (recorded.length === 0) return undefined;\n return recorded.find((s) => FAILING_TEST_STATUSES.has(s)) ?? recorded[0];\n}\n\nfunction deriveReportAttributesFromHtml(\n htmlPath: string,\n index: number,\n): ReportFileAttributes {\n const fallbackId = `${path.basename(path.dirname(htmlPath)) || path.basename(htmlPath, path.extname(htmlPath))}-${index + 1}`;\n const runnerDump = extractTestRunReportDumpSync(htmlPath);\n try {\n const { baseDump, executions } = collectDedupedExecutions(htmlPath);\n // Prefer a status the source report already recorded (keeps Playwright's\n // precise timedOut/skipped/interrupted); otherwise infer it from the dump\n // so a failing case is not silently merged in as passed.\n const recordedStatus = readSourceTestStatus(htmlPath);\n const testStatus =\n recordedStatus && FAILING_TEST_STATUSES.has(recordedStatus)\n ? recordedStatus\n : runnerDump?.status === 'failed'\n ? 'failed'\n : (recordedStatus ??\n (runnerDump ? 'passed' : deriveCaseStatus(executions)));\n return {\n testId: fallbackId,\n testTitle: baseDump.groupName || fallbackId,\n testDescription: baseDump.groupDescription ?? '',\n testDuration:\n runnerDump?.durationMs ??\n collectReportSummary({ executions }).timing.wallTimeMs ??\n 0,\n testStatus,\n };\n } catch {\n return {\n testId: fallbackId,\n testTitle: fallbackId,\n testDescription: '',\n testDuration: 0,\n testStatus: runnerDump?.status === 'failed' ? 'failed' : 'passed',\n };\n }\n}\n\nexport function mergeReportFiles(\n options: MergeReportFilesOptions,\n): MergeReportFilesResult {\n const { htmlPaths, outputDir, outputName, overwrite = false } = options;\n if (!htmlPaths || htmlPaths.length === 0) {\n throw new Error('mergeReportFiles: htmlPaths is required');\n }\n\n const resolvedPaths = htmlPaths.map((p) => resolveReportHtmlPath(p));\n\n const tool = new ReportMergingTool();\n resolvedPaths.forEach((htmlPath, index) => {\n tool.append({\n reportFilePath: htmlPath,\n reportAttributes: deriveReportAttributesFromHtml(htmlPath, index),\n });\n });\n\n const mergedReportPath = tool.mergeReports(outputName ?? 'AUTO', {\n overwrite,\n outputDir,\n });\n\n if (!mergedReportPath) {\n throw new Error('mergeReportFiles: failed to produce a merged report');\n }\n\n return { mergedReportPath };\n}\n\nfunction normalizeHtmlReportArg(raw: unknown): string[] | undefined {\n if (raw === undefined || raw === null) return undefined;\n if (Array.isArray(raw)) {\n return raw.filter(\n (p): p is string => typeof p === 'string' && p.length > 0,\n );\n }\n if (typeof raw === 'string') {\n const trimmed = raw.trim();\n return trimmed ? [trimmed] : [];\n }\n return undefined;\n}\n\nconst reportCommandDefinition: ReportCliCommandDefinition = {\n name: 'report-tool',\n description:\n 'Transform Midscene report artifacts, including splitting executions, converting to markdown, and merging multiple reports.',\n schema: {\n action: z\n .enum(['split', 'to-markdown', 'merge-html'])\n .optional()\n .describe(\n 'Report action to run. Supports: split, to-markdown, merge-html. Defaults to split.',\n ),\n htmlPath: z\n .string()\n .optional()\n .describe(\n 'Input report HTML path (e.g. ./report/index.html). Used by split and to-markdown.',\n ),\n htmlReport: z\n .union([z.string(), z.array(z.string())])\n .optional()\n .describe(\n 'Input report HTML path for the merge action. Repeat the flag to merge multiple reports (e.g. --htmlReport ./a/index.html --htmlReport ./b.html).',\n ),\n outputDir: z\n .string()\n .optional()\n .describe(\n 'Output directory for generated report artifacts. For merge, defaults to the Midscene report directory.',\n ),\n outputName: z\n .string()\n .optional()\n .describe(\n 'Output report file/directory name (without .html) for the merge action. Defaults to an auto-generated name.',\n ),\n overwrite: z\n .union([z.boolean(), z.string()])\n .optional()\n .describe(\n 'Overwrite the existing merged report file if present (merge action only).',\n ),\n },\n handler: async (args) => {\n const {\n action = 'split',\n htmlPath,\n htmlReport,\n outputDir,\n outputName,\n overwrite,\n } = args as {\n action?: string;\n htmlPath?: string;\n htmlReport?: unknown;\n outputDir?: string;\n outputName?: string;\n overwrite?: unknown;\n };\n if (\n action !== 'split' &&\n action !== 'to-markdown' &&\n action !== 'merge-html'\n ) {\n throw new Error(\n `report-tool: unsupported --action value \"${action}\". Currently supported: split, to-markdown, merge-html`,\n );\n }\n\n if (action === 'merge-html') {\n const paths = normalizeHtmlReportArg(htmlReport);\n if (!paths || paths.length === 0) {\n throw new Error(\n 'report-tool: --htmlReport is required for action \"merge-html\". Repeat --htmlReport for each report (e.g. --htmlReport ./a/index.html --htmlReport ./b.html).',\n );\n }\n\n const overwriteFlag =\n overwrite === true || overwrite === 'true' || overwrite === '1';\n\n const result = mergeReportFiles({\n htmlPaths: paths,\n outputDir,\n outputName,\n overwrite: overwriteFlag,\n });\n\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Merged ${paths.length} report(s) into ${result.mergedReportPath}`,\n },\n ],\n };\n }\n\n if (!htmlPath) {\n throw new Error('report-tool: --htmlPath is required');\n }\n\n if (!outputDir) {\n throw new Error('report-tool: --outputDir is required');\n }\n\n if (action === 'to-markdown') {\n const result = await reportFileToMarkdown({\n htmlPath,\n outputDir,\n });\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Markdown export completed. Generated ${result.markdownFiles.length} markdown file(s) and ${result.screenshotFiles.length} screenshot(s). Output path: ${outputDir}`,\n },\n ],\n };\n }\n\n const result = splitReportFile({\n htmlPath,\n outputDir,\n });\n\n return {\n isError: false,\n content: [\n {\n type: 'text',\n text: `Report split completed. Generated ${result.executionJsonFiles.length} execution JSON files and ${result.screenshotFiles.length} screenshots. Output path: ${outputDir}`,\n },\n ],\n };\n },\n};\n\nexport function createReportCliCommands(): ReportCliCommandEntry[] {\n return [\n {\n name: 'report-tool',\n def: reportCommandDefinition,\n },\n ];\n}\n"],"names":["__webpack_require__","definition","key","Object","obj","prop","Symbol","writeAttachmentFromReport","attachment","opts","suggestedFileName","id","mimeType","absolutePath","path","resolved","resolveScreenshotSource","rawBase64","writeFileSync","Buffer","existsSync","Error","copyFileSync","markdownFromReport","htmlPath","outputDir","screenshotsDir","mkdirSync","baseDump","executions","collectDedupedExecutions","mergedReport","ReportActionDump","result","reportToMarkdown","markdownFiles","writtenScreenshots","Set","mdPath","Array","f","resolveReportHtmlPath","normalizedPath","stats","statSync","indexHtmlPath","splitReportFile","options","resolvedHtmlPath","splitReportHtmlByExecution","reportFileToMarkdown","TEST_STATUS_VALUES","FAILING_TEST_STATUSES","readSourceTestStatus","recorded","openTag","extractAllDumpScriptsSync","match","status","decodeURIComponent","s","deriveReportAttributesFromHtml","index","fallbackId","runnerDump","extractTestRunReportDumpSync","recordedStatus","testStatus","deriveCaseStatus","collectReportSummary","mergeReportFiles","htmlPaths","outputName","overwrite","resolvedPaths","p","tool","ReportMergingTool","mergedReportPath","normalizeHtmlReportArg","raw","trimmed","reportCommandDefinition","z","args","action","htmlReport","paths","overwriteFlag","createReportCliCommands"],"mappings":"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"}