tabwright 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (562) hide show
  1. package/dist/aria-snapshot.d.ts +0 -1
  2. package/dist/aria-snapshot.js +9 -1
  3. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts +0 -1
  4. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js +0 -1
  5. package/dist/bippy.js +1 -1
  6. package/dist/browser-config.d.ts +0 -1
  7. package/dist/browser-config.js +0 -1
  8. package/dist/browser-install.d.ts +0 -1
  9. package/dist/browser-install.js +0 -1
  10. package/dist/browser-launch.d.ts +0 -1
  11. package/dist/browser-launch.js +0 -1
  12. package/dist/builtin-capabilities.d.ts +0 -1
  13. package/dist/builtin-capabilities.js +0 -1
  14. package/dist/capability-agent-skill.d.ts +0 -1
  15. package/dist/capability-agent-skill.js +0 -1
  16. package/dist/capability-auth-state.d.ts +0 -1
  17. package/dist/capability-auth-state.js +0 -1
  18. package/dist/capability-auth.d.ts +0 -1
  19. package/dist/capability-auth.js +0 -1
  20. package/dist/capability-options.d.ts +0 -1
  21. package/dist/capability-options.js +0 -1
  22. package/dist/capability-package.d.ts +0 -1
  23. package/dist/capability-package.js +0 -1
  24. package/dist/capability-registry.d.ts +0 -1
  25. package/dist/capability-registry.js +0 -1
  26. package/dist/capability-runner.d.ts +0 -1
  27. package/dist/capability-runner.js +0 -1
  28. package/dist/capability-studio.d.ts +0 -1
  29. package/dist/capability-studio.js +0 -1
  30. package/dist/cdp-log.d.ts +0 -1
  31. package/dist/cdp-log.js +0 -1
  32. package/dist/cdp-relay.d.ts +0 -1
  33. package/dist/cdp-relay.js +0 -1
  34. package/dist/cdp-session.d.ts +0 -1
  35. package/dist/cdp-session.js +0 -1
  36. package/dist/cdp-types.d.ts +0 -1
  37. package/dist/cdp-types.js +0 -1
  38. package/dist/chrome-discovery.d.ts +0 -1
  39. package/dist/chrome-discovery.js +0 -1
  40. package/dist/clean-html.d.ts +0 -1
  41. package/dist/clean-html.js +0 -1
  42. package/dist/cli.d.ts +0 -1
  43. package/dist/cli.js +9 -2
  44. package/dist/cloud-client.d.ts +0 -1
  45. package/dist/cloud-client.js +0 -1
  46. package/dist/create-logger.d.ts +0 -1
  47. package/dist/create-logger.js +0 -1
  48. package/dist/debugger-examples-types.d.ts +0 -1
  49. package/dist/debugger-examples-types.js +0 -1
  50. package/dist/debugger-examples.d.ts +0 -1
  51. package/dist/debugger-examples.js +0 -1
  52. package/dist/debugger.d.ts +0 -1
  53. package/dist/debugger.js +0 -1
  54. package/dist/diff-utils.d.ts +0 -1
  55. package/dist/diff-utils.js +0 -1
  56. package/dist/doctor.d.ts +0 -1
  57. package/dist/doctor.js +5 -7
  58. package/dist/editor-examples.d.ts +0 -1
  59. package/dist/editor-examples.js +0 -1
  60. package/dist/editor.d.ts +0 -1
  61. package/dist/editor.js +0 -1
  62. package/dist/env-compat.d.ts +0 -1
  63. package/dist/env-compat.js +0 -1
  64. package/dist/executor.d.ts +0 -1
  65. package/dist/executor.js +1 -2
  66. package/dist/extension/background.js +7 -3
  67. package/dist/extension/manifest.json +1 -1
  68. package/dist/ghost-browser.d.ts +0 -1
  69. package/dist/ghost-browser.js +0 -1
  70. package/dist/ghost-cursor-controller.d.ts +0 -1
  71. package/dist/ghost-cursor-controller.js +0 -1
  72. package/dist/ghost-cursor.d.ts +0 -1
  73. package/dist/ghost-cursor.js +0 -1
  74. package/dist/htmlrewrite.d.ts +0 -1
  75. package/dist/htmlrewrite.js +0 -1
  76. package/dist/index.d.ts +0 -1
  77. package/dist/index.js +0 -1
  78. package/dist/kill-port.d.ts +0 -1
  79. package/dist/kill-port.js +0 -1
  80. package/dist/kitty-graphics.d.ts +0 -1
  81. package/dist/kitty-graphics.js +0 -1
  82. package/dist/mcp.d.ts +0 -1
  83. package/dist/mcp.js +0 -1
  84. package/dist/package-paths.d.ts +0 -1
  85. package/dist/package-paths.js +0 -1
  86. package/dist/page-markdown.d.ts +0 -1
  87. package/dist/page-markdown.js +0 -1
  88. package/dist/performance-examples.d.ts +0 -1
  89. package/dist/performance-examples.js +0 -1
  90. package/dist/playwright-import.d.ts +0 -1
  91. package/dist/playwright-import.js +0 -1
  92. package/dist/product-paths.d.ts +0 -1
  93. package/dist/product-paths.js +0 -1
  94. package/dist/protocol.d.ts +0 -1
  95. package/dist/protocol.js +0 -1
  96. package/dist/react-source.d.ts +0 -1
  97. package/dist/react-source.js +0 -1
  98. package/dist/readability.js +1 -1
  99. package/dist/relay-client.d.ts +0 -1
  100. package/dist/relay-client.js +0 -1
  101. package/dist/relay-state.d.ts +0 -1
  102. package/dist/relay-state.js +0 -1
  103. package/dist/replay-ai-index.d.ts +0 -1
  104. package/dist/replay-ai-index.js +0 -1
  105. package/dist/replay-eval.d.ts +0 -1
  106. package/dist/replay-eval.js +0 -1
  107. package/dist/replay-handoff.d.ts +0 -1
  108. package/dist/replay-handoff.js +0 -1
  109. package/dist/replay-workflow-compiler.d.ts +0 -1
  110. package/dist/replay-workflow-compiler.js +0 -1
  111. package/dist/rrweb-recording-relay.d.ts +0 -1
  112. package/dist/rrweb-recording-relay.js +0 -1
  113. package/dist/rrweb-recording.d.ts +0 -1
  114. package/dist/rrweb-recording.js +0 -1
  115. package/dist/scoped-fs.d.ts +0 -1
  116. package/dist/scoped-fs.js +0 -1
  117. package/dist/selector-generator.js +1 -1
  118. package/dist/start-relay-server.d.ts +0 -1
  119. package/dist/start-relay-server.js +0 -1
  120. package/dist/styles-examples.d.ts +0 -1
  121. package/dist/styles-examples.js +0 -1
  122. package/dist/styles.d.ts +0 -1
  123. package/dist/styles.js +0 -1
  124. package/dist/tabwright-agent-skill.d.ts +0 -1
  125. package/dist/tabwright-agent-skill.js +0 -1
  126. package/dist/test-declarations.d.ts +0 -1
  127. package/dist/test-declarations.js +0 -1
  128. package/dist/utils.d.ts +0 -1
  129. package/dist/utils.js +2 -2
  130. package/dist/wait-for-page-load.d.ts +0 -1
  131. package/dist/wait-for-page-load.js +0 -1
  132. package/dist/workflow-capability.d.ts +0 -1
  133. package/dist/workflow-capability.js +0 -1
  134. package/package.json +9 -4
  135. package/readme.md +35 -0
  136. package/dist/aria-snapshot.d.ts.map +0 -1
  137. package/dist/aria-snapshot.js.map +0 -1
  138. package/dist/aria-snapshot.test.d.ts +0 -2
  139. package/dist/aria-snapshot.test.d.ts.map +0 -1
  140. package/dist/aria-snapshot.test.js +0 -270
  141. package/dist/aria-snapshot.test.js.map +0 -1
  142. package/dist/aria-snapshot.unit.test.d.ts +0 -2
  143. package/dist/aria-snapshot.unit.test.d.ts.map +0 -1
  144. package/dist/aria-snapshot.unit.test.js +0 -542
  145. package/dist/aria-snapshot.unit.test.js.map +0 -1
  146. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.d.ts.map +0 -1
  147. package/dist/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.js.map +0 -1
  148. package/dist/browser-config.d.ts.map +0 -1
  149. package/dist/browser-config.js.map +0 -1
  150. package/dist/browser-install.d.ts.map +0 -1
  151. package/dist/browser-install.js.map +0 -1
  152. package/dist/browser-launch.d.ts.map +0 -1
  153. package/dist/browser-launch.js.map +0 -1
  154. package/dist/builtin-capabilities.d.ts.map +0 -1
  155. package/dist/builtin-capabilities.js.map +0 -1
  156. package/dist/capability-agent-skill.d.ts.map +0 -1
  157. package/dist/capability-agent-skill.js.map +0 -1
  158. package/dist/capability-auth-state.d.ts.map +0 -1
  159. package/dist/capability-auth-state.js.map +0 -1
  160. package/dist/capability-auth.d.ts.map +0 -1
  161. package/dist/capability-auth.js.map +0 -1
  162. package/dist/capability-cli-confirmation.test.d.ts +0 -2
  163. package/dist/capability-cli-confirmation.test.d.ts.map +0 -1
  164. package/dist/capability-cli-confirmation.test.js +0 -79
  165. package/dist/capability-cli-confirmation.test.js.map +0 -1
  166. package/dist/capability-options.d.ts.map +0 -1
  167. package/dist/capability-options.js.map +0 -1
  168. package/dist/capability-options.test.d.ts +0 -2
  169. package/dist/capability-options.test.d.ts.map +0 -1
  170. package/dist/capability-options.test.js +0 -81
  171. package/dist/capability-options.test.js.map +0 -1
  172. package/dist/capability-package.d.ts.map +0 -1
  173. package/dist/capability-package.js.map +0 -1
  174. package/dist/capability-package.test.d.ts +0 -2
  175. package/dist/capability-package.test.d.ts.map +0 -1
  176. package/dist/capability-package.test.js +0 -179
  177. package/dist/capability-package.test.js.map +0 -1
  178. package/dist/capability-registry.d.ts.map +0 -1
  179. package/dist/capability-registry.js.map +0 -1
  180. package/dist/capability-registry.test.d.ts +0 -2
  181. package/dist/capability-registry.test.d.ts.map +0 -1
  182. package/dist/capability-registry.test.js +0 -1683
  183. package/dist/capability-registry.test.js.map +0 -1
  184. package/dist/capability-runner.d.ts.map +0 -1
  185. package/dist/capability-runner.js.map +0 -1
  186. package/dist/capability-search-relevance.test.d.ts +0 -2
  187. package/dist/capability-search-relevance.test.d.ts.map +0 -1
  188. package/dist/capability-search-relevance.test.js +0 -67
  189. package/dist/capability-search-relevance.test.js.map +0 -1
  190. package/dist/capability-studio.d.ts.map +0 -1
  191. package/dist/capability-studio.js.map +0 -1
  192. package/dist/capability-studio.test.d.ts +0 -2
  193. package/dist/capability-studio.test.d.ts.map +0 -1
  194. package/dist/capability-studio.test.js +0 -41
  195. package/dist/capability-studio.test.js.map +0 -1
  196. package/dist/cdp-log.d.ts.map +0 -1
  197. package/dist/cdp-log.js.map +0 -1
  198. package/dist/cdp-log.test.d.ts +0 -2
  199. package/dist/cdp-log.test.d.ts.map +0 -1
  200. package/dist/cdp-log.test.js +0 -109
  201. package/dist/cdp-log.test.js.map +0 -1
  202. package/dist/cdp-relay.d.ts.map +0 -1
  203. package/dist/cdp-relay.js.map +0 -1
  204. package/dist/cdp-session.d.ts.map +0 -1
  205. package/dist/cdp-session.js.map +0 -1
  206. package/dist/cdp-types.d.ts.map +0 -1
  207. package/dist/cdp-types.js.map +0 -1
  208. package/dist/channel-owner-inspect.test.d.ts +0 -2
  209. package/dist/channel-owner-inspect.test.d.ts.map +0 -1
  210. package/dist/channel-owner-inspect.test.js +0 -75
  211. package/dist/channel-owner-inspect.test.js.map +0 -1
  212. package/dist/chrome-discovery.d.ts.map +0 -1
  213. package/dist/chrome-discovery.js.map +0 -1
  214. package/dist/chrome-discovery.test.d.ts +0 -2
  215. package/dist/chrome-discovery.test.d.ts.map +0 -1
  216. package/dist/chrome-discovery.test.js +0 -41
  217. package/dist/chrome-discovery.test.js.map +0 -1
  218. package/dist/clean-html.d.ts.map +0 -1
  219. package/dist/clean-html.js.map +0 -1
  220. package/dist/cli-help.test.d.ts +0 -2
  221. package/dist/cli-help.test.d.ts.map +0 -1
  222. package/dist/cli-help.test.js +0 -98
  223. package/dist/cli-help.test.js.map +0 -1
  224. package/dist/cli.d.ts.map +0 -1
  225. package/dist/cli.js.map +0 -1
  226. package/dist/cloud-client.d.ts.map +0 -1
  227. package/dist/cloud-client.js.map +0 -1
  228. package/dist/create-logger.d.ts.map +0 -1
  229. package/dist/create-logger.js.map +0 -1
  230. package/dist/debugger-examples-types.d.ts.map +0 -1
  231. package/dist/debugger-examples-types.js.map +0 -1
  232. package/dist/debugger-examples.d.ts.map +0 -1
  233. package/dist/debugger-examples.js.map +0 -1
  234. package/dist/debugger.d.ts.map +0 -1
  235. package/dist/debugger.js.map +0 -1
  236. package/dist/diff-utils.d.ts.map +0 -1
  237. package/dist/diff-utils.js.map +0 -1
  238. package/dist/diff-utils.test.d.ts +0 -2
  239. package/dist/diff-utils.test.d.ts.map +0 -1
  240. package/dist/diff-utils.test.js +0 -137
  241. package/dist/diff-utils.test.js.map +0 -1
  242. package/dist/doctor.d.ts.map +0 -1
  243. package/dist/doctor.js.map +0 -1
  244. package/dist/doctor.test.d.ts +0 -2
  245. package/dist/doctor.test.d.ts.map +0 -1
  246. package/dist/doctor.test.js +0 -208
  247. package/dist/doctor.test.js.map +0 -1
  248. package/dist/editor-examples.d.ts.map +0 -1
  249. package/dist/editor-examples.js.map +0 -1
  250. package/dist/editor.d.ts.map +0 -1
  251. package/dist/editor.js.map +0 -1
  252. package/dist/env-compat.d.ts.map +0 -1
  253. package/dist/env-compat.js.map +0 -1
  254. package/dist/executor.d.ts.map +0 -1
  255. package/dist/executor.js.map +0 -1
  256. package/dist/executor.unit.test.d.ts +0 -2
  257. package/dist/executor.unit.test.d.ts.map +0 -1
  258. package/dist/executor.unit.test.js +0 -143
  259. package/dist/executor.unit.test.js.map +0 -1
  260. package/dist/extension-connection.test.d.ts +0 -2
  261. package/dist/extension-connection.test.d.ts.map +0 -1
  262. package/dist/extension-connection.test.js +0 -708
  263. package/dist/extension-connection.test.js.map +0 -1
  264. package/dist/extension-icon-warning.test.d.ts +0 -2
  265. package/dist/extension-icon-warning.test.d.ts.map +0 -1
  266. package/dist/extension-icon-warning.test.js +0 -33
  267. package/dist/extension-icon-warning.test.js.map +0 -1
  268. package/dist/ghost-browser.d.ts.map +0 -1
  269. package/dist/ghost-browser.js.map +0 -1
  270. package/dist/ghost-cursor-controller.d.ts.map +0 -1
  271. package/dist/ghost-cursor-controller.js.map +0 -1
  272. package/dist/ghost-cursor.d.ts.map +0 -1
  273. package/dist/ghost-cursor.js.map +0 -1
  274. package/dist/htmlrewrite.d.ts.map +0 -1
  275. package/dist/htmlrewrite.js.map +0 -1
  276. package/dist/htmlrewrite.test.d.ts +0 -2
  277. package/dist/htmlrewrite.test.d.ts.map +0 -1
  278. package/dist/htmlrewrite.test.js +0 -7975
  279. package/dist/htmlrewrite.test.js.map +0 -1
  280. package/dist/index.d.ts.map +0 -1
  281. package/dist/index.js.map +0 -1
  282. package/dist/kill-port.d.ts.map +0 -1
  283. package/dist/kill-port.js.map +0 -1
  284. package/dist/kitty-graphics.d.ts.map +0 -1
  285. package/dist/kitty-graphics.js.map +0 -1
  286. package/dist/kitty-graphics.test.d.ts +0 -2
  287. package/dist/kitty-graphics.test.d.ts.map +0 -1
  288. package/dist/kitty-graphics.test.js +0 -125
  289. package/dist/kitty-graphics.test.js.map +0 -1
  290. package/dist/locator-selector.test.d.ts +0 -2
  291. package/dist/locator-selector.test.d.ts.map +0 -1
  292. package/dist/locator-selector.test.js +0 -98
  293. package/dist/locator-selector.test.js.map +0 -1
  294. package/dist/mcp-client.d.ts +0 -20
  295. package/dist/mcp-client.d.ts.map +0 -1
  296. package/dist/mcp-client.js +0 -56
  297. package/dist/mcp-client.js.map +0 -1
  298. package/dist/mcp.d.ts.map +0 -1
  299. package/dist/mcp.js.map +0 -1
  300. package/dist/on-mouse-action.test.d.ts +0 -2
  301. package/dist/on-mouse-action.test.d.ts.map +0 -1
  302. package/dist/on-mouse-action.test.js +0 -180
  303. package/dist/on-mouse-action.test.js.map +0 -1
  304. package/dist/options-page.test.d.ts +0 -2
  305. package/dist/options-page.test.d.ts.map +0 -1
  306. package/dist/options-page.test.js +0 -773
  307. package/dist/options-page.test.js.map +0 -1
  308. package/dist/package-paths.d.ts.map +0 -1
  309. package/dist/package-paths.js.map +0 -1
  310. package/dist/page-markdown.d.ts.map +0 -1
  311. package/dist/page-markdown.js.map +0 -1
  312. package/dist/performance-examples.d.ts.map +0 -1
  313. package/dist/performance-examples.js.map +0 -1
  314. package/dist/playwright-import.d.ts.map +0 -1
  315. package/dist/playwright-import.js.map +0 -1
  316. package/dist/popup-relocation.test.d.ts +0 -7
  317. package/dist/popup-relocation.test.d.ts.map +0 -1
  318. package/dist/popup-relocation.test.js +0 -139
  319. package/dist/popup-relocation.test.js.map +0 -1
  320. package/dist/product-paths.d.ts.map +0 -1
  321. package/dist/product-paths.js.map +0 -1
  322. package/dist/product-paths.test.d.ts +0 -2
  323. package/dist/product-paths.test.d.ts.map +0 -1
  324. package/dist/product-paths.test.js +0 -39
  325. package/dist/product-paths.test.js.map +0 -1
  326. package/dist/protocol.d.ts.map +0 -1
  327. package/dist/protocol.js.map +0 -1
  328. package/dist/protocol.test.d.ts +0 -2
  329. package/dist/protocol.test.d.ts.map +0 -1
  330. package/dist/protocol.test.js +0 -31
  331. package/dist/protocol.test.js.map +0 -1
  332. package/dist/react-source.d.ts.map +0 -1
  333. package/dist/react-source.js.map +0 -1
  334. package/dist/relay-client.d.ts.map +0 -1
  335. package/dist/relay-client.js.map +0 -1
  336. package/dist/relay-client.test.d.ts +0 -2
  337. package/dist/relay-client.test.d.ts.map +0 -1
  338. package/dist/relay-client.test.js +0 -305
  339. package/dist/relay-client.test.js.map +0 -1
  340. package/dist/relay-core.test.d.ts +0 -2
  341. package/dist/relay-core.test.d.ts.map +0 -1
  342. package/dist/relay-core.test.js +0 -1427
  343. package/dist/relay-core.test.js.map +0 -1
  344. package/dist/relay-navigation.test.d.ts +0 -2
  345. package/dist/relay-navigation.test.d.ts.map +0 -1
  346. package/dist/relay-navigation.test.js +0 -661
  347. package/dist/relay-navigation.test.js.map +0 -1
  348. package/dist/relay-session.test.d.ts +0 -2
  349. package/dist/relay-session.test.d.ts.map +0 -1
  350. package/dist/relay-session.test.js +0 -1121
  351. package/dist/relay-session.test.js.map +0 -1
  352. package/dist/relay-state.d.ts.map +0 -1
  353. package/dist/relay-state.js.map +0 -1
  354. package/dist/relay-state.test.d.ts +0 -2
  355. package/dist/relay-state.test.d.ts.map +0 -1
  356. package/dist/relay-state.test.js +0 -594
  357. package/dist/relay-state.test.js.map +0 -1
  358. package/dist/replay-ai-index.d.ts.map +0 -1
  359. package/dist/replay-ai-index.js.map +0 -1
  360. package/dist/replay-ai-index.test.d.ts +0 -2
  361. package/dist/replay-ai-index.test.d.ts.map +0 -1
  362. package/dist/replay-ai-index.test.js +0 -255
  363. package/dist/replay-ai-index.test.js.map +0 -1
  364. package/dist/replay-cli.test.d.ts +0 -2
  365. package/dist/replay-cli.test.d.ts.map +0 -1
  366. package/dist/replay-cli.test.js +0 -275
  367. package/dist/replay-cli.test.js.map +0 -1
  368. package/dist/replay-eval.d.ts.map +0 -1
  369. package/dist/replay-eval.js.map +0 -1
  370. package/dist/replay-handoff.d.ts.map +0 -1
  371. package/dist/replay-handoff.js.map +0 -1
  372. package/dist/replay-handoff.test.d.ts +0 -2
  373. package/dist/replay-handoff.test.d.ts.map +0 -1
  374. package/dist/replay-handoff.test.js +0 -123
  375. package/dist/replay-handoff.test.js.map +0 -1
  376. package/dist/replay-workflow-compiler.d.ts.map +0 -1
  377. package/dist/replay-workflow-compiler.js.map +0 -1
  378. package/dist/replay-workflow-compiler.test.d.ts +0 -2
  379. package/dist/replay-workflow-compiler.test.d.ts.map +0 -1
  380. package/dist/replay-workflow-compiler.test.js +0 -223
  381. package/dist/replay-workflow-compiler.test.js.map +0 -1
  382. package/dist/rrweb-recording-relay.d.ts.map +0 -1
  383. package/dist/rrweb-recording-relay.js.map +0 -1
  384. package/dist/rrweb-recording-relay.test.d.ts +0 -2
  385. package/dist/rrweb-recording-relay.test.d.ts.map +0 -1
  386. package/dist/rrweb-recording-relay.test.js +0 -85
  387. package/dist/rrweb-recording-relay.test.js.map +0 -1
  388. package/dist/rrweb-recording.d.ts.map +0 -1
  389. package/dist/rrweb-recording.js.map +0 -1
  390. package/dist/scoped-fs.d.ts.map +0 -1
  391. package/dist/scoped-fs.js.map +0 -1
  392. package/dist/scoped-fs.test.d.ts +0 -5
  393. package/dist/scoped-fs.test.d.ts.map +0 -1
  394. package/dist/scoped-fs.test.js +0 -73
  395. package/dist/scoped-fs.test.js.map +0 -1
  396. package/dist/skill-stub.test.d.ts +0 -2
  397. package/dist/skill-stub.test.d.ts.map +0 -1
  398. package/dist/skill-stub.test.js +0 -22
  399. package/dist/skill-stub.test.js.map +0 -1
  400. package/dist/snapshot-tools.test.d.ts +0 -2
  401. package/dist/snapshot-tools.test.d.ts.map +0 -1
  402. package/dist/snapshot-tools.test.js +0 -856
  403. package/dist/snapshot-tools.test.js.map +0 -1
  404. package/dist/start-relay-server.d.ts.map +0 -1
  405. package/dist/start-relay-server.js.map +0 -1
  406. package/dist/styles-examples.d.ts.map +0 -1
  407. package/dist/styles-examples.js.map +0 -1
  408. package/dist/styles.d.ts.map +0 -1
  409. package/dist/styles.js.map +0 -1
  410. package/dist/tabwright-agent-skill.d.ts.map +0 -1
  411. package/dist/tabwright-agent-skill.js.map +0 -1
  412. package/dist/tabwright-agent-skill.test.d.ts +0 -2
  413. package/dist/tabwright-agent-skill.test.d.ts.map +0 -1
  414. package/dist/tabwright-agent-skill.test.js +0 -51
  415. package/dist/tabwright-agent-skill.test.js.map +0 -1
  416. package/dist/test-declarations.d.ts.map +0 -1
  417. package/dist/test-declarations.js.map +0 -1
  418. package/dist/test-utils.d.ts +0 -69
  419. package/dist/test-utils.d.ts.map +0 -1
  420. package/dist/test-utils.js +0 -448
  421. package/dist/test-utils.js.map +0 -1
  422. package/dist/test-utils.test.d.ts +0 -2
  423. package/dist/test-utils.test.d.ts.map +0 -1
  424. package/dist/test-utils.test.js +0 -55
  425. package/dist/test-utils.test.js.map +0 -1
  426. package/dist/utils.d.ts.map +0 -1
  427. package/dist/utils.js.map +0 -1
  428. package/dist/wait-for-page-load.d.ts.map +0 -1
  429. package/dist/wait-for-page-load.js.map +0 -1
  430. package/dist/workflow-capability.d.ts.map +0 -1
  431. package/dist/workflow-capability.js.map +0 -1
  432. package/dist/workflow-script-flow.test.d.ts +0 -2
  433. package/dist/workflow-script-flow.test.d.ts.map +0 -1
  434. package/dist/workflow-script-flow.test.js +0 -285
  435. package/dist/workflow-script-flow.test.js.map +0 -1
  436. package/src/__snapshots__/x.com.processed.html +0 -1003
  437. package/src/__snapshots__/x.com.processed.withStyles.html +0 -2720
  438. package/src/a11y-client.ts +0 -241
  439. package/src/aria-snapshot.test.ts +0 -311
  440. package/src/aria-snapshot.ts +0 -1681
  441. package/src/aria-snapshot.unit.test.ts +0 -615
  442. package/src/aria-snapshots/github-interactive.txt +0 -193
  443. package/src/aria-snapshots/github-raw.txt +0 -309
  444. package/src/aria-snapshots/hackernews-interactive.txt +0 -477
  445. package/src/aria-snapshots/hackernews-raw.txt +0 -428
  446. package/src/aria-snapshots/prosemirror-interactive.txt +0 -66
  447. package/src/aria-snapshots/prosemirror-raw.txt +0 -145
  448. package/src/assets/aria-labels-hacker-news.png +0 -0
  449. package/src/assets/aria-labels-old-reddit.png +0 -0
  450. package/src/assets/cursors/screen-studio/pointer-macos-tahoe-data-url.ts +0 -5
  451. package/src/assets/cursors/screen-studio/pointer-macos-tahoe.svg +0 -18
  452. package/src/assets/x.com.html +0 -32946
  453. package/src/browser-config.ts +0 -179
  454. package/src/browser-install.ts +0 -284
  455. package/src/browser-launch.ts +0 -75
  456. package/src/builtin-capabilities.ts +0 -2765
  457. package/src/capability-agent-skill.ts +0 -305
  458. package/src/capability-auth-state.ts +0 -197
  459. package/src/capability-auth.ts +0 -249
  460. package/src/capability-cli-confirmation.test.ts +0 -90
  461. package/src/capability-options.test.ts +0 -89
  462. package/src/capability-options.ts +0 -57
  463. package/src/capability-package.test.ts +0 -204
  464. package/src/capability-package.ts +0 -555
  465. package/src/capability-registry.test.ts +0 -1905
  466. package/src/capability-registry.ts +0 -1234
  467. package/src/capability-runner.ts +0 -801
  468. package/src/capability-search-relevance.test.ts +0 -73
  469. package/src/capability-studio.test.ts +0 -45
  470. package/src/capability-studio.ts +0 -641
  471. package/src/cdp-log.test.ts +0 -131
  472. package/src/cdp-log.ts +0 -112
  473. package/src/cdp-relay.ts +0 -2944
  474. package/src/cdp-session.ts +0 -77
  475. package/src/cdp-types.ts +0 -158
  476. package/src/channel-owner-inspect.test.ts +0 -94
  477. package/src/chrome-discovery.test.ts +0 -50
  478. package/src/chrome-discovery.ts +0 -220
  479. package/src/clean-html.ts +0 -141
  480. package/src/cli-help.test.ts +0 -123
  481. package/src/cli.ts +0 -3181
  482. package/src/cloud-client.ts +0 -179
  483. package/src/create-logger.ts +0 -38
  484. package/src/debugger-examples-types.ts +0 -16
  485. package/src/debugger-examples.ts +0 -66
  486. package/src/debugger.ts +0 -708
  487. package/src/diff-utils.test.ts +0 -152
  488. package/src/diff-utils.ts +0 -70
  489. package/src/doctor.test.ts +0 -244
  490. package/src/doctor.ts +0 -321
  491. package/src/editor-examples.ts +0 -158
  492. package/src/editor.ts +0 -426
  493. package/src/env-compat.ts +0 -28
  494. package/src/executor.ts +0 -1824
  495. package/src/executor.unit.test.ts +0 -168
  496. package/src/extension-connection.test.ts +0 -855
  497. package/src/extension-icon-warning.test.ts +0 -45
  498. package/src/ghost-browser.ts +0 -149
  499. package/src/ghost-cursor-client.ts +0 -494
  500. package/src/ghost-cursor-controller.ts +0 -129
  501. package/src/ghost-cursor.ts +0 -123
  502. package/src/htmlrewrite.test.ts +0 -8008
  503. package/src/htmlrewrite.ts +0 -390
  504. package/src/index.ts +0 -53
  505. package/src/kill-port.ts +0 -219
  506. package/src/kitty-graphics.test.ts +0 -139
  507. package/src/kitty-graphics.ts +0 -79
  508. package/src/locator-selector.test.ts +0 -119
  509. package/src/mcp-client.ts +0 -78
  510. package/src/mcp.ts +0 -629
  511. package/src/on-mouse-action.test.ts +0 -226
  512. package/src/options-page.test.ts +0 -839
  513. package/src/package-paths.ts +0 -38
  514. package/src/page-markdown.ts +0 -240
  515. package/src/performance-examples.ts +0 -186
  516. package/src/playwright-import.ts +0 -59
  517. package/src/popup-relocation.test.ts +0 -163
  518. package/src/product-paths.test.ts +0 -47
  519. package/src/product-paths.ts +0 -28
  520. package/src/protocol.test.ts +0 -52
  521. package/src/protocol.ts +0 -351
  522. package/src/react-source.ts +0 -379
  523. package/src/relay-client.test.ts +0 -375
  524. package/src/relay-client.ts +0 -572
  525. package/src/relay-core.test.ts +0 -1603
  526. package/src/relay-navigation.test.ts +0 -790
  527. package/src/relay-session.test.ts +0 -1375
  528. package/src/relay-state.test.ts +0 -729
  529. package/src/relay-state.ts +0 -561
  530. package/src/replay-ai-index.test.ts +0 -278
  531. package/src/replay-ai-index.ts +0 -872
  532. package/src/replay-cli.test.ts +0 -325
  533. package/src/replay-eval.ts +0 -1039
  534. package/src/replay-handoff.test.ts +0 -163
  535. package/src/replay-handoff.ts +0 -109
  536. package/src/replay-workflow-compiler.test.ts +0 -250
  537. package/src/replay-workflow-compiler.ts +0 -700
  538. package/src/resource.md +0 -409
  539. package/src/rrweb-recording-relay.test.ts +0 -99
  540. package/src/rrweb-recording-relay.ts +0 -400
  541. package/src/rrweb-recording.ts +0 -322
  542. package/src/scoped-fs.test.ts +0 -80
  543. package/src/scoped-fs.ts +0 -420
  544. package/src/skill-stub.test.ts +0 -24
  545. package/src/snapshot-tools.test.ts +0 -1017
  546. package/src/snapshots/hacker-news-accessibility-full.md +0 -81
  547. package/src/snapshots/hacker-news-accessibility-interactive.md +0 -43
  548. package/src/snapshots/page-markdown-output.txt +0 -16
  549. package/src/snapshots/shadcn-ui-accessibility-full.md +0 -201
  550. package/src/snapshots/shadcn-ui-accessibility-interactive.md +0 -109
  551. package/src/start-relay-server.ts +0 -69
  552. package/src/styles-examples.ts +0 -84
  553. package/src/styles.ts +0 -343
  554. package/src/tabwright-agent-skill.test.ts +0 -61
  555. package/src/tabwright-agent-skill.ts +0 -105
  556. package/src/test-declarations.ts +0 -13
  557. package/src/test-utils.test.ts +0 -70
  558. package/src/test-utils.ts +0 -566
  559. package/src/utils.ts +0 -78
  560. package/src/wait-for-page-load.ts +0 -198
  561. package/src/workflow-capability.ts +0 -417
  562. 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
- })