@heojeongbo/wirestage 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +126 -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 +102 -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 +43 -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 +120 -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 +62 -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 +107 -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 +129 -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 +117 -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 +43 -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 +160 -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 +248 -0
  363. package/dist/widgets/stage/lib/create-stage.js.map +1 -0
  364. package/dist/widgets/stage/lib/http-router.d.ts +35 -0
  365. package/dist/widgets/stage/lib/http-router.d.ts.map +1 -0
  366. package/dist/widgets/stage/lib/http-router.js +455 -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 +9 -0
  381. package/dist/widgets/stage/lib/response-stream.d.ts.map +1 -0
  382. package/dist/widgets/stage/lib/response-stream.js +56 -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 +204 -0
  387. package/dist/widgets/stage/lib/socket-router.js.map +1 -0
  388. package/dist/widgets/stage/model/stage.d.ts +86 -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 +147 -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 +108 -0
  411. package/src/entities/plugin/model/stream.ts +43 -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 +128 -0
  434. package/src/features/sse/lib/encode.ts +28 -0
  435. package/src/features/sse/model/types.ts +66 -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 +141 -0
  445. package/src/features/websocket/lib/wire.ts +78 -0
  446. package/src/features/websocket/model/types.ts +130 -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 +145 -0
  454. package/src/shared/api/stream-server/index.ts +7 -0
  455. package/src/shared/api/stream-server/stream-server.ts +246 -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 +305 -0
  483. package/src/widgets/stage/lib/http-router.ts +620 -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 +66 -0
  488. package/src/widgets/stage/lib/socket-router.ts +271 -0
  489. package/src/widgets/stage/model/stage.ts +108 -0
@@ -0,0 +1,96 @@
1
+ import type { Clock } from "../../../shared/lib/clock/index.js";
2
+ import type {
3
+ LogScope,
4
+ ScopedLogger,
5
+ } from "../../../shared/lib/logger/index.js";
6
+ import type { Frame, FrameFilter, Timeline } from "../../frame/@x/plugin.js";
7
+ import type { Attachment } from "./attachment.js";
8
+ import type { HttpRouter } from "./http.js";
9
+ import type { PageHost } from "./page.js";
10
+ import type { SocketRouter } from "./socket.js";
11
+
12
+ export interface WaitForOptions {
13
+ /** Frames to wait for. Default 1. */
14
+ readonly count?: number;
15
+ /** Milliseconds. Default 5000. */
16
+ readonly timeout?: number;
17
+ /** Milliseconds between polls of the page buffers. Default 25. */
18
+ readonly interval?: number;
19
+ }
20
+
21
+ /**
22
+ * Everything a plugin may use. A plugin never drives the browser itself:
23
+ * routing and page hooks go through these services, built and owned by the
24
+ * stage, and that is the seam that lets each plugin be unit-tested against
25
+ * fakes and lets several plugins share one browser context without stepping
26
+ * on each other.
27
+ */
28
+ export interface PluginContext {
29
+ /**
30
+ * The name the plugin is set up under: its own, or the one `named()` gave
31
+ * it. A plugin records its frames with this as their `protocol` (and
32
+ * names its page modules by it), so two instances keep their traffic
33
+ * apart.
34
+ */
35
+ readonly name: string;
36
+ readonly timeline: Timeline;
37
+ readonly clock: Clock;
38
+ /** Scoped to the plugin's declared scope. */
39
+ readonly log: ScopedLogger;
40
+ /** HTTP: fetch, XHR, EventSource, and anything else a route can see. */
41
+ readonly http: HttpRouter;
42
+ /** WebSockets. */
43
+ readonly sockets: SocketRouter;
44
+ /** Code that runs inside every page, for what no route can see. */
45
+ readonly pages: PageHost;
46
+ readonly baseURL: string | undefined;
47
+ attachments(): readonly Attachment[];
48
+ /** Flush every page buffer into the timeline. Call before reading frames. */
49
+ sync(): Promise<void>;
50
+ waitFor(filter: FrameFilter, options?: WaitForOptions): Promise<Frame[]>;
51
+ /**
52
+ * Build on another plugin: set it up inside this one and get its API. A
53
+ * protocol layered on others (GraphQL over HTTP and WebSocket, say) is
54
+ * written in terms of theirs instead of re-deriving them. The stage runs
55
+ * the inner plugin's hooks with the outer's; it is not mounted on the
56
+ * stage. Wrap it in `named()` to keep its frames apart from another
57
+ * instance's.
58
+ */
59
+ use<Api>(plugin: WirePlugin<string, Api>): Api;
60
+ }
61
+
62
+ export interface PluginRuntime<Api> {
63
+ /** Mounted on the stage under the plugin's name. */
64
+ readonly api: Api;
65
+ /** A context was attached. Page modules and HTTP rules are already in place. */
66
+ attach?(attachment: Attachment): void | Promise<void>;
67
+ /** A context is being detached. */
68
+ detach?(attachment: Attachment): void | Promise<void>;
69
+ /** The stage is being disposed; release servers and timers. */
70
+ dispose?(): void | Promise<void>;
71
+ }
72
+
73
+ /**
74
+ * A protocol, as the stage sees it. The stage knows nothing about any
75
+ * protocol in particular: REST, Connect, WebRTC and the rest are all plugins,
76
+ * and a new protocol is a new plugin, not a change to the stage.
77
+ */
78
+ export interface WirePlugin<Name extends string = string, Api = unknown> {
79
+ /** The key its API is mounted under: `stage.<name>`. */
80
+ readonly name: Name;
81
+ /** The badge on the plugin's log lines; eight characters fit the column. */
82
+ readonly scope?: LogScope;
83
+ setup(context: PluginContext): PluginRuntime<Api>;
84
+ }
85
+
86
+ export type AnyWirePlugin = WirePlugin<string, unknown>;
87
+
88
+ /** `{ webrtc: WebRtcApi, connect: ConnectApi }` from `[webrtc(), connect()]`. */
89
+ export type PluginApis<Plugins extends readonly AnyWirePlugin[]> = {
90
+ readonly [Plugin in Plugins[number] as Plugin["name"]]: Plugin extends WirePlugin<
91
+ string,
92
+ infer Api
93
+ >
94
+ ? Api
95
+ : never;
96
+ };
@@ -0,0 +1,108 @@
1
+ import type { UrlMatcher } from "../../../shared/lib/match/index.js";
2
+
3
+ /** One end of a routed WebSocket: the page's, or the real server's. */
4
+ export interface SocketPeer {
5
+ /** Deliver a message to this end. */
6
+ send(data: string | Uint8Array): void;
7
+ /**
8
+ * Close this end. The page's end takes any code and reason, as a server
9
+ * may close with any code. The server's end is closed by the browser,
10
+ * which sends only 1000 and 3000-4999, no reason without a code, and no
11
+ * reason over 123 bytes of UTF-8: another code goes out as a normal
12
+ * closure, 1000, and the reason is cut to 123 bytes, so the close still
13
+ * happens.
14
+ */
15
+ close(options?: {
16
+ readonly code?: number;
17
+ readonly reason?: string;
18
+ }): Promise<void>;
19
+ /**
20
+ * Messages this end sends. One handler; a later call replaces it. Until
21
+ * one is set, what this end sends is dropped, not passed on to the other.
22
+ */
23
+ onMessage(handler: (data: string | Uint8Array) => void): void;
24
+ /**
25
+ * This end closed, by its own doing or through `close()` — and a close
26
+ * the page starts is reported again once `close()` completes it, so a
27
+ * handler must expect to run twice. Setting one turns off the automatic
28
+ * forwarding of a close to the other end: the page's socket then hangs in
29
+ * CLOSING until the handler closes the page's end itself.
30
+ */
31
+ onClose(
32
+ handler: (code: number | undefined, reason: string | undefined) => void,
33
+ ): void;
34
+ }
35
+
36
+ /** A WebSocket a page opened, as the rule that takes it sees it. */
37
+ export interface SocketView {
38
+ /** Links every frame of this connection. */
39
+ readonly correlation: string;
40
+ readonly url: URL;
41
+ readonly context: string;
42
+ /** The page's end. */
43
+ readonly page: SocketPeer;
44
+ /**
45
+ * Open the real server's end and return it. No message is forwarded for
46
+ * you: a rule that wants a proxy wires the two ends together itself, and
47
+ * a direction it does not wire is dropped — a rule that sets only the
48
+ * page's `onMessage` never lets the server's messages reach the page. A
49
+ * close is passed on to the other end unless the end that closed has an
50
+ * `onClose` handler.
51
+ */
52
+ connectToServer(): SocketPeer;
53
+ }
54
+
55
+ export interface SocketRule {
56
+ /**
57
+ * The `protocol` of this rule's frames: those it records, and the fault
58
+ * the stage records when `open` throws.
59
+ */
60
+ readonly protocol: string;
61
+ /** Names the rule in logs, diagnostics and the fault of a rule that throws. */
62
+ readonly label: string;
63
+ readonly url: UrlMatcher;
64
+ /** Stop taking connections after this many. */
65
+ readonly times?: number;
66
+ /**
67
+ * Take a connection: called once per matching socket. The page's socket
68
+ * opens when this settles — or, for a rule that connects the server,
69
+ * when the server's end opens — and sooner if the rule sends to it. Set
70
+ * the handlers before awaiting anything: an end drops what it sends
71
+ * until it has one.
72
+ *
73
+ * A rule that throws, or rejects, is a bug in the test: the stage logs
74
+ * it, records a `fault` frame (`meta.reason` `rule-error`, `meta.rule`
75
+ * the label, `meta.error` the message) and closes the page's end with
76
+ * 1011 and a reason naming the rule — and the server's end too, if the
77
+ * rule connected it. The ends' `onClose` handlers hear those closes as
78
+ * they hear any other.
79
+ */
80
+ open(socket: SocketView): void | Promise<void>;
81
+ }
82
+
83
+ export interface SocketRuleHandle {
84
+ /** Connections this rule took. */
85
+ readonly hits: number;
86
+ readonly active: boolean;
87
+ /** Stop taking connections. Open ones stay as they are. */
88
+ remove(): Promise<void>;
89
+ }
90
+
91
+ /**
92
+ * Where plugins put WebSocket rules. Rules apply to every attached context,
93
+ * present and future, and the newest rule that matches a socket takes it; a
94
+ * socket no rule takes goes to the real server.
95
+ *
96
+ * Routing replaces `WebSocket` in every document of the context, so it
97
+ * starts with the first rule and a stage with none leaves the page alone.
98
+ * The replacement reaches a document as it loads: a rule added later applies
99
+ * to the documents loaded after it, unless the plugin called `prepare()`.
100
+ */
101
+ export interface SocketRouter {
102
+ add(rule: SocketRule): Promise<SocketRuleHandle>;
103
+ /**
104
+ * Start routing as soon as a context is attached, before any rule exists,
105
+ * so a rule added mid-test reaches the pages already open. Call in `setup`.
106
+ */
107
+ prepare(): void;
108
+ }
@@ -0,0 +1,43 @@
1
+ /** How an open response ended. */
2
+ export type StreamEnd =
3
+ /** The rule ended it cleanly. */
4
+ | "ended"
5
+ /** The rule reset the connection. */
6
+ | "cut"
7
+ /** The page went away: it closed the stream, navigated, or was closed. */
8
+ | "cancelled";
9
+
10
+ /**
11
+ * A response that stays open, written at the test's pace: what a server
12
+ * stream, an event stream or a long poll is made of. A rule gets one by
13
+ * answering `{ type: "stream" }`.
14
+ */
15
+ export interface ResponseStream {
16
+ /**
17
+ * The request's headers as the server received them, names lower-cased.
18
+ * They include what the browser's network stack adds after Playwright's
19
+ * route has seen the request, such as `last-event-id`.
20
+ */
21
+ readonly requestHeaders: Readonly<Record<string, string>>;
22
+ /**
23
+ * Set the status and headers. They go out with the first `write`, or at
24
+ * once on `flush`. Without a call the response is a bare 200.
25
+ */
26
+ head(status: number, headers?: Readonly<Record<string, string>>): void;
27
+ /** Send the head now, before any body: an EventSource opens on it. */
28
+ flush(): void;
29
+ /**
30
+ * Write a chunk. Resolves once it is handed to the socket; rejects with
31
+ * `CLOSED` when the response is over.
32
+ */
33
+ write(chunk: Uint8Array | string): Promise<void>;
34
+ /** End the response cleanly. Resolves once it is closed. */
35
+ end(): Promise<void>;
36
+ /**
37
+ * Reset the connection mid-response: no terminator, no trailer — what a
38
+ * network drop looks like to the page.
39
+ */
40
+ cut(): void;
41
+ /** Resolves when the response is over, with how. */
42
+ readonly closed: Promise<StreamEnd>;
43
+ }
@@ -0,0 +1,23 @@
1
+ export { createRpcClient } from "./lib/client.js";
2
+ export { connect } from "./lib/create-connect.js";
3
+ export { statusOf } from "./lib/status-of.js";
4
+ export type {
5
+ ClientOptions,
6
+ ConnectApi,
7
+ ConnectOptions,
8
+ HeldCall,
9
+ HoldOptions,
10
+ HoldStub,
11
+ RpcCall,
12
+ RpcProtocol,
13
+ RpcServer,
14
+ ServeOptions,
15
+ StandaloneClientOptions,
16
+ StreamCall,
17
+ StreamEnd,
18
+ StreamStub,
19
+ StubHandle,
20
+ StubOptions,
21
+ UnaryReply,
22
+ UnaryStub,
23
+ } from "./model/types.js";
@@ -0,0 +1,58 @@
1
+ import type { DescMessage, MessageShape } from "@bufbuild/protobuf";
2
+
3
+ import type { Frame } from "../../../entities/frame/index.js";
4
+ import type { RpcCall, RpcProtocol } from "../model/types.js";
5
+
6
+ /**
7
+ * One method's frames as calls: one per correlation, oldest first. A frame
8
+ * another writer left without a correlation is a call of its own.
9
+ */
10
+ export function assembleCalls<I extends DescMessage, O extends DescMessage>(
11
+ frames: readonly Frame[],
12
+ ): RpcCall<I, O>[] {
13
+ const byCall = new Map<string, Frame[]>();
14
+ for (const frame of frames) {
15
+ const key = frame.correlation ?? `#${frame.seq}`;
16
+ const list = byCall.get(key) ?? [];
17
+ list.push(frame);
18
+ byCall.set(key, list);
19
+ }
20
+ return [...byCall].map(([correlation, own]) => {
21
+ const first = own[0] as Frame;
22
+ const requests = own.filter(
23
+ (frame) => frame.direction === "out" && frame.value !== undefined,
24
+ );
25
+ // Everything that is not the caller's is the answer: messages, the
26
+ // status, or the fault that stood in for one.
27
+ const answer = own.findLast((frame) => frame.direction !== "out");
28
+ const status = own.findLast((frame) => frame.kind === "response");
29
+ const fault = own.findLast((frame) => frame.kind === "fault");
30
+ return {
31
+ correlation,
32
+ caller: requests[0]?.source,
33
+ answeredBy: answer?.source,
34
+ page: first.page,
35
+ context: first.context,
36
+ protocol: first.meta["rpcProtocol"] as RpcProtocol | undefined,
37
+ request: requests[0]?.value as MessageShape<I> | undefined,
38
+ requests: requests.map((frame) => frame.value as MessageShape<I>),
39
+ responses: own
40
+ .filter(
41
+ (frame) =>
42
+ frame.direction === "in" &&
43
+ frame.kind === "message" &&
44
+ frame.value !== undefined,
45
+ )
46
+ .map((frame) => frame.value as MessageShape<O>),
47
+ status:
48
+ status === undefined
49
+ ? undefined
50
+ : {
51
+ code: Number(status.meta["code"]),
52
+ message: String(status.meta["message"] ?? ""),
53
+ },
54
+ fault: fault === undefined ? undefined : String(fault.meta["reason"]),
55
+ frames: own,
56
+ };
57
+ });
58
+ }
@@ -0,0 +1,203 @@
1
+ import type { DescService } from "@bufbuild/protobuf";
2
+ import {
3
+ type Client,
4
+ ConnectError,
5
+ createClient,
6
+ type Interceptor,
7
+ type Transport,
8
+ } from "@connectrpc/connect";
9
+ import {
10
+ createConnectTransport,
11
+ createGrpcTransport,
12
+ createGrpcWebTransport,
13
+ Http2SessionManager,
14
+ } from "@connectrpc/connect-node";
15
+
16
+ import type { CookieJarPort } from "../../../shared/api/playwright/index.js";
17
+ import { parseSetCookie } from "../../../shared/lib/cookies/index.js";
18
+ import type { RpcProtocol, StandaloneClientOptions } from "../model/types.js";
19
+ import { type CallInfo, type Recorder, tapIterable } from "./recorder.js";
20
+
21
+ export interface ClientDeps {
22
+ /** Resolved per call, so a context attached after the client exists still counts. */
23
+ readonly session: () => CookieJarPort | undefined;
24
+ /** The time cookies are stamped against; the stage's clock in a stage. */
25
+ readonly now?: () => number;
26
+ readonly recorder?: Recorder;
27
+ readonly correlation?: () => string;
28
+ /**
29
+ * Handed the HTTP/2 connection a gRPC client opens, so its owner can close
30
+ * it. connect-node keeps an idle one for 15 minutes, and a server in the
31
+ * same process cannot finish closing while it is open (Node 22 waits).
32
+ */
33
+ readonly onConnection?: (connection: Http2SessionManager) => void;
34
+ }
35
+
36
+ /** Sent with every call that did not set the header itself. */
37
+ function headersInterceptor(
38
+ headers: Readonly<Record<string, string>>,
39
+ ): Interceptor {
40
+ return (next) => (request) => {
41
+ for (const [name, value] of Object.entries(headers))
42
+ if (!request.header.has(name)) request.header.set(name, value);
43
+ return next(request);
44
+ };
45
+ }
46
+
47
+ /**
48
+ * Shares a context's cookie jar both ways, as Playwright's own request
49
+ * context does — the reason hand-rolled gRPC-web helpers go through it. The
50
+ * jar's cookies go out on the call, read per call: a sign-in during the test
51
+ * is picked up, and a cookie scoped to another host or path is not sent. The
52
+ * cookies a response sets come back into the jar — an error response's too,
53
+ * which is how a sign-out or a refused sign-in clears a session.
54
+ *
55
+ * Cookies are scoped to the host the request names: its `host` header when
56
+ * one was set, since that is the site the server takes it for.
57
+ */
58
+ function sessionInterceptor(
59
+ session: () => CookieJarPort | undefined,
60
+ now: () => number,
61
+ ): Interceptor {
62
+ return (next) => async (request) => {
63
+ const jar = session();
64
+ if (jar === undefined) return next(request);
65
+ const named = request.header.get("host");
66
+ const site = new URL(request.url);
67
+ if (named !== null) site.host = named;
68
+ const cookies = await jar.cookies(site.href);
69
+ if (cookies.length > 0 && !request.header.has("cookie"))
70
+ request.header.set(
71
+ "cookie",
72
+ cookies.map((c) => `${c.name}=${c.value}`).join("; "),
73
+ );
74
+ const keep = async (headers: Headers) => {
75
+ const set = headers
76
+ .getSetCookie()
77
+ .flatMap((line) => parseSetCookie(line, site.href, now()) ?? []);
78
+ if (set.length > 0) await jar.addCookies?.(set);
79
+ };
80
+ try {
81
+ const response = await next(request);
82
+ await keep(response.header);
83
+ return response;
84
+ } catch (error) {
85
+ // A failed call's headers ride on its error; one that never reached
86
+ // the server has none.
87
+ await keep(ConnectError.from(error).metadata);
88
+ throw error;
89
+ }
90
+ };
91
+ }
92
+
93
+ /** Records drive calls on the timeline: the test's requests, the backend's answers. */
94
+ function recordingInterceptor(
95
+ recorder: Recorder,
96
+ correlation: () => string,
97
+ protocol: RpcProtocol,
98
+ ): Interceptor {
99
+ return (next) => async (request) => {
100
+ const call: CallInfo = {
101
+ correlation: correlation(),
102
+ from: "test",
103
+ answeredBy: "network",
104
+ rpcProtocol: protocol,
105
+ };
106
+ const method = request.method;
107
+ try {
108
+ if (!request.stream) recorder.out(call, method, request.message, true);
109
+ const response = await next(
110
+ request.stream
111
+ ? {
112
+ ...request,
113
+ message: tapIterable(request.message, (m, i) =>
114
+ recorder.out(call, method, m, i === 0),
115
+ ),
116
+ }
117
+ : request,
118
+ );
119
+ if (!response.stream) {
120
+ recorder.in(call, method, response.message);
121
+ recorder.status(call, method);
122
+ return response;
123
+ }
124
+ return {
125
+ ...response,
126
+ message: (async function* () {
127
+ try {
128
+ for await (const message of response.message) {
129
+ recorder.in(call, method, message);
130
+ yield message;
131
+ }
132
+ recorder.status(call, method);
133
+ } catch (error) {
134
+ recorder.status(call, method, ConnectError.from(error));
135
+ throw error;
136
+ }
137
+ })(),
138
+ };
139
+ } catch (error) {
140
+ recorder.status(call, method, ConnectError.from(error));
141
+ throw error;
142
+ }
143
+ };
144
+ }
145
+
146
+ export function createTransport(
147
+ options: StandaloneClientOptions,
148
+ deps: ClientDeps,
149
+ ): Transport {
150
+ const protocol = options.protocol ?? "connect";
151
+ // Outermost first: the defaults, then the session (which reads the `host`
152
+ // the defaults may have named), then the recorder, closest to the wire.
153
+ const interceptors: Interceptor[] = [];
154
+ if (options.headers !== undefined)
155
+ interceptors.push(headersInterceptor(options.headers));
156
+ interceptors.push(sessionInterceptor(deps.session, deps.now ?? Date.now));
157
+ if (deps.recorder !== undefined && deps.correlation !== undefined)
158
+ interceptors.push(
159
+ recordingInterceptor(deps.recorder, deps.correlation, protocol),
160
+ );
161
+ const nodeOptions = options.ignoreHttpsErrors
162
+ ? { rejectUnauthorized: false }
163
+ : undefined;
164
+ const common = {
165
+ baseUrl: options.baseURL,
166
+ interceptors,
167
+ useBinaryFormat: options.binary ?? true,
168
+ };
169
+ if (protocol === "grpc") {
170
+ // The transport's own session manager would be out of reach; this one
171
+ // is handed to whoever has to close it.
172
+ const sessionManager = new Http2SessionManager(
173
+ options.baseURL,
174
+ undefined,
175
+ nodeOptions,
176
+ );
177
+ deps.onConnection?.(sessionManager);
178
+ return createGrpcTransport({ ...common, sessionManager });
179
+ }
180
+ const http1 = { ...common, httpVersion: "1.1" as const, nodeOptions };
181
+ if (protocol === "grpc-web") return createGrpcWebTransport(http1);
182
+ return createConnectTransport(http1);
183
+ }
184
+
185
+ /**
186
+ * A typed client for a real backend, outside any stage: the `rpc` helper an
187
+ * e2e suite otherwise writes by hand — framing, status parsing and all — per
188
+ * protocol. Errors arrive as `ConnectError` with the server's code.
189
+ *
190
+ * Over gRPC it holds an HTTP/2 connection, which connect-node keeps for 15
191
+ * minutes once idle. It does not keep the process alive, but a server in the
192
+ * same process waits for it before it finishes closing. A stage's client is
193
+ * closed with the stage.
194
+ */
195
+ export function createRpcClient<S extends DescService>(
196
+ service: S,
197
+ options: StandaloneClientOptions,
198
+ ): Client<S> {
199
+ return createClient(
200
+ service,
201
+ createTransport(options, { session: () => options.session }),
202
+ );
203
+ }
@@ -0,0 +1,164 @@
1
+ import type { DescService } from "@bufbuild/protobuf";
2
+ import { type Client, createClient } from "@connectrpc/connect";
3
+ import type { Http2SessionManager } from "@connectrpc/connect-node";
4
+
5
+ import {
6
+ definePlugin,
7
+ type PluginContext,
8
+ type WirePlugin,
9
+ } from "../../../entities/plugin/index.js";
10
+ import { WirestageError } from "../../../shared/lib/errors/index.js";
11
+ import { createSequence } from "../../../shared/lib/id/index.js";
12
+ import type {
13
+ ClientOptions,
14
+ ConnectApi,
15
+ ConnectOptions,
16
+ RpcServer,
17
+ } from "../model/types.js";
18
+ import { assembleCalls } from "./calls.js";
19
+ import { createTransport } from "./client.js";
20
+ import { stubAbort, stubHold } from "./faults.js";
21
+ import { recordService } from "./record.js";
22
+ import { createRecorder, endpointOf } from "./recorder.js";
23
+ import { createRpcRules } from "./rules.js";
24
+ import { serveRoutes } from "./serve.js";
25
+ import { stubFailure, stubService, stubStream, stubUnary } from "./stubs.js";
26
+
27
+ /**
28
+ * Connect, gRPC-web and gRPC: stubs answered by connect-es's own protocol
29
+ * handlers, recording of real traffic, drive clients, and out-of-process
30
+ * servers.
31
+ *
32
+ * The stubs never encode a frame by hand. Each one is a one-method Connect
33
+ * router whose handler runs the real server-side protocol, so the bytes the
34
+ * page receives are exactly what a Connect server would send — errors,
35
+ * trailers, JSON and binary alike.
36
+ */
37
+ export function connect(
38
+ options: ConnectOptions = {},
39
+ ): WirePlugin<"connect", ConnectApi> {
40
+ return definePlugin({
41
+ name: "connect",
42
+ scope: "Connect",
43
+ setup: (context) => createConnect(context, options),
44
+ });
45
+ }
46
+
47
+ function createConnect(context: PluginContext, options: ConnectOptions) {
48
+ const protocol = context.name;
49
+ const live = (options.streaming ?? "live") === "live";
50
+ const recorder = createRecorder(context.timeline, protocol);
51
+ const rules = createRpcRules(context, options, recorder, protocol);
52
+ const testCorrelation = createSequence("t");
53
+ const serverCorrelation = createSequence("s");
54
+ const servers = new Set<RpcServer>();
55
+ /** Lets go of each call a hold still has, when the stage goes away. */
56
+ const heldCalls = new Set<() => void>();
57
+ /** The drive clients' HTTP/2 connections, closed with the stage. */
58
+ const connections = new Set<Http2SessionManager>();
59
+ /** The record rules being added, once: every context gets them. */
60
+ let recording: Promise<unknown> | undefined;
61
+
62
+ const api: ConnectApi = {
63
+ service: (service, impl, stubOptions = {}) =>
64
+ stubService(rules, service, impl, stubOptions),
65
+ unary: (method, reply, stubOptions = {}) =>
66
+ stubUnary(context, rules, method, reply, stubOptions),
67
+ stream: (method, stubOptions = {}) =>
68
+ stubStream(context, rules, recorder, method, stubOptions, live),
69
+ fail: (method, code, message, stubOptions = {}) =>
70
+ stubFailure(rules, method, code, message, stubOptions),
71
+ abort: (method, abortOptions = {}) =>
72
+ stubAbort(rules, recorder, method, abortOptions),
73
+ hold: (method, holdOptions = {}) =>
74
+ stubHold(context, rules, recorder, method, holdOptions, heldCalls),
75
+ record: (service) => recordService(rules, service),
76
+ async calls(method) {
77
+ await context.sync();
78
+ return assembleCalls(
79
+ context.timeline.frames({ protocol, endpoint: endpointOf(method) }),
80
+ );
81
+ },
82
+ client<S extends DescService>(
83
+ service: S,
84
+ clientOptions: ClientOptions = {},
85
+ ): Client<S> {
86
+ const {
87
+ session,
88
+ baseURL: requested,
89
+ ...transportOptions
90
+ } = clientOptions;
91
+ const base = requested ?? options.baseURL ?? "/";
92
+ const origin = context.attachments()[0]?.baseURL ?? context.baseURL;
93
+ if (!/^https?:\/\//.test(base) && origin === undefined)
94
+ throw new WirestageError(
95
+ "INVALID_ARGUMENT",
96
+ `client(${service.typeName}): "${base}" is relative and the stage has no baseURL; pass an absolute baseURL or give the stage one`,
97
+ );
98
+ return createClient(
99
+ service,
100
+ createTransport(
101
+ {
102
+ ...transportOptions,
103
+ baseURL: new URL(base, origin).href.replace(/\/+$/, ""),
104
+ protocol:
105
+ transportOptions.protocol ?? options.clientProtocol ?? "connect",
106
+ },
107
+ {
108
+ // Resolved per call: a context attached after the client was made
109
+ // still lends it its session.
110
+ session: () =>
111
+ session === null
112
+ ? undefined
113
+ : (session ?? context.attachments()[0]?.cookies),
114
+ now: () => context.clock.now(),
115
+ recorder,
116
+ correlation: testCorrelation,
117
+ onConnection: (connection) => connections.add(connection),
118
+ },
119
+ ),
120
+ );
121
+ },
122
+ async serve(routes, serveOptions = {}) {
123
+ const server = await serveRoutes(routes, serveOptions, {
124
+ recorder,
125
+ correlation: serverCorrelation,
126
+ });
127
+ const tracked: RpcServer = {
128
+ url: server.url,
129
+ port: server.port,
130
+ close: async () => {
131
+ servers.delete(tracked);
132
+ await server.close();
133
+ },
134
+ };
135
+ servers.add(tracked);
136
+ return tracked;
137
+ },
138
+ };
139
+
140
+ return {
141
+ api,
142
+ async attach() {
143
+ const services = options.record;
144
+ if (services === undefined) return;
145
+ recording ??= (async () => {
146
+ for (const service of services) await api.record(service);
147
+ })().catch((error: unknown) => {
148
+ // Only the first rule can fail, installing the context's route, and
149
+ // the router takes it back: the next attach starts over.
150
+ recording = undefined;
151
+ throw error;
152
+ });
153
+ await recording;
154
+ },
155
+ async dispose() {
156
+ for (const letGo of [...heldCalls]) letGo();
157
+ // A drive client's connection outlives its calls; left open, a
158
+ // server in the test's own process would wait for it on close.
159
+ for (const connection of connections) connection.abort();
160
+ connections.clear();
161
+ await Promise.all([...servers].map((server) => server.close()));
162
+ },
163
+ };
164
+ }