@heojeongbo/wirestage 0.0.0-stage → 0.1.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 (489) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES.md +21 -0
  3. package/README.md +150 -2
  4. package/dist/app/playwright/index.d.ts +3 -0
  5. package/dist/app/playwright/index.d.ts.map +1 -0
  6. package/dist/app/playwright/index.js +2 -0
  7. package/dist/app/playwright/index.js.map +1 -0
  8. package/dist/app/playwright/lib/fixture.d.ts +61 -0
  9. package/dist/app/playwright/lib/fixture.d.ts.map +1 -0
  10. package/dist/app/playwright/lib/fixture.js +110 -0
  11. package/dist/app/playwright/lib/fixture.js.map +1 -0
  12. package/dist/connect.d.ts +7 -0
  13. package/dist/connect.d.ts.map +1 -0
  14. package/dist/connect.js +7 -0
  15. package/dist/connect.js.map +1 -0
  16. package/dist/entities/frame/@x/plugin.d.ts +9 -0
  17. package/dist/entities/frame/@x/plugin.d.ts.map +1 -0
  18. package/dist/entities/frame/@x/plugin.js +2 -0
  19. package/dist/entities/frame/@x/plugin.js.map +1 -0
  20. package/dist/entities/frame/index.d.ts +6 -0
  21. package/dist/entities/frame/index.d.ts.map +1 -0
  22. package/dist/entities/frame/index.js +4 -0
  23. package/dist/entities/frame/index.js.map +1 -0
  24. package/dist/entities/frame/lib/filter.d.ts +8 -0
  25. package/dist/entities/frame/lib/filter.d.ts.map +1 -0
  26. package/dist/entities/frame/lib/filter.js +62 -0
  27. package/dist/entities/frame/lib/filter.js.map +1 -0
  28. package/dist/entities/frame/lib/payload.d.ts +8 -0
  29. package/dist/entities/frame/lib/payload.d.ts.map +1 -0
  30. package/dist/entities/frame/lib/payload.js +42 -0
  31. package/dist/entities/frame/lib/payload.js.map +1 -0
  32. package/dist/entities/frame/lib/snapshot.d.ts +15 -0
  33. package/dist/entities/frame/lib/snapshot.d.ts.map +1 -0
  34. package/dist/entities/frame/lib/snapshot.js +90 -0
  35. package/dist/entities/frame/lib/snapshot.js.map +1 -0
  36. package/dist/entities/frame/lib/timeline.d.ts +23 -0
  37. package/dist/entities/frame/lib/timeline.d.ts.map +1 -0
  38. package/dist/entities/frame/lib/timeline.js +122 -0
  39. package/dist/entities/frame/lib/timeline.js.map +1 -0
  40. package/dist/entities/frame/model/frame.d.ts +182 -0
  41. package/dist/entities/frame/model/frame.d.ts.map +1 -0
  42. package/dist/entities/frame/model/frame.js +2 -0
  43. package/dist/entities/frame/model/frame.js.map +1 -0
  44. package/dist/entities/plugin/index.d.ts +9 -0
  45. package/dist/entities/plugin/index.d.ts.map +1 -0
  46. package/dist/entities/plugin/index.js +3 -0
  47. package/dist/entities/plugin/index.js.map +1 -0
  48. package/dist/entities/plugin/lib/define-plugin.d.ts +8 -0
  49. package/dist/entities/plugin/lib/define-plugin.d.ts.map +1 -0
  50. package/dist/entities/plugin/lib/define-plugin.js +9 -0
  51. package/dist/entities/plugin/lib/define-plugin.js.map +1 -0
  52. package/dist/entities/plugin/lib/named.d.ts +10 -0
  53. package/dist/entities/plugin/lib/named.d.ts.map +1 -0
  54. package/dist/entities/plugin/lib/named.js +17 -0
  55. package/dist/entities/plugin/lib/named.js.map +1 -0
  56. package/dist/entities/plugin/model/attachment.d.ts +18 -0
  57. package/dist/entities/plugin/model/attachment.d.ts.map +1 -0
  58. package/dist/entities/plugin/model/attachment.js +2 -0
  59. package/dist/entities/plugin/model/attachment.js.map +1 -0
  60. package/dist/entities/plugin/model/http.d.ts +135 -0
  61. package/dist/entities/plugin/model/http.d.ts.map +1 -0
  62. package/dist/entities/plugin/model/http.js +2 -0
  63. package/dist/entities/plugin/model/http.js.map +1 -0
  64. package/dist/entities/plugin/model/page.d.ts +100 -0
  65. package/dist/entities/plugin/model/page.d.ts.map +1 -0
  66. package/dist/entities/plugin/model/page.js +2 -0
  67. package/dist/entities/plugin/model/page.js.map +1 -0
  68. package/dist/entities/plugin/model/plugin.d.ts +83 -0
  69. package/dist/entities/plugin/model/plugin.d.ts.map +1 -0
  70. package/dist/entities/plugin/model/plugin.js +2 -0
  71. package/dist/entities/plugin/model/plugin.js.map +1 -0
  72. package/dist/entities/plugin/model/socket.d.ts +107 -0
  73. package/dist/entities/plugin/model/socket.d.ts.map +1 -0
  74. package/dist/entities/plugin/model/socket.js +2 -0
  75. package/dist/entities/plugin/model/socket.js.map +1 -0
  76. package/dist/entities/plugin/model/stream.d.ts +46 -0
  77. package/dist/entities/plugin/model/stream.d.ts.map +1 -0
  78. package/dist/entities/plugin/model/stream.js +2 -0
  79. package/dist/entities/plugin/model/stream.js.map +1 -0
  80. package/dist/features/connect/index.d.ts +5 -0
  81. package/dist/features/connect/index.d.ts.map +1 -0
  82. package/dist/features/connect/index.js +4 -0
  83. package/dist/features/connect/index.js.map +1 -0
  84. package/dist/features/connect/lib/calls.d.ts +9 -0
  85. package/dist/features/connect/lib/calls.d.ts.map +1 -0
  86. package/dist/features/connect/lib/calls.js +46 -0
  87. package/dist/features/connect/lib/calls.js.map +1 -0
  88. package/dist/features/connect/lib/client.d.ts +33 -0
  89. package/dist/features/connect/lib/client.d.ts.map +1 -0
  90. package/dist/features/connect/lib/client.js +147 -0
  91. package/dist/features/connect/lib/client.js.map +1 -0
  92. package/dist/features/connect/lib/create-connect.d.ts +14 -0
  93. package/dist/features/connect/lib/create-connect.d.ts.map +1 -0
  94. package/dist/features/connect/lib/create-connect.js +124 -0
  95. package/dist/features/connect/lib/create-connect.js.map +1 -0
  96. package/dist/features/connect/lib/faults.d.ts +18 -0
  97. package/dist/features/connect/lib/faults.d.ts.map +1 -0
  98. package/dist/features/connect/lib/faults.js +85 -0
  99. package/dist/features/connect/lib/faults.js.map +1 -0
  100. package/dist/features/connect/lib/protocol.d.ts +42 -0
  101. package/dist/features/connect/lib/protocol.d.ts.map +1 -0
  102. package/dist/features/connect/lib/protocol.js +228 -0
  103. package/dist/features/connect/lib/protocol.js.map +1 -0
  104. package/dist/features/connect/lib/record.d.ts +14 -0
  105. package/dist/features/connect/lib/record.d.ts.map +1 -0
  106. package/dist/features/connect/lib/record.js +36 -0
  107. package/dist/features/connect/lib/record.js.map +1 -0
  108. package/dist/features/connect/lib/recorder.d.ts +77 -0
  109. package/dist/features/connect/lib/recorder.d.ts.map +1 -0
  110. package/dist/features/connect/lib/recorder.js +209 -0
  111. package/dist/features/connect/lib/recorder.js.map +1 -0
  112. package/dist/features/connect/lib/rules.d.ts +51 -0
  113. package/dist/features/connect/lib/rules.d.ts.map +1 -0
  114. package/dist/features/connect/lib/rules.js +222 -0
  115. package/dist/features/connect/lib/rules.js.map +1 -0
  116. package/dist/features/connect/lib/serve.d.ts +10 -0
  117. package/dist/features/connect/lib/serve.d.ts.map +1 -0
  118. package/dist/features/connect/lib/serve.js +78 -0
  119. package/dist/features/connect/lib/serve.js.map +1 -0
  120. package/dist/features/connect/lib/status-of.d.ts +11 -0
  121. package/dist/features/connect/lib/status-of.d.ts.map +1 -0
  122. package/dist/features/connect/lib/status-of.js +22 -0
  123. package/dist/features/connect/lib/status-of.js.map +1 -0
  124. package/dist/features/connect/lib/stubs.d.ts +18 -0
  125. package/dist/features/connect/lib/stubs.d.ts.map +1 -0
  126. package/dist/features/connect/lib/stubs.js +174 -0
  127. package/dist/features/connect/lib/stubs.js.map +1 -0
  128. package/dist/features/connect/lib/universal.d.ts +19 -0
  129. package/dist/features/connect/lib/universal.d.ts.map +1 -0
  130. package/dist/features/connect/lib/universal.js +71 -0
  131. package/dist/features/connect/lib/universal.js.map +1 -0
  132. package/dist/features/connect/model/types.d.ts +247 -0
  133. package/dist/features/connect/model/types.d.ts.map +1 -0
  134. package/dist/features/connect/model/types.js +2 -0
  135. package/dist/features/connect/model/types.js.map +1 -0
  136. package/dist/features/rest/index.d.ts +5 -0
  137. package/dist/features/rest/index.d.ts.map +1 -0
  138. package/dist/features/rest/index.js +3 -0
  139. package/dist/features/rest/index.js.map +1 -0
  140. package/dist/features/rest/lib/calls.d.ts +8 -0
  141. package/dist/features/rest/lib/calls.d.ts.map +1 -0
  142. package/dist/features/rest/lib/calls.js +32 -0
  143. package/dist/features/rest/lib/calls.js.map +1 -0
  144. package/dist/features/rest/lib/create-rest.d.ts +8 -0
  145. package/dist/features/rest/lib/create-rest.d.ts.map +1 -0
  146. package/dist/features/rest/lib/create-rest.js +136 -0
  147. package/dist/features/rest/lib/create-rest.js.map +1 -0
  148. package/dist/features/rest/lib/path.d.ts +14 -0
  149. package/dist/features/rest/lib/path.d.ts.map +1 -0
  150. package/dist/features/rest/lib/path.js +58 -0
  151. package/dist/features/rest/lib/path.js.map +1 -0
  152. package/dist/features/rest/lib/reply.d.ts +25 -0
  153. package/dist/features/rest/lib/reply.d.ts.map +1 -0
  154. package/dist/features/rest/lib/reply.js +40 -0
  155. package/dist/features/rest/lib/reply.js.map +1 -0
  156. package/dist/features/rest/model/types.d.ts +74 -0
  157. package/dist/features/rest/model/types.d.ts.map +1 -0
  158. package/dist/features/rest/model/types.js +2 -0
  159. package/dist/features/rest/model/types.js.map +1 -0
  160. package/dist/features/sse/index.d.ts +4 -0
  161. package/dist/features/sse/index.d.ts.map +1 -0
  162. package/dist/features/sse/index.js +3 -0
  163. package/dist/features/sse/index.js.map +1 -0
  164. package/dist/features/sse/lib/create-sse.d.ts +9 -0
  165. package/dist/features/sse/lib/create-sse.d.ts.map +1 -0
  166. package/dist/features/sse/lib/create-sse.js +121 -0
  167. package/dist/features/sse/lib/create-sse.js.map +1 -0
  168. package/dist/features/sse/lib/encode.d.ts +8 -0
  169. package/dist/features/sse/lib/encode.d.ts.map +1 -0
  170. package/dist/features/sse/lib/encode.js +29 -0
  171. package/dist/features/sse/lib/encode.js.map +1 -0
  172. package/dist/features/sse/model/types.d.ts +68 -0
  173. package/dist/features/sse/model/types.d.ts.map +1 -0
  174. package/dist/features/sse/model/types.js +2 -0
  175. package/dist/features/sse/model/types.js.map +1 -0
  176. package/dist/features/webrtc/index.d.ts +3 -0
  177. package/dist/features/webrtc/index.d.ts.map +1 -0
  178. package/dist/features/webrtc/index.js +2 -0
  179. package/dist/features/webrtc/index.js.map +1 -0
  180. package/dist/features/webrtc/lib/create-webrtc.d.ts +9 -0
  181. package/dist/features/webrtc/lib/create-webrtc.d.ts.map +1 -0
  182. package/dist/features/webrtc/lib/create-webrtc.js +123 -0
  183. package/dist/features/webrtc/lib/create-webrtc.js.map +1 -0
  184. package/dist/features/webrtc/lib/rtc-page-module.d.ts +22 -0
  185. package/dist/features/webrtc/lib/rtc-page-module.d.ts.map +1 -0
  186. package/dist/features/webrtc/lib/rtc-page-module.js +161 -0
  187. package/dist/features/webrtc/lib/rtc-page-module.js.map +1 -0
  188. package/dist/features/webrtc/lib/to-frame.d.ts +9 -0
  189. package/dist/features/webrtc/lib/to-frame.d.ts.map +1 -0
  190. package/dist/features/webrtc/lib/to-frame.js +43 -0
  191. package/dist/features/webrtc/lib/to-frame.js.map +1 -0
  192. package/dist/features/webrtc/model/types.d.ts +70 -0
  193. package/dist/features/webrtc/model/types.d.ts.map +1 -0
  194. package/dist/features/webrtc/model/types.js +2 -0
  195. package/dist/features/webrtc/model/types.js.map +1 -0
  196. package/dist/features/websocket/index.d.ts +3 -0
  197. package/dist/features/websocket/index.d.ts.map +1 -0
  198. package/dist/features/websocket/index.js +2 -0
  199. package/dist/features/websocket/index.js.map +1 -0
  200. package/dist/features/websocket/lib/create-websocket.d.ts +9 -0
  201. package/dist/features/websocket/lib/create-websocket.d.ts.map +1 -0
  202. package/dist/features/websocket/lib/create-websocket.js +34 -0
  203. package/dist/features/websocket/lib/create-websocket.js.map +1 -0
  204. package/dist/features/websocket/lib/mock.d.ts +6 -0
  205. package/dist/features/websocket/lib/mock.d.ts.map +1 -0
  206. package/dist/features/websocket/lib/mock.js +96 -0
  207. package/dist/features/websocket/lib/mock.js.map +1 -0
  208. package/dist/features/websocket/lib/proxy.d.ts +9 -0
  209. package/dist/features/websocket/lib/proxy.d.ts.map +1 -0
  210. package/dist/features/websocket/lib/proxy.js +120 -0
  211. package/dist/features/websocket/lib/proxy.js.map +1 -0
  212. package/dist/features/websocket/lib/wire.d.ts +16 -0
  213. package/dist/features/websocket/lib/wire.d.ts.map +1 -0
  214. package/dist/features/websocket/lib/wire.js +46 -0
  215. package/dist/features/websocket/lib/wire.js.map +1 -0
  216. package/dist/features/websocket/model/types.d.ts +133 -0
  217. package/dist/features/websocket/model/types.d.ts.map +1 -0
  218. package/dist/features/websocket/model/types.js +2 -0
  219. package/dist/features/websocket/model/types.js.map +1 -0
  220. package/dist/index.d.ts +27 -0
  221. package/dist/index.d.ts.map +1 -0
  222. package/dist/index.js +16 -0
  223. package/dist/index.js.map +1 -0
  224. package/dist/playwright.d.ts +4 -0
  225. package/dist/playwright.d.ts.map +1 -0
  226. package/dist/playwright.js +2 -0
  227. package/dist/playwright.js.map +1 -0
  228. package/dist/rest.d.ts +3 -0
  229. package/dist/rest.d.ts.map +1 -0
  230. package/dist/rest.js +3 -0
  231. package/dist/rest.js.map +1 -0
  232. package/dist/shared/api/http-server/index.d.ts +3 -0
  233. package/dist/shared/api/http-server/index.d.ts.map +1 -0
  234. package/dist/shared/api/http-server/index.js +2 -0
  235. package/dist/shared/api/http-server/index.js.map +1 -0
  236. package/dist/shared/api/http-server/listen.d.ts +25 -0
  237. package/dist/shared/api/http-server/listen.d.ts.map +1 -0
  238. package/dist/shared/api/http-server/listen.js +44 -0
  239. package/dist/shared/api/http-server/listen.js.map +1 -0
  240. package/dist/shared/api/playwright/index.d.ts +2 -0
  241. package/dist/shared/api/playwright/index.d.ts.map +1 -0
  242. package/dist/shared/api/playwright/index.js +2 -0
  243. package/dist/shared/api/playwright/index.js.map +1 -0
  244. package/dist/shared/api/playwright/ports.d.ts +126 -0
  245. package/dist/shared/api/playwright/ports.d.ts.map +1 -0
  246. package/dist/shared/api/playwright/ports.js +11 -0
  247. package/dist/shared/api/playwright/ports.js.map +1 -0
  248. package/dist/shared/api/stream-server/index.d.ts +3 -0
  249. package/dist/shared/api/stream-server/index.d.ts.map +1 -0
  250. package/dist/shared/api/stream-server/index.js +2 -0
  251. package/dist/shared/api/stream-server/index.js.map +1 -0
  252. package/dist/shared/api/stream-server/stream-server.d.ts +45 -0
  253. package/dist/shared/api/stream-server/stream-server.d.ts.map +1 -0
  254. package/dist/shared/api/stream-server/stream-server.js +167 -0
  255. package/dist/shared/api/stream-server/stream-server.js.map +1 -0
  256. package/dist/shared/lib/async/arrivals.d.ts +31 -0
  257. package/dist/shared/lib/async/arrivals.d.ts.map +1 -0
  258. package/dist/shared/lib/async/arrivals.js +57 -0
  259. package/dist/shared/lib/async/arrivals.js.map +1 -0
  260. package/dist/shared/lib/async/async-queue.d.ts +23 -0
  261. package/dist/shared/lib/async/async-queue.d.ts.map +1 -0
  262. package/dist/shared/lib/async/async-queue.js +80 -0
  263. package/dist/shared/lib/async/async-queue.js.map +1 -0
  264. package/dist/shared/lib/async/deferred.d.ts +12 -0
  265. package/dist/shared/lib/async/deferred.d.ts.map +1 -0
  266. package/dist/shared/lib/async/deferred.js +34 -0
  267. package/dist/shared/lib/async/deferred.js.map +1 -0
  268. package/dist/shared/lib/async/index.d.ts +8 -0
  269. package/dist/shared/lib/async/index.d.ts.map +1 -0
  270. package/dist/shared/lib/async/index.js +5 -0
  271. package/dist/shared/lib/async/index.js.map +1 -0
  272. package/dist/shared/lib/async/run-all.d.ts +8 -0
  273. package/dist/shared/lib/async/run-all.d.ts.map +1 -0
  274. package/dist/shared/lib/async/run-all.js +22 -0
  275. package/dist/shared/lib/async/run-all.js.map +1 -0
  276. package/dist/shared/lib/bytes/bytes.d.ts +18 -0
  277. package/dist/shared/lib/bytes/bytes.d.ts.map +1 -0
  278. package/dist/shared/lib/bytes/bytes.js +48 -0
  279. package/dist/shared/lib/bytes/bytes.js.map +1 -0
  280. package/dist/shared/lib/bytes/index.d.ts +3 -0
  281. package/dist/shared/lib/bytes/index.d.ts.map +1 -0
  282. package/dist/shared/lib/bytes/index.js +2 -0
  283. package/dist/shared/lib/bytes/index.js.map +1 -0
  284. package/dist/shared/lib/clock/clock.d.ts +29 -0
  285. package/dist/shared/lib/clock/clock.d.ts.map +1 -0
  286. package/dist/shared/lib/clock/clock.js +85 -0
  287. package/dist/shared/lib/clock/clock.js.map +1 -0
  288. package/dist/shared/lib/clock/index.d.ts +3 -0
  289. package/dist/shared/lib/clock/index.d.ts.map +1 -0
  290. package/dist/shared/lib/clock/index.js +2 -0
  291. package/dist/shared/lib/clock/index.js.map +1 -0
  292. package/dist/shared/lib/cookies/index.d.ts +3 -0
  293. package/dist/shared/lib/cookies/index.d.ts.map +1 -0
  294. package/dist/shared/lib/cookies/index.js +2 -0
  295. package/dist/shared/lib/cookies/index.js.map +1 -0
  296. package/dist/shared/lib/cookies/set-cookie.d.ts +22 -0
  297. package/dist/shared/lib/cookies/set-cookie.d.ts.map +1 -0
  298. package/dist/shared/lib/cookies/set-cookie.js +89 -0
  299. package/dist/shared/lib/cookies/set-cookie.js.map +1 -0
  300. package/dist/shared/lib/envelope/envelope.d.ts +50 -0
  301. package/dist/shared/lib/envelope/envelope.d.ts.map +1 -0
  302. package/dist/shared/lib/envelope/envelope.js +111 -0
  303. package/dist/shared/lib/envelope/envelope.js.map +1 -0
  304. package/dist/shared/lib/envelope/index.d.ts +3 -0
  305. package/dist/shared/lib/envelope/index.d.ts.map +1 -0
  306. package/dist/shared/lib/envelope/index.js +2 -0
  307. package/dist/shared/lib/envelope/index.js.map +1 -0
  308. package/dist/shared/lib/errors/index.d.ts +3 -0
  309. package/dist/shared/lib/errors/index.d.ts.map +1 -0
  310. package/dist/shared/lib/errors/index.js +2 -0
  311. package/dist/shared/lib/errors/index.js.map +1 -0
  312. package/dist/shared/lib/errors/wirestage-error.d.ts +28 -0
  313. package/dist/shared/lib/errors/wirestage-error.d.ts.map +1 -0
  314. package/dist/shared/lib/errors/wirestage-error.js +16 -0
  315. package/dist/shared/lib/errors/wirestage-error.js.map +1 -0
  316. package/dist/shared/lib/id/id.d.ts +10 -0
  317. package/dist/shared/lib/id/id.d.ts.map +1 -0
  318. package/dist/shared/lib/id/id.js +26 -0
  319. package/dist/shared/lib/id/id.js.map +1 -0
  320. package/dist/shared/lib/id/index.d.ts +2 -0
  321. package/dist/shared/lib/id/index.d.ts.map +1 -0
  322. package/dist/shared/lib/id/index.js +2 -0
  323. package/dist/shared/lib/id/index.js.map +1 -0
  324. package/dist/shared/lib/logger/index.d.ts +3 -0
  325. package/dist/shared/lib/logger/index.d.ts.map +1 -0
  326. package/dist/shared/lib/logger/index.js +2 -0
  327. package/dist/shared/lib/logger/index.js.map +1 -0
  328. package/dist/shared/lib/logger/logger.d.ts +46 -0
  329. package/dist/shared/lib/logger/logger.d.ts.map +1 -0
  330. package/dist/shared/lib/logger/logger.js +67 -0
  331. package/dist/shared/lib/logger/logger.js.map +1 -0
  332. package/dist/shared/lib/match/index.d.ts +3 -0
  333. package/dist/shared/lib/match/index.d.ts.map +1 -0
  334. package/dist/shared/lib/match/index.js +2 -0
  335. package/dist/shared/lib/match/index.js.map +1 -0
  336. package/dist/shared/lib/match/url-matcher.d.ts +27 -0
  337. package/dist/shared/lib/match/url-matcher.d.ts.map +1 -0
  338. package/dist/shared/lib/match/url-matcher.js +88 -0
  339. package/dist/shared/lib/match/url-matcher.js.map +1 -0
  340. package/dist/sse.d.ts +3 -0
  341. package/dist/sse.d.ts.map +1 -0
  342. package/dist/sse.js +3 -0
  343. package/dist/sse.js.map +1 -0
  344. package/dist/webrtc.d.ts +3 -0
  345. package/dist/webrtc.d.ts.map +1 -0
  346. package/dist/webrtc.js +3 -0
  347. package/dist/webrtc.js.map +1 -0
  348. package/dist/websocket.d.ts +3 -0
  349. package/dist/websocket.d.ts.map +1 -0
  350. package/dist/websocket.js +3 -0
  351. package/dist/websocket.js.map +1 -0
  352. package/dist/widgets/stage/index.d.ts +3 -0
  353. package/dist/widgets/stage/index.d.ts.map +1 -0
  354. package/dist/widgets/stage/index.js +2 -0
  355. package/dist/widgets/stage/index.js.map +1 -0
  356. package/dist/widgets/stage/lib/attached-context.d.ts +10 -0
  357. package/dist/widgets/stage/lib/attached-context.d.ts.map +1 -0
  358. package/dist/widgets/stage/lib/attached-context.js +2 -0
  359. package/dist/widgets/stage/lib/attached-context.js.map +1 -0
  360. package/dist/widgets/stage/lib/create-stage.d.ts +12 -0
  361. package/dist/widgets/stage/lib/create-stage.d.ts.map +1 -0
  362. package/dist/widgets/stage/lib/create-stage.js +250 -0
  363. package/dist/widgets/stage/lib/create-stage.js.map +1 -0
  364. package/dist/widgets/stage/lib/http-router.d.ts +44 -0
  365. package/dist/widgets/stage/lib/http-router.d.ts.map +1 -0
  366. package/dist/widgets/stage/lib/http-router.js +696 -0
  367. package/dist/widgets/stage/lib/http-router.js.map +1 -0
  368. package/dist/widgets/stage/lib/page-host.d.ts +36 -0
  369. package/dist/widgets/stage/lib/page-host.d.ts.map +1 -0
  370. package/dist/widgets/stage/lib/page-host.js +240 -0
  371. package/dist/widgets/stage/lib/page-host.js.map +1 -0
  372. package/dist/widgets/stage/lib/page-runtime.d.ts +93 -0
  373. package/dist/widgets/stage/lib/page-runtime.d.ts.map +1 -0
  374. package/dist/widgets/stage/lib/page-runtime.js +247 -0
  375. package/dist/widgets/stage/lib/page-runtime.js.map +1 -0
  376. package/dist/widgets/stage/lib/reads.d.ts +21 -0
  377. package/dist/widgets/stage/lib/reads.d.ts.map +1 -0
  378. package/dist/widgets/stage/lib/reads.js +97 -0
  379. package/dist/widgets/stage/lib/reads.js.map +1 -0
  380. package/dist/widgets/stage/lib/response-stream.d.ts +23 -0
  381. package/dist/widgets/stage/lib/response-stream.d.ts.map +1 -0
  382. package/dist/widgets/stage/lib/response-stream.js +108 -0
  383. package/dist/widgets/stage/lib/response-stream.js.map +1 -0
  384. package/dist/widgets/stage/lib/socket-router.d.ts +25 -0
  385. package/dist/widgets/stage/lib/socket-router.d.ts.map +1 -0
  386. package/dist/widgets/stage/lib/socket-router.js +233 -0
  387. package/dist/widgets/stage/lib/socket-router.js.map +1 -0
  388. package/dist/widgets/stage/model/stage.d.ts +88 -0
  389. package/dist/widgets/stage/model/stage.d.ts.map +1 -0
  390. package/dist/widgets/stage/model/stage.js +2 -0
  391. package/dist/widgets/stage/model/stage.js.map +1 -0
  392. package/package.json +140 -5
  393. package/src/app/playwright/index.ts +7 -0
  394. package/src/app/playwright/lib/fixture.ts +201 -0
  395. package/src/connect.ts +6 -0
  396. package/src/entities/frame/@x/plugin.ts +8 -0
  397. package/src/entities/frame/index.ts +19 -0
  398. package/src/entities/frame/lib/filter.ts +58 -0
  399. package/src/entities/frame/lib/payload.ts +42 -0
  400. package/src/entities/frame/lib/snapshot.ts +105 -0
  401. package/src/entities/frame/lib/timeline.ts +147 -0
  402. package/src/entities/frame/model/frame.ts +191 -0
  403. package/src/entities/plugin/index.ts +36 -0
  404. package/src/entities/plugin/lib/define-plugin.ts +12 -0
  405. package/src/entities/plugin/lib/named.ts +21 -0
  406. package/src/entities/plugin/model/attachment.ts +18 -0
  407. package/src/entities/plugin/model/http.ts +156 -0
  408. package/src/entities/plugin/model/page.ts +110 -0
  409. package/src/entities/plugin/model/plugin.ts +96 -0
  410. package/src/entities/plugin/model/socket.ts +113 -0
  411. package/src/entities/plugin/model/stream.ts +46 -0
  412. package/src/features/connect/index.ts +23 -0
  413. package/src/features/connect/lib/calls.ts +58 -0
  414. package/src/features/connect/lib/client.ts +203 -0
  415. package/src/features/connect/lib/create-connect.ts +164 -0
  416. package/src/features/connect/lib/faults.ts +132 -0
  417. package/src/features/connect/lib/protocol.ts +321 -0
  418. package/src/features/connect/lib/record.ts +48 -0
  419. package/src/features/connect/lib/recorder.ts +306 -0
  420. package/src/features/connect/lib/rules.ts +385 -0
  421. package/src/features/connect/lib/serve.ts +114 -0
  422. package/src/features/connect/lib/status-of.ts +20 -0
  423. package/src/features/connect/lib/stubs.ts +282 -0
  424. package/src/features/connect/lib/universal.ts +100 -0
  425. package/src/features/connect/model/types.ts +305 -0
  426. package/src/features/rest/index.ts +23 -0
  427. package/src/features/rest/lib/calls.ts +31 -0
  428. package/src/features/rest/lib/create-rest.ts +169 -0
  429. package/src/features/rest/lib/path.ts +70 -0
  430. package/src/features/rest/lib/reply.ts +74 -0
  431. package/src/features/rest/model/types.ts +113 -0
  432. package/src/features/sse/index.ts +10 -0
  433. package/src/features/sse/lib/create-sse.ts +129 -0
  434. package/src/features/sse/lib/encode.ts +28 -0
  435. package/src/features/sse/model/types.ts +72 -0
  436. package/src/features/webrtc/index.ts +6 -0
  437. package/src/features/webrtc/lib/create-webrtc.ts +149 -0
  438. package/src/features/webrtc/lib/rtc-page-module.ts +189 -0
  439. package/src/features/webrtc/lib/to-frame.ts +63 -0
  440. package/src/features/webrtc/model/types.ts +80 -0
  441. package/src/features/websocket/index.ts +15 -0
  442. package/src/features/websocket/lib/create-websocket.ts +41 -0
  443. package/src/features/websocket/lib/mock.ts +114 -0
  444. package/src/features/websocket/lib/proxy.ts +153 -0
  445. package/src/features/websocket/lib/wire.ts +78 -0
  446. package/src/features/websocket/model/types.ts +134 -0
  447. package/src/index.ts +111 -0
  448. package/src/playwright.ts +8 -0
  449. package/src/rest.ts +2 -0
  450. package/src/shared/api/http-server/index.ts +2 -0
  451. package/src/shared/api/http-server/listen.ts +71 -0
  452. package/src/shared/api/playwright/index.ts +17 -0
  453. package/src/shared/api/playwright/ports.ts +150 -0
  454. package/src/shared/api/stream-server/index.ts +7 -0
  455. package/src/shared/api/stream-server/stream-server.ts +254 -0
  456. package/src/shared/lib/async/arrivals.ts +91 -0
  457. package/src/shared/lib/async/async-queue.ts +101 -0
  458. package/src/shared/lib/async/deferred.ts +38 -0
  459. package/src/shared/lib/async/index.ts +11 -0
  460. package/src/shared/lib/async/run-all.ts +19 -0
  461. package/src/shared/lib/bytes/bytes.ts +53 -0
  462. package/src/shared/lib/bytes/index.ts +9 -0
  463. package/src/shared/lib/clock/clock.ts +114 -0
  464. package/src/shared/lib/clock/index.ts +2 -0
  465. package/src/shared/lib/cookies/index.ts +2 -0
  466. package/src/shared/lib/cookies/set-cookie.ts +105 -0
  467. package/src/shared/lib/envelope/envelope.ts +133 -0
  468. package/src/shared/lib/envelope/index.ts +9 -0
  469. package/src/shared/lib/errors/index.ts +5 -0
  470. package/src/shared/lib/errors/wirestage-error.ts +50 -0
  471. package/src/shared/lib/id/id.ts +29 -0
  472. package/src/shared/lib/id/index.ts +1 -0
  473. package/src/shared/lib/logger/index.ts +12 -0
  474. package/src/shared/lib/logger/logger.ts +126 -0
  475. package/src/shared/lib/match/index.ts +6 -0
  476. package/src/shared/lib/match/url-matcher.ts +107 -0
  477. package/src/sse.ts +2 -0
  478. package/src/webrtc.ts +2 -0
  479. package/src/websocket.ts +2 -0
  480. package/src/widgets/stage/index.ts +9 -0
  481. package/src/widgets/stage/lib/attached-context.ts +10 -0
  482. package/src/widgets/stage/lib/create-stage.ts +307 -0
  483. package/src/widgets/stage/lib/http-router.ts +954 -0
  484. package/src/widgets/stage/lib/page-host.ts +375 -0
  485. package/src/widgets/stage/lib/page-runtime.ts +314 -0
  486. package/src/widgets/stage/lib/reads.ts +128 -0
  487. package/src/widgets/stage/lib/response-stream.ts +139 -0
  488. package/src/widgets/stage/lib/socket-router.ts +300 -0
  489. package/src/widgets/stage/model/stage.ts +110 -0
@@ -0,0 +1,113 @@
1
+ import type { Frame } from "../../../entities/frame/index.js";
2
+ import type {
3
+ AbortErrorCode,
4
+ HttpRequestView,
5
+ } from "../../../entities/plugin/index.js";
6
+ import type { UrlMatcher } from "../../../shared/lib/match/index.js";
7
+
8
+ export type RestMethod =
9
+ | "GET"
10
+ | "POST"
11
+ | "PUT"
12
+ | "PATCH"
13
+ | "DELETE"
14
+ | "HEAD"
15
+ | "OPTIONS";
16
+
17
+ export interface RestOptions {
18
+ /** A path prefix (`/api/v1`) or an origin (`https://api.example.test`) routes are relative to. */
19
+ readonly baseURL?: string;
20
+ /**
21
+ * Record requests that no route answers, with their real responses.
22
+ * `true` records everything under `baseURL`. A request a route does
23
+ * answer is recorded by that route, not twice.
24
+ */
25
+ readonly record?: boolean | UrlMatcher;
26
+ }
27
+
28
+ export interface RestRequest extends HttpRequestView {
29
+ /** `:name` segments of the matched path. */
30
+ readonly params: Readonly<Record<string, string>>;
31
+ readonly query: URLSearchParams;
32
+ }
33
+
34
+ /** What a resolver answers. Build one with the helpers: `json`, `text`, `abort`, ... */
35
+ export type RestReply =
36
+ | {
37
+ readonly kind: "response";
38
+ readonly status: number;
39
+ readonly headers: Readonly<Record<string, string>>;
40
+ readonly body?: Uint8Array | string;
41
+ }
42
+ | { readonly kind: "passthrough" }
43
+ | { readonly kind: "abort"; readonly error: AbortErrorCode }
44
+ | { readonly kind: "delay"; readonly ms: number; readonly reply: RestReply };
45
+
46
+ export type RestResolver =
47
+ | RestReply
48
+ | ((request: RestRequest) => RestReply | Promise<RestReply>);
49
+
50
+ export interface RestRouteOptions {
51
+ readonly times?: number;
52
+ readonly match?: (request: RestRequest) => boolean;
53
+ }
54
+
55
+ export interface RestRoute {
56
+ /** Requests this route answered. */
57
+ readonly hits: number;
58
+ readonly requests: readonly RestRequest[];
59
+ /** The next request this route answers (or answered and nobody took). */
60
+ next(options?: { readonly timeout?: number }): Promise<RestRequest>;
61
+ remove(): Promise<void>;
62
+ }
63
+
64
+ export interface RestCall {
65
+ readonly correlation: string;
66
+ readonly method: string;
67
+ readonly url: string;
68
+ readonly request: Frame;
69
+ readonly response: Frame | undefined;
70
+ /** The HTTP status; undefined for an aborted or unfinished request. */
71
+ readonly status: number | undefined;
72
+ }
73
+
74
+ export type RestPath = string | RegExp;
75
+
76
+ export interface RestApi {
77
+ on(
78
+ method: RestMethod | "*",
79
+ path: RestPath,
80
+ resolver: RestResolver,
81
+ options?: RestRouteOptions,
82
+ ): Promise<RestRoute>;
83
+ get(
84
+ path: RestPath,
85
+ resolver: RestResolver,
86
+ options?: RestRouteOptions,
87
+ ): Promise<RestRoute>;
88
+ post(
89
+ path: RestPath,
90
+ resolver: RestResolver,
91
+ options?: RestRouteOptions,
92
+ ): Promise<RestRoute>;
93
+ put(
94
+ path: RestPath,
95
+ resolver: RestResolver,
96
+ options?: RestRouteOptions,
97
+ ): Promise<RestRoute>;
98
+ patch(
99
+ path: RestPath,
100
+ resolver: RestResolver,
101
+ options?: RestRouteOptions,
102
+ ): Promise<RestRoute>;
103
+ delete(
104
+ path: RestPath,
105
+ resolver: RestResolver,
106
+ options?: RestRouteOptions,
107
+ ): Promise<RestRoute>;
108
+ /** Recorded REST exchanges, oldest first. */
109
+ calls(filter?: {
110
+ readonly method?: string;
111
+ readonly path?: RestPath;
112
+ }): Promise<RestCall[]>;
113
+ }
@@ -0,0 +1,10 @@
1
+ export { sse } from "./lib/create-sse.js";
2
+ export { encodeSseEvent } from "./lib/encode.js";
3
+ export type {
4
+ SseApi,
5
+ SseConnection,
6
+ SseEnd,
7
+ SseEvent,
8
+ SseStreamOptions,
9
+ SseStub,
10
+ } from "./model/types.js";
@@ -0,0 +1,129 @@
1
+ import {
2
+ definePlugin,
3
+ type WirePlugin,
4
+ } from "../../../entities/plugin/index.js";
5
+ import {
6
+ type Arrivals,
7
+ createArrivals,
8
+ } from "../../../shared/lib/async/index.js";
9
+ import type { SseApi, SseConnection, SseEvent } from "../model/types.js";
10
+ import { encodeSseEvent } from "./encode.js";
11
+
12
+ /**
13
+ * Server-sent events: answer an EventSource (or a fetch reading an event
14
+ * stream) with a response the test writes one event at a time, with clean
15
+ * ends and cuts that exercise the client's reconnect.
16
+ */
17
+ export function sse(): WirePlugin<"sse", SseApi> {
18
+ return definePlugin({
19
+ name: "sse",
20
+ scope: "Sse",
21
+ setup(context) {
22
+ const { name } = context;
23
+ const api: SseApi = {
24
+ async stream(url, streamOptions = {}) {
25
+ /** Requests the stub answered: each should become a connection. */
26
+ let taken = 0;
27
+ const connections: Arrivals<SseConnection> = createArrivals({
28
+ clock: context.clock,
29
+ label: "SSE connection",
30
+ // The stub answered, yet no connection came of it: the
31
+ // redirect to the stream server is what went missing.
32
+ hint: (): string | undefined =>
33
+ taken > connections.items.length
34
+ ? `${taken - connections.items.length} request(s) matched but never reached the local stream server — see "Live streams" in docs/troubleshooting.md`
35
+ : undefined,
36
+ });
37
+ const rule = await context.http.add({
38
+ protocol: name,
39
+ label: `sse ${String(url)}`,
40
+ url,
41
+ ...(streamOptions.times === undefined
42
+ ? {}
43
+ : { times: streamOptions.times }),
44
+ ...(streamOptions.match === undefined
45
+ ? {}
46
+ : { match: streamOptions.match }),
47
+ handle(view) {
48
+ taken += 1;
49
+ return {
50
+ type: "stream",
51
+ open(stream) {
52
+ stream.head(streamOptions.status ?? 200, {
53
+ "content-type": "text/event-stream; charset=utf-8",
54
+ "cache-control": "no-cache",
55
+ ...streamOptions.headers,
56
+ });
57
+ // An EventSource opens on the headers, before any event.
58
+ stream.flush();
59
+ let sent = 0;
60
+ const connection: SseConnection = {
61
+ request: view,
62
+ lastEventId: stream.requestHeaders["last-event-id"],
63
+ get sent() {
64
+ return sent;
65
+ },
66
+ async send(event: SseEvent | string) {
67
+ const chunk = encodeSseEvent(event);
68
+ await stream.write(chunk);
69
+ sent += 1;
70
+ context.timeline.append({
71
+ protocol: name,
72
+ kind: "event",
73
+ direction: "in",
74
+ endpoint: view.url.pathname,
75
+ source: "stub",
76
+ data: chunk,
77
+ value: event,
78
+ correlation: view.correlation,
79
+ context: view.context,
80
+ ...(view.page === undefined ? {} : { page: view.page }),
81
+ });
82
+ },
83
+ end: () => stream.end(),
84
+ async cut() {
85
+ stream.cut();
86
+ // The stage records the end before `closed` resolves.
87
+ await stream.closed;
88
+ },
89
+ closed: stream.closed,
90
+ };
91
+ if (streamOptions.retry !== undefined)
92
+ void stream
93
+ .write(`retry: ${streamOptions.retry}\n\n`)
94
+ .catch(() => {});
95
+ connections.push(connection);
96
+ },
97
+ };
98
+ },
99
+ });
100
+ return {
101
+ get hits() {
102
+ return rule.hits;
103
+ },
104
+ next: (waitOptions) => connections.next(waitOptions),
105
+ get connections() {
106
+ return connections.items;
107
+ },
108
+ async broadcast(event) {
109
+ for (const connection of connections.items)
110
+ await connection.send(event).catch(() => {});
111
+ },
112
+ remove: () => rule.remove(),
113
+ };
114
+ },
115
+ async events(filter = {}) {
116
+ await context.sync();
117
+ return context.timeline.frames({
118
+ protocol: name,
119
+ kind: "event",
120
+ ...(filter.endpoint === undefined
121
+ ? {}
122
+ : { endpoint: filter.endpoint }),
123
+ });
124
+ },
125
+ };
126
+ return { api };
127
+ },
128
+ });
129
+ }
@@ -0,0 +1,28 @@
1
+ import type { SseEvent } from "../model/types.js";
2
+
3
+ /**
4
+ * Encode one event in the text/event-stream format. Multi-line data becomes
5
+ * several `data:` lines — a single line with an embedded newline would end
6
+ * the field early and the client would see half the payload.
7
+ */
8
+ export function encodeSseEvent(event: SseEvent | string): string {
9
+ if (typeof event === "string") return `${dataLines(event)}\n`;
10
+ let out = "";
11
+ if (event.comment !== undefined)
12
+ for (const line of event.comment.split(/\r?\n/)) out += `: ${line}\n`;
13
+ if (event.event !== undefined) out += `event: ${event.event}\n`;
14
+ if (event.id !== undefined) out += `id: ${event.id}\n`;
15
+ if (event.retry !== undefined) out += `retry: ${event.retry}\n`;
16
+ if (event.data !== undefined)
17
+ out += dataLines(
18
+ typeof event.data === "string" ? event.data : JSON.stringify(event.data),
19
+ );
20
+ return `${out}\n`;
21
+ }
22
+
23
+ function dataLines(text: string): string {
24
+ return text
25
+ .split(/\r?\n/)
26
+ .map((line) => `data: ${line}\n`)
27
+ .join("");
28
+ }
@@ -0,0 +1,72 @@
1
+ import type { Frame } from "../../../entities/frame/index.js";
2
+ import type {
3
+ HttpRequestView,
4
+ StreamEnd,
5
+ } from "../../../entities/plugin/index.js";
6
+ import type { UrlMatcher } from "../../../shared/lib/match/index.js";
7
+
8
+ /** One server-sent event. A non-string `data` is sent as JSON. */
9
+ export interface SseEvent {
10
+ readonly data?: unknown;
11
+ readonly event?: string;
12
+ readonly id?: string;
13
+ /** Reconnection delay the client should use, in milliseconds. */
14
+ readonly retry?: number;
15
+ /** A `:` comment line, e.g. a keep-alive. */
16
+ readonly comment?: string;
17
+ }
18
+
19
+ /** How a connection ended. `cancelled` means the page closed it. */
20
+ export type SseEnd = StreamEnd;
21
+
22
+ export interface SseConnection {
23
+ readonly request: HttpRequestView;
24
+ /**
25
+ * The `Last-Event-ID` the client reconnected with. Read off the request the
26
+ * stream server received: the browser's network stack adds it after
27
+ * Playwright's route has seen the request, so `request.headers` lacks it.
28
+ */
29
+ readonly lastEventId: string | undefined;
30
+ /** Events written so far. */
31
+ readonly sent: number;
32
+ /** Write an event. A string is sent as `data`. Resolves once written. */
33
+ send(event: SseEvent | string): Promise<void>;
34
+ /**
35
+ * Close cleanly; EventSource will reconnect after its retry delay.
36
+ * Resolves once the end is on the timeline.
37
+ */
38
+ end(): Promise<void>;
39
+ /**
40
+ * Reset the connection, as a network drop would. Resolves once the cut is
41
+ * on the timeline.
42
+ */
43
+ cut(): Promise<void>;
44
+ readonly closed: Promise<SseEnd>;
45
+ }
46
+
47
+ export interface SseStreamOptions {
48
+ readonly times?: number;
49
+ readonly status?: number;
50
+ readonly headers?: Readonly<Record<string, string>>;
51
+ /** Sent before anything else: the client's reconnection delay. */
52
+ readonly retry?: number;
53
+ readonly match?: (request: HttpRequestView) => boolean;
54
+ }
55
+
56
+ export interface SseStub {
57
+ /** Requests this stub answered. */
58
+ readonly hits: number;
59
+ /** The next connection the page opens (or opened and nobody took). */
60
+ next(options?: { readonly timeout?: number }): Promise<SseConnection>;
61
+ readonly connections: readonly SseConnection[];
62
+ /** Send to every open connection. */
63
+ broadcast(event: SseEvent | string): Promise<void>;
64
+ remove(): Promise<void>;
65
+ }
66
+
67
+ export interface SseApi {
68
+ /** Answer matching requests with an event stream the test drives. */
69
+ stream(url: UrlMatcher, options?: SseStreamOptions): Promise<SseStub>;
70
+ /** Events sent so far, oldest first. */
71
+ events(filter?: { readonly endpoint?: string | RegExp }): Promise<Frame[]>;
72
+ }
@@ -0,0 +1,6 @@
1
+ export { webrtc } from "./lib/create-webrtc.js";
2
+ export type {
3
+ RtcDirection,
4
+ WebRtcApi,
5
+ WebRtcOptions,
6
+ } from "./model/types.js";
@@ -0,0 +1,149 @@
1
+ import {
2
+ definePlugin,
3
+ type WirePlugin,
4
+ } from "../../../entities/plugin/index.js";
5
+ import { toBase64, toBytes } from "../../../shared/lib/bytes/index.js";
6
+ import { WirestageError } from "../../../shared/lib/errors/index.js";
7
+ import type {
8
+ RtcInjectArgs,
9
+ RtcPageConfig,
10
+ RtcPageState,
11
+ WebRtcApi,
12
+ WebRtcOptions,
13
+ } from "../model/types.js";
14
+ import { installWebRtc } from "./rtc-page-module.js";
15
+ import { toFrame } from "./to-frame.js";
16
+
17
+ /** The calls in force on each label; a label with none is not suppressed. */
18
+ type Suppressions = Map<string, Set<symbol>>;
19
+
20
+ /**
21
+ * WebRTC data channels: record what the page sends and receives, drop
22
+ * messages on one channel while it stays open, and inject messages as the
23
+ * remote peer.
24
+ */
25
+ export function webrtc(
26
+ options: WebRtcOptions = {},
27
+ ): WirePlugin<"webrtc", WebRtcApi> {
28
+ return definePlugin({
29
+ name: "webrtc",
30
+ scope: "Rtc",
31
+ setup(context) {
32
+ const { name } = context;
33
+ const suppressIn: Suppressions = new Map();
34
+ const blockOut: Suppressions = new Map();
35
+ const directions = options.record ?? ["in", "out"];
36
+ const config: RtcPageConfig = {
37
+ labels: options.labels ?? null,
38
+ recordIn: directions.includes("in"),
39
+ recordOut: directions.includes("out"),
40
+ lifecycle: options.lifecycle ?? true,
41
+ };
42
+ context.pages.register<RtcPageState, RtcPageConfig>({
43
+ id: name,
44
+ install: installWebRtc,
45
+ config,
46
+ state: () => ({
47
+ suppressIn: [...suppressIn.keys()],
48
+ blockOut: [...blockOut.keys()],
49
+ }),
50
+ receive(entries, origin) {
51
+ for (const entry of entries)
52
+ context.timeline.append(toFrame(name, entry, origin));
53
+ },
54
+ });
55
+ const refresh = () => context.pages.refresh(name);
56
+ const hold = (held: Suppressions, label: string, token: symbol) => {
57
+ const tokens = held.get(label) ?? new Set();
58
+ tokens.add(token);
59
+ held.set(label, tokens);
60
+ };
61
+ const release = (held: Suppressions, label: string, token: symbol) => {
62
+ const tokens = held.get(label);
63
+ // Gone already when `restore` cleared the label first.
64
+ if (tokens === undefined) return;
65
+ tokens.delete(token);
66
+ if (tokens.size === 0) held.delete(label);
67
+ };
68
+
69
+ const api: WebRtcApi = {
70
+ async frames(filter = {}) {
71
+ await context.sync();
72
+ return context.timeline.frames({ ...filter, protocol: name });
73
+ },
74
+ async messages(label, messageOptions = {}) {
75
+ await context.sync();
76
+ return context.timeline.frames({
77
+ protocol: name,
78
+ kind: "message",
79
+ endpoint: label,
80
+ ...(messageOptions.direction === undefined
81
+ ? {}
82
+ : { direction: messageOptions.direction }),
83
+ });
84
+ },
85
+ async suppress(label, suppressOptions = {}) {
86
+ const direction = suppressOptions.direction ?? "in";
87
+ const token = Symbol(label);
88
+ if (direction !== "out") hold(suppressIn, label, token);
89
+ if (direction !== "in") hold(blockOut, label, token);
90
+ await refresh();
91
+ return async () => {
92
+ release(suppressIn, label, token);
93
+ release(blockOut, label, token);
94
+ await refresh();
95
+ };
96
+ },
97
+ async restore(label) {
98
+ if (label === undefined) {
99
+ suppressIn.clear();
100
+ blockOut.clear();
101
+ } else {
102
+ suppressIn.delete(label);
103
+ blockOut.delete(label);
104
+ }
105
+ await refresh();
106
+ },
107
+ async inject(label, data, injectOptions = {}) {
108
+ const args: RtcInjectArgs =
109
+ typeof data === "string"
110
+ ? { label, text: data }
111
+ : { label, data: toBase64(toBytes(data)) };
112
+ const results = await context.pages.command(
113
+ name,
114
+ "inject",
115
+ args,
116
+ injectOptions.page === undefined
117
+ ? {}
118
+ : { page: context.pages.pageId(injectOptions.page) },
119
+ );
120
+ return results.reduce<number>(
121
+ (sum, delivered) => sum + Number(delivered),
122
+ 0,
123
+ );
124
+ },
125
+ async waitForChannel(label, waitOptions = {}) {
126
+ if (!config.lifecycle)
127
+ // It would only ever time out: there is no open frame to wait for.
128
+ throw new WirestageError(
129
+ "INVALID_STATE",
130
+ `waitForChannel("${label}") needs lifecycle frames, and this plugin was created with \`lifecycle: false\``,
131
+ );
132
+ const [frame] = await context.waitFor(
133
+ {
134
+ protocol: name,
135
+ kind: "lifecycle",
136
+ endpoint: label,
137
+ where: (f) => f.meta["event"] === "open",
138
+ },
139
+ waitOptions.timeout === undefined
140
+ ? {}
141
+ : { timeout: waitOptions.timeout },
142
+ );
143
+ return frame as NonNullable<typeof frame>;
144
+ },
145
+ };
146
+ return { api };
147
+ },
148
+ });
149
+ }
@@ -0,0 +1,189 @@
1
+ import type { PageModuleEnv } from "../../../entities/plugin/index.js";
2
+ import type {
3
+ RtcInjectArgs,
4
+ RtcPageConfig,
5
+ RtcPageState,
6
+ } from "../model/types.js";
7
+
8
+ /**
9
+ * Runs INSIDE the page (serialized): self-contained, `function` declaration.
10
+ *
11
+ * Three hooks, each placed so the page's own code is untouched:
12
+ *
13
+ * - `RTCDataChannel.prototype.send` is wrapped, so every send on every channel
14
+ * is seen as it leaves — the last point the page and a real peer agree on.
15
+ * - `createDataChannel` is wrapped and the constructor is replaced by one that
16
+ * adds a `datachannel` listener first, so every channel — page-created or
17
+ * remote-created — gets OUR `message` listener before the page's. Only a
18
+ * first listener can `stopImmediatePropagation()` the rest, which is what
19
+ * suppression needs; wrapping `onmessage` instead would race the page's own
20
+ * assignment of it.
21
+ * - Injected messages are dispatched as real `MessageEvent`s on the channel,
22
+ * so the page's handlers cannot tell them from the peer's.
23
+ *
24
+ * All three come off again when the stage detaches.
25
+ */
26
+ export function installWebRtc(
27
+ env: PageModuleEnv<RtcPageState, RtcPageConfig>,
28
+ ): void {
29
+ const win = env.window;
30
+ const PeerConnection = win.RTCPeerConnection;
31
+ const DataChannel = win.RTCDataChannel;
32
+ if (typeof PeerConnection !== "function" || typeof DataChannel !== "function")
33
+ return;
34
+ const config = env.config;
35
+ let suppressIn = new Set<string>();
36
+ let blockOut = new Set<string>();
37
+ const open = new Map<string, Set<RTCDataChannel>>();
38
+ const watched = new WeakSet<RTCDataChannel>();
39
+ const injected = new WeakSet<Event>();
40
+ /** Listeners added to the page's channels, to take off on detach. */
41
+ const unwatch: (() => void)[] = [];
42
+
43
+ const wanted = (label: string) =>
44
+ config.labels === null || config.labels.includes(label);
45
+
46
+ const emit = (
47
+ type: string,
48
+ label: string,
49
+ data: unknown,
50
+ extra: Record<string, unknown> = {},
51
+ ) => {
52
+ const meta: Record<string, unknown> = { label, ...extra };
53
+ let payload: { data?: string; text?: string } = {};
54
+ if (typeof data === "string") payload = { text: data };
55
+ else if (data instanceof ArrayBuffer)
56
+ payload = { data: env.encode(new Uint8Array(data)) };
57
+ else if (ArrayBuffer.isView(data))
58
+ payload = {
59
+ data: env.encode(
60
+ new Uint8Array(data.buffer, data.byteOffset, data.byteLength),
61
+ ),
62
+ };
63
+ // A Blob would need an async read, which would put it out of order with
64
+ // the frames around it; it is recorded as skipped instead.
65
+ else if (data !== undefined) meta["skipped"] = "blob";
66
+ env.emit({ type, at: env.now(), ...payload, meta });
67
+ };
68
+
69
+ const watch = (channel: RTCDataChannel) => {
70
+ if (watched.has(channel)) return;
71
+ watched.add(channel);
72
+ const label = channel.label;
73
+ const track = () => {
74
+ let set = open.get(label);
75
+ if (set === undefined) {
76
+ set = new Set();
77
+ open.set(label, set);
78
+ }
79
+ set.add(channel);
80
+ if (config.lifecycle && wanted(label))
81
+ emit("open", label, undefined, { id: channel.id });
82
+ };
83
+ const untrack = () => {
84
+ open.get(label)?.delete(channel);
85
+ if (config.lifecycle && wanted(label))
86
+ emit("close", label, undefined, { id: channel.id });
87
+ };
88
+ const onMessage = (event: MessageEvent) => {
89
+ const mine = injected.has(event);
90
+ // What the test injects is the point of suppressing the peer:
91
+ // silence the real one, speak in its place.
92
+ if (suppressIn.has(label) && !mine) {
93
+ event.stopImmediatePropagation();
94
+ if (wanted(label)) emit("suppressed", label, event.data);
95
+ return;
96
+ }
97
+ if (config.recordIn && wanted(label))
98
+ emit("in", label, event.data, mine ? { injected: true } : {});
99
+ };
100
+ if (channel.readyState === "open") track();
101
+ else channel.addEventListener("open", track, { once: true });
102
+ channel.addEventListener("close", untrack, { once: true });
103
+ channel.addEventListener("message", onMessage);
104
+ unwatch.push(() => {
105
+ channel.removeEventListener("open", track);
106
+ channel.removeEventListener("close", untrack);
107
+ channel.removeEventListener("message", onMessage);
108
+ });
109
+ };
110
+
111
+ const send = DataChannel.prototype.send;
112
+ DataChannel.prototype.send = function wirestageSend(
113
+ this: RTCDataChannel,
114
+ data: never,
115
+ ): void {
116
+ const label = this.label;
117
+ if (blockOut.has(label)) {
118
+ if (wanted(label)) emit("blocked", label, data);
119
+ return;
120
+ }
121
+ // Recorded once it has left: a send the browser refuses (a channel not
122
+ // open yet) throws here, and must not land on the timeline as sent.
123
+ send.call(this, data);
124
+ if (config.recordOut && wanted(label)) emit("out", label, data);
125
+ };
126
+
127
+ const createDataChannel = PeerConnection.prototype.createDataChannel;
128
+ PeerConnection.prototype.createDataChannel = function wirestageCreate(
129
+ this: RTCPeerConnection,
130
+ label: string,
131
+ init?: RTCDataChannelInit,
132
+ ): RTCDataChannel {
133
+ const channel = createDataChannel.call(this, label, init);
134
+ watch(channel);
135
+ return channel;
136
+ };
137
+
138
+ function WirestagePeerConnection(
139
+ ...args: ConstructorParameters<typeof RTCPeerConnection>
140
+ ): RTCPeerConnection {
141
+ // `new.target`, so a page's own subclass still gets its prototype —
142
+ // and, as with the real constructor, calling it without `new` throws.
143
+ const connection: RTCPeerConnection = Reflect.construct(
144
+ PeerConnection,
145
+ args,
146
+ new.target as never,
147
+ );
148
+ connection.addEventListener("datachannel", (event: RTCDataChannelEvent) =>
149
+ watch(event.channel),
150
+ );
151
+ return connection;
152
+ }
153
+ // Same prototype, so `instanceof RTCPeerConnection` and the patched
154
+ // methods above keep working; statics such as generateCertificate come
155
+ // through the prototype chain of the function itself.
156
+ WirestagePeerConnection.prototype = PeerConnection.prototype;
157
+ Object.setPrototypeOf(WirestagePeerConnection, PeerConnection);
158
+ win.RTCPeerConnection =
159
+ WirestagePeerConnection as unknown as typeof RTCPeerConnection;
160
+
161
+ env.onState((state) => {
162
+ suppressIn = new Set(state.suppressIn);
163
+ blockOut = new Set(state.blockOut);
164
+ });
165
+ env.onCommand("inject", (args: RtcInjectArgs) => {
166
+ let delivered = 0;
167
+ for (const channel of open.get(args.label) ?? []) {
168
+ if (channel.readyState !== "open") continue;
169
+ let data: unknown = args.text;
170
+ if (data === undefined) {
171
+ const buffer = env.decode(args.data ?? "").buffer as ArrayBuffer;
172
+ data = channel.binaryType === "blob" ? new win.Blob([buffer]) : buffer;
173
+ }
174
+ const event = new win.MessageEvent("message", { data });
175
+ injected.add(event);
176
+ channel.dispatchEvent(event);
177
+ delivered += 1;
178
+ }
179
+ return delivered;
180
+ });
181
+ env.onDetach(() => {
182
+ DataChannel.prototype.send = send;
183
+ PeerConnection.prototype.createDataChannel = createDataChannel;
184
+ win.RTCPeerConnection = PeerConnection;
185
+ for (const undo of unwatch.splice(0)) undo();
186
+ suppressIn = new Set();
187
+ blockOut = new Set();
188
+ });
189
+ }