playwright-intelligence-engine 0.0.0-stage → 2.0.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 (353) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +229 -3
  3. package/bin/pw-intel.js +37 -0
  4. package/dist/authoring/author.d.ts +83 -0
  5. package/dist/authoring/author.d.ts.map +1 -0
  6. package/dist/authoring/generation-contract.d.ts +90 -0
  7. package/dist/authoring/generation-contract.d.ts.map +1 -0
  8. package/dist/authoring/hybrid-author.d.ts +6 -0
  9. package/dist/authoring/hybrid-author.d.ts.map +1 -0
  10. package/dist/authoring/index.d.ts +7 -0
  11. package/dist/authoring/index.d.ts.map +1 -0
  12. package/dist/authoring/llm-author.d.ts +28 -0
  13. package/dist/authoring/llm-author.d.ts.map +1 -0
  14. package/dist/authoring/naming/index.d.ts +4 -0
  15. package/dist/authoring/naming/index.d.ts.map +1 -0
  16. package/dist/authoring/naming/name-plan.d.ts +45 -0
  17. package/dist/authoring/naming/name-plan.d.ts.map +1 -0
  18. package/dist/authoring/naming/name-planner.d.ts +25 -0
  19. package/dist/authoring/naming/name-planner.d.ts.map +1 -0
  20. package/dist/authoring/naming/naming-linter.d.ts +20 -0
  21. package/dist/authoring/naming/naming-linter.d.ts.map +1 -0
  22. package/dist/authoring/template-author.d.ts +6 -0
  23. package/dist/authoring/template-author.d.ts.map +1 -0
  24. package/dist/cli/index.d.ts +2 -0
  25. package/dist/cli/index.d.ts.map +1 -0
  26. package/dist/collectors/collector-interface.d.ts +7 -0
  27. package/dist/collectors/collector-interface.d.ts.map +1 -0
  28. package/dist/collectors/environment-collector.d.ts +12 -0
  29. package/dist/collectors/environment-collector.d.ts.map +1 -0
  30. package/dist/collectors/error-collector.d.ts +18 -0
  31. package/dist/collectors/error-collector.d.ts.map +1 -0
  32. package/dist/collectors/git-diff-collector.d.ts +20 -0
  33. package/dist/collectors/git-diff-collector.d.ts.map +1 -0
  34. package/dist/collectors/live-browser-collector.d.ts +45 -0
  35. package/dist/collectors/live-browser-collector.d.ts.map +1 -0
  36. package/dist/collectors/network-collector.d.ts +28 -0
  37. package/dist/collectors/network-collector.d.ts.map +1 -0
  38. package/dist/collectors/static-ast-collector.d.ts +32 -0
  39. package/dist/collectors/static-ast-collector.d.ts.map +1 -0
  40. package/dist/collectors/trace-collector.d.ts +12 -0
  41. package/dist/collectors/trace-collector.d.ts.map +1 -0
  42. package/dist/config/project-config.d.ts +33 -0
  43. package/dist/config/project-config.d.ts.map +1 -0
  44. package/dist/core/errors/engine-errors.d.ts +34 -0
  45. package/dist/core/errors/engine-errors.d.ts.map +1 -0
  46. package/dist/core/types/diagnostic-context.d.ts +151 -0
  47. package/dist/core/types/diagnostic-context.d.ts.map +1 -0
  48. package/dist/core/types/repair-evidence.d.ts +56 -0
  49. package/dist/core/types/repair-evidence.d.ts.map +1 -0
  50. package/dist/core/version.d.ts +13 -0
  51. package/dist/core/version.d.ts.map +1 -0
  52. package/dist/diagnostics/diagnostic-contract.d.ts +52 -0
  53. package/dist/diagnostics/diagnostic-contract.d.ts.map +1 -0
  54. package/dist/diagnostics/diagnostic-report.d.ts +11 -0
  55. package/dist/diagnostics/diagnostic-report.d.ts.map +1 -0
  56. package/dist/diagnostics/evidence-correlator.d.ts +22 -0
  57. package/dist/diagnostics/evidence-correlator.d.ts.map +1 -0
  58. package/dist/diagnostics/external-failure-evidence.d.ts +18 -0
  59. package/dist/diagnostics/external-failure-evidence.d.ts.map +1 -0
  60. package/dist/diagnostics/failure-category.d.ts +4 -0
  61. package/dist/diagnostics/failure-category.d.ts.map +1 -0
  62. package/dist/diagnostics/failure-classifier.d.ts +58 -0
  63. package/dist/diagnostics/failure-classifier.d.ts.map +1 -0
  64. package/dist/evaluation/evaluation-scorecard.d.ts +72 -0
  65. package/dist/evaluation/evaluation-scorecard.d.ts.map +1 -0
  66. package/dist/evaluation/repository-inspector.d.ts +21 -0
  67. package/dist/evaluation/repository-inspector.d.ts.map +1 -0
  68. package/dist/evaluation/repository-runner.d.ts +24 -0
  69. package/dist/evaluation/repository-runner.d.ts.map +1 -0
  70. package/dist/evidence/evidence-model.d.ts +88 -0
  71. package/dist/evidence/evidence-model.d.ts.map +1 -0
  72. package/dist/evidence/evidence-normalizer.d.ts +19 -0
  73. package/dist/evidence/evidence-normalizer.d.ts.map +1 -0
  74. package/dist/evidence/evidence-redactor.d.ts +37 -0
  75. package/dist/evidence/evidence-redactor.d.ts.map +1 -0
  76. package/dist/framework/config-adapter.d.ts +28 -0
  77. package/dist/framework/config-adapter.d.ts.map +1 -0
  78. package/dist/gate/index.d.ts +21 -0
  79. package/dist/gate/index.d.ts.map +1 -0
  80. package/dist/gate/oracle.d.ts +26 -0
  81. package/dist/gate/oracle.d.ts.map +1 -0
  82. package/dist/gate/quality.d.ts +27 -0
  83. package/dist/gate/quality.d.ts.map +1 -0
  84. package/dist/gate/safety.d.ts +28 -0
  85. package/dist/gate/safety.d.ts.map +1 -0
  86. package/dist/generator/architecture-generator.d.ts +55 -0
  87. package/dist/generator/architecture-generator.d.ts.map +1 -0
  88. package/dist/generator/assertion-quality-gate.d.ts +18 -0
  89. package/dist/generator/assertion-quality-gate.d.ts.map +1 -0
  90. package/dist/generator/candidate-writer.d.ts +34 -0
  91. package/dist/generator/candidate-writer.d.ts.map +1 -0
  92. package/dist/generator/test-generator.d.ts +26 -0
  93. package/dist/generator/test-generator.d.ts.map +1 -0
  94. package/dist/governance/audit-log.d.ts +23 -0
  95. package/dist/governance/audit-log.d.ts.map +1 -0
  96. package/dist/governance/governance-policy.d.ts +24 -0
  97. package/dist/governance/governance-policy.d.ts.map +1 -0
  98. package/dist/governance/team-dashboard.d.ts +51 -0
  99. package/dist/governance/team-dashboard.d.ts.map +1 -0
  100. package/dist/governance/workflow-audit.d.ts +13 -0
  101. package/dist/governance/workflow-audit.d.ts.map +1 -0
  102. package/dist/index.d.ts +90 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +20318 -0
  105. package/dist/integrations/ci-summary.d.ts +14 -0
  106. package/dist/integrations/ci-summary.d.ts.map +1 -0
  107. package/dist/integrations/playwright-report-adapter.d.ts +22 -0
  108. package/dist/integrations/playwright-report-adapter.d.ts.map +1 -0
  109. package/dist/integrations/quality-gate.d.ts +35 -0
  110. package/dist/integrations/quality-gate.d.ts.map +1 -0
  111. package/dist/integrations/sarif-report.d.ts +48 -0
  112. package/dist/integrations/sarif-report.d.ts.map +1 -0
  113. package/dist/intelligence/autonomous-repair-workflow.d.ts +31 -0
  114. package/dist/intelligence/autonomous-repair-workflow.d.ts.map +1 -0
  115. package/dist/intelligence/business-intent.d.ts +58 -0
  116. package/dist/intelligence/business-intent.d.ts.map +1 -0
  117. package/dist/intelligence/evidence-bundle.d.ts +364 -0
  118. package/dist/intelligence/evidence-bundle.d.ts.map +1 -0
  119. package/dist/intelligence/failure-clustering.d.ts +38 -0
  120. package/dist/intelligence/failure-clustering.d.ts.map +1 -0
  121. package/dist/intelligence/generation-agent.d.ts +120 -0
  122. package/dist/intelligence/generation-agent.d.ts.map +1 -0
  123. package/dist/intelligence/generation-verifier.d.ts +151 -0
  124. package/dist/intelligence/generation-verifier.d.ts.map +1 -0
  125. package/dist/intelligence/llm-proposal.d.ts +377 -0
  126. package/dist/intelligence/llm-proposal.d.ts.map +1 -0
  127. package/dist/intelligence/locator-disambiguation-engine.d.ts +38 -0
  128. package/dist/intelligence/locator-disambiguation-engine.d.ts.map +1 -0
  129. package/dist/intelligence/model-contract.d.ts +86 -0
  130. package/dist/intelligence/model-contract.d.ts.map +1 -0
  131. package/dist/intelligence/model-provider.d.ts +75 -0
  132. package/dist/intelligence/model-provider.d.ts.map +1 -0
  133. package/dist/intelligence/project-intelligence.d.ts +22 -0
  134. package/dist/intelligence/project-intelligence.d.ts.map +1 -0
  135. package/dist/intelligence/repair-agent.d.ts +46 -0
  136. package/dist/intelligence/repair-agent.d.ts.map +1 -0
  137. package/dist/intelligence/repository-profile.d.ts +131 -0
  138. package/dist/intelligence/repository-profile.d.ts.map +1 -0
  139. package/dist/intelligence/suite-intelligence.d.ts +59 -0
  140. package/dist/intelligence/suite-intelligence.d.ts.map +1 -0
  141. package/dist/intelligence/test-intelligence.d.ts +44 -0
  142. package/dist/intelligence/test-intelligence.d.ts.map +1 -0
  143. package/dist/intelligence/trace-proposal-workflow.d.ts +30 -0
  144. package/dist/intelligence/trace-proposal-workflow.d.ts.map +1 -0
  145. package/dist/intelligence/verified-llm-repair-workflow.d.ts +21 -0
  146. package/dist/intelligence/verified-llm-repair-workflow.d.ts.map +1 -0
  147. package/dist/intelligence/workflow-planner.d.ts +278 -0
  148. package/dist/intelligence/workflow-planner.d.ts.map +1 -0
  149. package/dist/mcp/server.d.ts +32 -0
  150. package/dist/mcp/server.d.ts.map +1 -0
  151. package/dist/memory/stability-ledger.d.ts +46 -0
  152. package/dist/memory/stability-ledger.d.ts.map +1 -0
  153. package/dist/perception/app-model.d.ts +55 -0
  154. package/dist/perception/app-model.d.ts.map +1 -0
  155. package/dist/perception/crawler.d.ts +18 -0
  156. package/dist/perception/crawler.d.ts.map +1 -0
  157. package/dist/perception/index.d.ts +3 -0
  158. package/dist/perception/index.d.ts.map +1 -0
  159. package/dist/repair/approval-service.d.ts +139 -0
  160. package/dist/repair/approval-service.d.ts.map +1 -0
  161. package/dist/repair/assertion-hasher.d.ts +62 -0
  162. package/dist/repair/assertion-hasher.d.ts.map +1 -0
  163. package/dist/repair/ast-patcher.d.ts +36 -0
  164. package/dist/repair/ast-patcher.d.ts.map +1 -0
  165. package/dist/repair/browser-operation-executor.d.ts +27 -0
  166. package/dist/repair/browser-operation-executor.d.ts.map +1 -0
  167. package/dist/repair/diff-allowlist.d.ts +74 -0
  168. package/dist/repair/diff-allowlist.d.ts.map +1 -0
  169. package/dist/repair/intent-validator.d.ts +157 -0
  170. package/dist/repair/intent-validator.d.ts.map +1 -0
  171. package/dist/repair/repair-operations.d.ts +40 -0
  172. package/dist/repair/repair-operations.d.ts.map +1 -0
  173. package/dist/repair/repair-planner.d.ts +98 -0
  174. package/dist/repair/repair-planner.d.ts.map +1 -0
  175. package/dist/repair/repair-workflow.d.ts +42 -0
  176. package/dist/repair/repair-workflow.d.ts.map +1 -0
  177. package/dist/repair/safe-operation-compiler.d.ts +15 -0
  178. package/dist/repair/safe-operation-compiler.d.ts.map +1 -0
  179. package/dist/repair/source-mapper.d.ts +19 -0
  180. package/dist/repair/source-mapper.d.ts.map +1 -0
  181. package/dist/repair/statement-patcher.d.ts +75 -0
  182. package/dist/repair/statement-patcher.d.ts.map +1 -0
  183. package/dist/repair/verification-gates.d.ts +25 -0
  184. package/dist/repair/verification-gates.d.ts.map +1 -0
  185. package/dist/repair/workflow-plan.d.ts +35 -0
  186. package/dist/repair/workflow-plan.d.ts.map +1 -0
  187. package/dist/repair/workspace-promoter.d.ts +43 -0
  188. package/dist/repair/workspace-promoter.d.ts.map +1 -0
  189. package/dist/reproduce/diagnostic-rerunner.d.ts +40 -0
  190. package/dist/reproduce/diagnostic-rerunner.d.ts.map +1 -0
  191. package/dist/reproduce/direct-browser-investigator.d.ts +41 -0
  192. package/dist/reproduce/direct-browser-investigator.d.ts.map +1 -0
  193. package/dist/reproduce/interactive-workflow-runner.d.ts +43 -0
  194. package/dist/reproduce/interactive-workflow-runner.d.ts.map +1 -0
  195. package/dist/sandbox/dependency-strategy.d.ts +45 -0
  196. package/dist/sandbox/dependency-strategy.d.ts.map +1 -0
  197. package/dist/sandbox/execution-runner.d.ts +81 -0
  198. package/dist/sandbox/execution-runner.d.ts.map +1 -0
  199. package/dist/sandbox/worktree-manager.d.ts +51 -0
  200. package/dist/sandbox/worktree-manager.d.ts.map +1 -0
  201. package/dist/selector/abstention-policy.d.ts +34 -0
  202. package/dist/selector/abstention-policy.d.ts.map +1 -0
  203. package/dist/selector/candidate-extractor.d.ts +71 -0
  204. package/dist/selector/candidate-extractor.d.ts.map +1 -0
  205. package/dist/selector/live-locator-verifier.d.ts +28 -0
  206. package/dist/selector/live-locator-verifier.d.ts.map +1 -0
  207. package/dist/selector/repair-candidate-ranker.d.ts +10 -0
  208. package/dist/selector/repair-candidate-ranker.d.ts.map +1 -0
  209. package/dist/selector/selector-scorer.d.ts +43 -0
  210. package/dist/selector/selector-scorer.d.ts.map +1 -0
  211. package/dist/trace/adapters/adapter-registry.d.ts +18 -0
  212. package/dist/trace/adapters/adapter-registry.d.ts.map +1 -0
  213. package/dist/trace/adapters/trace-adapter.d.ts +21 -0
  214. package/dist/trace/adapters/trace-adapter.d.ts.map +1 -0
  215. package/dist/trace/adapters/unknown-adapter.d.ts +10 -0
  216. package/dist/trace/adapters/unknown-adapter.d.ts.map +1 -0
  217. package/dist/trace/adapters/v10-adapter.d.ts +10 -0
  218. package/dist/trace/adapters/v10-adapter.d.ts.map +1 -0
  219. package/dist/trace/adapters/v4-adapter.d.ts +11 -0
  220. package/dist/trace/adapters/v4-adapter.d.ts.map +1 -0
  221. package/dist/trace/adapters/v8-adapter.d.ts +10 -0
  222. package/dist/trace/adapters/v8-adapter.d.ts.map +1 -0
  223. package/dist/trace/adapters/v9-adapter.d.ts +10 -0
  224. package/dist/trace/adapters/v9-adapter.d.ts.map +1 -0
  225. package/dist/trace/archive-reader.d.ts +44 -0
  226. package/dist/trace/archive-reader.d.ts.map +1 -0
  227. package/dist/trace/snapshot-resolver.d.ts +55 -0
  228. package/dist/trace/snapshot-resolver.d.ts.map +1 -0
  229. package/dist/trace/trace-parser.d.ts +50 -0
  230. package/dist/trace/trace-parser.d.ts.map +1 -0
  231. package/dist/trace/version-detector.d.ts +20 -0
  232. package/dist/trace/version-detector.d.ts.map +1 -0
  233. package/package.json +76 -6
  234. package/skills/README.md +20 -0
  235. package/skills/pie-failure-investigation/SKILL.md +16 -0
  236. package/skills/pie-quality-engineer/SKILL.md +66 -0
  237. package/skills/pie-release-readiness/SKILL.md +22 -0
  238. package/skills/pie-repair-review/SKILL.md +21 -0
  239. package/skills/pie-trace-to-repair/SKILL.md +35 -0
  240. package/src/authoring/author.ts +81 -0
  241. package/src/authoring/generation-contract.ts +283 -0
  242. package/src/authoring/hybrid-author.ts +115 -0
  243. package/src/authoring/index.ts +7 -0
  244. package/src/authoring/llm-author.ts +282 -0
  245. package/src/authoring/naming/index.ts +4 -0
  246. package/src/authoring/naming/name-plan.ts +28 -0
  247. package/src/authoring/naming/name-planner.ts +339 -0
  248. package/src/authoring/naming/naming-linter.ts +292 -0
  249. package/src/authoring/template-author.ts +39 -0
  250. package/src/cli/index.ts +1172 -0
  251. package/src/collectors/collector-interface.ts +8 -0
  252. package/src/collectors/environment-collector.ts +103 -0
  253. package/src/collectors/error-collector.ts +177 -0
  254. package/src/collectors/git-diff-collector.ts +190 -0
  255. package/src/collectors/live-browser-collector.ts +412 -0
  256. package/src/collectors/network-collector.ts +90 -0
  257. package/src/collectors/static-ast-collector.ts +226 -0
  258. package/src/collectors/trace-collector.ts +66 -0
  259. package/src/config/project-config.ts +114 -0
  260. package/src/core/errors/engine-errors.ts +90 -0
  261. package/src/core/types/diagnostic-context.ts +94 -0
  262. package/src/core/types/repair-evidence.ts +45 -0
  263. package/src/core/version.ts +25 -0
  264. package/src/diagnostics/diagnostic-contract.ts +79 -0
  265. package/src/diagnostics/diagnostic-report.ts +204 -0
  266. package/src/diagnostics/evidence-correlator.ts +117 -0
  267. package/src/diagnostics/external-failure-evidence.ts +72 -0
  268. package/src/diagnostics/failure-category.ts +40 -0
  269. package/src/diagnostics/failure-classifier.ts +701 -0
  270. package/src/evaluation/evaluation-scorecard.ts +198 -0
  271. package/src/evaluation/repository-inspector.ts +143 -0
  272. package/src/evaluation/repository-runner.ts +197 -0
  273. package/src/evidence/evidence-model.ts +76 -0
  274. package/src/evidence/evidence-normalizer.ts +322 -0
  275. package/src/evidence/evidence-redactor.ts +218 -0
  276. package/src/framework/config-adapter.ts +131 -0
  277. package/src/gate/index.ts +43 -0
  278. package/src/gate/oracle.ts +69 -0
  279. package/src/gate/quality.ts +101 -0
  280. package/src/gate/safety.ts +94 -0
  281. package/src/generator/architecture-generator.ts +582 -0
  282. package/src/generator/assertion-quality-gate.ts +88 -0
  283. package/src/generator/candidate-writer.ts +288 -0
  284. package/src/generator/test-generator.ts +52 -0
  285. package/src/governance/audit-log.ts +115 -0
  286. package/src/governance/governance-policy.ts +72 -0
  287. package/src/governance/team-dashboard.ts +189 -0
  288. package/src/governance/workflow-audit.ts +72 -0
  289. package/src/index.ts +107 -0
  290. package/src/integrations/ci-summary.ts +37 -0
  291. package/src/integrations/playwright-report-adapter.ts +173 -0
  292. package/src/integrations/quality-gate.ts +95 -0
  293. package/src/integrations/sarif-report.ts +110 -0
  294. package/src/intelligence/autonomous-repair-workflow.ts +139 -0
  295. package/src/intelligence/business-intent.ts +163 -0
  296. package/src/intelligence/evidence-bundle.ts +300 -0
  297. package/src/intelligence/failure-clustering.ts +134 -0
  298. package/src/intelligence/generation-agent.ts +561 -0
  299. package/src/intelligence/generation-verifier.ts +1115 -0
  300. package/src/intelligence/llm-proposal.ts +519 -0
  301. package/src/intelligence/locator-disambiguation-engine.ts +331 -0
  302. package/src/intelligence/model-contract.ts +205 -0
  303. package/src/intelligence/model-provider.ts +348 -0
  304. package/src/intelligence/project-intelligence.ts +140 -0
  305. package/src/intelligence/repair-agent.ts +297 -0
  306. package/src/intelligence/repository-profile.ts +796 -0
  307. package/src/intelligence/suite-intelligence.ts +305 -0
  308. package/src/intelligence/test-intelligence.ts +136 -0
  309. package/src/intelligence/trace-proposal-workflow.ts +318 -0
  310. package/src/intelligence/verified-llm-repair-workflow.ts +48 -0
  311. package/src/intelligence/workflow-planner.ts +966 -0
  312. package/src/mcp/server.ts +970 -0
  313. package/src/memory/stability-ledger.ts +243 -0
  314. package/src/perception/app-model.ts +32 -0
  315. package/src/perception/crawler.ts +263 -0
  316. package/src/perception/index.ts +3 -0
  317. package/src/repair/approval-service.ts +541 -0
  318. package/src/repair/assertion-hasher.ts +386 -0
  319. package/src/repair/ast-patcher.ts +223 -0
  320. package/src/repair/browser-operation-executor.ts +263 -0
  321. package/src/repair/diff-allowlist.ts +612 -0
  322. package/src/repair/intent-validator.ts +761 -0
  323. package/src/repair/repair-operations.ts +65 -0
  324. package/src/repair/repair-planner.ts +467 -0
  325. package/src/repair/repair-workflow.ts +241 -0
  326. package/src/repair/safe-operation-compiler.ts +68 -0
  327. package/src/repair/source-mapper.ts +156 -0
  328. package/src/repair/statement-patcher.ts +544 -0
  329. package/src/repair/verification-gates.ts +46 -0
  330. package/src/repair/workflow-plan.ts +59 -0
  331. package/src/repair/workspace-promoter.ts +313 -0
  332. package/src/reproduce/diagnostic-rerunner.ts +287 -0
  333. package/src/reproduce/direct-browser-investigator.ts +208 -0
  334. package/src/reproduce/interactive-workflow-runner.ts +828 -0
  335. package/src/sandbox/dependency-strategy.ts +215 -0
  336. package/src/sandbox/execution-runner.ts +408 -0
  337. package/src/sandbox/worktree-manager.ts +287 -0
  338. package/src/selector/abstention-policy.ts +175 -0
  339. package/src/selector/candidate-extractor.ts +343 -0
  340. package/src/selector/live-locator-verifier.ts +139 -0
  341. package/src/selector/repair-candidate-ranker.ts +92 -0
  342. package/src/selector/selector-scorer.ts +344 -0
  343. package/src/trace/adapters/adapter-registry.ts +49 -0
  344. package/src/trace/adapters/trace-adapter.ts +28 -0
  345. package/src/trace/adapters/unknown-adapter.ts +50 -0
  346. package/src/trace/adapters/v10-adapter.ts +39 -0
  347. package/src/trace/adapters/v4-adapter.ts +31 -0
  348. package/src/trace/adapters/v8-adapter.ts +42 -0
  349. package/src/trace/adapters/v9-adapter.ts +39 -0
  350. package/src/trace/archive-reader.ts +246 -0
  351. package/src/trace/snapshot-resolver.ts +308 -0
  352. package/src/trace/trace-parser.ts +467 -0
  353. package/src/trace/version-detector.ts +81 -0
@@ -0,0 +1,246 @@
1
+ import AdmZip from 'adm-zip';
2
+ import path from 'node:path';
3
+ import fs from 'node:fs/promises';
4
+ import { ZipSlipSecurityError, TraceCorruptedError } from '../core/errors/engine-errors.js';
5
+
6
+ export interface UnpackOptions {
7
+ maxTotalSize?: number; // Default 500 MB
8
+ maxEntries?: number; // Default 10,000 entries
9
+ targetDir?: string; // Directory to extract to
10
+ }
11
+
12
+ export interface ExtractedTraceArchive {
13
+ archivePath: string;
14
+ extractDir: string;
15
+ entryNames: string[];
16
+ hasTraceEventFile: boolean;
17
+ hasNetworkEventFile: boolean;
18
+ }
19
+
20
+ export interface TraceEntryDiscovery {
21
+ /** Best stream for backward-compatible single-stream consumers. */
22
+ traceEntryName?: string | undefined;
23
+ networkEntryName?: string | undefined;
24
+ /** All streams, ordered with explicit failure streams first. */
25
+ traceEntryNames?: string[] | undefined;
26
+ networkEntryNames?: string[] | undefined;
27
+ }
28
+
29
+ export class ArchiveReader {
30
+ private static readonly DEFAULT_MAX_SIZE = 100 * 1024 * 1024; // 100 MB max uncompressed
31
+ private static readonly DEFAULT_MAX_ENTRIES = 10000;
32
+ private static readonly MAX_COMPRESSION_RATIO = 10; // 10:1 ratio protection
33
+ private static readonly MAX_SINGLE_ENTRY_SIZE = 50 * 1024 * 1024; // 50 MB max single entry
34
+
35
+ /**
36
+ * Automatically discovers the primary trace and network JSONL entry names inside a Playwright trace archive.
37
+ * Matches stream and worker IDs between .trace and .network files to prevent cross-worker mismatch.
38
+ */
39
+ public static findTraceFileEntries(zipPath: string): TraceEntryDiscovery {
40
+ try {
41
+ const zip = new AdmZip(zipPath);
42
+ const entries = zip.getEntries();
43
+
44
+ // A Playwright archive can contain several streams. The largest stream is
45
+ // often a browser/page stream, while the runner's test.trace contains the
46
+ // actual failed pw:api action and error. Prefer the stream with explicit
47
+ // failed-action evidence, then fall back to the largest stream for older
48
+ // or minimal archives.
49
+ const traceCandidates = entries
50
+ .filter((e) => e.entryName.endsWith('.trace'))
51
+ .map((entry) => {
52
+ const text = entry.getData().toString('utf8');
53
+ const lines = text.split(/\r?\n/);
54
+ let failureScore = 0;
55
+ for (const line of lines) {
56
+ if (!line.includes('"type":"')) continue;
57
+ if (line.includes('"type":"after"') && line.includes('"error"')) failureScore += 100;
58
+ if (line.includes('"type":"error"') && (line.includes('"message"') || line.includes('"error"'))) failureScore += 80;
59
+ if (line.includes('"type":"log"')) failureScore += 1;
60
+ }
61
+ if (path.basename(entry.entryName).toLowerCase() === 'test.trace' && failureScore > 0) {
62
+ failureScore += 10;
63
+ }
64
+ return { entry, failureScore };
65
+ })
66
+ .sort((a, b) => b.failureScore - a.failureScore || b.entry.header.size - a.entry.header.size);
67
+
68
+ const orderedTraceNames = traceCandidates.map(({ entry }) => entry.entryName);
69
+ const bestTrace = orderedTraceNames[0];
70
+ if (!bestTrace) {
71
+ return {};
72
+ }
73
+
74
+ // Correlate network entry with matching stream / worker prefix if possible
75
+ const traceBase = path.basename(bestTrace, '.trace');
76
+ const networkCandidates = entries.filter((e) => e.entryName.endsWith('.network'));
77
+ const networkEntryNames = networkCandidates.map((entry) => entry.entryName);
78
+
79
+ let bestNetwork: string | undefined;
80
+ // 1. Exact base match (e.g. 0.trace -> 0.network, trace.trace -> trace.network)
81
+ const exactMatch = networkCandidates.find((e) => path.basename(e.entryName, '.network') === traceBase);
82
+ if (exactMatch) {
83
+ bestNetwork = exactMatch.entryName;
84
+ } else {
85
+ // 2. Prefix match (e.g. 0-trace.trace -> 0-network.network or 0.network)
86
+ const workerPrefix = traceBase.split('-')[0] ?? '';
87
+ const prefixMatch = networkCandidates.find((e) => path.basename(e.entryName, '.network').startsWith(workerPrefix));
88
+ bestNetwork = prefixMatch ? prefixMatch.entryName : networkCandidates.sort((a, b) => b.header.size - a.header.size)[0]?.entryName;
89
+ }
90
+
91
+ return {
92
+ traceEntryName: bestTrace,
93
+ networkEntryName: bestNetwork,
94
+ traceEntryNames: orderedTraceNames,
95
+ networkEntryNames,
96
+ };
97
+ } catch {
98
+ return {};
99
+ }
100
+ }
101
+
102
+ /** Reads multiple archive entries and joins them as one logical JSONL stream. */
103
+ public static async readEntriesAsText(zipPath: string, entryNames: string[]): Promise<string> {
104
+ const uniqueNames = [...new Set(entryNames.filter(Boolean))];
105
+ const texts = await Promise.all(uniqueNames.map((entryName) => ArchiveReader.readEntryAsText(zipPath, entryName)));
106
+ return texts.join('\n');
107
+ }
108
+
109
+ /**
110
+ * Reads a single entry as a UTF-8 string directly from a .zip file in memory
111
+ * with strict per-entry size limits.
112
+ */
113
+ public static async readEntryAsText(zipPath: string, entryName: string): Promise<string> {
114
+ try {
115
+ const zip = new AdmZip(zipPath);
116
+ const entry = zip.getEntry(entryName);
117
+ if (!entry) {
118
+ throw new Error(`Entry '${entryName}' not found in archive '${zipPath}'`);
119
+ }
120
+ if (entry.header.size > ArchiveReader.MAX_SINGLE_ENTRY_SIZE) {
121
+ throw new TraceCorruptedError(
122
+ zipPath,
123
+ `Entry '${entryName}' size ${entry.header.size} bytes exceeds single entry limit of ${ArchiveReader.MAX_SINGLE_ENTRY_SIZE} bytes`
124
+ );
125
+ }
126
+ return entry.getData().toString('utf8');
127
+ } catch (err: any) {
128
+ if (err instanceof ZipSlipSecurityError) throw err;
129
+ throw new TraceCorruptedError(zipPath, err.message);
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Safely unpacks a Playwright trace.zip archive to an isolated destination directory
135
+ * with strict Zip-Slip and Zip-Bomb protections.
136
+ */
137
+ public static async unpackSafely(
138
+ zipPath: string,
139
+ destinationDir: string,
140
+ options?: UnpackOptions
141
+ ): Promise<ExtractedTraceArchive> {
142
+ const maxSize = options?.maxTotalSize ?? ArchiveReader.DEFAULT_MAX_SIZE;
143
+ const maxEntries = options?.maxEntries ?? ArchiveReader.DEFAULT_MAX_ENTRIES;
144
+
145
+ const resolvedDest = path.resolve(destinationDir);
146
+ await fs.mkdir(resolvedDest, { recursive: true });
147
+
148
+ let zip: AdmZip;
149
+ try {
150
+ zip = new AdmZip(zipPath);
151
+ } catch (err: any) {
152
+ throw new TraceCorruptedError(zipPath, `Failed to initialize zip reader: ${err.message}`);
153
+ }
154
+
155
+ const entries = zip.getEntries();
156
+ if (entries.length > maxEntries) {
157
+ throw new TraceCorruptedError(
158
+ zipPath,
159
+ `Zip-Bomb suspected: archive contains ${entries.length} entries (max allowed: ${maxEntries})`
160
+ );
161
+ }
162
+
163
+ let totalSize = 0;
164
+ const entryNames: string[] = [];
165
+
166
+ // Phase 1: Validate all entries BEFORE writing anything to disk (All-or-nothing check)
167
+ for (const entry of entries) {
168
+ totalSize += entry.header.size;
169
+ if (totalSize > maxSize) {
170
+ throw new TraceCorruptedError(
171
+ zipPath,
172
+ `Zip-Bomb suspected: uncompressed size exceeded limit of ${maxSize} bytes`
173
+ );
174
+ }
175
+
176
+ // Check for Zip-Bomb compression ratio anomaly (e.g. 100:1 ratio)
177
+ if (entry.header.compressedSize > 0 && entry.header.size > 512 * 1024) {
178
+ const ratio = entry.header.size / entry.header.compressedSize;
179
+ if (ratio > ArchiveReader.MAX_COMPRESSION_RATIO) {
180
+ throw new TraceCorruptedError(
181
+ zipPath,
182
+ `Zip-Bomb suspected: compression ratio ${ratio.toFixed(1)}:1 exceeds limit of ${ArchiveReader.MAX_COMPRESSION_RATIO}:1 for entry '${entry.entryName}'`
183
+ );
184
+ }
185
+ }
186
+
187
+ // Check for Zip-Slip path traversal vulnerability
188
+ const entryName = entry.entryName;
189
+ // Cross-platform check: reject absolute paths, drive letters, UNC paths, and root-relative paths
190
+ if (
191
+ path.isAbsolute(entryName) ||
192
+ path.win32.isAbsolute(entryName) ||
193
+ path.posix.isAbsolute(entryName) ||
194
+ /^[a-zA-Z]:[/\\]/.test(entryName) ||
195
+ entryName.startsWith('/') ||
196
+ entryName.startsWith('\\')
197
+ ) {
198
+ throw new ZipSlipSecurityError(entry.entryName);
199
+ }
200
+
201
+ // Ensure target path is strictly within destination directory on both Windows and POSIX
202
+ const normalizedEntry = entryName.replace(/\\/g, '/');
203
+ const targetFilePath = path.resolve(resolvedDest, normalizedEntry);
204
+ const relative = path.relative(resolvedDest, targetFilePath);
205
+ if (
206
+ relative.startsWith('..') ||
207
+ path.isAbsolute(relative) ||
208
+ path.win32.isAbsolute(relative) ||
209
+ path.posix.isAbsolute(relative)
210
+ ) {
211
+ throw new ZipSlipSecurityError(entry.entryName);
212
+ }
213
+
214
+ entryNames.push(entry.entryName);
215
+ }
216
+
217
+ // Phase 2: Safe extraction
218
+ for (const entry of entries) {
219
+ const normalizedEntry = entry.entryName.replace(/\\/g, '/');
220
+ const targetFilePath = path.resolve(resolvedDest, normalizedEntry);
221
+ if (entry.isDirectory) {
222
+ await fs.mkdir(targetFilePath, { recursive: true });
223
+ } else {
224
+ await fs.mkdir(path.dirname(targetFilePath), { recursive: true });
225
+ const content = entry.getData();
226
+ await fs.writeFile(targetFilePath, content);
227
+ }
228
+ }
229
+
230
+ const hasTraceEventFile = entryNames.some(
231
+ (name) => name === 'trace.trace' || name.endsWith('.trace')
232
+ );
233
+ const hasNetworkEventFile = entryNames.some(
234
+ (name) => name === 'trace.network' || name.endsWith('.network')
235
+ );
236
+
237
+ return {
238
+ archivePath: zipPath,
239
+ extractDir: resolvedDest,
240
+ entryNames,
241
+ hasTraceEventFile,
242
+ hasNetworkEventFile,
243
+ };
244
+ }
245
+ }
246
+
@@ -0,0 +1,308 @@
1
+ export type NodeSnapshot =
2
+ | string
3
+ | number
4
+ | [string, Record<string, string>, ...NodeSnapshot[]];
5
+
6
+ export interface ResolvedElement {
7
+ tag: string;
8
+ attributes: Record<string, string>;
9
+ text: string;
10
+ children: ResolvedElement[];
11
+ role?: string | undefined;
12
+ accessibleName?: string | undefined;
13
+ }
14
+
15
+ export type SnapshotCompleteness = 'COMPLETE' | 'PARTIAL' | 'UNAVAILABLE';
16
+
17
+ export interface ResolvedSnapshot {
18
+ frameId: string;
19
+ callId?: string | undefined;
20
+ phase: 'before' | 'action' | 'after';
21
+ completeness: SnapshotCompleteness;
22
+ unresolvedReferences: string[];
23
+ dom: ResolvedElement[];
24
+ }
25
+
26
+ export interface FrameSnapshotRecord {
27
+ frameId: string;
28
+ pageId?: string | undefined;
29
+ callId?: string | undefined;
30
+ snapshotName?: string | undefined;
31
+ phase?: 'before' | 'action' | 'after' | undefined;
32
+ node?: NodeSnapshot | undefined;
33
+ }
34
+
35
+ export class SnapshotResolver {
36
+ /**
37
+ * Reconstructs an in-memory resolved element tree from Playwright's array-based NodeSnapshot,
38
+ * tracking provenance, snapshot phase, and evidence completeness.
39
+ */
40
+ public static resolveSnapshot(record: FrameSnapshotRecord): ResolvedSnapshot {
41
+ const frameId = record.frameId || 'main-frame';
42
+ const phase = record.phase ?? 'before';
43
+ const unresolvedReferences: string[] = [];
44
+
45
+ if (!record.node) {
46
+ return {
47
+ frameId,
48
+ callId: record.callId,
49
+ phase,
50
+ completeness: 'UNAVAILABLE',
51
+ unresolvedReferences: ['Missing root node in snapshot record'],
52
+ dom: [],
53
+ };
54
+ }
55
+
56
+ const rootElement = SnapshotResolver.resolveNode(record.node, unresolvedReferences);
57
+ const completeness: SnapshotCompleteness =
58
+ unresolvedReferences.length > 0 ? 'PARTIAL' : rootElement ? 'COMPLETE' : 'UNAVAILABLE';
59
+
60
+ return {
61
+ frameId,
62
+ callId: record.callId,
63
+ phase,
64
+ completeness,
65
+ unresolvedReferences,
66
+ dom: rootElement ? [rootElement] : [],
67
+ };
68
+ }
69
+
70
+ /**
71
+ * Reconstructs an in-memory resolved element tree from Playwright's array-based NodeSnapshot.
72
+ */
73
+ public static resolveNode(node: NodeSnapshot, unresolvedRefs?: string[]): ResolvedElement | null {
74
+ if (typeof node === 'string' || typeof node === 'number') {
75
+ if (typeof node === 'string' && node.startsWith('ref:')) {
76
+ unresolvedRefs?.push(node);
77
+ }
78
+ return null;
79
+ }
80
+
81
+ if (!Array.isArray(node) || node.length < 2) {
82
+ return null;
83
+ }
84
+
85
+ const tag = (node[0] as string).toLowerCase();
86
+ const attributes = (node[1] as Record<string, string>) ?? {};
87
+ const rawChildren = node.slice(2) as NodeSnapshot[];
88
+
89
+ const children: ResolvedElement[] = [];
90
+ let textAccumulator = '';
91
+
92
+ for (const child of rawChildren) {
93
+ if (typeof child === 'string') {
94
+ if (child.startsWith('ref:')) {
95
+ unresolvedRefs?.push(child);
96
+ } else {
97
+ textAccumulator += child.trim() + ' ';
98
+ }
99
+ } else if (Array.isArray(child)) {
100
+ const resolvedChild = SnapshotResolver.resolveNode(child, unresolvedRefs);
101
+ if (resolvedChild) {
102
+ children.push(resolvedChild);
103
+ textAccumulator += resolvedChild.text + ' ';
104
+ }
105
+ }
106
+ }
107
+
108
+ const role = attributes['role'] ?? SnapshotResolver.inferRoleFromTag(tag, attributes);
109
+ const accessibleName =
110
+ attributes['aria-label'] ??
111
+ attributes['alt'] ??
112
+ attributes['title'] ??
113
+ attributes['placeholder'] ??
114
+ textAccumulator.trim();
115
+
116
+ return {
117
+ tag,
118
+ attributes,
119
+ text: textAccumulator.trim(),
120
+ children,
121
+ role,
122
+ accessibleName: accessibleName || undefined,
123
+ };
124
+ }
125
+
126
+ /**
127
+ * Traverses and collects all nodes in depth-first order as Playwright's snapshotNodes does.
128
+ */
129
+ public static snapshotNodes(snap: any): any[] {
130
+ if (!snap) return [];
131
+ if (snap._nodes) return snap._nodes;
132
+ const nodes: any[] = [];
133
+ const visit = (n: any) => {
134
+ if (typeof n === 'string' || typeof n === 'number') {
135
+ nodes.push(n);
136
+ } else if (Array.isArray(n) && typeof n[0] === 'string') {
137
+ const [, , ...children] = n;
138
+ for (const child of children) visit(child);
139
+ nodes.push(n);
140
+ }
141
+ };
142
+ if (snap.html) visit(snap.html);
143
+ else if (snap.node) visit(snap.node);
144
+ snap._nodes = nodes;
145
+ return nodes;
146
+ }
147
+
148
+ /**
149
+ * Resolves a modern Playwright differential snapshot node with support for subtree references [ [ delta, nodeIndex ] ].
150
+ */
151
+ public static resolveDifferentialNode(
152
+ n: any,
153
+ snapshotIndex: number,
154
+ allSnapshots: any[],
155
+ unresolvedRefs?: string[]
156
+ ): ResolvedElement | string | null {
157
+ if (typeof n === 'string' || typeof n === 'number') {
158
+ return String(n);
159
+ }
160
+
161
+ // Check for Playwright subtree reference [ [ delta, nodeIndex ] ]
162
+ if (
163
+ Array.isArray(n) &&
164
+ n.length === 1 &&
165
+ Array.isArray(n[0]) &&
166
+ n[0].length === 2 &&
167
+ typeof n[0][0] === 'number' &&
168
+ typeof n[0][1] === 'number'
169
+ ) {
170
+ const referenceIndex = snapshotIndex - n[0][0];
171
+ if (referenceIndex >= 0 && referenceIndex < allSnapshots.length) {
172
+ const nodes = SnapshotResolver.snapshotNodes(allSnapshots[referenceIndex]);
173
+ const nodeIndex = n[0][1];
174
+ if (nodeIndex >= 0 && nodeIndex < nodes.length) {
175
+ return SnapshotResolver.resolveDifferentialNode(
176
+ nodes[nodeIndex],
177
+ referenceIndex,
178
+ allSnapshots,
179
+ unresolvedRefs
180
+ );
181
+ }
182
+ }
183
+ unresolvedRefs?.push(`Subtree reference out of bounds: [${n[0][0]}, ${n[0][1]}]`);
184
+ return null;
185
+ }
186
+
187
+ if (!Array.isArray(n) || n.length < 2 || typeof n[0] !== 'string') {
188
+ return null;
189
+ }
190
+
191
+ const tag = (n[0] as string).toLowerCase();
192
+ const attributes = (n[1] as Record<string, string>) ?? {};
193
+ const rawChildren = n.slice(2);
194
+
195
+ const children: ResolvedElement[] = [];
196
+ let textAccumulator = '';
197
+
198
+ for (const child of rawChildren) {
199
+ const resolved = SnapshotResolver.resolveDifferentialNode(
200
+ child,
201
+ snapshotIndex,
202
+ allSnapshots,
203
+ unresolvedRefs
204
+ );
205
+ if (typeof resolved === 'string') {
206
+ textAccumulator += resolved.trim() + ' ';
207
+ } else if (resolved && typeof resolved === 'object' && 'tag' in resolved) {
208
+ children.push(resolved as ResolvedElement);
209
+ textAccumulator += (resolved as ResolvedElement).text + ' ';
210
+ }
211
+ }
212
+
213
+ const role = attributes['role'] ?? SnapshotResolver.inferRoleFromTag(tag, attributes);
214
+ const accessibleName =
215
+ attributes['aria-label'] ??
216
+ attributes['alt'] ??
217
+ attributes['title'] ??
218
+ attributes['placeholder'] ??
219
+ textAccumulator.trim();
220
+
221
+ return {
222
+ tag,
223
+ attributes,
224
+ text: textAccumulator.trim(),
225
+ children,
226
+ role,
227
+ accessibleName: accessibleName || undefined,
228
+ };
229
+ }
230
+
231
+ /**
232
+ * Serializes a resolved element tree back into clean, formatted HTML.
233
+ */
234
+ public static toHtml(element: ResolvedElement, indent = 0): string {
235
+ const spacing = ' '.repeat(indent);
236
+ const attrs = Object.entries(element.attributes)
237
+ .map(([k, v]) => `${k}="${v.replace(/"/g, '&quot;')}"`)
238
+ .join(' ');
239
+ const attrString = attrs.length > 0 ? ` ${attrs}` : '';
240
+
241
+ if (element.children.length === 0) {
242
+ return `${spacing}<${element.tag}${attrString}>${element.text}</${element.tag}>`;
243
+ }
244
+
245
+ const childrenHtml = element.children.map((c) => SnapshotResolver.toHtml(c, indent + 1)).join('\n');
246
+ return `${spacing}<${element.tag}${attrString}>\n${childrenHtml}\n${spacing}</${element.tag}>`;
247
+ }
248
+
249
+ /**
250
+ * Collects all descendant interactive elements (buttons, inputs, links, selects, roles).
251
+ */
252
+ public static extractInteractiveElements(root: ResolvedElement): ResolvedElement[] {
253
+ const interactiveTags = new Set(['button', 'a', 'input', 'select', 'textarea', 'option']);
254
+ const interactiveRoles = new Set([
255
+ 'button',
256
+ 'link',
257
+ 'textbox',
258
+ 'combobox',
259
+ 'checkbox',
260
+ 'radio',
261
+ 'tab',
262
+ 'menuitem',
263
+ ]);
264
+
265
+ const results: ResolvedElement[] = [];
266
+
267
+ function traverse(node: ResolvedElement) {
268
+ const isInteractive =
269
+ interactiveTags.has(node.tag) ||
270
+ (node.role && interactiveRoles.has(node.role.toLowerCase())) ||
271
+ Boolean(node.attributes?.['onclick']) ||
272
+ node.attributes?.['cursor'] === 'pointer';
273
+
274
+ if (isInteractive) {
275
+ results.push(node);
276
+ }
277
+
278
+ for (const child of node.children) {
279
+ traverse(child);
280
+ }
281
+ }
282
+
283
+ traverse(root);
284
+ return results;
285
+ }
286
+
287
+ private static inferRoleFromTag(tag: string, attributes: Record<string, string>): string | undefined {
288
+ switch (tag) {
289
+ case 'button':
290
+ return 'button';
291
+ case 'a':
292
+ return attributes['href'] ? 'link' : undefined;
293
+ case 'input': {
294
+ const type = attributes['type']?.toLowerCase() ?? 'text';
295
+ if (type === 'button' || type === 'submit' || type === 'reset') return 'button';
296
+ if (type === 'checkbox') return 'checkbox';
297
+ if (type === 'radio') return 'radio';
298
+ return 'textbox';
299
+ }
300
+ case 'select':
301
+ return 'combobox';
302
+ case 'textarea':
303
+ return 'textbox';
304
+ default:
305
+ return undefined;
306
+ }
307
+ }
308
+ }