@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,306 @@
1
+ import {
2
+ create,
3
+ type DescMessage,
4
+ type DescMethod,
5
+ isMessage,
6
+ type MessageShape,
7
+ toBinary,
8
+ } from "@bufbuild/protobuf";
9
+ import {
10
+ Code,
11
+ ConnectError,
12
+ createContextKey,
13
+ type HandlerContext,
14
+ } from "@connectrpc/connect";
15
+
16
+ import type { FrameSource, Timeline } from "../../../entities/frame/index.js";
17
+ import type { RpcProtocol } from "../model/types.js";
18
+ import type { RpcStatus } from "./protocol.js";
19
+
20
+ /** What is known about one call, by whoever records its frames. */
21
+ export interface CallInfo {
22
+ readonly correlation: string;
23
+ /** Who makes the call: the page, the test's drive client, an outside client. */
24
+ readonly from: FrameSource;
25
+ /** Who answers it: a stub, the real network, a served fake. */
26
+ readonly answeredBy: FrameSource;
27
+ readonly rpcProtocol: RpcProtocol | undefined;
28
+ readonly page?: string | undefined;
29
+ readonly context?: string | undefined;
30
+ /**
31
+ * The request side is already on the timeline: the rule that took the
32
+ * request recorded it before deciding how to answer.
33
+ */
34
+ readonly requestRecorded?: boolean;
35
+ /** Set on live streams: resets the underlying connection. */
36
+ cut?: () => void;
37
+ /** Set on live streams: settles once the response is over. */
38
+ over?: Promise<unknown>;
39
+ }
40
+
41
+ /** A call nobody identified; the key's default, so a lookup never misses. */
42
+ const ANONYMOUS: CallInfo = Object.freeze({
43
+ correlation: "anonymous",
44
+ from: "page",
45
+ answeredBy: "stub",
46
+ rpcProtocol: undefined,
47
+ });
48
+
49
+ /**
50
+ * How a call's identity reaches the implementation. Connect threads context
51
+ * values from the server adapter into `HandlerContext.values`, which keeps the
52
+ * recording wrapper free of globals and safe under concurrent calls.
53
+ */
54
+ export const kCall = createContextKey<CallInfo>(ANONYMOUS, {
55
+ description: "wirestage call",
56
+ });
57
+
58
+ export const endpointOf = (method: DescMethod): string =>
59
+ `${method.parent.typeName}/${method.name}`;
60
+
61
+ export interface Recorder {
62
+ /** A request-side message: the first is the `request` frame. */
63
+ out(
64
+ call: CallInfo,
65
+ method: DescMethod,
66
+ message: unknown,
67
+ first: boolean,
68
+ ): void;
69
+ /** A response-side message. */
70
+ in(call: CallInfo, method: DescMethod, message: unknown): void;
71
+ /** The call ended: OK when `error` is undefined. */
72
+ status(call: CallInfo, method: DescMethod, error?: unknown): void;
73
+ /** A status read off the wire (passthrough). */
74
+ statusOf(call: CallInfo, method: DescMethod, status: RpcStatus): void;
75
+ /**
76
+ * The call ended without a status: an abort, a cut, a dropped connection.
77
+ * `by` is who caused it, when that is not who was answering.
78
+ */
79
+ fault(
80
+ call: CallInfo,
81
+ method: DescMethod,
82
+ reason: string,
83
+ detail?: { readonly error?: string | undefined; readonly by?: FrameSource },
84
+ ): void;
85
+ }
86
+
87
+ /**
88
+ * Writes RPC frames. Every RPC, however it was answered, lands as the same
89
+ * shape: a `request` frame from whoever called, `message` frames each way,
90
+ * and one closing frame — a `response` with the status, or a `fault` when
91
+ * there was none — so `calls()` reassembles stubbed, passed-through and
92
+ * served calls the same way.
93
+ *
94
+ * A call ends once. A stream the test cut is over at the cut, whatever the
95
+ * implementation goes on to return; the first ending is the one recorded.
96
+ */
97
+ export function createRecorder(timeline: Timeline, protocol: string): Recorder {
98
+ const ended = new WeakSet<CallInfo>();
99
+ const base = (call: CallInfo, method: DescMethod) => ({
100
+ protocol,
101
+ endpoint: endpointOf(method),
102
+ correlation: call.correlation,
103
+ ...(call.page === undefined ? {} : { page: call.page }),
104
+ ...(call.context === undefined ? {} : { context: call.context }),
105
+ });
106
+ const asMessage = <D extends DescMessage>(schema: D, value: unknown) =>
107
+ (isMessage(value, schema)
108
+ ? value
109
+ : create(schema, value as never)) as MessageShape<D>;
110
+ const close = (
111
+ call: CallInfo,
112
+ method: DescMethod,
113
+ status: RpcStatus,
114
+ extra: Readonly<Record<string, unknown>> = {},
115
+ ) => {
116
+ if (ended.has(call)) return;
117
+ ended.add(call);
118
+ timeline.append({
119
+ ...base(call, method),
120
+ kind: "response",
121
+ direction: "in",
122
+ source: call.answeredBy,
123
+ meta: {
124
+ rpcProtocol: call.rpcProtocol,
125
+ code: status.code,
126
+ message: status.message,
127
+ ...extra,
128
+ },
129
+ });
130
+ };
131
+ return {
132
+ out(call, method, message, first) {
133
+ const value = asMessage(method.input, message);
134
+ timeline.append({
135
+ ...base(call, method),
136
+ kind: first ? "request" : "message",
137
+ direction: "out",
138
+ source: call.from,
139
+ value,
140
+ data: () => toBinary(method.input, value),
141
+ meta: { rpcProtocol: call.rpcProtocol },
142
+ });
143
+ },
144
+ in(call, method, message) {
145
+ if (ended.has(call)) return;
146
+ const value = asMessage(method.output, message);
147
+ timeline.append({
148
+ ...base(call, method),
149
+ kind: "message",
150
+ direction: "in",
151
+ source: call.answeredBy,
152
+ value,
153
+ data: () => toBinary(method.output, value),
154
+ meta: { rpcProtocol: call.rpcProtocol },
155
+ });
156
+ },
157
+ status(call, method, error) {
158
+ if (error === undefined) {
159
+ close(call, method, { code: 0, message: "" });
160
+ return;
161
+ }
162
+ if (error instanceof ConnectError) {
163
+ close(call, method, { code: error.code, message: error.rawMessage });
164
+ return;
165
+ }
166
+ // Anything else reaches the page as Connect's own "internal error".
167
+ // The frame records that, and keeps what was really thrown where
168
+ // the test can read it and the page cannot.
169
+ close(
170
+ call,
171
+ method,
172
+ { code: Code.Internal, message: "internal error" },
173
+ { thrown: String(error) },
174
+ );
175
+ },
176
+ statusOf: (call, method, status) => close(call, method, status),
177
+ fault(call, method, reason, detail = {}) {
178
+ if (ended.has(call)) return;
179
+ ended.add(call);
180
+ timeline.append({
181
+ ...base(call, method),
182
+ kind: "fault",
183
+ direction: "none",
184
+ source: detail.by ?? call.answeredBy,
185
+ meta: {
186
+ rpcProtocol: call.rpcProtocol,
187
+ reason,
188
+ ...(detail.error === undefined ? {} : { error: detail.error }),
189
+ },
190
+ });
191
+ },
192
+ };
193
+ }
194
+
195
+ /**
196
+ * The call a handler is serving. An unidentified one is recorded as
197
+ * anonymous — each as a call of its own, so one ending does not end the next.
198
+ */
199
+ export function callOf(context: HandlerContext): CallInfo {
200
+ const call = context.values.get(kCall);
201
+ return call === ANONYMOUS ? { ...ANONYMOUS } : call;
202
+ }
203
+
204
+ /** Wrap an iterable so `tap` sees every value as the consumer takes it. */
205
+ export async function* tapIterable<T>(
206
+ source: AsyncIterable<T>,
207
+ tap: (value: T, index: number) => void,
208
+ ): AsyncGenerator<T> {
209
+ let index = 0;
210
+ for await (const value of source) {
211
+ tap(value, index++);
212
+ yield value;
213
+ }
214
+ }
215
+
216
+ type AnyImpl = (input: never, context: HandlerContext) => unknown;
217
+
218
+ /**
219
+ * Wrap an implementation of any of the four method kinds so every message
220
+ * and the final status are recorded. The implementation itself is untouched:
221
+ * it sees the same requests, and the client sees the same responses.
222
+ */
223
+ export function recordingImpl(
224
+ method: DescMethod,
225
+ impl: AnyImpl,
226
+ recorder: Recorder,
227
+ ): AnyImpl {
228
+ const run = impl as (input: unknown, context: HandlerContext) => unknown;
229
+ /** The request side, unless the rule that took the request recorded it. */
230
+ const request = (call: CallInfo, message: unknown, first: boolean) => {
231
+ if (!call.requestRecorded) recorder.out(call, method, message, first);
232
+ };
233
+ switch (method.methodKind) {
234
+ case "unary":
235
+ return async (input: unknown, context: HandlerContext) => {
236
+ const call = callOf(context);
237
+ request(call, input, true);
238
+ try {
239
+ const response = await run(input, context);
240
+ recorder.in(call, method, response);
241
+ recorder.status(call, method);
242
+ return response;
243
+ } catch (error) {
244
+ recorder.status(call, method, error);
245
+ throw error;
246
+ }
247
+ };
248
+ case "server_streaming":
249
+ return async function* (input: unknown, context: HandlerContext) {
250
+ const call = callOf(context);
251
+ request(call, input, true);
252
+ try {
253
+ for await (const response of run(
254
+ input,
255
+ context,
256
+ ) as AsyncIterable<unknown>) {
257
+ recorder.in(call, method, response);
258
+ yield response;
259
+ }
260
+ recorder.status(call, method);
261
+ } catch (error) {
262
+ recorder.status(call, method, error);
263
+ throw error;
264
+ }
265
+ };
266
+ case "client_streaming":
267
+ return async (
268
+ requests: AsyncIterable<unknown>,
269
+ context: HandlerContext,
270
+ ) => {
271
+ const call = callOf(context);
272
+ try {
273
+ const response = await run(
274
+ tapIterable(requests, (m, i) => request(call, m, i === 0)),
275
+ context,
276
+ );
277
+ recorder.in(call, method, response);
278
+ recorder.status(call, method);
279
+ return response;
280
+ } catch (error) {
281
+ recorder.status(call, method, error);
282
+ throw error;
283
+ }
284
+ };
285
+ case "bidi_streaming":
286
+ return async function* (
287
+ requests: AsyncIterable<unknown>,
288
+ context: HandlerContext,
289
+ ) {
290
+ const call = callOf(context);
291
+ try {
292
+ for await (const response of run(
293
+ tapIterable(requests, (m, i) => request(call, m, i === 0)),
294
+ context,
295
+ ) as AsyncIterable<unknown>) {
296
+ recorder.in(call, method, response);
297
+ yield response;
298
+ }
299
+ recorder.status(call, method);
300
+ } catch (error) {
301
+ recorder.status(call, method, error);
302
+ throw error;
303
+ }
304
+ };
305
+ }
306
+ }
@@ -0,0 +1,385 @@
1
+ import type { DescMessage, DescMethod, MessageShape } from "@bufbuild/protobuf";
2
+ import { MethodOptions_IdempotencyLevel } from "@bufbuild/protobuf/wkt";
3
+ import {
4
+ Code,
5
+ createConnectRouter,
6
+ createContextValues,
7
+ type HandlerContext,
8
+ } from "@connectrpc/connect";
9
+ import type { UniversalHandler } from "@connectrpc/connect/protocol";
10
+
11
+ import type {
12
+ HttpOutcome,
13
+ HttpRequestView,
14
+ HttpRuleHandle,
15
+ PassthroughResponse,
16
+ PluginContext,
17
+ } from "../../../entities/plugin/index.js";
18
+ import { WirestageError } from "../../../shared/lib/errors/index.js";
19
+ import type {
20
+ ConnectOptions,
21
+ HoldOptions,
22
+ RpcProtocol,
23
+ } from "../model/types.js";
24
+ import {
25
+ decodeRequestBody,
26
+ decodeResponseBody,
27
+ detectFormat,
28
+ parseMessage,
29
+ type WireFormat,
30
+ } from "./protocol.js";
31
+ import {
32
+ type CallInfo,
33
+ endpointOf,
34
+ kCall,
35
+ type Recorder,
36
+ recordingImpl,
37
+ } from "./recorder.js";
38
+ import {
39
+ collectResponse,
40
+ pipeResponse,
41
+ toUniversalRequest,
42
+ } from "./universal.js";
43
+
44
+ export type AnyImpl = (input: never, context: HandlerContext) => unknown;
45
+
46
+ /** One method's rule, as each kind of stub describes it. */
47
+ export interface RuleSpec<I extends DescMessage> {
48
+ readonly method: DescMethod;
49
+ readonly label: string;
50
+ readonly options: HoldOptions<I> & { readonly delayMs?: number | undefined };
51
+ /**
52
+ * Take calls in any RPC format, not only the protocols stubs answer: for
53
+ * a rule that watches and answers nothing.
54
+ */
55
+ readonly anyProtocol?: boolean;
56
+ /** Hear how a call this rule let through ended, and record it. */
57
+ readonly observe?: boolean;
58
+ /**
59
+ * Decide the call. Its request is already recorded, and `messages` are
60
+ * the request's messages decoded (none when they would not decode).
61
+ */
62
+ respond(
63
+ view: HttpRequestView,
64
+ messages: readonly unknown[],
65
+ ): HttpOutcome | Promise<HttpOutcome>;
66
+ }
67
+
68
+ export interface RpcRules {
69
+ /**
70
+ * Add one method's rule. Every stub goes through here, so every stub
71
+ * means the same by "a call to this method": the path, the protocols the
72
+ * plugin answers, the content type, `match` on the decoded request,
73
+ * `times`, the request on the timeline before the decision, `delayMs`.
74
+ */
75
+ add<I extends DescMessage>(spec: RuleSpec<I>): Promise<HttpRuleHandle>;
76
+ /**
77
+ * Connect's own server-side handler for one method: what makes a stub's
78
+ * bytes exactly a Connect server's. Built once per stub, not per call.
79
+ */
80
+ handler(method: DescMethod, impl: AnyImpl): UniversalHandler;
81
+ /** Answer a call through a handler. */
82
+ answer(
83
+ handler: UniversalHandler,
84
+ method: DescMethod,
85
+ view: HttpRequestView,
86
+ ): Promise<HttpOutcome>;
87
+ /** The call a request is, as a stub answers it. */
88
+ callOf(view: HttpRequestView): CallInfo;
89
+ }
90
+
91
+ /** Method kinds a page can call: fetch sends one request body, not a stream. */
92
+ const callable = (method: DescMethod): boolean =>
93
+ method.methodKind === "unary" || method.methodKind === "server_streaming";
94
+
95
+ /**
96
+ * Whether a request in `format` is a call `method` can be made with. Connect
97
+ * names unary and streaming calls by content type — a stream's is not a
98
+ * unary method's, nor the reverse — and offers GET only for a unary method
99
+ * declared free of side effects; gRPC and gRPC-web make no such distinction.
100
+ */
101
+ const speaks = (
102
+ method: DescMethod,
103
+ format: WireFormat,
104
+ view: HttpRequestView,
105
+ ): boolean => {
106
+ const unary = method.methodKind === "unary";
107
+ if (view.method === "GET")
108
+ return (
109
+ unary &&
110
+ method.idempotency === MethodOptions_IdempotencyLevel.NO_SIDE_EFFECTS
111
+ );
112
+ if (view.method !== "POST") return false;
113
+ return format.protocol !== "connect" || format.enveloped !== unary;
114
+ };
115
+
116
+ /**
117
+ * How an RPC method becomes an HTTP rule, and how its traffic is recorded.
118
+ * The one place that knows both, shared by every stub of the plugin.
119
+ */
120
+ export function createRpcRules(
121
+ context: PluginContext,
122
+ options: ConnectOptions,
123
+ recorder: Recorder,
124
+ protocol: string,
125
+ ): RpcRules {
126
+ const protocols = new Set<RpcProtocol>(
127
+ options.protocols ?? ["connect", "grpc-web"],
128
+ );
129
+ const live = (options.streaming ?? "live") === "live";
130
+ const base = options.baseURL?.replace(/\/+$/, "");
131
+ const absolute = base !== undefined && /^[a-z][a-z\d+.-]*:\/\//i.test(base);
132
+ /** What each request decodes to, worked out once however many rules ask. */
133
+ const decoded = new WeakMap<
134
+ HttpRequestView,
135
+ { readonly messages: readonly unknown[] } | { readonly error: unknown }
136
+ >();
137
+ /** Requests whose request side is on the timeline. */
138
+ const recorded = new WeakSet<HttpRequestView>();
139
+
140
+ const matchesPath = (requestPath: string) => (url: URL) => {
141
+ if (base === undefined) return url.pathname.endsWith(requestPath);
142
+ const subject = absolute ? `${url.origin}${url.pathname}` : url.pathname;
143
+ return subject === `${base}${requestPath}`;
144
+ };
145
+
146
+ /** The format of a call this rule may take. */
147
+ const accepts = (
148
+ view: HttpRequestView,
149
+ anyProtocol: boolean,
150
+ ): WireFormat | undefined => {
151
+ const format = detectFormat(view);
152
+ if (format === undefined || anyProtocol) return format;
153
+ // gRPC-web-text is base64 on the wire, which Connect's handlers do
154
+ // not speak: such a call is left to the real backend, not answered
155
+ // with a 415.
156
+ return protocols.has(format.protocol) && !format.base64
157
+ ? format
158
+ : undefined;
159
+ };
160
+
161
+ const decode = (method: DescMethod, view: HttpRequestView) => {
162
+ let result = decoded.get(view);
163
+ if (result === undefined) {
164
+ // `accepts` passed before any rule decodes: the request has a format.
165
+ const format = detectFormat(view) as WireFormat;
166
+ try {
167
+ result = {
168
+ messages: decodeRequestBody(format, view).map((bytes) =>
169
+ parseMessage(
170
+ method.input,
171
+ bytes,
172
+ format.binary,
173
+ options.jsonOptions,
174
+ ),
175
+ ),
176
+ };
177
+ } catch (error) {
178
+ result = { error };
179
+ }
180
+ decoded.set(view, result);
181
+ }
182
+ return result;
183
+ };
184
+
185
+ const callOf = (view: HttpRequestView): CallInfo => ({
186
+ correlation: view.correlation,
187
+ from: "page",
188
+ answeredBy: "stub",
189
+ rpcProtocol: detectFormat(view)?.protocol,
190
+ page: view.page,
191
+ context: view.context,
192
+ requestRecorded: true,
193
+ });
194
+
195
+ /** Put the request side on the timeline, once per request. */
196
+ const recordRequest = (
197
+ method: DescMethod,
198
+ view: HttpRequestView,
199
+ ): readonly unknown[] => {
200
+ const result = decode(method, view);
201
+ const failed = "error" in result;
202
+ if (!recorded.has(view)) {
203
+ recorded.add(view);
204
+ const call = callOf(view);
205
+ if (failed) {
206
+ // It would not decode; it still lands, raw, so nothing vanishes.
207
+ context.log.debug(
208
+ "could not decode an RPC request",
209
+ { endpoint: endpointOf(method) },
210
+ result.error,
211
+ );
212
+ context.timeline.append({
213
+ protocol,
214
+ kind: "request",
215
+ direction: "out",
216
+ endpoint: endpointOf(method),
217
+ source: call.from,
218
+ correlation: call.correlation,
219
+ data: view.body(),
220
+ meta: {
221
+ rpcProtocol: call.rpcProtocol,
222
+ decodeError: String(result.error),
223
+ },
224
+ page: call.page,
225
+ context: call.context,
226
+ });
227
+ } else
228
+ for (const [index, message] of result.messages.entries())
229
+ recorder.out(call, method, message, index === 0);
230
+ }
231
+ return failed ? [] : result.messages;
232
+ };
233
+
234
+ /** Record what the real backend answered a call this plugin let through. */
235
+ const recordResponse = (
236
+ method: DescMethod,
237
+ view: HttpRequestView,
238
+ response: PassthroughResponse,
239
+ ) => {
240
+ const format = detectFormat(view) as WireFormat;
241
+ const call: CallInfo = { ...callOf(view), answeredBy: "network" };
242
+ const body = decodeResponseBody(format, response);
243
+ for (const bytes of body.messages) {
244
+ try {
245
+ recorder.in(
246
+ call,
247
+ method,
248
+ parseMessage(
249
+ method.output,
250
+ bytes,
251
+ format.binary,
252
+ options.jsonOptions,
253
+ ),
254
+ );
255
+ } catch (error) {
256
+ context.log.debug(
257
+ "could not decode an RPC response",
258
+ { endpoint: endpointOf(method) },
259
+ error,
260
+ );
261
+ context.timeline.append({
262
+ protocol,
263
+ kind: "message",
264
+ direction: "in",
265
+ endpoint: endpointOf(method),
266
+ source: call.answeredBy,
267
+ correlation: call.correlation,
268
+ data: bytes,
269
+ meta: {
270
+ rpcProtocol: call.rpcProtocol,
271
+ decodeError: String(error),
272
+ },
273
+ page: call.page,
274
+ context: call.context,
275
+ });
276
+ }
277
+ }
278
+ recorder.statusOf(
279
+ call,
280
+ method,
281
+ body.status ?? { code: Code.Internal, message: "missing trailer" },
282
+ );
283
+ };
284
+
285
+ const createHandler = (
286
+ method: DescMethod,
287
+ impl: AnyImpl,
288
+ ): UniversalHandler => {
289
+ const router = createConnectRouter({
290
+ connect: protocols.has("connect"),
291
+ grpcWeb: protocols.has("grpc-web"),
292
+ grpc: protocols.has("grpc"),
293
+ ...(options.jsonOptions === undefined
294
+ ? {}
295
+ : { jsonOptions: options.jsonOptions }),
296
+ });
297
+ router.rpc(method, recordingImpl(method, impl, recorder) as never);
298
+ return router.handlers[0] as UniversalHandler;
299
+ };
300
+
301
+ return {
302
+ callOf,
303
+ handler: createHandler,
304
+ async answer(handler, method, view) {
305
+ const call = callOf(view);
306
+ const values = createContextValues().set(kCall, call);
307
+ if (!live || method.methodKind !== "server_streaming")
308
+ return {
309
+ type: "fulfill",
310
+ ...(await collectResponse(
311
+ await handler(toUniversalRequest(view, values)),
312
+ )),
313
+ };
314
+ return {
315
+ type: "stream",
316
+ async open(stream) {
317
+ call.cut = () => stream.cut();
318
+ call.over = stream.closed;
319
+ // The page letting go of the stream is the call's cancellation.
320
+ const gone = new AbortController();
321
+ void stream.closed.then(() => gone.abort());
322
+ await pipeResponse(
323
+ await handler(toUniversalRequest(view, values, gone.signal)),
324
+ stream,
325
+ );
326
+ },
327
+ };
328
+ },
329
+ async add<I extends DescMessage>(spec: RuleSpec<I>) {
330
+ const { method, options: stub } = spec;
331
+ if (!callable(method))
332
+ throw new WirestageError(
333
+ "UNSUPPORTED",
334
+ `${endpointOf(method)} is a ${method.methodKind.replace("_", "-")} method, which a page cannot call through fetch; fake it for clients outside the browser with serve()`,
335
+ );
336
+ const anyProtocol = spec.anyProtocol === true;
337
+ return context.http.add({
338
+ protocol,
339
+ label: spec.label,
340
+ url: matchesPath(`/${method.parent.typeName}/${method.name}`),
341
+ record: false,
342
+ ...(stub.times === undefined ? {} : { times: stub.times }),
343
+ endpoint: () => endpointOf(method),
344
+ match(view) {
345
+ const format = accepts(view, anyProtocol);
346
+ if (format === undefined) return false;
347
+ if (anyProtocol) return true;
348
+ if (!speaks(method, format, view)) return false;
349
+ if (stub.match === undefined) return true;
350
+ const result = decode(method, view);
351
+ // A request that does not decode is not this stub's to answer.
352
+ return (
353
+ "messages" in result &&
354
+ result.messages[0] !== undefined &&
355
+ stub.match(result.messages[0] as MessageShape<I>, view)
356
+ );
357
+ },
358
+ async handle(view) {
359
+ const messages = recordRequest(method, view);
360
+ if (stub.delayMs !== undefined)
361
+ await context.clock.sleep(stub.delayMs);
362
+ return spec.respond(view, messages);
363
+ },
364
+ ...(spec.observe
365
+ ? {
366
+ onPassthroughResponse: (
367
+ response: PassthroughResponse,
368
+ view: HttpRequestView,
369
+ ) => recordResponse(method, view, response),
370
+ onPassthroughFailure: (
371
+ error: string | undefined,
372
+ view: HttpRequestView,
373
+ ) =>
374
+ recorder.fault(
375
+ { ...callOf(view), answeredBy: "network" },
376
+ method,
377
+ "network",
378
+ { error },
379
+ ),
380
+ }
381
+ : {}),
382
+ });
383
+ },
384
+ };
385
+ }