@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,5 @@
1
+ export type {
2
+ WirestageErrorCode,
3
+ WirestageErrorOptions,
4
+ } from "./wirestage-error.js";
5
+ export { isWirestageError, WirestageError } from "./wirestage-error.js";
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Every failure the library raises on purpose carries a code, so a test can
3
+ * assert on the kind of failure instead of on message text that will change.
4
+ */
5
+ export type WirestageErrorCode =
6
+ /** A caller passed something the API cannot work with. */
7
+ | "INVALID_ARGUMENT"
8
+ /** The call is valid but not now: before attach, after dispose, twice. */
9
+ | "INVALID_STATE"
10
+ /** A wait ran out of time. The message lists what did arrive. */
11
+ | "TIMEOUT"
12
+ /** The environment cannot do this, e.g. a live stream on an https page without TLS. */
13
+ | "UNSUPPORTED"
14
+ /** The stream, connection or stage this call needs has already closed. */
15
+ | "CLOSED";
16
+
17
+ export interface WirestageErrorOptions {
18
+ readonly cause?: unknown;
19
+ readonly details?: Readonly<Record<string, unknown>>;
20
+ }
21
+
22
+ export class WirestageError extends Error {
23
+ override readonly name = "WirestageError";
24
+ readonly code: WirestageErrorCode;
25
+ readonly details: Readonly<Record<string, unknown>>;
26
+
27
+ constructor(
28
+ code: WirestageErrorCode,
29
+ message: string,
30
+ options: WirestageErrorOptions = {},
31
+ ) {
32
+ super(
33
+ message,
34
+ options.cause === undefined ? undefined : { cause: options.cause },
35
+ );
36
+ this.code = code;
37
+ this.details = options.details ?? {};
38
+ }
39
+ }
40
+
41
+ /** True when `value` is a WirestageError, optionally of one code. */
42
+ export function isWirestageError(
43
+ value: unknown,
44
+ code?: WirestageErrorCode,
45
+ ): value is WirestageError {
46
+ return (
47
+ value instanceof WirestageError &&
48
+ (code === undefined || value.code === code)
49
+ );
50
+ }
@@ -0,0 +1,29 @@
1
+ /** `prefix1`, `prefix2`, ... — short ids that read well in a frame dump. */
2
+ export function createSequence(prefix: string): () => string {
3
+ let next = 0;
4
+ return () => `${prefix}${++next}`;
5
+ }
6
+
7
+ /**
8
+ * A stable id per object, without holding the object alive: a closed page
9
+ * must be collectable even though its frames still name it.
10
+ */
11
+ export function createObjectIds<T extends object>(
12
+ prefix: string,
13
+ ): (value: T) => string {
14
+ const ids = new WeakMap<T, string>();
15
+ const nextId = createSequence(prefix);
16
+ return (value) => {
17
+ let id = ids.get(value);
18
+ if (id === undefined) {
19
+ id = nextId();
20
+ ids.set(value, id);
21
+ }
22
+ return id;
23
+ };
24
+ }
25
+
26
+ /** An unguessable token, for URLs a page could otherwise stumble onto. */
27
+ export function randomToken(): string {
28
+ return crypto.randomUUID().replaceAll("-", "");
29
+ }
@@ -0,0 +1 @@
1
+ export { createObjectIds, createSequence, randomToken } from "./id.js";
@@ -0,0 +1,12 @@
1
+ export type {
2
+ LogFields,
3
+ LogScope,
4
+ LogThreshold,
5
+ ScopedLogger,
6
+ } from "./logger.js";
7
+ export {
8
+ createScopedLogger,
9
+ getLogThreshold,
10
+ parseLogThreshold,
11
+ setLogThreshold,
12
+ } from "./logger.js";
@@ -0,0 +1,126 @@
1
+ import { createLogger, type LogLevel } from "@heojeongbo/log-palette";
2
+
3
+ /**
4
+ * The library's logging surface, and the only module that imports log-palette.
5
+ *
6
+ * The loggers are the library's own. log-palette also keeps a process-wide
7
+ * registry (`getLogger`, `setGlobalLevel`, `disableAll`, `configure`) that an
8
+ * app or a test suite using the same package shares; going through it would
9
+ * let importing this library change the level of loggers that are not its
10
+ * own, so nothing here touches it.
11
+ *
12
+ * Output goes to the test runner's stdout. Node ignores the `%c` styling
13
+ * log-palette emits, so the badge reads as plain `[...Stage]` text there.
14
+ */
15
+
16
+ /**
17
+ * The stage's own subsystems, plus whatever scope a plugin brings. The
18
+ * built-in names are spelled out so `"Http"` and `"HTTP"` cannot become two
19
+ * badges for one subsystem; eight characters fit the badge column, and a
20
+ * longer scope is cut to fit.
21
+ */
22
+ export type LogScope =
23
+ | "Stage"
24
+ | "Http"
25
+ | "Socket"
26
+ | "Stream"
27
+ | "Page"
28
+ | "Connect"
29
+ | "Rest"
30
+ | "Sse"
31
+ | "Ws"
32
+ | "Rtc"
33
+ | "Fixture"
34
+ | "Plugin"
35
+ | (string & {});
36
+
37
+ /** A level, or `silent` for no output at all. */
38
+ export type LogThreshold = LogLevel | "silent";
39
+
40
+ export type LogFields = Readonly<Record<string, unknown>>;
41
+
42
+ export interface ScopedLogger {
43
+ debug(message: string, fields?: LogFields, error?: unknown): void;
44
+ info(message: string, fields?: LogFields, error?: unknown): void;
45
+ warn(message: string, fields?: LogFields, error?: unknown): void;
46
+ error(message: string, fields?: LogFields, error?: unknown): void;
47
+ /**
48
+ * Log without paying for `build` when the level would drop the line.
49
+ * log-palette checks the level inside its emit, so arguments are built
50
+ * even for a dropped `debug` — a frame dump on every request adds up.
51
+ */
52
+ lazy(level: LogLevel, message: string, build: () => LogFields): void;
53
+ }
54
+
55
+ const RANK: Record<LogThreshold, number> = {
56
+ debug: 0,
57
+ log: 1,
58
+ info: 2,
59
+ warn: 3,
60
+ error: 4,
61
+ silent: 5,
62
+ };
63
+
64
+ type Palette = ReturnType<typeof createLogger>;
65
+
66
+ /** One logger per scope, so a scope's badge colour is stable. */
67
+ const loggers = new Map<string, Palette>();
68
+ let threshold: LogThreshold = "warn";
69
+
70
+ function apply(logger: Palette): void {
71
+ logger.setEnabled(threshold !== "silent");
72
+ if (threshold !== "silent") logger.setLevel(threshold);
73
+ }
74
+
75
+ /**
76
+ * The default is `warn`: a test suite's output is for the test results, and
77
+ * the library only speaks up when something is wrong. `WIRESTAGE_LOG=debug`
78
+ * (read by the Playwright fixture) turns the rest on.
79
+ */
80
+ export function setLogThreshold(next: LogThreshold): void {
81
+ threshold = next;
82
+ for (const logger of loggers.values()) apply(logger);
83
+ }
84
+
85
+ export function getLogThreshold(): LogThreshold {
86
+ return threshold;
87
+ }
88
+
89
+ /** Parse an environment value. Unknown values return `undefined`, never throw. */
90
+ export function parseLogThreshold(
91
+ value: string | undefined,
92
+ ): LogThreshold | undefined {
93
+ const normalized = value?.trim().toLowerCase();
94
+ return normalized !== undefined && normalized in RANK
95
+ ? (normalized as LogThreshold)
96
+ : undefined;
97
+ }
98
+
99
+ export function createScopedLogger(scope: LogScope): ScopedLogger {
100
+ let base = loggers.get(scope);
101
+ if (base === undefined) {
102
+ base = createLogger(scope);
103
+ apply(base);
104
+ loggers.set(scope, base);
105
+ }
106
+ const palette = base;
107
+ const emit =
108
+ (level: LogLevel) =>
109
+ (message: string, fields?: LogFields, error?: unknown): void => {
110
+ const args: unknown[] = [message];
111
+ if (fields !== undefined) args.push(fields);
112
+ // The error goes last and raw, so the stack survives.
113
+ if (error !== undefined) args.push(error);
114
+ palette[level](...args);
115
+ };
116
+ return {
117
+ debug: emit("debug"),
118
+ info: emit("info"),
119
+ warn: emit("warn"),
120
+ error: emit("error"),
121
+ lazy(level, message, build) {
122
+ if (RANK[level] < RANK[threshold]) return;
123
+ palette[level](message, build());
124
+ },
125
+ };
126
+ }
@@ -0,0 +1,6 @@
1
+ export type { GlobSource, UrlMatcher } from "./url-matcher.js";
2
+ export {
3
+ compileUrlMatcher,
4
+ globSource,
5
+ globToRegExp,
6
+ } from "./url-matcher.js";
@@ -0,0 +1,107 @@
1
+ import { WirestageError } from "../errors/index.js";
2
+
3
+ /**
4
+ * What a rule matches: a glob, a RegExp over the full URL, or a predicate.
5
+ *
6
+ * Globs follow Playwright's routing semantics (`*` stops at `/`, `**` does
7
+ * not, a `**` directory may be absent, `{a,b}` alternates), with one
8
+ * addition: a glob that starts with `/` matches the PATH on any origin, and
9
+ * so ignores the query. A stub for `/api/...` means the same thing on the
10
+ * dev server, the preview build and CI, whose origins all differ.
11
+ */
12
+ export type UrlMatcher = string | RegExp | ((url: URL) => boolean);
13
+
14
+ export function compileUrlMatcher(matcher: UrlMatcher): (url: URL) => boolean {
15
+ if (typeof matcher === "function") return matcher;
16
+ if (matcher instanceof RegExp) {
17
+ return (url) => {
18
+ // A /g or /y RegExp is stateful: without the reset every other
19
+ // request to the same URL would miss.
20
+ matcher.lastIndex = 0;
21
+ return matcher.test(url.href);
22
+ };
23
+ }
24
+ const pattern = globToRegExp(matcher);
25
+ if (!matcher.startsWith("/")) return (url) => pattern.test(url.href);
26
+ if (matcher.includes("?"))
27
+ // It would compile, and then never match anything.
28
+ throw new WirestageError(
29
+ "INVALID_ARGUMENT",
30
+ `"${matcher}" matches the path, which has no query: drop the "?…", or match the whole URL ("**${matcher}")`,
31
+ );
32
+ return (url) => pattern.test(url.pathname);
33
+ }
34
+
35
+ /** Characters with a meaning in a RegExp: escaped wherever a glob means them literally. */
36
+ const SPECIAL = /[.*+?^${}()|[\]\\/]/;
37
+
38
+ /** Translate a glob into an anchored RegExp. */
39
+ export function globToRegExp(glob: string): RegExp {
40
+ return new RegExp(`^${globSource(glob).source}$`);
41
+ }
42
+
43
+ /** A glob as RegExp source (unanchored), and the params it captures, in order. */
44
+ export interface GlobSource {
45
+ readonly source: string;
46
+ readonly params: readonly string[];
47
+ }
48
+
49
+ /**
50
+ * The RegExp source of a glob. With `params`, a `:name` (a letter or `_`,
51
+ * then word characters) matches one segment and captures it: the routes of a
52
+ * REST plugin, in the same glob language as every other pattern.
53
+ */
54
+ export function globSource(
55
+ glob: string,
56
+ options: { readonly params?: boolean } = {},
57
+ ): GlobSource {
58
+ const params: string[] = [];
59
+ let source = "";
60
+ let inGroup = false;
61
+ for (let i = 0; i < glob.length; i++) {
62
+ const char = glob.charAt(i);
63
+ if (char === "\\" && i + 1 < glob.length) {
64
+ source += escapeChar(glob.charAt(++i));
65
+ } else if (char === "*") {
66
+ if (glob.charAt(i + 1) !== "*") {
67
+ source += "[^/]*";
68
+ } else if (glob.charAt(i + 2) === "/") {
69
+ // `a/**/b` matches `a/b` too: the directories may be none.
70
+ source += "(?:.+/)?";
71
+ i += 2;
72
+ } else {
73
+ source += ".*";
74
+ i++;
75
+ }
76
+ } else if (char === "{") {
77
+ inGroup = true;
78
+ source += "(?:";
79
+ } else if (char === "}" && inGroup) {
80
+ inGroup = false;
81
+ source += ")";
82
+ } else if (char === "," && inGroup) {
83
+ source += "|";
84
+ } else if (char === ":" && options.params) {
85
+ // `:8080` in an origin is not a param: a name starts with a letter.
86
+ const name = /^[A-Za-z_]\w*/.exec(glob.slice(i + 1))?.[0];
87
+ if (name === undefined) source += char;
88
+ else {
89
+ params.push(name);
90
+ source += "([^/]+)";
91
+ i += name.length;
92
+ }
93
+ } else {
94
+ source += escapeChar(char);
95
+ }
96
+ }
97
+ if (inGroup)
98
+ throw new WirestageError(
99
+ "INVALID_ARGUMENT",
100
+ `glob "${glob}" opens a "{" it never closes; write a literal brace as "\\{"`,
101
+ );
102
+ return { source, params };
103
+ }
104
+
105
+ function escapeChar(char: string): string {
106
+ return SPECIAL.test(char) ? `\\${char}` : char;
107
+ }
package/src/sse.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** @heojeongbo/wirestage/sse — server-sent events. */
2
+ export * from "./features/sse/index.js";
package/src/webrtc.ts ADDED
@@ -0,0 +1,2 @@
1
+ /** @heojeongbo/wirestage/webrtc — WebRTC data channels. */
2
+ export * from "./features/webrtc/index.js";
@@ -0,0 +1,2 @@
1
+ /** @heojeongbo/wirestage/websocket — WebSockets. */
2
+ export * from "./features/websocket/index.js";
@@ -0,0 +1,9 @@
1
+ export { createStage } from "./lib/create-stage.js";
2
+ export type {
3
+ AttachOptions,
4
+ ReportOptions,
5
+ Stage,
6
+ StageCore,
7
+ StageOptions,
8
+ StageReport,
9
+ } from "./model/stage.js";
@@ -0,0 +1,10 @@
1
+ import type { Attachment } from "../../../entities/plugin/index.js";
2
+ import type { ContextPort } from "../../../shared/api/playwright/index.js";
3
+
4
+ /**
5
+ * An attachment as the stage's engines see it: with the browser context that
6
+ * plugins are not given.
7
+ */
8
+ export interface AttachedContext extends Attachment {
9
+ readonly context: ContextPort;
10
+ }
@@ -0,0 +1,307 @@
1
+ import { createTimeline } from "../../../entities/frame/index.js";
2
+ import type {
3
+ AnyWirePlugin,
4
+ Attachment,
5
+ PluginContext,
6
+ PluginRuntime,
7
+ } from "../../../entities/plugin/index.js";
8
+ import type {
9
+ ContextPort,
10
+ CookieJarPort,
11
+ PagePort,
12
+ } from "../../../shared/api/playwright/index.js";
13
+ import { createStreamServer } from "../../../shared/api/stream-server/index.js";
14
+ import { runAll } from "../../../shared/lib/async/index.js";
15
+ import { createSystemClock } from "../../../shared/lib/clock/index.js";
16
+ import type { CookieInit } from "../../../shared/lib/cookies/index.js";
17
+ import { WirestageError } from "../../../shared/lib/errors/index.js";
18
+ import {
19
+ createObjectIds,
20
+ createSequence,
21
+ randomToken,
22
+ } from "../../../shared/lib/id/index.js";
23
+ import { createScopedLogger } from "../../../shared/lib/logger/index.js";
24
+ import type { Stage, StageCore, StageOptions } from "../model/stage.js";
25
+ import type { AttachedContext } from "./attached-context.js";
26
+ import { createHttpRouter } from "./http-router.js";
27
+ import { createPageHost } from "./page-host.js";
28
+ import { createReads } from "./reads.js";
29
+ import { createSocketRouter } from "./socket-router.js";
30
+
31
+ /** Names a plugin may not take: they would shadow the stage's own API. */
32
+ const RESERVED = new Set<string>([
33
+ "id",
34
+ "timeline",
35
+ "attachments",
36
+ "disposed",
37
+ "attach",
38
+ "detach",
39
+ "pageId",
40
+ "sync",
41
+ "frames",
42
+ "drain",
43
+ "mark",
44
+ "since",
45
+ "between",
46
+ "waitFor",
47
+ "report",
48
+ "dispose",
49
+ ]);
50
+
51
+ interface Attached {
52
+ readonly attachment: AttachedContext;
53
+ /** What plugins and callers are given of the context: never the context. */
54
+ readonly view: Attachment;
55
+ readonly onClose: () => void;
56
+ /** Plugins whose `attach` was started for this context, in order. */
57
+ readonly runtimes: PluginRuntime<unknown>[];
58
+ /** Settles once the attach has finished installing, however it ended. */
59
+ readonly installing: Promise<void>;
60
+ }
61
+
62
+ /**
63
+ * Build a stage: one timeline, the shared services plugins are given, and the
64
+ * plugins themselves, each mounted as `stage.<name>`.
65
+ *
66
+ * The stage is the composition root of everything below it. It is the only
67
+ * place the routers, the page host and the stream server are constructed, so
68
+ * a plugin can only ever reach them through its `PluginContext`.
69
+ */
70
+ export function createStage<const Plugins extends readonly AnyWirePlugin[]>(
71
+ options: StageOptions<Plugins>,
72
+ ): Stage<Plugins> {
73
+ const clock = options.clock ?? createSystemClock();
74
+ const id = randomToken().slice(0, 12);
75
+ const log = createScopedLogger("Stage");
76
+ const timeline = createTimeline({
77
+ clock,
78
+ capacity: options.capacity,
79
+ // A subscriber is the test's own code; its failure is its own, and
80
+ // must not break the route handler or page flush that appended.
81
+ onListenerError: (error) =>
82
+ log.error("a timeline subscriber threw", {}, error),
83
+ });
84
+ const pageId = createObjectIds<PagePort>("p");
85
+ const contextId = createSequence("c");
86
+ const streamServer = createStreamServer({
87
+ ...options.streamServer,
88
+ log: createScopedLogger("Stream"),
89
+ });
90
+ const http = createHttpRouter({
91
+ timeline,
92
+ log: createScopedLogger("Http"),
93
+ streams: streamServer,
94
+ pageId,
95
+ correlation: createSequence("x"),
96
+ clock,
97
+ timeoutMs: options.pageTimeoutMs ?? 2_000,
98
+ });
99
+ const sockets = createSocketRouter({
100
+ timeline,
101
+ log: createScopedLogger("Socket"),
102
+ correlation: createSequence("w"),
103
+ });
104
+ const pages = createPageHost({
105
+ stage: id,
106
+ log: createScopedLogger("Page"),
107
+ clock,
108
+ pageId,
109
+ flushMs: options.pageFlushMs ?? 25,
110
+ timeoutMs: options.pageTimeoutMs ?? 2_000,
111
+ });
112
+ const attached = new Map<ContextPort, Attached>();
113
+ /** Every plugin's runtime, a plugin's own dependencies before it. */
114
+ const runtimes: PluginRuntime<unknown>[] = [];
115
+ let disposed = false;
116
+ let built = false;
117
+
118
+ const sync = async (): Promise<void> => {
119
+ await pages.sync();
120
+ await http.settle();
121
+ };
122
+ const reads = createReads({
123
+ timeline,
124
+ clock,
125
+ sync,
126
+ wait: {
127
+ timeout: options.wait?.timeout ?? 5_000,
128
+ interval: options.wait?.interval ?? 25,
129
+ },
130
+ });
131
+
132
+ /**
133
+ * Undo an attach. One still installing is waited for first: undoing half
134
+ * of it would leave the rest installed for good. Every step runs even when
135
+ * one fails: a plugin's bug must not leak a route.
136
+ */
137
+ const detachOne = async (context: ContextPort): Promise<void> => {
138
+ const entry = attached.get(context);
139
+ if (entry === undefined) return;
140
+ attached.delete(context);
141
+ await entry.installing;
142
+ context.off("close", entry.onClose);
143
+ const { attachment, view } = entry;
144
+ await runAll([
145
+ ...[...entry.runtimes]
146
+ .reverse()
147
+ .map((runtime) => () => runtime.detach?.(view)),
148
+ () => sockets.detach(attachment),
149
+ () => http.detach(attachment),
150
+ () => pages.detach(attachment),
151
+ ]);
152
+ log.debug("detached", { context: attachment.id });
153
+ };
154
+
155
+ const core: StageCore = {
156
+ id,
157
+ timeline,
158
+ get attachments() {
159
+ return [...attached.values()].map((entry) => entry.view);
160
+ },
161
+ get disposed() {
162
+ return disposed;
163
+ },
164
+ async attach(context, attachOptions = {}) {
165
+ if (disposed)
166
+ throw new WirestageError(
167
+ "INVALID_STATE",
168
+ "cannot attach: the stage is disposed",
169
+ );
170
+ if (attached.has(context))
171
+ throw new WirestageError(
172
+ "INVALID_STATE",
173
+ "this context is already attached to the stage",
174
+ );
175
+ // The context stays with the stage and its engines. A plugin gets its
176
+ // id, its base URL and a jar that only reads and adds cookies: what
177
+ // it could do with the context itself would go round the engines.
178
+ const cookies: CookieJarPort = Object.freeze({
179
+ cookies: (urls?: string | string[]) => context.cookies(urls),
180
+ addCookies: (init: CookieInit[]) => context.addCookies(init),
181
+ });
182
+ const view: Attachment = Object.freeze({
183
+ id: contextId(),
184
+ baseURL: attachOptions.baseURL ?? options.baseURL,
185
+ cookies,
186
+ });
187
+ const attachment: AttachedContext = { ...view, context };
188
+ // A closed context takes its routes with it; forgetting it here keeps
189
+ // the next sync from evaluating in pages that no longer exist.
190
+ const onClose = () => {
191
+ detachOne(context).catch((error: unknown) =>
192
+ log.debug("detaching a closed context failed", {}, error),
193
+ );
194
+ };
195
+ const started: PluginRuntime<unknown>[] = [];
196
+ const installing = (async () => {
197
+ await pages.attach(attachment);
198
+ await http.attach(attachment);
199
+ await sockets.attach(attachment);
200
+ for (const runtime of runtimes) {
201
+ started.push(runtime);
202
+ await runtime.attach?.(view);
203
+ }
204
+ })();
205
+ const entry: Attached = {
206
+ attachment,
207
+ view,
208
+ onClose,
209
+ runtimes: started,
210
+ installing: installing.then(
211
+ () => undefined,
212
+ () => undefined,
213
+ ),
214
+ };
215
+ attached.set(context, entry);
216
+ context.on("close", onClose);
217
+ try {
218
+ await installing;
219
+ } catch (error) {
220
+ // Half-attached is worse than not attached: undo what took, so the
221
+ // caller can fix the cause and attach this context again.
222
+ await detachOne(context).catch((secondary: unknown) =>
223
+ log.debug("undoing a failed attach failed", {}, secondary),
224
+ );
225
+ throw error;
226
+ }
227
+ if (attached.get(context) !== entry)
228
+ // Detached, or closed, while it was installing: that detach is
229
+ // undoing all of it, so this attach did not take.
230
+ throw new WirestageError(
231
+ "INVALID_STATE",
232
+ "the context was detached while it was being attached",
233
+ );
234
+ log.debug("attached", { context: attachment.id });
235
+ return () => detachOne(context);
236
+ },
237
+ async detach(context) {
238
+ if (context !== undefined) return detachOne(context);
239
+ await runAll([...attached.keys()].map((each) => () => detachOne(each)));
240
+ },
241
+ pageId,
242
+ sync,
243
+ ...reads,
244
+ async dispose() {
245
+ if (disposed) return;
246
+ disposed = true;
247
+ await runAll([
248
+ () => core.detach(),
249
+ ...[...runtimes].reverse().map((runtime) => () => runtime.dispose?.()),
250
+ () => streamServer.close(),
251
+ ]);
252
+ },
253
+ };
254
+
255
+ // The services, not the engines behind them: attaching, detaching and
256
+ // settling are the stage's to do.
257
+ const services = {
258
+ http: Object.freeze({ add: http.add }),
259
+ sockets: Object.freeze({ add: sockets.add, prepare: sockets.prepare }),
260
+ pages: Object.freeze({
261
+ register: pages.register,
262
+ command: pages.command,
263
+ refresh: pages.refresh,
264
+ pageId: pages.pageId,
265
+ }),
266
+ } satisfies Pick<PluginContext, "http" | "sockets" | "pages">;
267
+
268
+ /** Set a plugin up, and whatever it builds on first. */
269
+ const mount = (plugin: AnyWirePlugin): PluginRuntime<unknown> => {
270
+ const context: PluginContext = {
271
+ name: plugin.name,
272
+ timeline,
273
+ clock,
274
+ log: createScopedLogger(plugin.scope ?? "Plugin"),
275
+ ...services,
276
+ baseURL: options.baseURL,
277
+ attachments: () => core.attachments,
278
+ sync,
279
+ waitFor: reads.waitFor,
280
+ use(inner) {
281
+ if (built)
282
+ throw new WirestageError(
283
+ "INVALID_STATE",
284
+ `plugin "${plugin.name}" asked for "${inner.name}" after setup; call use() inside setup(), before any context is attached`,
285
+ );
286
+ return mount(inner).api as never;
287
+ },
288
+ };
289
+ const runtime = plugin.setup(context);
290
+ runtimes.push(runtime);
291
+ return runtime;
292
+ };
293
+
294
+ const apis = new Map<string, unknown>();
295
+ for (const plugin of options.plugins) {
296
+ if (RESERVED.has(plugin.name) || apis.has(plugin.name))
297
+ throw new WirestageError(
298
+ "INVALID_ARGUMENT",
299
+ RESERVED.has(plugin.name)
300
+ ? `plugin name "${plugin.name}" is reserved by the stage`
301
+ : `two plugins are named "${plugin.name}"`,
302
+ );
303
+ apis.set(plugin.name, mount(plugin).api);
304
+ }
305
+ built = true;
306
+ return Object.assign(core, Object.fromEntries(apis)) as Stage<Plugins>;
307
+ }