tabwright 1.0.0 → 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 (568) hide show
  1. package/dist/agent-skills/tabwright/SKILL.md +3 -0
  2. package/dist/aria-snapshot.d.ts +0 -1
  3. package/dist/aria-snapshot.js +9 -1
  4. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts +0 -1
  5. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js +0 -1
  6. package/dist/bippy.js +5 -5
  7. package/dist/browser-config.d.ts +0 -1
  8. package/dist/browser-config.js +0 -1
  9. package/dist/browser-install.d.ts +0 -1
  10. package/dist/browser-install.js +0 -1
  11. package/dist/browser-launch.d.ts +0 -1
  12. package/dist/browser-launch.js +0 -1
  13. package/dist/capability-agent-skill.d.ts +0 -1
  14. package/dist/capability-agent-skill.js +0 -1
  15. package/dist/capability-auth-state.d.ts +0 -1
  16. package/dist/capability-auth-state.js +0 -1
  17. package/dist/capability-auth.d.ts +0 -1
  18. package/dist/capability-auth.js +0 -1
  19. package/dist/capability-options.d.ts +0 -1
  20. package/dist/capability-options.js +0 -1
  21. package/dist/capability-package.d.ts +0 -1
  22. package/dist/capability-package.js +47 -2
  23. package/dist/capability-registry.d.ts +0 -1
  24. package/dist/capability-registry.js +0 -1
  25. package/dist/capability-runner.d.ts +0 -1
  26. package/dist/capability-runner.js +0 -1
  27. package/dist/capability-studio.d.ts +0 -1
  28. package/dist/capability-studio.js +0 -1
  29. package/dist/cdp-log.d.ts +0 -1
  30. package/dist/cdp-log.js +0 -1
  31. package/dist/cdp-relay.d.ts +0 -1
  32. package/dist/cdp-relay.js +0 -1
  33. package/dist/cdp-session.d.ts +0 -1
  34. package/dist/cdp-session.js +0 -1
  35. package/dist/cdp-types.d.ts +0 -1
  36. package/dist/cdp-types.js +0 -1
  37. package/dist/chrome-discovery.d.ts +0 -1
  38. package/dist/chrome-discovery.js +0 -1
  39. package/dist/clean-html.d.ts +0 -1
  40. package/dist/clean-html.js +0 -1
  41. package/dist/cli.d.ts +0 -1
  42. package/dist/cli.js +10 -56
  43. package/dist/cloud-client.d.ts +0 -1
  44. package/dist/cloud-client.js +0 -1
  45. package/dist/create-logger.d.ts +0 -1
  46. package/dist/create-logger.js +0 -1
  47. package/dist/debugger-examples-types.d.ts +0 -1
  48. package/dist/debugger-examples-types.js +0 -1
  49. package/dist/debugger-examples.d.ts +0 -1
  50. package/dist/debugger-examples.js +0 -1
  51. package/dist/debugger.d.ts +0 -1
  52. package/dist/debugger.js +0 -1
  53. package/dist/diff-utils.d.ts +0 -1
  54. package/dist/diff-utils.js +0 -1
  55. package/dist/doctor.d.ts +0 -1
  56. package/dist/doctor.js +5 -7
  57. package/dist/editor-examples.d.ts +0 -1
  58. package/dist/editor-examples.js +0 -1
  59. package/dist/editor.d.ts +0 -1
  60. package/dist/editor.js +0 -1
  61. package/dist/env-compat.d.ts +0 -1
  62. package/dist/env-compat.js +0 -1
  63. package/dist/executor.d.ts +0 -1
  64. package/dist/executor.js +1 -2
  65. package/dist/extension/background.js +10 -6
  66. package/dist/extension/manifest.json +1 -1
  67. package/dist/extension/options.js +1 -1
  68. package/dist/extension/rrweb-recorder.js +1 -1
  69. package/dist/ghost-browser.d.ts +0 -1
  70. package/dist/ghost-browser.js +0 -1
  71. package/dist/ghost-cursor-controller.d.ts +0 -1
  72. package/dist/ghost-cursor-controller.js +0 -1
  73. package/dist/ghost-cursor.d.ts +0 -1
  74. package/dist/ghost-cursor.js +0 -1
  75. package/dist/htmlrewrite.d.ts +0 -1
  76. package/dist/htmlrewrite.js +0 -1
  77. package/dist/index.d.ts +0 -1
  78. package/dist/index.js +0 -1
  79. package/dist/kill-port.d.ts +0 -1
  80. package/dist/kill-port.js +0 -1
  81. package/dist/kitty-graphics.d.ts +0 -1
  82. package/dist/kitty-graphics.js +0 -1
  83. package/dist/mcp.d.ts +0 -1
  84. package/dist/mcp.js +0 -1
  85. package/dist/package-paths.d.ts +0 -1
  86. package/dist/package-paths.js +0 -1
  87. package/dist/page-markdown.d.ts +0 -1
  88. package/dist/page-markdown.js +0 -1
  89. package/dist/performance-examples.d.ts +0 -1
  90. package/dist/performance-examples.js +0 -1
  91. package/dist/playwright-import.d.ts +0 -1
  92. package/dist/playwright-import.js +0 -1
  93. package/dist/product-paths.d.ts +0 -1
  94. package/dist/product-paths.js +0 -1
  95. package/dist/protocol.d.ts +0 -1
  96. package/dist/protocol.js +0 -1
  97. package/dist/react-source.d.ts +0 -1
  98. package/dist/react-source.js +0 -1
  99. package/dist/readability.js +4 -4
  100. package/dist/relay-client.d.ts +0 -1
  101. package/dist/relay-client.js +0 -1
  102. package/dist/relay-state.d.ts +0 -1
  103. package/dist/relay-state.js +0 -1
  104. package/dist/replay-ai-index.d.ts +0 -1
  105. package/dist/replay-ai-index.js +0 -1
  106. package/dist/replay-eval.d.ts +0 -1
  107. package/dist/replay-eval.js +0 -1
  108. package/dist/replay-handoff.d.ts +0 -1
  109. package/dist/replay-handoff.js +0 -1
  110. package/dist/replay-workflow-compiler.d.ts +0 -1
  111. package/dist/replay-workflow-compiler.js +0 -1
  112. package/dist/rrweb-recording-relay.d.ts +0 -1
  113. package/dist/rrweb-recording-relay.js +0 -1
  114. package/dist/rrweb-recording.d.ts +0 -1
  115. package/dist/rrweb-recording.js +0 -1
  116. package/dist/scoped-fs.d.ts +0 -1
  117. package/dist/scoped-fs.js +0 -1
  118. package/dist/selector-generator.js +2 -2
  119. package/dist/start-relay-server.d.ts +0 -1
  120. package/dist/start-relay-server.js +0 -1
  121. package/dist/styles-examples.d.ts +0 -1
  122. package/dist/styles-examples.js +0 -1
  123. package/dist/styles.d.ts +0 -1
  124. package/dist/styles.js +0 -1
  125. package/dist/tabwright-agent-skill.d.ts +0 -1
  126. package/dist/tabwright-agent-skill.js +0 -1
  127. package/dist/test-declarations.d.ts +0 -1
  128. package/dist/test-declarations.js +0 -1
  129. package/dist/utils.d.ts +0 -1
  130. package/dist/utils.js +2 -2
  131. package/dist/wait-for-page-load.d.ts +0 -1
  132. package/dist/wait-for-page-load.js +0 -1
  133. package/dist/workflow-capability.d.ts +0 -1
  134. package/dist/workflow-capability.js +0 -1
  135. package/package.json +8 -4
  136. package/readme.md +43 -0
  137. package/src/skill.md +5 -2
  138. package/dist/aria-snapshot.d.ts.map +0 -1
  139. package/dist/aria-snapshot.js.map +0 -1
  140. package/dist/aria-snapshot.test.d.ts +0 -2
  141. package/dist/aria-snapshot.test.d.ts.map +0 -1
  142. package/dist/aria-snapshot.test.js +0 -270
  143. package/dist/aria-snapshot.test.js.map +0 -1
  144. package/dist/aria-snapshot.unit.test.d.ts +0 -2
  145. package/dist/aria-snapshot.unit.test.d.ts.map +0 -1
  146. package/dist/aria-snapshot.unit.test.js +0 -542
  147. package/dist/aria-snapshot.unit.test.js.map +0 -1
  148. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts.map +0 -1
  149. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js.map +0 -1
  150. package/dist/browser-config.d.ts.map +0 -1
  151. package/dist/browser-config.js.map +0 -1
  152. package/dist/browser-install.d.ts.map +0 -1
  153. package/dist/browser-install.js.map +0 -1
  154. package/dist/browser-launch.d.ts.map +0 -1
  155. package/dist/browser-launch.js.map +0 -1
  156. package/dist/builtin-capabilities.d.ts +0 -28
  157. package/dist/builtin-capabilities.d.ts.map +0 -1
  158. package/dist/builtin-capabilities.js +0 -2673
  159. package/dist/builtin-capabilities.js.map +0 -1
  160. package/dist/capability-agent-skill.d.ts.map +0 -1
  161. package/dist/capability-agent-skill.js.map +0 -1
  162. package/dist/capability-auth-state.d.ts.map +0 -1
  163. package/dist/capability-auth-state.js.map +0 -1
  164. package/dist/capability-auth.d.ts.map +0 -1
  165. package/dist/capability-auth.js.map +0 -1
  166. package/dist/capability-cli-confirmation.test.d.ts +0 -2
  167. package/dist/capability-cli-confirmation.test.d.ts.map +0 -1
  168. package/dist/capability-cli-confirmation.test.js +0 -79
  169. package/dist/capability-cli-confirmation.test.js.map +0 -1
  170. package/dist/capability-options.d.ts.map +0 -1
  171. package/dist/capability-options.js.map +0 -1
  172. package/dist/capability-options.test.d.ts +0 -2
  173. package/dist/capability-options.test.d.ts.map +0 -1
  174. package/dist/capability-options.test.js +0 -81
  175. package/dist/capability-options.test.js.map +0 -1
  176. package/dist/capability-package.d.ts.map +0 -1
  177. package/dist/capability-package.js.map +0 -1
  178. package/dist/capability-package.test.d.ts +0 -2
  179. package/dist/capability-package.test.d.ts.map +0 -1
  180. package/dist/capability-package.test.js +0 -179
  181. package/dist/capability-package.test.js.map +0 -1
  182. package/dist/capability-registry.d.ts.map +0 -1
  183. package/dist/capability-registry.js.map +0 -1
  184. package/dist/capability-registry.test.d.ts +0 -2
  185. package/dist/capability-registry.test.d.ts.map +0 -1
  186. package/dist/capability-registry.test.js +0 -1683
  187. package/dist/capability-registry.test.js.map +0 -1
  188. package/dist/capability-runner.d.ts.map +0 -1
  189. package/dist/capability-runner.js.map +0 -1
  190. package/dist/capability-search-relevance.test.d.ts +0 -2
  191. package/dist/capability-search-relevance.test.d.ts.map +0 -1
  192. package/dist/capability-search-relevance.test.js +0 -67
  193. package/dist/capability-search-relevance.test.js.map +0 -1
  194. package/dist/capability-studio.d.ts.map +0 -1
  195. package/dist/capability-studio.js.map +0 -1
  196. package/dist/capability-studio.test.d.ts +0 -2
  197. package/dist/capability-studio.test.d.ts.map +0 -1
  198. package/dist/capability-studio.test.js +0 -41
  199. package/dist/capability-studio.test.js.map +0 -1
  200. package/dist/cdp-log.d.ts.map +0 -1
  201. package/dist/cdp-log.js.map +0 -1
  202. package/dist/cdp-log.test.d.ts +0 -2
  203. package/dist/cdp-log.test.d.ts.map +0 -1
  204. package/dist/cdp-log.test.js +0 -109
  205. package/dist/cdp-log.test.js.map +0 -1
  206. package/dist/cdp-relay.d.ts.map +0 -1
  207. package/dist/cdp-relay.js.map +0 -1
  208. package/dist/cdp-session.d.ts.map +0 -1
  209. package/dist/cdp-session.js.map +0 -1
  210. package/dist/cdp-types.d.ts.map +0 -1
  211. package/dist/cdp-types.js.map +0 -1
  212. package/dist/channel-owner-inspect.test.d.ts +0 -2
  213. package/dist/channel-owner-inspect.test.d.ts.map +0 -1
  214. package/dist/channel-owner-inspect.test.js +0 -75
  215. package/dist/channel-owner-inspect.test.js.map +0 -1
  216. package/dist/chrome-discovery.d.ts.map +0 -1
  217. package/dist/chrome-discovery.js.map +0 -1
  218. package/dist/chrome-discovery.test.d.ts +0 -2
  219. package/dist/chrome-discovery.test.d.ts.map +0 -1
  220. package/dist/chrome-discovery.test.js +0 -41
  221. package/dist/chrome-discovery.test.js.map +0 -1
  222. package/dist/clean-html.d.ts.map +0 -1
  223. package/dist/clean-html.js.map +0 -1
  224. package/dist/cli-help.test.d.ts +0 -2
  225. package/dist/cli-help.test.d.ts.map +0 -1
  226. package/dist/cli-help.test.js +0 -98
  227. package/dist/cli-help.test.js.map +0 -1
  228. package/dist/cli.d.ts.map +0 -1
  229. package/dist/cli.js.map +0 -1
  230. package/dist/cloud-client.d.ts.map +0 -1
  231. package/dist/cloud-client.js.map +0 -1
  232. package/dist/create-logger.d.ts.map +0 -1
  233. package/dist/create-logger.js.map +0 -1
  234. package/dist/debugger-examples-types.d.ts.map +0 -1
  235. package/dist/debugger-examples-types.js.map +0 -1
  236. package/dist/debugger-examples.d.ts.map +0 -1
  237. package/dist/debugger-examples.js.map +0 -1
  238. package/dist/debugger.d.ts.map +0 -1
  239. package/dist/debugger.js.map +0 -1
  240. package/dist/diff-utils.d.ts.map +0 -1
  241. package/dist/diff-utils.js.map +0 -1
  242. package/dist/diff-utils.test.d.ts +0 -2
  243. package/dist/diff-utils.test.d.ts.map +0 -1
  244. package/dist/diff-utils.test.js +0 -137
  245. package/dist/diff-utils.test.js.map +0 -1
  246. package/dist/doctor.d.ts.map +0 -1
  247. package/dist/doctor.js.map +0 -1
  248. package/dist/doctor.test.d.ts +0 -2
  249. package/dist/doctor.test.d.ts.map +0 -1
  250. package/dist/doctor.test.js +0 -208
  251. package/dist/doctor.test.js.map +0 -1
  252. package/dist/editor-examples.d.ts.map +0 -1
  253. package/dist/editor-examples.js.map +0 -1
  254. package/dist/editor.d.ts.map +0 -1
  255. package/dist/editor.js.map +0 -1
  256. package/dist/env-compat.d.ts.map +0 -1
  257. package/dist/env-compat.js.map +0 -1
  258. package/dist/executor.d.ts.map +0 -1
  259. package/dist/executor.js.map +0 -1
  260. package/dist/executor.unit.test.d.ts +0 -2
  261. package/dist/executor.unit.test.d.ts.map +0 -1
  262. package/dist/executor.unit.test.js +0 -143
  263. package/dist/executor.unit.test.js.map +0 -1
  264. package/dist/extension-connection.test.d.ts +0 -2
  265. package/dist/extension-connection.test.d.ts.map +0 -1
  266. package/dist/extension-connection.test.js +0 -708
  267. package/dist/extension-connection.test.js.map +0 -1
  268. package/dist/extension-icon-warning.test.d.ts +0 -2
  269. package/dist/extension-icon-warning.test.d.ts.map +0 -1
  270. package/dist/extension-icon-warning.test.js +0 -33
  271. package/dist/extension-icon-warning.test.js.map +0 -1
  272. package/dist/ghost-browser.d.ts.map +0 -1
  273. package/dist/ghost-browser.js.map +0 -1
  274. package/dist/ghost-cursor-controller.d.ts.map +0 -1
  275. package/dist/ghost-cursor-controller.js.map +0 -1
  276. package/dist/ghost-cursor.d.ts.map +0 -1
  277. package/dist/ghost-cursor.js.map +0 -1
  278. package/dist/htmlrewrite.d.ts.map +0 -1
  279. package/dist/htmlrewrite.js.map +0 -1
  280. package/dist/htmlrewrite.test.d.ts +0 -2
  281. package/dist/htmlrewrite.test.d.ts.map +0 -1
  282. package/dist/htmlrewrite.test.js +0 -7975
  283. package/dist/htmlrewrite.test.js.map +0 -1
  284. package/dist/index.d.ts.map +0 -1
  285. package/dist/index.js.map +0 -1
  286. package/dist/kill-port.d.ts.map +0 -1
  287. package/dist/kill-port.js.map +0 -1
  288. package/dist/kitty-graphics.d.ts.map +0 -1
  289. package/dist/kitty-graphics.js.map +0 -1
  290. package/dist/kitty-graphics.test.d.ts +0 -2
  291. package/dist/kitty-graphics.test.d.ts.map +0 -1
  292. package/dist/kitty-graphics.test.js +0 -125
  293. package/dist/kitty-graphics.test.js.map +0 -1
  294. package/dist/locator-selector.test.d.ts +0 -2
  295. package/dist/locator-selector.test.d.ts.map +0 -1
  296. package/dist/locator-selector.test.js +0 -98
  297. package/dist/locator-selector.test.js.map +0 -1
  298. package/dist/mcp-client.d.ts +0 -20
  299. package/dist/mcp-client.d.ts.map +0 -1
  300. package/dist/mcp-client.js +0 -56
  301. package/dist/mcp-client.js.map +0 -1
  302. package/dist/mcp.d.ts.map +0 -1
  303. package/dist/mcp.js.map +0 -1
  304. package/dist/on-mouse-action.test.d.ts +0 -2
  305. package/dist/on-mouse-action.test.d.ts.map +0 -1
  306. package/dist/on-mouse-action.test.js +0 -180
  307. package/dist/on-mouse-action.test.js.map +0 -1
  308. package/dist/options-page.test.d.ts +0 -2
  309. package/dist/options-page.test.d.ts.map +0 -1
  310. package/dist/options-page.test.js +0 -773
  311. package/dist/options-page.test.js.map +0 -1
  312. package/dist/package-paths.d.ts.map +0 -1
  313. package/dist/package-paths.js.map +0 -1
  314. package/dist/page-markdown.d.ts.map +0 -1
  315. package/dist/page-markdown.js.map +0 -1
  316. package/dist/performance-examples.d.ts.map +0 -1
  317. package/dist/performance-examples.js.map +0 -1
  318. package/dist/playwright-import.d.ts.map +0 -1
  319. package/dist/playwright-import.js.map +0 -1
  320. package/dist/popup-relocation.test.d.ts +0 -7
  321. package/dist/popup-relocation.test.d.ts.map +0 -1
  322. package/dist/popup-relocation.test.js +0 -139
  323. package/dist/popup-relocation.test.js.map +0 -1
  324. package/dist/product-paths.d.ts.map +0 -1
  325. package/dist/product-paths.js.map +0 -1
  326. package/dist/product-paths.test.d.ts +0 -2
  327. package/dist/product-paths.test.d.ts.map +0 -1
  328. package/dist/product-paths.test.js +0 -39
  329. package/dist/product-paths.test.js.map +0 -1
  330. package/dist/protocol.d.ts.map +0 -1
  331. package/dist/protocol.js.map +0 -1
  332. package/dist/protocol.test.d.ts +0 -2
  333. package/dist/protocol.test.d.ts.map +0 -1
  334. package/dist/protocol.test.js +0 -31
  335. package/dist/protocol.test.js.map +0 -1
  336. package/dist/react-source.d.ts.map +0 -1
  337. package/dist/react-source.js.map +0 -1
  338. package/dist/relay-client.d.ts.map +0 -1
  339. package/dist/relay-client.js.map +0 -1
  340. package/dist/relay-client.test.d.ts +0 -2
  341. package/dist/relay-client.test.d.ts.map +0 -1
  342. package/dist/relay-client.test.js +0 -305
  343. package/dist/relay-client.test.js.map +0 -1
  344. package/dist/relay-core.test.d.ts +0 -2
  345. package/dist/relay-core.test.d.ts.map +0 -1
  346. package/dist/relay-core.test.js +0 -1427
  347. package/dist/relay-core.test.js.map +0 -1
  348. package/dist/relay-navigation.test.d.ts +0 -2
  349. package/dist/relay-navigation.test.d.ts.map +0 -1
  350. package/dist/relay-navigation.test.js +0 -661
  351. package/dist/relay-navigation.test.js.map +0 -1
  352. package/dist/relay-session.test.d.ts +0 -2
  353. package/dist/relay-session.test.d.ts.map +0 -1
  354. package/dist/relay-session.test.js +0 -1121
  355. package/dist/relay-session.test.js.map +0 -1
  356. package/dist/relay-state.d.ts.map +0 -1
  357. package/dist/relay-state.js.map +0 -1
  358. package/dist/relay-state.test.d.ts +0 -2
  359. package/dist/relay-state.test.d.ts.map +0 -1
  360. package/dist/relay-state.test.js +0 -594
  361. package/dist/relay-state.test.js.map +0 -1
  362. package/dist/replay-ai-index.d.ts.map +0 -1
  363. package/dist/replay-ai-index.js.map +0 -1
  364. package/dist/replay-ai-index.test.d.ts +0 -2
  365. package/dist/replay-ai-index.test.d.ts.map +0 -1
  366. package/dist/replay-ai-index.test.js +0 -255
  367. package/dist/replay-ai-index.test.js.map +0 -1
  368. package/dist/replay-cli.test.d.ts +0 -2
  369. package/dist/replay-cli.test.d.ts.map +0 -1
  370. package/dist/replay-cli.test.js +0 -275
  371. package/dist/replay-cli.test.js.map +0 -1
  372. package/dist/replay-eval.d.ts.map +0 -1
  373. package/dist/replay-eval.js.map +0 -1
  374. package/dist/replay-handoff.d.ts.map +0 -1
  375. package/dist/replay-handoff.js.map +0 -1
  376. package/dist/replay-handoff.test.d.ts +0 -2
  377. package/dist/replay-handoff.test.d.ts.map +0 -1
  378. package/dist/replay-handoff.test.js +0 -123
  379. package/dist/replay-handoff.test.js.map +0 -1
  380. package/dist/replay-workflow-compiler.d.ts.map +0 -1
  381. package/dist/replay-workflow-compiler.js.map +0 -1
  382. package/dist/replay-workflow-compiler.test.d.ts +0 -2
  383. package/dist/replay-workflow-compiler.test.d.ts.map +0 -1
  384. package/dist/replay-workflow-compiler.test.js +0 -223
  385. package/dist/replay-workflow-compiler.test.js.map +0 -1
  386. package/dist/rrweb-recording-relay.d.ts.map +0 -1
  387. package/dist/rrweb-recording-relay.js.map +0 -1
  388. package/dist/rrweb-recording-relay.test.d.ts +0 -2
  389. package/dist/rrweb-recording-relay.test.d.ts.map +0 -1
  390. package/dist/rrweb-recording-relay.test.js +0 -85
  391. package/dist/rrweb-recording-relay.test.js.map +0 -1
  392. package/dist/rrweb-recording.d.ts.map +0 -1
  393. package/dist/rrweb-recording.js.map +0 -1
  394. package/dist/scoped-fs.d.ts.map +0 -1
  395. package/dist/scoped-fs.js.map +0 -1
  396. package/dist/scoped-fs.test.d.ts +0 -5
  397. package/dist/scoped-fs.test.d.ts.map +0 -1
  398. package/dist/scoped-fs.test.js +0 -73
  399. package/dist/scoped-fs.test.js.map +0 -1
  400. package/dist/skill-stub.test.d.ts +0 -2
  401. package/dist/skill-stub.test.d.ts.map +0 -1
  402. package/dist/skill-stub.test.js +0 -22
  403. package/dist/skill-stub.test.js.map +0 -1
  404. package/dist/snapshot-tools.test.d.ts +0 -2
  405. package/dist/snapshot-tools.test.d.ts.map +0 -1
  406. package/dist/snapshot-tools.test.js +0 -856
  407. package/dist/snapshot-tools.test.js.map +0 -1
  408. package/dist/start-relay-server.d.ts.map +0 -1
  409. package/dist/start-relay-server.js.map +0 -1
  410. package/dist/styles-examples.d.ts.map +0 -1
  411. package/dist/styles-examples.js.map +0 -1
  412. package/dist/styles.d.ts.map +0 -1
  413. package/dist/styles.js.map +0 -1
  414. package/dist/tabwright-agent-skill.d.ts.map +0 -1
  415. package/dist/tabwright-agent-skill.js.map +0 -1
  416. package/dist/tabwright-agent-skill.test.d.ts +0 -2
  417. package/dist/tabwright-agent-skill.test.d.ts.map +0 -1
  418. package/dist/tabwright-agent-skill.test.js +0 -51
  419. package/dist/tabwright-agent-skill.test.js.map +0 -1
  420. package/dist/test-declarations.d.ts.map +0 -1
  421. package/dist/test-declarations.js.map +0 -1
  422. package/dist/test-utils.d.ts +0 -69
  423. package/dist/test-utils.d.ts.map +0 -1
  424. package/dist/test-utils.js +0 -448
  425. package/dist/test-utils.js.map +0 -1
  426. package/dist/test-utils.test.d.ts +0 -2
  427. package/dist/test-utils.test.d.ts.map +0 -1
  428. package/dist/test-utils.test.js +0 -55
  429. package/dist/test-utils.test.js.map +0 -1
  430. package/dist/utils.d.ts.map +0 -1
  431. package/dist/utils.js.map +0 -1
  432. package/dist/wait-for-page-load.d.ts.map +0 -1
  433. package/dist/wait-for-page-load.js.map +0 -1
  434. package/dist/workflow-capability.d.ts.map +0 -1
  435. package/dist/workflow-capability.js.map +0 -1
  436. package/dist/workflow-script-flow.test.d.ts +0 -2
  437. package/dist/workflow-script-flow.test.d.ts.map +0 -1
  438. package/dist/workflow-script-flow.test.js +0 -285
  439. package/dist/workflow-script-flow.test.js.map +0 -1
  440. package/src/__snapshots__/x.com.processed.html +0 -1003
  441. package/src/__snapshots__/x.com.processed.withStyles.html +0 -2720
  442. package/src/a11y-client.ts +0 -241
  443. package/src/agent-skills/conan-config/SKILL.md +0 -141
  444. package/src/agent-skills/conan-config/agents/openai.yaml +0 -4
  445. package/src/aria-snapshot.test.ts +0 -311
  446. package/src/aria-snapshot.ts +0 -1681
  447. package/src/aria-snapshot.unit.test.ts +0 -615
  448. package/src/aria-snapshots/github-interactive.txt +0 -193
  449. package/src/aria-snapshots/github-raw.txt +0 -309
  450. package/src/aria-snapshots/hackernews-interactive.txt +0 -477
  451. package/src/aria-snapshots/hackernews-raw.txt +0 -428
  452. package/src/aria-snapshots/prosemirror-interactive.txt +0 -66
  453. package/src/aria-snapshots/prosemirror-raw.txt +0 -145
  454. package/src/assets/aria-labels-hacker-news.png +0 -0
  455. package/src/assets/aria-labels-old-reddit.png +0 -0
  456. package/src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts +0 -5
  457. package/src/assets/cursors/screen-studio/pointer-macos-tahoe.svg +0 -18
  458. package/src/assets/x.com.html +0 -32946
  459. package/src/browser-config.ts +0 -179
  460. package/src/browser-install.ts +0 -284
  461. package/src/browser-launch.ts +0 -75
  462. package/src/builtin-capabilities.ts +0 -2765
  463. package/src/capability-agent-skill.ts +0 -305
  464. package/src/capability-auth-state.ts +0 -197
  465. package/src/capability-auth.ts +0 -249
  466. package/src/capability-cli-confirmation.test.ts +0 -90
  467. package/src/capability-options.test.ts +0 -89
  468. package/src/capability-options.ts +0 -57
  469. package/src/capability-package.test.ts +0 -204
  470. package/src/capability-package.ts +0 -555
  471. package/src/capability-registry.test.ts +0 -1905
  472. package/src/capability-registry.ts +0 -1234
  473. package/src/capability-runner.ts +0 -801
  474. package/src/capability-search-relevance.test.ts +0 -73
  475. package/src/capability-studio.test.ts +0 -45
  476. package/src/capability-studio.ts +0 -641
  477. package/src/cdp-log.test.ts +0 -131
  478. package/src/cdp-log.ts +0 -112
  479. package/src/cdp-relay.ts +0 -2944
  480. package/src/cdp-session.ts +0 -77
  481. package/src/cdp-types.ts +0 -158
  482. package/src/channel-owner-inspect.test.ts +0 -94
  483. package/src/chrome-discovery.test.ts +0 -50
  484. package/src/chrome-discovery.ts +0 -220
  485. package/src/clean-html.ts +0 -141
  486. package/src/cli-help.test.ts +0 -123
  487. package/src/cli.ts +0 -3181
  488. package/src/cloud-client.ts +0 -179
  489. package/src/create-logger.ts +0 -38
  490. package/src/debugger-examples-types.ts +0 -16
  491. package/src/debugger-examples.ts +0 -66
  492. package/src/debugger.ts +0 -708
  493. package/src/diff-utils.test.ts +0 -152
  494. package/src/diff-utils.ts +0 -70
  495. package/src/doctor.test.ts +0 -244
  496. package/src/doctor.ts +0 -321
  497. package/src/editor-examples.ts +0 -158
  498. package/src/editor.ts +0 -426
  499. package/src/env-compat.ts +0 -28
  500. package/src/executor.ts +0 -1824
  501. package/src/executor.unit.test.ts +0 -168
  502. package/src/extension-connection.test.ts +0 -855
  503. package/src/extension-icon-warning.test.ts +0 -45
  504. package/src/ghost-browser.ts +0 -149
  505. package/src/ghost-cursor-client.ts +0 -494
  506. package/src/ghost-cursor-controller.ts +0 -129
  507. package/src/ghost-cursor.ts +0 -123
  508. package/src/htmlrewrite.test.ts +0 -8008
  509. package/src/htmlrewrite.ts +0 -390
  510. package/src/index.ts +0 -53
  511. package/src/kill-port.ts +0 -219
  512. package/src/kitty-graphics.test.ts +0 -139
  513. package/src/kitty-graphics.ts +0 -79
  514. package/src/locator-selector.test.ts +0 -119
  515. package/src/mcp-client.ts +0 -78
  516. package/src/mcp.ts +0 -629
  517. package/src/on-mouse-action.test.ts +0 -226
  518. package/src/options-page.test.ts +0 -839
  519. package/src/package-paths.ts +0 -38
  520. package/src/page-markdown.ts +0 -240
  521. package/src/performance-examples.ts +0 -186
  522. package/src/playwright-import.ts +0 -59
  523. package/src/popup-relocation.test.ts +0 -163
  524. package/src/product-paths.test.ts +0 -47
  525. package/src/product-paths.ts +0 -28
  526. package/src/protocol.test.ts +0 -52
  527. package/src/protocol.ts +0 -351
  528. package/src/react-source.ts +0 -379
  529. package/src/relay-client.test.ts +0 -375
  530. package/src/relay-client.ts +0 -572
  531. package/src/relay-core.test.ts +0 -1603
  532. package/src/relay-navigation.test.ts +0 -790
  533. package/src/relay-session.test.ts +0 -1375
  534. package/src/relay-state.test.ts +0 -729
  535. package/src/relay-state.ts +0 -561
  536. package/src/replay-ai-index.test.ts +0 -278
  537. package/src/replay-ai-index.ts +0 -872
  538. package/src/replay-cli.test.ts +0 -325
  539. package/src/replay-eval.ts +0 -1039
  540. package/src/replay-handoff.test.ts +0 -163
  541. package/src/replay-handoff.ts +0 -109
  542. package/src/replay-workflow-compiler.test.ts +0 -250
  543. package/src/replay-workflow-compiler.ts +0 -700
  544. package/src/resource.md +0 -409
  545. package/src/rrweb-recording-relay.test.ts +0 -99
  546. package/src/rrweb-recording-relay.ts +0 -400
  547. package/src/rrweb-recording.ts +0 -322
  548. package/src/scoped-fs.test.ts +0 -80
  549. package/src/scoped-fs.ts +0 -420
  550. package/src/skill-stub.test.ts +0 -24
  551. package/src/snapshot-tools.test.ts +0 -1017
  552. package/src/snapshots/hacker-news-accessibility-full.md +0 -81
  553. package/src/snapshots/hacker-news-accessibility-interactive.md +0 -43
  554. package/src/snapshots/page-markdown-output.txt +0 -16
  555. package/src/snapshots/shadcn-ui-accessibility-full.md +0 -201
  556. package/src/snapshots/shadcn-ui-accessibility-interactive.md +0 -109
  557. package/src/start-relay-server.ts +0 -69
  558. package/src/styles-examples.ts +0 -84
  559. package/src/styles.ts +0 -343
  560. package/src/tabwright-agent-skill.test.ts +0 -61
  561. package/src/tabwright-agent-skill.ts +0 -105
  562. package/src/test-declarations.ts +0 -13
  563. package/src/test-utils.test.ts +0 -70
  564. package/src/test-utils.ts +0 -566
  565. package/src/utils.ts +0 -78
  566. package/src/wait-for-page-load.ts +0 -198
  567. package/src/workflow-capability.ts +0 -417
  568. package/src/workflow-script-flow.test.ts +0 -388
@@ -1,729 +0,0 @@
1
- /**
2
- * Unit tests for relay state transitions.
3
- * Data-in / data-out transitions for the unified extension map.
4
- */
5
- import { describe, test, expect, beforeAll, afterAll } from 'vitest'
6
- import type { WSContext } from 'hono/ws'
7
- import type WebSocket from 'ws'
8
- import type { Protocol } from './cdp-types.js'
9
- import * as relayState from './relay-state.js'
10
- import { CURRENT_EXTENSION_FEATURES } from './protocol.js'
11
-
12
- // ---------------------------------------------------------------------------
13
- // Helpers
14
- // ---------------------------------------------------------------------------
15
-
16
- function emptyState(): relayState.RelayState {
17
- return {
18
- extensions: new Map(),
19
- playwrightClients: new Map(),
20
- }
21
- }
22
-
23
- function fakeWs(): WSContext<WebSocket> {
24
- return {} as WSContext<WebSocket>
25
- }
26
-
27
- function makeTargetInfo(overrides: Partial<Protocol.Target.TargetInfo> = {}): Protocol.Target.TargetInfo {
28
- return {
29
- targetId: 'target-1',
30
- type: 'page',
31
- title: 'Test Page',
32
- url: 'https://example.com',
33
- attached: true,
34
- canAccessOpener: false,
35
- ...overrides,
36
- }
37
- }
38
-
39
- function stateWithExtension(
40
- extensionId = 'ext-1',
41
- info: relayState.ExtensionInfo = { browser: 'Chrome' },
42
- stableKey = 'profile:chrome-1',
43
- ): relayState.RelayState {
44
- return relayState.addExtension(emptyState(), { id: extensionId, info, stableKey, ws: fakeWs() })
45
- }
46
-
47
- // ---------------------------------------------------------------------------
48
- // createRelayStore
49
- // ---------------------------------------------------------------------------
50
-
51
- describe('createRelayStore', () => {
52
- test('creates store with empty maps', () => {
53
- const store = relayState.createRelayStore()
54
- const state = store.getState()
55
- expect(state.extensions.size).toBe(0)
56
- expect(state.playwrightClients.size).toBe(0)
57
- })
58
- })
59
-
60
- describe('buildStableExtensionKey', () => {
61
- test('uses install id before account identity so same-account profiles stay separate', () => {
62
- const profiles = ['profile-a-install', 'profile-b-install'].map((installId) => {
63
- return relayState.buildStableExtensionKey(
64
- {
65
- browser: 'Chrome',
66
- email: 'tommy@example.com',
67
- id: 'same-google-account-id',
68
- installId,
69
- },
70
- 'connection-fallback',
71
- )
72
- })
73
-
74
- expect(profiles).toMatchInlineSnapshot(`
75
- [
76
- "install:Chrome:profile-a-install",
77
- "install:Chrome:profile-b-install",
78
- ]
79
- `)
80
- })
81
-
82
- test('falls back to account identity for older extensions without install ids', () => {
83
- const key = relayState.buildStableExtensionKey(
84
- { browser: 'Chrome', email: 'tommy@example.com', id: 'google-account-id' },
85
- 'connection-fallback',
86
- )
87
-
88
- expect(key).toMatchInlineSnapshot(`"profile:google-account-id"`)
89
- })
90
- })
91
-
92
- describe('getExtensionNegotiationStatus', () => {
93
- test('distinguishes legacy, limited, and ready feature handshakes', () => {
94
- expect(relayState.getExtensionNegotiationStatus({ browser: 'Chrome' })).toEqual({
95
- connectionHealth: 'legacy',
96
- missingFeatures: [],
97
- })
98
- expect(
99
- relayState.getExtensionNegotiationStatus({ browser: 'Chrome', features: ['heartbeat-v1'] }),
100
- ).toMatchObject({
101
- connectionHealth: 'limited',
102
- missingFeatures: expect.arrayContaining(['rrweb-recording-v1']),
103
- })
104
- expect(
105
- relayState.getExtensionNegotiationStatus({
106
- browser: 'Chrome',
107
- features: [...CURRENT_EXTENSION_FEATURES],
108
- }),
109
- ).toEqual({ connectionHealth: 'ready', missingFeatures: [] })
110
- })
111
- })
112
-
113
- // ---------------------------------------------------------------------------
114
- // addExtension / removeExtension
115
- // ---------------------------------------------------------------------------
116
-
117
- describe('addExtension', () => {
118
- test('adds extension to empty state', () => {
119
- const before = emptyState()
120
- const after = relayState.addExtension(before, {
121
- id: 'ext-1',
122
- info: { browser: 'Chrome', protocolVersion: 1, features: ['heartbeat-v1'] },
123
- stableKey: 'profile:chrome-1',
124
- ws: fakeWs(),
125
- })
126
-
127
- expect(after.extensions.size).toBe(1)
128
- const ext = after.extensions.get('ext-1')!
129
- expect(ext.stableKey).toBe('profile:chrome-1')
130
- expect(ext.connectedTargets.size).toBe(0)
131
- expect(ext.ws).toBeTruthy()
132
- expect(ext.messageId).toBe(0)
133
- expect(ext.pendingRequests.size).toBe(0)
134
- expect(ext.pingInterval).toBeNull()
135
- expect(ext.lastPongAt).toBeNull()
136
- expect(ext.info).toMatchObject({ protocolVersion: 1, features: ['heartbeat-v1'] })
137
- // Original unchanged (immutable)
138
- expect(before.extensions.size).toBe(0)
139
- })
140
-
141
- test('adding extension with same stableKey keeps old entry (removed on socket close)', () => {
142
- const s1 = relayState.addExtension(emptyState(), {
143
- id: 'ext-old',
144
- info: { browser: 'Chrome' },
145
- stableKey: 'profile:chrome-1',
146
- ws: fakeWs(),
147
- })
148
- const s2 = relayState.addExtension(s1, {
149
- id: 'ext-new',
150
- info: { browser: 'Chrome', email: 'test@example.com' },
151
- stableKey: 'profile:chrome-1',
152
- ws: fakeWs(),
153
- })
154
-
155
- // Both coexist — old stays routable until its socket closes
156
- expect(s2.extensions.size).toBe(2)
157
- expect(s2.extensions.has('ext-old')).toBe(true)
158
- expect(s2.extensions.has('ext-new')).toBe(true)
159
- // findExtensionByStableKey returns newest
160
- expect(relayState.findExtensionByStableKey(s2, 'profile:chrome-1')?.id).toBe('ext-new')
161
- // Original unchanged
162
- expect(s1.extensions.size).toBe(1)
163
- })
164
-
165
- test('allows multiple extensions with different stableKeys', () => {
166
- let state = emptyState()
167
- state = relayState.addExtension(state, { id: 'ext-1', info: { browser: 'Chrome' }, stableKey: 'profile:a', ws: fakeWs() })
168
- state = relayState.addExtension(state, { id: 'ext-2', info: { browser: 'Firefox' }, stableKey: 'profile:b', ws: fakeWs() })
169
-
170
- expect(state.extensions.size).toBe(2)
171
- })
172
- })
173
-
174
- describe('removeExtension', () => {
175
- test('removes existing extension', () => {
176
- const before = stateWithExtension('ext-1')
177
- const after = relayState.removeExtension(before, { extensionId: 'ext-1' })
178
-
179
- expect(after.extensions.size).toBe(0)
180
- // Original unchanged
181
- expect(before.extensions.size).toBe(1)
182
- })
183
-
184
- test('no-op for nonexistent extension', () => {
185
- const before = stateWithExtension('ext-1')
186
- const after = relayState.removeExtension(before, { extensionId: 'ext-999' })
187
-
188
- expect(after).toBe(before) // Same reference — no allocation
189
- })
190
-
191
- test('also removes playwright clients bound to the extension', () => {
192
- let state = stateWithExtension('ext-1')
193
- state = relayState.addPlaywrightClient(state, { id: 'c1', extensionId: 'ext-1', ws: fakeWs() })
194
- state = relayState.addPlaywrightClient(state, { id: 'c2', extensionId: 'ext-1', ws: fakeWs() })
195
- state = relayState.addPlaywrightClient(state, { id: 'c3', extensionId: 'ext-2', ws: fakeWs() })
196
-
197
- const after = relayState.removeExtension(state, { extensionId: 'ext-1' })
198
-
199
- expect(after.extensions.size).toBe(0)
200
- expect(after.playwrightClients.size).toBe(1)
201
- expect(after.playwrightClients.has('c3')).toBe(true)
202
- })
203
- })
204
-
205
- // ---------------------------------------------------------------------------
206
- // addPlaywrightClient / removePlaywrightClient
207
- // ---------------------------------------------------------------------------
208
-
209
- describe('addPlaywrightClient', () => {
210
- test('adds client with ws handle', () => {
211
- const before = emptyState()
212
- const ws = fakeWs()
213
- const after = relayState.addPlaywrightClient(before, { id: 'client-1', extensionId: 'ext-1', ws })
214
-
215
- expect(after.playwrightClients.size).toBe(1)
216
- const client = after.playwrightClients.get('client-1')!
217
- expect(client.extensionId).toBe('ext-1')
218
- expect(client.ws).toBe(ws)
219
- expect(before.playwrightClients.size).toBe(0)
220
- })
221
- })
222
-
223
- describe('removePlaywrightClient', () => {
224
- test('removes client', () => {
225
- const before = relayState.addPlaywrightClient(emptyState(), { id: 'c1', extensionId: null, ws: fakeWs() })
226
- const after = relayState.removePlaywrightClient(before, { clientId: 'c1' })
227
-
228
- expect(after.playwrightClients.size).toBe(0)
229
- expect(before.playwrightClients.size).toBe(1)
230
- })
231
-
232
- test('no-op for nonexistent client', () => {
233
- const before = emptyState()
234
- const after = relayState.removePlaywrightClient(before, { clientId: 'nope' })
235
-
236
- expect(after).toBe(before)
237
- })
238
- })
239
-
240
- describe('extension heartbeat', () => {
241
- test('keeps a responsive extension connected', () => {
242
- let state = stateWithExtension('ext-1')
243
- state = relayState.updateExtensionIO(state, { extensionId: 'ext-1', lastPongAt: 10_000 })
244
-
245
- expect(
246
- relayState.hasExtensionHeartbeatExpired({
247
- lastPongAt: state.extensions.get('ext-1')?.lastPongAt ?? null,
248
- now: 24_999,
249
- }),
250
- ).toBe(false)
251
- })
252
-
253
- test('expires an extension after a pong becomes stale', () => {
254
- expect(
255
- relayState.hasExtensionHeartbeatExpired({
256
- lastPongAt: 10_000,
257
- now: 25_001,
258
- }),
259
- ).toBe(true)
260
- })
261
-
262
- test('keeps legacy extensions that have never sent pong connected', () => {
263
- expect(
264
- relayState.hasExtensionHeartbeatExpired({
265
- lastPongAt: null,
266
- now: Number.MAX_SAFE_INTEGER,
267
- }),
268
- ).toBe(false)
269
- })
270
- })
271
-
272
- describe('extension I/O fields', () => {
273
- test('adds and removes pending extension requests', () => {
274
- let state = stateWithExtension('ext-1')
275
-
276
- const pending = {
277
- resolve: (_result: unknown) => {
278
- return
279
- },
280
- reject: (_error: Error) => {
281
- return
282
- },
283
- }
284
-
285
- state = relayState.addExtensionPendingRequest(state, {
286
- extensionId: 'ext-1',
287
- requestId: 7,
288
- pendingRequest: pending,
289
- })
290
- expect(state.extensions.get('ext-1')?.pendingRequests.get(7)).toBe(pending)
291
-
292
- state = relayState.removeExtensionPendingRequest(state, { extensionId: 'ext-1', requestId: 7 })
293
- expect(state.extensions.get('ext-1')?.pendingRequests.has(7)).toBe(false)
294
- })
295
-
296
- test('updateExtensionIO updates ws, pingInterval, and lastPongAt', () => {
297
- let state = stateWithExtension('ext-1')
298
- expect(state.extensions.get('ext-1')?.pingInterval).toBeNull()
299
- expect(state.extensions.get('ext-1')?.lastPongAt).toBeNull()
300
-
301
- const interval = setInterval(() => {}, 99999)
302
- try {
303
- state = relayState.updateExtensionIO(state, { extensionId: 'ext-1', pingInterval: interval })
304
- expect(state.extensions.get('ext-1')?.pingInterval).toBe(interval)
305
-
306
- state = relayState.updateExtensionIO(state, { extensionId: 'ext-1', lastPongAt: 1234 })
307
- expect(state.extensions.get('ext-1')?.lastPongAt).toBe(1234)
308
-
309
- state = relayState.updateExtensionIO(state, { extensionId: 'ext-1', pingInterval: null })
310
- expect(state.extensions.get('ext-1')?.pingInterval).toBeNull()
311
- } finally {
312
- clearInterval(interval)
313
- }
314
- })
315
-
316
- test('playwright client ws handle is co-located with state', () => {
317
- let state = emptyState()
318
- const ws = fakeWs()
319
- state = relayState.addPlaywrightClient(state, { id: 'c1', extensionId: null, ws })
320
- expect(state.playwrightClients.get('c1')?.ws).toBe(ws)
321
-
322
- state = relayState.removePlaywrightClient(state, { clientId: 'c1' })
323
- expect(state.playwrightClients.size).toBe(0)
324
- })
325
- })
326
-
327
- describe('rebindClientsToExtension', () => {
328
- test('rebinds all clients from old extension to successor extension', () => {
329
- let state = stateWithExtension('ext-old')
330
- state = relayState.addExtension(state, {
331
- id: 'ext-new',
332
- info: { browser: 'Chrome' },
333
- stableKey: 'profile:chrome-1',
334
- ws: fakeWs(),
335
- })
336
- state = relayState.addPlaywrightClient(state, { id: 'c1', extensionId: 'ext-old', ws: fakeWs() })
337
- state = relayState.addPlaywrightClient(state, { id: 'c2', extensionId: 'ext-old', ws: fakeWs() })
338
- state = relayState.addPlaywrightClient(state, { id: 'c3', extensionId: 'ext-new', ws: fakeWs() })
339
-
340
- const after = relayState.rebindClientsToExtension(state, {
341
- fromExtensionId: 'ext-old',
342
- toExtensionId: 'ext-new',
343
- })
344
-
345
- expect(after.playwrightClients.get('c1')?.extensionId).toBe('ext-new')
346
- expect(after.playwrightClients.get('c2')?.extensionId).toBe('ext-new')
347
- expect(after.playwrightClients.get('c3')?.extensionId).toBe('ext-new')
348
- })
349
- })
350
-
351
- // ---------------------------------------------------------------------------
352
- // addTarget / removeTarget / removeTargetByCrash
353
- // ---------------------------------------------------------------------------
354
-
355
- describe('addTarget', () => {
356
- test('adds target to extension', () => {
357
- const before = stateWithExtension('ext-1')
358
- const targetInfo = makeTargetInfo()
359
- const after = relayState.addTarget(before, {
360
- extensionId: 'ext-1',
361
- sessionId: 'pw-tab-1',
362
- targetId: 'target-1',
363
- targetInfo,
364
- })
365
-
366
- const ext = after.extensions.get('ext-1')!
367
- expect(ext.connectedTargets.size).toBe(1)
368
- expect(ext.connectedTargets.get('pw-tab-1')?.targetId).toBe('target-1')
369
- // Original unchanged
370
- expect(before.extensions.get('ext-1')!.connectedTargets.size).toBe(0)
371
- })
372
-
373
- test('no-op if extension does not exist', () => {
374
- const before = emptyState()
375
- const after = relayState.addTarget(before, {
376
- extensionId: 'ext-nope',
377
- sessionId: 'pw-tab-1',
378
- targetId: 'target-1',
379
- targetInfo: makeTargetInfo(),
380
- })
381
-
382
- expect(after).toBe(before)
383
- })
384
-
385
- test('preserves existing frameIds on update', () => {
386
- let state = stateWithExtension('ext-1')
387
- state = relayState.addTarget(state, {
388
- extensionId: 'ext-1',
389
- sessionId: 'pw-tab-1',
390
- targetId: 'target-1',
391
- targetInfo: makeTargetInfo(),
392
- existingFrameIds: new Set(['frame-A']),
393
- })
394
-
395
- // Update the same target with new targetInfo but no explicit frameIds
396
- state = relayState.addTarget(state, {
397
- extensionId: 'ext-1',
398
- sessionId: 'pw-tab-1',
399
- targetId: 'target-1',
400
- targetInfo: makeTargetInfo({ url: 'https://updated.com' }),
401
- })
402
-
403
- const target = state.extensions.get('ext-1')!.connectedTargets.get('pw-tab-1')!
404
- expect(target.targetInfo.url).toBe('https://updated.com')
405
- expect(target.frameIds.has('frame-A')).toBe(true)
406
- })
407
- })
408
-
409
- describe('removeTarget', () => {
410
- test('removes target by sessionId', () => {
411
- let state = stateWithExtension('ext-1')
412
- state = relayState.addTarget(state, {
413
- extensionId: 'ext-1',
414
- sessionId: 'pw-tab-1',
415
- targetId: 'target-1',
416
- targetInfo: makeTargetInfo(),
417
- })
418
- const after = relayState.removeTarget(state, { extensionId: 'ext-1', sessionId: 'pw-tab-1' })
419
-
420
- expect(after.extensions.get('ext-1')!.connectedTargets.size).toBe(0)
421
- })
422
-
423
- test('no-op if target does not exist', () => {
424
- const before = stateWithExtension('ext-1')
425
- const after = relayState.removeTarget(before, { extensionId: 'ext-1', sessionId: 'pw-tab-nope' })
426
-
427
- expect(after).toBe(before)
428
- })
429
- })
430
-
431
- describe('removeTargetByCrash', () => {
432
- test('removes target by targetId', () => {
433
- let state = stateWithExtension('ext-1')
434
- state = relayState.addTarget(state, {
435
- extensionId: 'ext-1',
436
- sessionId: 'pw-tab-1',
437
- targetId: 'target-1',
438
- targetInfo: makeTargetInfo(),
439
- })
440
- const after = relayState.removeTargetByCrash(state, { extensionId: 'ext-1', targetId: 'target-1' })
441
-
442
- expect(after.extensions.get('ext-1')!.connectedTargets.size).toBe(0)
443
- })
444
-
445
- test('no-op if targetId not found', () => {
446
- const before = stateWithExtension('ext-1')
447
- const after = relayState.removeTargetByCrash(before, { extensionId: 'ext-1', targetId: 'nope' })
448
-
449
- expect(after).toBe(before)
450
- })
451
- })
452
-
453
- // ---------------------------------------------------------------------------
454
- // updateTargetInfo
455
- // ---------------------------------------------------------------------------
456
-
457
- describe('updateTargetInfo', () => {
458
- test('updates targetInfo matched by targetId', () => {
459
- let state = stateWithExtension('ext-1')
460
- state = relayState.addTarget(state, {
461
- extensionId: 'ext-1',
462
- sessionId: 'pw-tab-1',
463
- targetId: 'target-1',
464
- targetInfo: makeTargetInfo({ title: 'Old Title' }),
465
- })
466
-
467
- const newInfo = makeTargetInfo({ title: 'New Title' })
468
- const after = relayState.updateTargetInfo(state, { extensionId: 'ext-1', targetInfo: newInfo })
469
-
470
- expect(after.extensions.get('ext-1')!.connectedTargets.get('pw-tab-1')!.targetInfo.title).toBe('New Title')
471
- // Original unchanged
472
- expect(state.extensions.get('ext-1')!.connectedTargets.get('pw-tab-1')!.targetInfo.title).toBe('Old Title')
473
- })
474
- })
475
-
476
- // ---------------------------------------------------------------------------
477
- // addFrameId / removeFrameId
478
- // ---------------------------------------------------------------------------
479
-
480
- describe('addFrameId', () => {
481
- test('adds frameId to target', () => {
482
- let state = stateWithExtension('ext-1')
483
- state = relayState.addTarget(state, {
484
- extensionId: 'ext-1',
485
- sessionId: 'pw-tab-1',
486
- targetId: 'target-1',
487
- targetInfo: makeTargetInfo(),
488
- })
489
- const after = relayState.addFrameId(state, { extensionId: 'ext-1', sessionId: 'pw-tab-1', frameId: 'frame-1' })
490
-
491
- expect(after.extensions.get('ext-1')!.connectedTargets.get('pw-tab-1')!.frameIds.has('frame-1')).toBe(true)
492
- })
493
-
494
- test('no-op if frameId already present', () => {
495
- let state = stateWithExtension('ext-1')
496
- state = relayState.addTarget(state, {
497
- extensionId: 'ext-1',
498
- sessionId: 'pw-tab-1',
499
- targetId: 'target-1',
500
- targetInfo: makeTargetInfo(),
501
- })
502
- state = relayState.addFrameId(state, { extensionId: 'ext-1', sessionId: 'pw-tab-1', frameId: 'frame-1' })
503
- const after = relayState.addFrameId(state, { extensionId: 'ext-1', sessionId: 'pw-tab-1', frameId: 'frame-1' })
504
-
505
- expect(after).toBe(state) // Same reference
506
- })
507
- })
508
-
509
- describe('removeFrameId', () => {
510
- test('removes frameId from owning target', () => {
511
- let state = stateWithExtension('ext-1')
512
- state = relayState.addTarget(state, {
513
- extensionId: 'ext-1',
514
- sessionId: 'pw-tab-1',
515
- targetId: 'target-1',
516
- targetInfo: makeTargetInfo(),
517
- })
518
- state = relayState.addFrameId(state, { extensionId: 'ext-1', sessionId: 'pw-tab-1', frameId: 'frame-1' })
519
- const after = relayState.removeFrameId(state, { extensionId: 'ext-1', frameId: 'frame-1' })
520
-
521
- expect(after.extensions.get('ext-1')!.connectedTargets.get('pw-tab-1')!.frameIds.has('frame-1')).toBe(false)
522
- })
523
-
524
- test('no-op if frameId not found', () => {
525
- const before = stateWithExtension('ext-1')
526
- const after = relayState.removeFrameId(before, { extensionId: 'ext-1', frameId: 'nope' })
527
-
528
- expect(after).toBe(before)
529
- })
530
- })
531
-
532
- // ---------------------------------------------------------------------------
533
- // updateTargetUrl
534
- // ---------------------------------------------------------------------------
535
-
536
- describe('updateTargetUrl', () => {
537
- test('frameNavigated on top-level frame updates URL and title', () => {
538
- let state = stateWithExtension('ext-1')
539
- state = relayState.addTarget(state, {
540
- extensionId: 'ext-1',
541
- sessionId: 'pw-tab-1',
542
- targetId: 'target-1',
543
- targetInfo: makeTargetInfo({ url: 'https://old.com', title: 'Old' }),
544
- })
545
-
546
- const after = relayState.updateTargetUrl(state, {
547
- extensionId: 'ext-1',
548
- sessionId: 'pw-tab-1',
549
- url: 'https://new.com',
550
- title: 'New Page',
551
- })
552
-
553
- const target = after.extensions.get('ext-1')!.connectedTargets.get('pw-tab-1')!
554
- expect(target.targetInfo.url).toBe('https://new.com')
555
- expect(target.targetInfo.title).toBe('New Page')
556
- })
557
-
558
- test('navigatedWithinDocument updates URL only (no title)', () => {
559
- let state = stateWithExtension('ext-1')
560
- state = relayState.addTarget(state, {
561
- extensionId: 'ext-1',
562
- sessionId: 'pw-tab-1',
563
- targetId: 'target-1',
564
- targetInfo: makeTargetInfo({ url: 'https://example.com', title: 'Keep This' }),
565
- })
566
-
567
- const after = relayState.updateTargetUrl(state, {
568
- extensionId: 'ext-1',
569
- sessionId: 'pw-tab-1',
570
- url: 'https://example.com/new-path',
571
- })
572
-
573
- const target = after.extensions.get('ext-1')!.connectedTargets.get('pw-tab-1')!
574
- expect(target.targetInfo.url).toBe('https://example.com/new-path')
575
- expect(target.targetInfo.title).toBe('Keep This')
576
- })
577
-
578
- test('no-op if target does not exist', () => {
579
- const before = stateWithExtension('ext-1')
580
- const after = relayState.updateTargetUrl(before, {
581
- extensionId: 'ext-1',
582
- sessionId: 'pw-tab-nope',
583
- url: 'https://example.com',
584
- })
585
-
586
- expect(after).toBe(before)
587
- })
588
- })
589
-
590
-
591
-
592
- // ---------------------------------------------------------------------------
593
- // Derivation helpers
594
- // ---------------------------------------------------------------------------
595
-
596
- describe('findExtensionByStableKey', () => {
597
- test('finds extension by stableKey', () => {
598
- const state = stateWithExtension('ext-1', { browser: 'Chrome' }, 'profile:chrome-1')
599
-
600
- const found = relayState.findExtensionByStableKey(state, 'profile:chrome-1')
601
- expect(found?.id).toBe('ext-1')
602
- })
603
-
604
- test('returns undefined if not found', () => {
605
- const state = emptyState()
606
- expect(relayState.findExtensionByStableKey(state, 'profile:nope')).toBeUndefined()
607
- })
608
- })
609
-
610
- describe('findExtensionIdByCdpSession', () => {
611
- test('finds extension owning a CDP sessionId', () => {
612
- let state = stateWithExtension('ext-1')
613
- state = relayState.addTarget(state, {
614
- extensionId: 'ext-1',
615
- sessionId: 'pw-tab-1',
616
- targetId: 'target-1',
617
- targetInfo: makeTargetInfo(),
618
- })
619
-
620
- expect(relayState.findExtensionIdByCdpSession(state, 'pw-tab-1')).toBe('ext-1')
621
- })
622
-
623
- test('returns null if session not found', () => {
624
- const state = stateWithExtension('ext-1')
625
- expect(relayState.findExtensionIdByCdpSession(state, 'pw-tab-nope')).toBeNull()
626
- })
627
- })
628
-
629
- // ---------------------------------------------------------------------------
630
- // Zustand store integration
631
- // ---------------------------------------------------------------------------
632
-
633
- describe('store.setState with transitions', () => {
634
- test('setState updates store atomically', () => {
635
- const store = relayState.createRelayStore()
636
-
637
- store.setState((s) => {
638
- return relayState.addExtension(s, { id: 'ext-1', info: { browser: 'Chrome' }, stableKey: 'profile:1', ws: fakeWs() })
639
- })
640
-
641
- expect(store.getState().extensions.size).toBe(1)
642
- })
643
-
644
- test('chained transitions compose correctly', () => {
645
- const store = relayState.createRelayStore()
646
-
647
- store.setState((s) => {
648
- let next = relayState.addExtension(s, { id: 'ext-1', info: {}, stableKey: 'k1', ws: fakeWs() })
649
- next = relayState.addTarget(next, {
650
- extensionId: 'ext-1',
651
- sessionId: 'pw-tab-1',
652
- targetId: 'target-1',
653
- targetInfo: makeTargetInfo(),
654
- })
655
- next = relayState.addPlaywrightClient(next, { id: 'c1', extensionId: 'ext-1', ws: fakeWs() })
656
- return next
657
- })
658
-
659
- const state = store.getState()
660
- expect(state.extensions.get('ext-1')!.connectedTargets.size).toBe(1)
661
- expect(state.playwrightClients.size).toBe(1)
662
- })
663
- })
664
-
665
- // ---------------------------------------------------------------------------
666
- // DNS rebinding protection — Host header validation
667
- // Node.js fetch/undici treats Host as a forbidden header, so we use the raw
668
- // http module to send requests with arbitrary Host values.
669
- // ---------------------------------------------------------------------------
670
-
671
- import http from 'node:http'
672
-
673
- function httpGet({ port, path, host }: { port: number; path: string; host: string }): Promise<{ status: number; body: string }> {
674
- return new Promise((resolve, reject) => {
675
- const req = http.request({ hostname: '127.0.0.1', port, path, method: 'GET', headers: { Host: host } }, (res) => {
676
- let body = ''
677
- res.on('data', (chunk: Buffer) => {
678
- body += chunk.toString()
679
- })
680
- res.on('end', () => {
681
- resolve({ status: res.statusCode!, body })
682
- })
683
- })
684
- req.on('error', reject)
685
- req.end()
686
- })
687
- }
688
-
689
- describe('Host header validation (DNS rebinding protection)', () => {
690
- let server: { close(): void } | null = null
691
- const TEST_PORT = 19996
692
-
693
- beforeAll(async () => {
694
- const { startTabwrightCDPRelayServer } = await import('./cdp-relay.js')
695
- server = await startTabwrightCDPRelayServer({ port: TEST_PORT })
696
- })
697
-
698
- afterAll(async () => {
699
- server?.close()
700
- server = null
701
- })
702
-
703
- test('rejects requests with non-localhost Host header', async () => {
704
- // DNS rebinding: evil.com resolves to 127.0.0.1
705
- const res = await httpGet({ port: TEST_PORT, path: '/', host: 'evil.com' })
706
- expect(res.status).toBe(403)
707
- expect(res.body).toContain('Invalid Host header')
708
-
709
- // With port suffix
710
- const res2 = await httpGet({ port: TEST_PORT, path: '/version', host: 'evil.com:19996' })
711
- expect(res2.status).toBe(403)
712
- })
713
-
714
- test('allows requests with localhost Host headers', async () => {
715
- const localhostVariants = [
716
- `localhost:${TEST_PORT}`,
717
- `127.0.0.1:${TEST_PORT}`,
718
- `[::1]:${TEST_PORT}`,
719
- 'localhost',
720
- '127.0.0.1',
721
- 'LOCALHOST',
722
- ]
723
-
724
- for (const hostValue of localhostVariants) {
725
- const res = await httpGet({ port: TEST_PORT, path: '/', host: hostValue })
726
- expect(res.status, `Expected 200 for Host: ${hostValue}`).toBe(200)
727
- }
728
- })
729
- })