@heojeongbo/wirestage 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (489) hide show
  1. package/LICENSE +21 -0
  2. package/LICENSES.md +21 -0
  3. package/README.md +150 -2
  4. package/dist/app/playwright/index.d.ts +3 -0
  5. package/dist/app/playwright/index.d.ts.map +1 -0
  6. package/dist/app/playwright/index.js +2 -0
  7. package/dist/app/playwright/index.js.map +1 -0
  8. package/dist/app/playwright/lib/fixture.d.ts +61 -0
  9. package/dist/app/playwright/lib/fixture.d.ts.map +1 -0
  10. package/dist/app/playwright/lib/fixture.js +110 -0
  11. package/dist/app/playwright/lib/fixture.js.map +1 -0
  12. package/dist/connect.d.ts +7 -0
  13. package/dist/connect.d.ts.map +1 -0
  14. package/dist/connect.js +7 -0
  15. package/dist/connect.js.map +1 -0
  16. package/dist/entities/frame/@x/plugin.d.ts +9 -0
  17. package/dist/entities/frame/@x/plugin.d.ts.map +1 -0
  18. package/dist/entities/frame/@x/plugin.js +2 -0
  19. package/dist/entities/frame/@x/plugin.js.map +1 -0
  20. package/dist/entities/frame/index.d.ts +6 -0
  21. package/dist/entities/frame/index.d.ts.map +1 -0
  22. package/dist/entities/frame/index.js +4 -0
  23. package/dist/entities/frame/index.js.map +1 -0
  24. package/dist/entities/frame/lib/filter.d.ts +8 -0
  25. package/dist/entities/frame/lib/filter.d.ts.map +1 -0
  26. package/dist/entities/frame/lib/filter.js +62 -0
  27. package/dist/entities/frame/lib/filter.js.map +1 -0
  28. package/dist/entities/frame/lib/payload.d.ts +8 -0
  29. package/dist/entities/frame/lib/payload.d.ts.map +1 -0
  30. package/dist/entities/frame/lib/payload.js +42 -0
  31. package/dist/entities/frame/lib/payload.js.map +1 -0
  32. package/dist/entities/frame/lib/snapshot.d.ts +15 -0
  33. package/dist/entities/frame/lib/snapshot.d.ts.map +1 -0
  34. package/dist/entities/frame/lib/snapshot.js +90 -0
  35. package/dist/entities/frame/lib/snapshot.js.map +1 -0
  36. package/dist/entities/frame/lib/timeline.d.ts +23 -0
  37. package/dist/entities/frame/lib/timeline.d.ts.map +1 -0
  38. package/dist/entities/frame/lib/timeline.js +122 -0
  39. package/dist/entities/frame/lib/timeline.js.map +1 -0
  40. package/dist/entities/frame/model/frame.d.ts +182 -0
  41. package/dist/entities/frame/model/frame.d.ts.map +1 -0
  42. package/dist/entities/frame/model/frame.js +2 -0
  43. package/dist/entities/frame/model/frame.js.map +1 -0
  44. package/dist/entities/plugin/index.d.ts +9 -0
  45. package/dist/entities/plugin/index.d.ts.map +1 -0
  46. package/dist/entities/plugin/index.js +3 -0
  47. package/dist/entities/plugin/index.js.map +1 -0
  48. package/dist/entities/plugin/lib/define-plugin.d.ts +8 -0
  49. package/dist/entities/plugin/lib/define-plugin.d.ts.map +1 -0
  50. package/dist/entities/plugin/lib/define-plugin.js +9 -0
  51. package/dist/entities/plugin/lib/define-plugin.js.map +1 -0
  52. package/dist/entities/plugin/lib/named.d.ts +10 -0
  53. package/dist/entities/plugin/lib/named.d.ts.map +1 -0
  54. package/dist/entities/plugin/lib/named.js +17 -0
  55. package/dist/entities/plugin/lib/named.js.map +1 -0
  56. package/dist/entities/plugin/model/attachment.d.ts +18 -0
  57. package/dist/entities/plugin/model/attachment.d.ts.map +1 -0
  58. package/dist/entities/plugin/model/attachment.js +2 -0
  59. package/dist/entities/plugin/model/attachment.js.map +1 -0
  60. package/dist/entities/plugin/model/http.d.ts +135 -0
  61. package/dist/entities/plugin/model/http.d.ts.map +1 -0
  62. package/dist/entities/plugin/model/http.js +2 -0
  63. package/dist/entities/plugin/model/http.js.map +1 -0
  64. package/dist/entities/plugin/model/page.d.ts +100 -0
  65. package/dist/entities/plugin/model/page.d.ts.map +1 -0
  66. package/dist/entities/plugin/model/page.js +2 -0
  67. package/dist/entities/plugin/model/page.js.map +1 -0
  68. package/dist/entities/plugin/model/plugin.d.ts +83 -0
  69. package/dist/entities/plugin/model/plugin.d.ts.map +1 -0
  70. package/dist/entities/plugin/model/plugin.js +2 -0
  71. package/dist/entities/plugin/model/plugin.js.map +1 -0
  72. package/dist/entities/plugin/model/socket.d.ts +107 -0
  73. package/dist/entities/plugin/model/socket.d.ts.map +1 -0
  74. package/dist/entities/plugin/model/socket.js +2 -0
  75. package/dist/entities/plugin/model/socket.js.map +1 -0
  76. package/dist/entities/plugin/model/stream.d.ts +46 -0
  77. package/dist/entities/plugin/model/stream.d.ts.map +1 -0
  78. package/dist/entities/plugin/model/stream.js +2 -0
  79. package/dist/entities/plugin/model/stream.js.map +1 -0
  80. package/dist/features/connect/index.d.ts +5 -0
  81. package/dist/features/connect/index.d.ts.map +1 -0
  82. package/dist/features/connect/index.js +4 -0
  83. package/dist/features/connect/index.js.map +1 -0
  84. package/dist/features/connect/lib/calls.d.ts +9 -0
  85. package/dist/features/connect/lib/calls.d.ts.map +1 -0
  86. package/dist/features/connect/lib/calls.js +46 -0
  87. package/dist/features/connect/lib/calls.js.map +1 -0
  88. package/dist/features/connect/lib/client.d.ts +33 -0
  89. package/dist/features/connect/lib/client.d.ts.map +1 -0
  90. package/dist/features/connect/lib/client.js +147 -0
  91. package/dist/features/connect/lib/client.js.map +1 -0
  92. package/dist/features/connect/lib/create-connect.d.ts +14 -0
  93. package/dist/features/connect/lib/create-connect.d.ts.map +1 -0
  94. package/dist/features/connect/lib/create-connect.js +124 -0
  95. package/dist/features/connect/lib/create-connect.js.map +1 -0
  96. package/dist/features/connect/lib/faults.d.ts +18 -0
  97. package/dist/features/connect/lib/faults.d.ts.map +1 -0
  98. package/dist/features/connect/lib/faults.js +85 -0
  99. package/dist/features/connect/lib/faults.js.map +1 -0
  100. package/dist/features/connect/lib/protocol.d.ts +42 -0
  101. package/dist/features/connect/lib/protocol.d.ts.map +1 -0
  102. package/dist/features/connect/lib/protocol.js +228 -0
  103. package/dist/features/connect/lib/protocol.js.map +1 -0
  104. package/dist/features/connect/lib/record.d.ts +14 -0
  105. package/dist/features/connect/lib/record.d.ts.map +1 -0
  106. package/dist/features/connect/lib/record.js +36 -0
  107. package/dist/features/connect/lib/record.js.map +1 -0
  108. package/dist/features/connect/lib/recorder.d.ts +77 -0
  109. package/dist/features/connect/lib/recorder.d.ts.map +1 -0
  110. package/dist/features/connect/lib/recorder.js +209 -0
  111. package/dist/features/connect/lib/recorder.js.map +1 -0
  112. package/dist/features/connect/lib/rules.d.ts +51 -0
  113. package/dist/features/connect/lib/rules.d.ts.map +1 -0
  114. package/dist/features/connect/lib/rules.js +222 -0
  115. package/dist/features/connect/lib/rules.js.map +1 -0
  116. package/dist/features/connect/lib/serve.d.ts +10 -0
  117. package/dist/features/connect/lib/serve.d.ts.map +1 -0
  118. package/dist/features/connect/lib/serve.js +78 -0
  119. package/dist/features/connect/lib/serve.js.map +1 -0
  120. package/dist/features/connect/lib/status-of.d.ts +11 -0
  121. package/dist/features/connect/lib/status-of.d.ts.map +1 -0
  122. package/dist/features/connect/lib/status-of.js +22 -0
  123. package/dist/features/connect/lib/status-of.js.map +1 -0
  124. package/dist/features/connect/lib/stubs.d.ts +18 -0
  125. package/dist/features/connect/lib/stubs.d.ts.map +1 -0
  126. package/dist/features/connect/lib/stubs.js +174 -0
  127. package/dist/features/connect/lib/stubs.js.map +1 -0
  128. package/dist/features/connect/lib/universal.d.ts +19 -0
  129. package/dist/features/connect/lib/universal.d.ts.map +1 -0
  130. package/dist/features/connect/lib/universal.js +71 -0
  131. package/dist/features/connect/lib/universal.js.map +1 -0
  132. package/dist/features/connect/model/types.d.ts +247 -0
  133. package/dist/features/connect/model/types.d.ts.map +1 -0
  134. package/dist/features/connect/model/types.js +2 -0
  135. package/dist/features/connect/model/types.js.map +1 -0
  136. package/dist/features/rest/index.d.ts +5 -0
  137. package/dist/features/rest/index.d.ts.map +1 -0
  138. package/dist/features/rest/index.js +3 -0
  139. package/dist/features/rest/index.js.map +1 -0
  140. package/dist/features/rest/lib/calls.d.ts +8 -0
  141. package/dist/features/rest/lib/calls.d.ts.map +1 -0
  142. package/dist/features/rest/lib/calls.js +32 -0
  143. package/dist/features/rest/lib/calls.js.map +1 -0
  144. package/dist/features/rest/lib/create-rest.d.ts +8 -0
  145. package/dist/features/rest/lib/create-rest.d.ts.map +1 -0
  146. package/dist/features/rest/lib/create-rest.js +136 -0
  147. package/dist/features/rest/lib/create-rest.js.map +1 -0
  148. package/dist/features/rest/lib/path.d.ts +14 -0
  149. package/dist/features/rest/lib/path.d.ts.map +1 -0
  150. package/dist/features/rest/lib/path.js +58 -0
  151. package/dist/features/rest/lib/path.js.map +1 -0
  152. package/dist/features/rest/lib/reply.d.ts +25 -0
  153. package/dist/features/rest/lib/reply.d.ts.map +1 -0
  154. package/dist/features/rest/lib/reply.js +40 -0
  155. package/dist/features/rest/lib/reply.js.map +1 -0
  156. package/dist/features/rest/model/types.d.ts +74 -0
  157. package/dist/features/rest/model/types.d.ts.map +1 -0
  158. package/dist/features/rest/model/types.js +2 -0
  159. package/dist/features/rest/model/types.js.map +1 -0
  160. package/dist/features/sse/index.d.ts +4 -0
  161. package/dist/features/sse/index.d.ts.map +1 -0
  162. package/dist/features/sse/index.js +3 -0
  163. package/dist/features/sse/index.js.map +1 -0
  164. package/dist/features/sse/lib/create-sse.d.ts +9 -0
  165. package/dist/features/sse/lib/create-sse.d.ts.map +1 -0
  166. package/dist/features/sse/lib/create-sse.js +121 -0
  167. package/dist/features/sse/lib/create-sse.js.map +1 -0
  168. package/dist/features/sse/lib/encode.d.ts +8 -0
  169. package/dist/features/sse/lib/encode.d.ts.map +1 -0
  170. package/dist/features/sse/lib/encode.js +29 -0
  171. package/dist/features/sse/lib/encode.js.map +1 -0
  172. package/dist/features/sse/model/types.d.ts +68 -0
  173. package/dist/features/sse/model/types.d.ts.map +1 -0
  174. package/dist/features/sse/model/types.js +2 -0
  175. package/dist/features/sse/model/types.js.map +1 -0
  176. package/dist/features/webrtc/index.d.ts +3 -0
  177. package/dist/features/webrtc/index.d.ts.map +1 -0
  178. package/dist/features/webrtc/index.js +2 -0
  179. package/dist/features/webrtc/index.js.map +1 -0
  180. package/dist/features/webrtc/lib/create-webrtc.d.ts +9 -0
  181. package/dist/features/webrtc/lib/create-webrtc.d.ts.map +1 -0
  182. package/dist/features/webrtc/lib/create-webrtc.js +123 -0
  183. package/dist/features/webrtc/lib/create-webrtc.js.map +1 -0
  184. package/dist/features/webrtc/lib/rtc-page-module.d.ts +22 -0
  185. package/dist/features/webrtc/lib/rtc-page-module.d.ts.map +1 -0
  186. package/dist/features/webrtc/lib/rtc-page-module.js +161 -0
  187. package/dist/features/webrtc/lib/rtc-page-module.js.map +1 -0
  188. package/dist/features/webrtc/lib/to-frame.d.ts +9 -0
  189. package/dist/features/webrtc/lib/to-frame.d.ts.map +1 -0
  190. package/dist/features/webrtc/lib/to-frame.js +43 -0
  191. package/dist/features/webrtc/lib/to-frame.js.map +1 -0
  192. package/dist/features/webrtc/model/types.d.ts +70 -0
  193. package/dist/features/webrtc/model/types.d.ts.map +1 -0
  194. package/dist/features/webrtc/model/types.js +2 -0
  195. package/dist/features/webrtc/model/types.js.map +1 -0
  196. package/dist/features/websocket/index.d.ts +3 -0
  197. package/dist/features/websocket/index.d.ts.map +1 -0
  198. package/dist/features/websocket/index.js +2 -0
  199. package/dist/features/websocket/index.js.map +1 -0
  200. package/dist/features/websocket/lib/create-websocket.d.ts +9 -0
  201. package/dist/features/websocket/lib/create-websocket.d.ts.map +1 -0
  202. package/dist/features/websocket/lib/create-websocket.js +34 -0
  203. package/dist/features/websocket/lib/create-websocket.js.map +1 -0
  204. package/dist/features/websocket/lib/mock.d.ts +6 -0
  205. package/dist/features/websocket/lib/mock.d.ts.map +1 -0
  206. package/dist/features/websocket/lib/mock.js +96 -0
  207. package/dist/features/websocket/lib/mock.js.map +1 -0
  208. package/dist/features/websocket/lib/proxy.d.ts +9 -0
  209. package/dist/features/websocket/lib/proxy.d.ts.map +1 -0
  210. package/dist/features/websocket/lib/proxy.js +120 -0
  211. package/dist/features/websocket/lib/proxy.js.map +1 -0
  212. package/dist/features/websocket/lib/wire.d.ts +16 -0
  213. package/dist/features/websocket/lib/wire.d.ts.map +1 -0
  214. package/dist/features/websocket/lib/wire.js +46 -0
  215. package/dist/features/websocket/lib/wire.js.map +1 -0
  216. package/dist/features/websocket/model/types.d.ts +133 -0
  217. package/dist/features/websocket/model/types.d.ts.map +1 -0
  218. package/dist/features/websocket/model/types.js +2 -0
  219. package/dist/features/websocket/model/types.js.map +1 -0
  220. package/dist/index.d.ts +27 -0
  221. package/dist/index.d.ts.map +1 -0
  222. package/dist/index.js +16 -0
  223. package/dist/index.js.map +1 -0
  224. package/dist/playwright.d.ts +4 -0
  225. package/dist/playwright.d.ts.map +1 -0
  226. package/dist/playwright.js +2 -0
  227. package/dist/playwright.js.map +1 -0
  228. package/dist/rest.d.ts +3 -0
  229. package/dist/rest.d.ts.map +1 -0
  230. package/dist/rest.js +3 -0
  231. package/dist/rest.js.map +1 -0
  232. package/dist/shared/api/http-server/index.d.ts +3 -0
  233. package/dist/shared/api/http-server/index.d.ts.map +1 -0
  234. package/dist/shared/api/http-server/index.js +2 -0
  235. package/dist/shared/api/http-server/index.js.map +1 -0
  236. package/dist/shared/api/http-server/listen.d.ts +25 -0
  237. package/dist/shared/api/http-server/listen.d.ts.map +1 -0
  238. package/dist/shared/api/http-server/listen.js +44 -0
  239. package/dist/shared/api/http-server/listen.js.map +1 -0
  240. package/dist/shared/api/playwright/index.d.ts +2 -0
  241. package/dist/shared/api/playwright/index.d.ts.map +1 -0
  242. package/dist/shared/api/playwright/index.js +2 -0
  243. package/dist/shared/api/playwright/index.js.map +1 -0
  244. package/dist/shared/api/playwright/ports.d.ts +126 -0
  245. package/dist/shared/api/playwright/ports.d.ts.map +1 -0
  246. package/dist/shared/api/playwright/ports.js +11 -0
  247. package/dist/shared/api/playwright/ports.js.map +1 -0
  248. package/dist/shared/api/stream-server/index.d.ts +3 -0
  249. package/dist/shared/api/stream-server/index.d.ts.map +1 -0
  250. package/dist/shared/api/stream-server/index.js +2 -0
  251. package/dist/shared/api/stream-server/index.js.map +1 -0
  252. package/dist/shared/api/stream-server/stream-server.d.ts +45 -0
  253. package/dist/shared/api/stream-server/stream-server.d.ts.map +1 -0
  254. package/dist/shared/api/stream-server/stream-server.js +167 -0
  255. package/dist/shared/api/stream-server/stream-server.js.map +1 -0
  256. package/dist/shared/lib/async/arrivals.d.ts +31 -0
  257. package/dist/shared/lib/async/arrivals.d.ts.map +1 -0
  258. package/dist/shared/lib/async/arrivals.js +57 -0
  259. package/dist/shared/lib/async/arrivals.js.map +1 -0
  260. package/dist/shared/lib/async/async-queue.d.ts +23 -0
  261. package/dist/shared/lib/async/async-queue.d.ts.map +1 -0
  262. package/dist/shared/lib/async/async-queue.js +80 -0
  263. package/dist/shared/lib/async/async-queue.js.map +1 -0
  264. package/dist/shared/lib/async/deferred.d.ts +12 -0
  265. package/dist/shared/lib/async/deferred.d.ts.map +1 -0
  266. package/dist/shared/lib/async/deferred.js +34 -0
  267. package/dist/shared/lib/async/deferred.js.map +1 -0
  268. package/dist/shared/lib/async/index.d.ts +8 -0
  269. package/dist/shared/lib/async/index.d.ts.map +1 -0
  270. package/dist/shared/lib/async/index.js +5 -0
  271. package/dist/shared/lib/async/index.js.map +1 -0
  272. package/dist/shared/lib/async/run-all.d.ts +8 -0
  273. package/dist/shared/lib/async/run-all.d.ts.map +1 -0
  274. package/dist/shared/lib/async/run-all.js +22 -0
  275. package/dist/shared/lib/async/run-all.js.map +1 -0
  276. package/dist/shared/lib/bytes/bytes.d.ts +18 -0
  277. package/dist/shared/lib/bytes/bytes.d.ts.map +1 -0
  278. package/dist/shared/lib/bytes/bytes.js +48 -0
  279. package/dist/shared/lib/bytes/bytes.js.map +1 -0
  280. package/dist/shared/lib/bytes/index.d.ts +3 -0
  281. package/dist/shared/lib/bytes/index.d.ts.map +1 -0
  282. package/dist/shared/lib/bytes/index.js +2 -0
  283. package/dist/shared/lib/bytes/index.js.map +1 -0
  284. package/dist/shared/lib/clock/clock.d.ts +29 -0
  285. package/dist/shared/lib/clock/clock.d.ts.map +1 -0
  286. package/dist/shared/lib/clock/clock.js +85 -0
  287. package/dist/shared/lib/clock/clock.js.map +1 -0
  288. package/dist/shared/lib/clock/index.d.ts +3 -0
  289. package/dist/shared/lib/clock/index.d.ts.map +1 -0
  290. package/dist/shared/lib/clock/index.js +2 -0
  291. package/dist/shared/lib/clock/index.js.map +1 -0
  292. package/dist/shared/lib/cookies/index.d.ts +3 -0
  293. package/dist/shared/lib/cookies/index.d.ts.map +1 -0
  294. package/dist/shared/lib/cookies/index.js +2 -0
  295. package/dist/shared/lib/cookies/index.js.map +1 -0
  296. package/dist/shared/lib/cookies/set-cookie.d.ts +22 -0
  297. package/dist/shared/lib/cookies/set-cookie.d.ts.map +1 -0
  298. package/dist/shared/lib/cookies/set-cookie.js +89 -0
  299. package/dist/shared/lib/cookies/set-cookie.js.map +1 -0
  300. package/dist/shared/lib/envelope/envelope.d.ts +50 -0
  301. package/dist/shared/lib/envelope/envelope.d.ts.map +1 -0
  302. package/dist/shared/lib/envelope/envelope.js +111 -0
  303. package/dist/shared/lib/envelope/envelope.js.map +1 -0
  304. package/dist/shared/lib/envelope/index.d.ts +3 -0
  305. package/dist/shared/lib/envelope/index.d.ts.map +1 -0
  306. package/dist/shared/lib/envelope/index.js +2 -0
  307. package/dist/shared/lib/envelope/index.js.map +1 -0
  308. package/dist/shared/lib/errors/index.d.ts +3 -0
  309. package/dist/shared/lib/errors/index.d.ts.map +1 -0
  310. package/dist/shared/lib/errors/index.js +2 -0
  311. package/dist/shared/lib/errors/index.js.map +1 -0
  312. package/dist/shared/lib/errors/wirestage-error.d.ts +28 -0
  313. package/dist/shared/lib/errors/wirestage-error.d.ts.map +1 -0
  314. package/dist/shared/lib/errors/wirestage-error.js +16 -0
  315. package/dist/shared/lib/errors/wirestage-error.js.map +1 -0
  316. package/dist/shared/lib/id/id.d.ts +10 -0
  317. package/dist/shared/lib/id/id.d.ts.map +1 -0
  318. package/dist/shared/lib/id/id.js +26 -0
  319. package/dist/shared/lib/id/id.js.map +1 -0
  320. package/dist/shared/lib/id/index.d.ts +2 -0
  321. package/dist/shared/lib/id/index.d.ts.map +1 -0
  322. package/dist/shared/lib/id/index.js +2 -0
  323. package/dist/shared/lib/id/index.js.map +1 -0
  324. package/dist/shared/lib/logger/index.d.ts +3 -0
  325. package/dist/shared/lib/logger/index.d.ts.map +1 -0
  326. package/dist/shared/lib/logger/index.js +2 -0
  327. package/dist/shared/lib/logger/index.js.map +1 -0
  328. package/dist/shared/lib/logger/logger.d.ts +46 -0
  329. package/dist/shared/lib/logger/logger.d.ts.map +1 -0
  330. package/dist/shared/lib/logger/logger.js +67 -0
  331. package/dist/shared/lib/logger/logger.js.map +1 -0
  332. package/dist/shared/lib/match/index.d.ts +3 -0
  333. package/dist/shared/lib/match/index.d.ts.map +1 -0
  334. package/dist/shared/lib/match/index.js +2 -0
  335. package/dist/shared/lib/match/index.js.map +1 -0
  336. package/dist/shared/lib/match/url-matcher.d.ts +27 -0
  337. package/dist/shared/lib/match/url-matcher.d.ts.map +1 -0
  338. package/dist/shared/lib/match/url-matcher.js +88 -0
  339. package/dist/shared/lib/match/url-matcher.js.map +1 -0
  340. package/dist/sse.d.ts +3 -0
  341. package/dist/sse.d.ts.map +1 -0
  342. package/dist/sse.js +3 -0
  343. package/dist/sse.js.map +1 -0
  344. package/dist/webrtc.d.ts +3 -0
  345. package/dist/webrtc.d.ts.map +1 -0
  346. package/dist/webrtc.js +3 -0
  347. package/dist/webrtc.js.map +1 -0
  348. package/dist/websocket.d.ts +3 -0
  349. package/dist/websocket.d.ts.map +1 -0
  350. package/dist/websocket.js +3 -0
  351. package/dist/websocket.js.map +1 -0
  352. package/dist/widgets/stage/index.d.ts +3 -0
  353. package/dist/widgets/stage/index.d.ts.map +1 -0
  354. package/dist/widgets/stage/index.js +2 -0
  355. package/dist/widgets/stage/index.js.map +1 -0
  356. package/dist/widgets/stage/lib/attached-context.d.ts +10 -0
  357. package/dist/widgets/stage/lib/attached-context.d.ts.map +1 -0
  358. package/dist/widgets/stage/lib/attached-context.js +2 -0
  359. package/dist/widgets/stage/lib/attached-context.js.map +1 -0
  360. package/dist/widgets/stage/lib/create-stage.d.ts +12 -0
  361. package/dist/widgets/stage/lib/create-stage.d.ts.map +1 -0
  362. package/dist/widgets/stage/lib/create-stage.js +250 -0
  363. package/dist/widgets/stage/lib/create-stage.js.map +1 -0
  364. package/dist/widgets/stage/lib/http-router.d.ts +44 -0
  365. package/dist/widgets/stage/lib/http-router.d.ts.map +1 -0
  366. package/dist/widgets/stage/lib/http-router.js +696 -0
  367. package/dist/widgets/stage/lib/http-router.js.map +1 -0
  368. package/dist/widgets/stage/lib/page-host.d.ts +36 -0
  369. package/dist/widgets/stage/lib/page-host.d.ts.map +1 -0
  370. package/dist/widgets/stage/lib/page-host.js +240 -0
  371. package/dist/widgets/stage/lib/page-host.js.map +1 -0
  372. package/dist/widgets/stage/lib/page-runtime.d.ts +93 -0
  373. package/dist/widgets/stage/lib/page-runtime.d.ts.map +1 -0
  374. package/dist/widgets/stage/lib/page-runtime.js +247 -0
  375. package/dist/widgets/stage/lib/page-runtime.js.map +1 -0
  376. package/dist/widgets/stage/lib/reads.d.ts +21 -0
  377. package/dist/widgets/stage/lib/reads.d.ts.map +1 -0
  378. package/dist/widgets/stage/lib/reads.js +97 -0
  379. package/dist/widgets/stage/lib/reads.js.map +1 -0
  380. package/dist/widgets/stage/lib/response-stream.d.ts +23 -0
  381. package/dist/widgets/stage/lib/response-stream.d.ts.map +1 -0
  382. package/dist/widgets/stage/lib/response-stream.js +108 -0
  383. package/dist/widgets/stage/lib/response-stream.js.map +1 -0
  384. package/dist/widgets/stage/lib/socket-router.d.ts +25 -0
  385. package/dist/widgets/stage/lib/socket-router.d.ts.map +1 -0
  386. package/dist/widgets/stage/lib/socket-router.js +233 -0
  387. package/dist/widgets/stage/lib/socket-router.js.map +1 -0
  388. package/dist/widgets/stage/model/stage.d.ts +88 -0
  389. package/dist/widgets/stage/model/stage.d.ts.map +1 -0
  390. package/dist/widgets/stage/model/stage.js +2 -0
  391. package/dist/widgets/stage/model/stage.js.map +1 -0
  392. package/package.json +140 -5
  393. package/src/app/playwright/index.ts +7 -0
  394. package/src/app/playwright/lib/fixture.ts +201 -0
  395. package/src/connect.ts +6 -0
  396. package/src/entities/frame/@x/plugin.ts +8 -0
  397. package/src/entities/frame/index.ts +19 -0
  398. package/src/entities/frame/lib/filter.ts +58 -0
  399. package/src/entities/frame/lib/payload.ts +42 -0
  400. package/src/entities/frame/lib/snapshot.ts +105 -0
  401. package/src/entities/frame/lib/timeline.ts +147 -0
  402. package/src/entities/frame/model/frame.ts +191 -0
  403. package/src/entities/plugin/index.ts +36 -0
  404. package/src/entities/plugin/lib/define-plugin.ts +12 -0
  405. package/src/entities/plugin/lib/named.ts +21 -0
  406. package/src/entities/plugin/model/attachment.ts +18 -0
  407. package/src/entities/plugin/model/http.ts +156 -0
  408. package/src/entities/plugin/model/page.ts +110 -0
  409. package/src/entities/plugin/model/plugin.ts +96 -0
  410. package/src/entities/plugin/model/socket.ts +113 -0
  411. package/src/entities/plugin/model/stream.ts +46 -0
  412. package/src/features/connect/index.ts +23 -0
  413. package/src/features/connect/lib/calls.ts +58 -0
  414. package/src/features/connect/lib/client.ts +203 -0
  415. package/src/features/connect/lib/create-connect.ts +164 -0
  416. package/src/features/connect/lib/faults.ts +132 -0
  417. package/src/features/connect/lib/protocol.ts +321 -0
  418. package/src/features/connect/lib/record.ts +48 -0
  419. package/src/features/connect/lib/recorder.ts +306 -0
  420. package/src/features/connect/lib/rules.ts +385 -0
  421. package/src/features/connect/lib/serve.ts +114 -0
  422. package/src/features/connect/lib/status-of.ts +20 -0
  423. package/src/features/connect/lib/stubs.ts +282 -0
  424. package/src/features/connect/lib/universal.ts +100 -0
  425. package/src/features/connect/model/types.ts +305 -0
  426. package/src/features/rest/index.ts +23 -0
  427. package/src/features/rest/lib/calls.ts +31 -0
  428. package/src/features/rest/lib/create-rest.ts +169 -0
  429. package/src/features/rest/lib/path.ts +70 -0
  430. package/src/features/rest/lib/reply.ts +74 -0
  431. package/src/features/rest/model/types.ts +113 -0
  432. package/src/features/sse/index.ts +10 -0
  433. package/src/features/sse/lib/create-sse.ts +129 -0
  434. package/src/features/sse/lib/encode.ts +28 -0
  435. package/src/features/sse/model/types.ts +72 -0
  436. package/src/features/webrtc/index.ts +6 -0
  437. package/src/features/webrtc/lib/create-webrtc.ts +149 -0
  438. package/src/features/webrtc/lib/rtc-page-module.ts +189 -0
  439. package/src/features/webrtc/lib/to-frame.ts +63 -0
  440. package/src/features/webrtc/model/types.ts +80 -0
  441. package/src/features/websocket/index.ts +15 -0
  442. package/src/features/websocket/lib/create-websocket.ts +41 -0
  443. package/src/features/websocket/lib/mock.ts +114 -0
  444. package/src/features/websocket/lib/proxy.ts +153 -0
  445. package/src/features/websocket/lib/wire.ts +78 -0
  446. package/src/features/websocket/model/types.ts +134 -0
  447. package/src/index.ts +111 -0
  448. package/src/playwright.ts +8 -0
  449. package/src/rest.ts +2 -0
  450. package/src/shared/api/http-server/index.ts +2 -0
  451. package/src/shared/api/http-server/listen.ts +71 -0
  452. package/src/shared/api/playwright/index.ts +17 -0
  453. package/src/shared/api/playwright/ports.ts +150 -0
  454. package/src/shared/api/stream-server/index.ts +7 -0
  455. package/src/shared/api/stream-server/stream-server.ts +254 -0
  456. package/src/shared/lib/async/arrivals.ts +91 -0
  457. package/src/shared/lib/async/async-queue.ts +101 -0
  458. package/src/shared/lib/async/deferred.ts +38 -0
  459. package/src/shared/lib/async/index.ts +11 -0
  460. package/src/shared/lib/async/run-all.ts +19 -0
  461. package/src/shared/lib/bytes/bytes.ts +53 -0
  462. package/src/shared/lib/bytes/index.ts +9 -0
  463. package/src/shared/lib/clock/clock.ts +114 -0
  464. package/src/shared/lib/clock/index.ts +2 -0
  465. package/src/shared/lib/cookies/index.ts +2 -0
  466. package/src/shared/lib/cookies/set-cookie.ts +105 -0
  467. package/src/shared/lib/envelope/envelope.ts +133 -0
  468. package/src/shared/lib/envelope/index.ts +9 -0
  469. package/src/shared/lib/errors/index.ts +5 -0
  470. package/src/shared/lib/errors/wirestage-error.ts +50 -0
  471. package/src/shared/lib/id/id.ts +29 -0
  472. package/src/shared/lib/id/index.ts +1 -0
  473. package/src/shared/lib/logger/index.ts +12 -0
  474. package/src/shared/lib/logger/logger.ts +126 -0
  475. package/src/shared/lib/match/index.ts +6 -0
  476. package/src/shared/lib/match/url-matcher.ts +107 -0
  477. package/src/sse.ts +2 -0
  478. package/src/webrtc.ts +2 -0
  479. package/src/websocket.ts +2 -0
  480. package/src/widgets/stage/index.ts +9 -0
  481. package/src/widgets/stage/lib/attached-context.ts +10 -0
  482. package/src/widgets/stage/lib/create-stage.ts +307 -0
  483. package/src/widgets/stage/lib/http-router.ts +954 -0
  484. package/src/widgets/stage/lib/page-host.ts +375 -0
  485. package/src/widgets/stage/lib/page-runtime.ts +314 -0
  486. package/src/widgets/stage/lib/reads.ts +128 -0
  487. package/src/widgets/stage/lib/response-stream.ts +139 -0
  488. package/src/widgets/stage/lib/socket-router.ts +300 -0
  489. package/src/widgets/stage/model/stage.ts +110 -0
@@ -0,0 +1,201 @@
1
+ import type { TestType } from "@playwright/test";
2
+
3
+ import type { AnyWirePlugin } from "../../../entities/plugin/index.js";
4
+ import type { ContextPort } from "../../../shared/api/playwright/index.js";
5
+ import {
6
+ createScopedLogger,
7
+ type LogThreshold,
8
+ parseLogThreshold,
9
+ setLogThreshold,
10
+ } from "../../../shared/lib/logger/index.js";
11
+ import {
12
+ createStage,
13
+ type Stage,
14
+ type StageOptions,
15
+ } from "../../../widgets/stage/index.js";
16
+
17
+ /** When the timeline is attached to the test report. */
18
+ export type ReportMode = "on-failure" | "always" | "never";
19
+
20
+ export interface StageFixtureOptions<Plugins extends readonly AnyWirePlugin[]>
21
+ extends Omit<StageOptions<Plugins>, "baseURL" | "clock"> {
22
+ /**
23
+ * Default `on-failure`. `WIRESTAGE_REPORT` overrides it. An empty timeline
24
+ * is never attached.
25
+ */
26
+ readonly report?: ReportMode;
27
+ /**
28
+ * Frames a report holds, counting back from the newest. Default 1,000: a
29
+ * report is read from its end, and one that grows with the test would
30
+ * attach tens of megabytes to a long run.
31
+ */
32
+ readonly reportLimit?: number;
33
+ /** Default `warn`. `WIRESTAGE_LOG` overrides it. */
34
+ readonly log?: LogThreshold;
35
+ /**
36
+ * Attach the test's `context` before the body runs. Default true. Turn it
37
+ * off when tests open their own contexts (`browser.newContext()`, two users
38
+ * at once): the fixture then never asks Playwright for `context`, so no
39
+ * unused one is created, and the test attaches what it opens.
40
+ */
41
+ readonly attach?: boolean;
42
+ }
43
+
44
+ export interface StageFixtureArgs {
45
+ /** Absent when the fixture does not attach (`attach: false`). */
46
+ readonly context?: ContextPort;
47
+ readonly baseURL: string | undefined;
48
+ }
49
+
50
+ /** The part of Playwright's TestInfo the fixture uses. */
51
+ export interface StageTestInfo {
52
+ readonly status?: string;
53
+ readonly expectedStatus: string;
54
+ attach(
55
+ name: string,
56
+ options: { readonly body: string; readonly contentType: string },
57
+ ): Promise<void>;
58
+ }
59
+
60
+ type Environment = Readonly<Record<string, string | undefined>>;
61
+
62
+ const REPORT_MODES: readonly ReportMode[] = ["on-failure", "always", "never"];
63
+
64
+ /**
65
+ * The environment's say, read once when the fixture is made. A value it
66
+ * cannot use is ignored with one warning: a typo in CI config must neither
67
+ * turn reports off silently nor fail every test, and saying so once is
68
+ * enough.
69
+ */
70
+ function readEnvironment(env: Environment): {
71
+ readonly report: ReportMode | undefined;
72
+ readonly log: LogThreshold | undefined;
73
+ } {
74
+ const warn = (name: string, value: string, allowed: readonly string[]) =>
75
+ createScopedLogger("Fixture").warn(`ignoring an unknown ${name}`, {
76
+ value,
77
+ allowed,
78
+ });
79
+ const wantedReport = env["WIRESTAGE_REPORT"]?.trim().toLowerCase();
80
+ const report = REPORT_MODES.find((mode) => mode === wantedReport);
81
+ if (wantedReport !== undefined && report === undefined)
82
+ warn("WIRESTAGE_REPORT", wantedReport, REPORT_MODES);
83
+ const wantedLog = env["WIRESTAGE_LOG"];
84
+ const log = parseLogThreshold(wantedLog);
85
+ if (wantedLog !== undefined && log === undefined)
86
+ warn("WIRESTAGE_LOG", wantedLog, [
87
+ "debug",
88
+ "log",
89
+ "info",
90
+ "warn",
91
+ "error",
92
+ "silent",
93
+ ]);
94
+ return { report, log };
95
+ }
96
+
97
+ /**
98
+ * The `stage` fixture as a plain function, so it can be tested without the
99
+ * Playwright runner: one stage per test, attached to the test's context
100
+ * before the test body runs, reported on failure, disposed afterwards.
101
+ */
102
+ export function createStageFixture<
103
+ const Plugins extends readonly AnyWirePlugin[],
104
+ >(
105
+ options: StageFixtureOptions<Plugins>,
106
+ env: Environment = process.env,
107
+ ): (
108
+ args: StageFixtureArgs,
109
+ use: (stage: Stage<Plugins>) => Promise<void>,
110
+ testInfo: StageTestInfo,
111
+ ) => Promise<void> {
112
+ const { report, reportLimit, log, attach = true, ...stageOptions } = options;
113
+ const fromEnv = readEnvironment(env);
114
+ const threshold = fromEnv.log ?? log;
115
+ if (threshold !== undefined) setLogThreshold(threshold);
116
+ const mode = fromEnv.report ?? report ?? "on-failure";
117
+ const fixtureLog = createScopedLogger("Fixture");
118
+
119
+ /** Attach the timeline to the test's report, when there is one to read. */
120
+ const attachReport = async (
121
+ stage: Stage<Plugins>,
122
+ testInfo: StageTestInfo,
123
+ ): Promise<void> => {
124
+ const failed = testInfo.status !== testInfo.expectedStatus;
125
+ if (mode === "never" || (mode === "on-failure" && !failed)) return;
126
+ // What the pages still hold is often the frame the failure is about.
127
+ await stage.sync();
128
+ // Nothing on the timeline is nothing to read: no empty attachment.
129
+ if (stage.timeline.size === 0 && stage.timeline.evicted === 0) return;
130
+ const { snapshot, text } = stage.report(
131
+ undefined,
132
+ reportLimit === undefined ? {} : { limit: reportLimit },
133
+ );
134
+ await testInfo.attach("wirestage-timeline.txt", {
135
+ body: text,
136
+ contentType: "text/plain",
137
+ });
138
+ await testInfo.attach("wirestage-timeline.json", {
139
+ body: JSON.stringify(snapshot, null, 2),
140
+ contentType: "application/json",
141
+ });
142
+ };
143
+
144
+ const run = async (
145
+ context: ContextPort | undefined,
146
+ baseURL: string | undefined,
147
+ use: (stage: Stage<Plugins>) => Promise<void>,
148
+ testInfo: StageTestInfo,
149
+ ): Promise<void> => {
150
+ const stage = createStage({
151
+ ...stageOptions,
152
+ ...(baseURL === undefined ? {} : { baseURL }),
153
+ });
154
+ try {
155
+ if (context !== undefined) await stage.attach(context);
156
+ await use(stage);
157
+ } finally {
158
+ try {
159
+ await attachReport(stage, testInfo);
160
+ } catch (error) {
161
+ // The report is a courtesy; failing to write one must not hide
162
+ // the test's own result, nor keep the stage from being disposed.
163
+ fixtureLog.warn("could not attach the timeline", {}, error);
164
+ } finally {
165
+ await stage.dispose();
166
+ }
167
+ }
168
+ };
169
+ // Playwright reads a fixture's dependencies off its SOURCE, so each mode
170
+ // is its own literal: naming `context` is what makes Playwright create one.
171
+ return attach
172
+ ? async ({ context, baseURL }, use, testInfo) =>
173
+ run(context, baseURL, use, testInfo)
174
+ : async ({ baseURL }, use, testInfo) =>
175
+ run(undefined, baseURL, use, testInfo);
176
+ }
177
+
178
+ /**
179
+ * Extend a Playwright `test` with a `stage` fixture:
180
+ *
181
+ * export const test = withStage(base, { plugins: [webrtc(), connect()] });
182
+ * test("...", async ({ page, stage }) => { ... });
183
+ */
184
+ export function withStage<
185
+ Args extends object,
186
+ Workers extends object,
187
+ const Plugins extends readonly AnyWirePlugin[],
188
+ >(
189
+ test: TestType<Args, Workers>,
190
+ options: StageFixtureOptions<Plugins>,
191
+ ): TestType<Args & { stage: Stage<Plugins> }, Workers> {
192
+ // Playwright reads a fixture's dependencies off the SOURCE of its first
193
+ // parameter, so the fixture must be passed as written — with its
194
+ // `({ context, baseURL }, use, testInfo)` destructuring — not wrapped. The
195
+ // cast is only for the generic `extend` signature, which TypeScript cannot
196
+ // resolve against an arbitrary `Args`.
197
+ const extend = test.extend.bind(test) as unknown as (fixtures: {
198
+ readonly stage: ReturnType<typeof createStageFixture<Plugins>>;
199
+ }) => TestType<Args & { stage: Stage<Plugins> }, Workers>;
200
+ return extend({ stage: createStageFixture(options) });
201
+ }
package/src/connect.ts ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @heojeongbo/wirestage/connect — Connect, gRPC-web and gRPC.
3
+ * Needs the optional peers @connectrpc/connect, @connectrpc/connect-node and
4
+ * @bufbuild/protobuf.
5
+ */
6
+ export * from "./features/connect/index.js";
@@ -0,0 +1,8 @@
1
+ /**
2
+ * The frame types the plugin contract is written in terms of.
3
+ *
4
+ * FSD's cross-import notation: `entities/plugin` may import this file and
5
+ * nothing else from `entities/frame`. It keeps the dependency between the two
6
+ * entities explicit and one-way — the frame model knows nothing of plugins.
7
+ */
8
+ export type { Frame, FrameFilter, Timeline } from "../model/frame.js";
@@ -0,0 +1,19 @@
1
+ export { compileFilter } from "./lib/filter.js";
2
+ export { createPayload } from "./lib/payload.js";
3
+ export type { TimelineOptions } from "./lib/timeline.js";
4
+ export { createTimeline } from "./lib/timeline.js";
5
+ export type {
6
+ Direction,
7
+ Frame,
8
+ FrameFilter,
9
+ FrameInit,
10
+ FrameKind,
11
+ FrameSource,
12
+ Payload,
13
+ PayloadInput,
14
+ SnapshotFrame,
15
+ SnapshotOptions,
16
+ Timeline,
17
+ TimelineCursor,
18
+ TimelineSnapshot,
19
+ } from "./model/frame.js";
@@ -0,0 +1,58 @@
1
+ import type { Frame, FrameFilter } from "../model/frame.js";
2
+
3
+ const ALWAYS = () => true;
4
+
5
+ /**
6
+ * Compile a filter into one predicate. Queries run over every frame of a long
7
+ * test, often in a polling loop, so the per-frame work is a handful of
8
+ * comparisons and the `where` callback runs only after the cheap fields pass.
9
+ */
10
+ export function compileFilter(
11
+ filter: FrameFilter | undefined,
12
+ ): (frame: Frame) => boolean {
13
+ if (filter === undefined) return ALWAYS;
14
+ const checks: ((frame: Frame) => boolean)[] = [];
15
+ const { protocol, kind, endpoint, where } = filter;
16
+ if (protocol !== undefined) {
17
+ if (typeof protocol === "string")
18
+ checks.push((f) => f.protocol === protocol);
19
+ else {
20
+ const set = new Set(protocol);
21
+ checks.push((f) => set.has(f.protocol));
22
+ }
23
+ }
24
+ if (kind !== undefined) {
25
+ if (typeof kind === "string") checks.push((f) => f.kind === kind);
26
+ else {
27
+ const set = new Set(kind);
28
+ checks.push((f) => set.has(f.kind));
29
+ }
30
+ }
31
+ for (const key of [
32
+ "direction",
33
+ "source",
34
+ "page",
35
+ "context",
36
+ "correlation",
37
+ ] as const) {
38
+ const expected = filter[key];
39
+ if (expected !== undefined) checks.push((f) => f[key] === expected);
40
+ }
41
+ if (endpoint !== undefined) {
42
+ if (typeof endpoint === "string")
43
+ checks.push((f) => f.endpoint === endpoint);
44
+ else
45
+ checks.push((f) => {
46
+ endpoint.lastIndex = 0;
47
+ return endpoint.test(f.endpoint);
48
+ });
49
+ }
50
+ const after = filter.after;
51
+ if (after !== undefined) checks.push((f) => f.seq > after);
52
+ if (where !== undefined) checks.push(where);
53
+ if (checks.length === 0) return ALWAYS;
54
+ return (frame) => {
55
+ for (const check of checks) if (!check(frame)) return false;
56
+ return true;
57
+ };
58
+ }
@@ -0,0 +1,42 @@
1
+ import { toBytes, utf8 } from "../../../shared/lib/bytes/index.js";
2
+ import type { Payload, PayloadInput } from "../model/frame.js";
3
+
4
+ const EMPTY = new Uint8Array(0);
5
+
6
+ /**
7
+ * A payload that converts lazily and remembers every conversion. Frames are
8
+ * written far more often than read, so nothing is encoded or decoded until a
9
+ * test asks, and asking twice costs once.
10
+ */
11
+ export function createPayload(input: PayloadInput | undefined): Payload {
12
+ let bytes: Uint8Array | undefined =
13
+ input instanceof Uint8Array ? input : undefined;
14
+ let text: string | undefined = typeof input === "string" ? input : undefined;
15
+ let decoded: WeakMap<(bytes: Uint8Array) => unknown, unknown> | undefined;
16
+
17
+ const getBytes = (): Uint8Array => {
18
+ if (bytes !== undefined) return bytes;
19
+ if (typeof input === "function") bytes = input();
20
+ else if (text !== undefined) bytes = toBytes(text);
21
+ else bytes = EMPTY;
22
+ return bytes;
23
+ };
24
+ const getText = (): string => {
25
+ text ??= utf8(getBytes());
26
+ return text;
27
+ };
28
+
29
+ return {
30
+ empty: input === undefined,
31
+ bytes: getBytes,
32
+ text: getText,
33
+ json: <T>() => JSON.parse(getText()) as T,
34
+ decode<T>(decoder: (bytes: Uint8Array) => T): T {
35
+ decoded ??= new WeakMap();
36
+ if (decoded.has(decoder)) return decoded.get(decoder) as T;
37
+ const value = decoder(getBytes());
38
+ decoded.set(decoder, value);
39
+ return value;
40
+ },
41
+ };
42
+ }
@@ -0,0 +1,105 @@
1
+ import { toBase64 } from "../../../shared/lib/bytes/index.js";
2
+ import type {
3
+ Frame,
4
+ SnapshotFrame,
5
+ SnapshotOptions,
6
+ TimelineSnapshot,
7
+ } from "../model/frame.js";
8
+
9
+ /**
10
+ * A JSON-safe copy of `frames`, with payloads cut at `maxBytes` and, given a
11
+ * `limit`, only the newest frames kept: a report is read from its end.
12
+ */
13
+ export function snapshotFrames(
14
+ frames: readonly Frame[],
15
+ evicted: number,
16
+ options: SnapshotOptions = {},
17
+ ): TimelineSnapshot {
18
+ const maxBytes = options.maxBytes ?? 4096;
19
+ const kept =
20
+ options.limit === undefined || frames.length <= options.limit
21
+ ? frames
22
+ : frames.slice(frames.length - options.limit);
23
+ return {
24
+ evicted,
25
+ omitted: frames.length - kept.length,
26
+ frames: kept.map((frame): SnapshotFrame => {
27
+ const base: SnapshotFrame = {
28
+ seq: frame.seq,
29
+ at: frame.at,
30
+ protocol: frame.protocol,
31
+ kind: frame.kind,
32
+ direction: frame.direction,
33
+ endpoint: frame.endpoint,
34
+ source: frame.source,
35
+ ...(frame.page === undefined ? {} : { page: frame.page }),
36
+ ...(frame.context === undefined ? {} : { context: frame.context }),
37
+ ...(frame.correlation === undefined
38
+ ? {}
39
+ : { correlation: frame.correlation }),
40
+ ...(Object.keys(frame.meta).length === 0
41
+ ? {}
42
+ : { meta: jsonSafe(frame.meta) }),
43
+ };
44
+ if (frame.payload.empty) return base;
45
+ const bytes = frame.payload.bytes();
46
+ return {
47
+ ...base,
48
+ size: bytes.byteLength,
49
+ data: toBase64(bytes.subarray(0, maxBytes)),
50
+ ...(bytes.byteLength > maxBytes ? { truncated: true } : {}),
51
+ };
52
+ }),
53
+ };
54
+ }
55
+
56
+ /**
57
+ * One line per frame: `#12 +3.2ms connect out request pkg.Svc/Method 42B`.
58
+ * Times are relative to the first frame listed, which is what a reader of a
59
+ * failure report compares.
60
+ */
61
+ export function formatFrames(
62
+ frames: readonly Frame[],
63
+ options: { readonly limit?: number } = {},
64
+ ): string {
65
+ const limit = options.limit ?? 200;
66
+ // Not `slice(-limit)`: a limit of 0 would be `slice(-0)`, every frame.
67
+ const shown =
68
+ frames.length > limit ? frames.slice(frames.length - limit) : frames;
69
+ const origin = shown[0]?.at ?? 0;
70
+ const lines = shown.map((frame) => {
71
+ const size = frame.payload.empty
72
+ ? ""
73
+ : ` ${frame.payload.bytes().byteLength}B`;
74
+ const page = frame.page === undefined ? "" : ` ${frame.page}`;
75
+ return `#${frame.seq} +${(frame.at - origin).toFixed(1)}ms ${frame.protocol} ${frame.direction} ${frame.kind} ${frame.endpoint}${page}${size}`;
76
+ });
77
+ if (frames.length > limit)
78
+ lines.unshift(`… ${frames.length - limit} earlier frames not shown`);
79
+ return lines.join("\n");
80
+ }
81
+
82
+ /**
83
+ * Make `meta` survive `JSON.stringify`: a bigint (protobuf int64) or a cycle
84
+ * would otherwise throw while writing the report — at the exact moment a test
85
+ * has already failed and the report is the only evidence.
86
+ */
87
+ function jsonSafe(
88
+ meta: Readonly<Record<string, unknown>>,
89
+ ): Record<string, unknown> {
90
+ // The objects on the path from the root to the value being written. Only
91
+ // those make a cycle: an object met again elsewhere is merely shared.
92
+ const ancestors: object[] = [];
93
+ return JSON.parse(
94
+ JSON.stringify(meta, function (this: unknown, _key, value: unknown) {
95
+ if (typeof value === "bigint") return value.toString();
96
+ if (value instanceof Uint8Array) return toBase64(value);
97
+ if (typeof value !== "object" || value === null) return value;
98
+ // `this` holds `value`: whatever was above it is finished.
99
+ while (ancestors.length > 0 && ancestors.at(-1) !== this) ancestors.pop();
100
+ if (ancestors.includes(value)) return "[circular]";
101
+ ancestors.push(value);
102
+ return value;
103
+ }),
104
+ ) as Record<string, unknown>;
105
+ }
@@ -0,0 +1,147 @@
1
+ import type { Clock } from "../../../shared/lib/clock/index.js";
2
+ import { WirestageError } from "../../../shared/lib/errors/index.js";
3
+ import type {
4
+ Frame,
5
+ FrameFilter,
6
+ FrameInit,
7
+ Timeline,
8
+ TimelineCursor,
9
+ } from "../model/frame.js";
10
+ import { compileFilter } from "./filter.js";
11
+ import { createPayload } from "./payload.js";
12
+ import { formatFrames, snapshotFrames } from "./snapshot.js";
13
+
14
+ export interface TimelineOptions {
15
+ readonly clock: Clock;
16
+ /**
17
+ * Frames kept before the oldest are dropped. A 60 Hz data channel makes
18
+ * 3,600 frames a minute; the default holds a long test without letting a
19
+ * soak run grow without bound.
20
+ */
21
+ readonly capacity?: number;
22
+ /**
23
+ * Told when a subscriber throws. By default the first such error is
24
+ * thrown to whoever appended the frame, once every subscriber has run.
25
+ */
26
+ readonly onListenerError?: (error: unknown) => void;
27
+ }
28
+
29
+ const DEFAULT_CAPACITY = 100_000;
30
+
31
+ /**
32
+ * The append-only log every protocol writes to. Order is arrival order (`seq`),
33
+ * not timestamp order: the stage flushes page buffers before every read, so
34
+ * arrival order is also the order things happened in.
35
+ */
36
+ export function createTimeline(options: TimelineOptions): Timeline {
37
+ const capacity = options.capacity ?? DEFAULT_CAPACITY;
38
+ if (!Number.isInteger(capacity) || capacity < 1)
39
+ throw new WirestageError(
40
+ "INVALID_ARGUMENT",
41
+ `timeline capacity must be a positive integer, got ${capacity}`,
42
+ );
43
+ let frames: Frame[] = [];
44
+ let evicted = 0;
45
+ let seq = 0;
46
+ const listeners = new Set<(frame: Frame) => void>();
47
+
48
+ const query = (filter: FrameFilter | undefined): Frame[] => {
49
+ if (filter === undefined) return frames.slice();
50
+ return frames.filter(compileFilter(filter));
51
+ };
52
+
53
+ /** Index of the first frame with `seq` greater than `position`. */
54
+ const indexAfter = (position: number): number => {
55
+ const first = frames[0];
56
+ if (first === undefined) return 0;
57
+ return Math.max(0, position - first.seq + 1);
58
+ };
59
+
60
+ return {
61
+ append(init: FrameInit): Frame {
62
+ const frame: Frame = {
63
+ seq: ++seq,
64
+ at: init.at ?? options.clock.now(),
65
+ protocol: init.protocol,
66
+ kind: init.kind,
67
+ direction: init.direction,
68
+ endpoint: init.endpoint,
69
+ source: init.source,
70
+ payload: createPayload(init.data),
71
+ value: init.value,
72
+ meta: init.meta ?? {},
73
+ page: init.page,
74
+ context: init.context,
75
+ correlation: init.correlation,
76
+ };
77
+ frames.push(frame);
78
+ if (frames.length > capacity) {
79
+ // Drop a tenth at once: shifting one frame per append would make a
80
+ // full timeline O(n) per frame.
81
+ const drop = Math.max(1, Math.floor(capacity / 10));
82
+ frames = frames.slice(drop);
83
+ evicted += drop;
84
+ }
85
+ // The frame is on the timeline whatever a subscriber does with it,
86
+ // and one that throws does not starve the rest.
87
+ const failures: unknown[] = [];
88
+ for (const listener of listeners) {
89
+ try {
90
+ listener(frame);
91
+ } catch (error) {
92
+ failures.push(error);
93
+ }
94
+ }
95
+ if (failures.length > 0) {
96
+ if (options.onListenerError === undefined) throw failures[0];
97
+ for (const failure of failures) options.onListenerError(failure);
98
+ }
99
+ return frame;
100
+ },
101
+ frames: query,
102
+ last(filter) {
103
+ const match = compileFilter(filter);
104
+ return frames.findLast(match);
105
+ },
106
+ marker(name) {
107
+ return frames.findLast((f) => f.kind === "marker" && f.endpoint === name);
108
+ },
109
+ subscribe(listener) {
110
+ listeners.add(listener);
111
+ return () => {
112
+ listeners.delete(listener);
113
+ };
114
+ },
115
+ cursor(): TimelineCursor {
116
+ let position = seq;
117
+ return {
118
+ take(filter) {
119
+ const fresh = frames.slice(indexAfter(position));
120
+ position = seq;
121
+ return filter === undefined
122
+ ? fresh
123
+ : fresh.filter(compileFilter(filter));
124
+ },
125
+ get position() {
126
+ return position;
127
+ },
128
+ };
129
+ },
130
+ clear() {
131
+ frames = [];
132
+ },
133
+ snapshot(filter, snapshotOptions) {
134
+ return snapshotFrames(query(filter), evicted, snapshotOptions);
135
+ },
136
+ format(filter, formatOptions) {
137
+ return formatFrames(query(filter), formatOptions);
138
+ },
139
+ get size() {
140
+ return frames.length;
141
+ },
142
+ get evicted() {
143
+ return evicted;
144
+ },
145
+ capacity,
146
+ };
147
+ }