reactor-effect-client 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (514) hide show
  1. package/NOTICE +7 -2
  2. package/README.md +103 -141
  3. package/dist/Coordinator.d.ts +382 -0
  4. package/dist/Coordinator.d.ts.map +1 -0
  5. package/dist/Coordinator.js +732 -0
  6. package/dist/Coordinator.js.map +1 -0
  7. package/dist/H3.d.ts +159 -0
  8. package/dist/H3.d.ts.map +1 -0
  9. package/dist/H3.js +21 -0
  10. package/dist/H3.js.map +1 -0
  11. package/dist/H3Source.d.ts +93 -0
  12. package/dist/H3Source.d.ts.map +1 -0
  13. package/dist/H3Source.js +401 -0
  14. package/dist/H3Source.js.map +1 -0
  15. package/dist/LocalSource.d.ts +68 -0
  16. package/dist/LocalSource.d.ts.map +1 -0
  17. package/dist/LocalSource.js +354 -0
  18. package/dist/LocalSource.js.map +1 -0
  19. package/dist/Media.d.ts +106 -0
  20. package/dist/Media.d.ts.map +1 -0
  21. package/dist/Media.js +16 -0
  22. package/dist/Media.js.map +1 -0
  23. package/dist/Peer.d.ts +134 -0
  24. package/dist/Peer.d.ts.map +1 -0
  25. package/dist/Peer.js +57 -0
  26. package/dist/Peer.js.map +1 -0
  27. package/dist/Playout.d.ts +724 -0
  28. package/dist/Playout.d.ts.map +1 -0
  29. package/dist/Playout.js +28 -0
  30. package/dist/Playout.js.map +1 -0
  31. package/dist/Reactor.d.ts +135 -0
  32. package/dist/Reactor.d.ts.map +1 -0
  33. package/dist/Reactor.js +158 -0
  34. package/dist/Reactor.js.map +1 -0
  35. package/dist/ReactorError.d.ts +331 -0
  36. package/dist/ReactorError.d.ts.map +1 -0
  37. package/dist/ReactorError.js +316 -0
  38. package/dist/ReactorError.js.map +1 -0
  39. package/dist/ReactorTest.d.ts +383 -0
  40. package/dist/ReactorTest.d.ts.map +1 -0
  41. package/dist/ReactorTest.js +317 -0
  42. package/dist/ReactorTest.js.map +1 -0
  43. package/dist/Session.d.ts +314 -0
  44. package/dist/Session.d.ts.map +1 -0
  45. package/dist/Session.js +51 -0
  46. package/dist/Session.js.map +1 -0
  47. package/dist/index.d.ts +12 -22
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +12 -11
  50. package/dist/index.js.map +1 -1
  51. package/dist/internal/correlator.d.ts +46 -0
  52. package/dist/internal/correlator.d.ts.map +1 -0
  53. package/dist/internal/correlator.js +150 -0
  54. package/dist/internal/correlator.js.map +1 -0
  55. package/dist/internal/deadline.d.ts +24 -0
  56. package/dist/internal/deadline.d.ts.map +1 -0
  57. package/dist/internal/deadline.js +44 -0
  58. package/dist/internal/deadline.js.map +1 -0
  59. package/dist/{h3/_internal/contracts.d.ts → internal/h3/commands.d.ts} +39 -33
  60. package/dist/internal/h3/commands.d.ts.map +1 -0
  61. package/dist/internal/h3/commands.js +128 -0
  62. package/dist/internal/h3/commands.js.map +1 -0
  63. package/dist/{h3 → internal/h3}/messages.d.ts +16 -11
  64. package/dist/internal/h3/messages.d.ts.map +1 -0
  65. package/dist/{h3 → internal/h3}/messages.js +19 -31
  66. package/dist/internal/h3/messages.js.map +1 -0
  67. package/dist/internal/h3/operations.d.ts +145 -0
  68. package/dist/internal/h3/operations.d.ts.map +1 -0
  69. package/dist/internal/h3/operations.js +284 -0
  70. package/dist/internal/h3/operations.js.map +1 -0
  71. package/dist/{h3 → internal/h3}/profile.d.ts +63 -65
  72. package/dist/internal/h3/profile.d.ts.map +1 -0
  73. package/dist/{h3 → internal/h3}/profile.js +39 -39
  74. package/dist/internal/h3/profile.js.map +1 -0
  75. package/dist/internal/h3/provider.d.ts +14 -0
  76. package/dist/internal/h3/provider.d.ts.map +1 -0
  77. package/dist/internal/h3/provider.js +672 -0
  78. package/dist/internal/h3/provider.js.map +1 -0
  79. package/dist/internal/h3/references.d.ts +68 -0
  80. package/dist/internal/h3/references.d.ts.map +1 -0
  81. package/dist/internal/h3/references.js +339 -0
  82. package/dist/internal/h3/references.js.map +1 -0
  83. package/dist/internal/h3/request.d.ts +56 -0
  84. package/dist/internal/h3/request.d.ts.map +1 -0
  85. package/dist/internal/h3/request.js +70 -0
  86. package/dist/internal/h3/request.js.map +1 -0
  87. package/dist/internal/h3/state.d.ts +120 -0
  88. package/dist/internal/h3/state.d.ts.map +1 -0
  89. package/dist/internal/h3/state.js +270 -0
  90. package/dist/internal/h3/state.js.map +1 -0
  91. package/dist/{Submission.d.ts → internal/h3/submission.d.ts} +1 -3
  92. package/dist/internal/h3/submission.d.ts.map +1 -0
  93. package/dist/{Submission.js → internal/h3/submission.js} +1 -9
  94. package/dist/internal/h3/submission.js.map +1 -0
  95. package/dist/internal/hub.d.ts +32 -0
  96. package/dist/internal/hub.d.ts.map +1 -0
  97. package/dist/internal/hub.js +92 -0
  98. package/dist/internal/hub.js.map +1 -0
  99. package/dist/internal/playout/errors.d.ts +51 -0
  100. package/dist/internal/playout/errors.d.ts.map +1 -0
  101. package/dist/internal/playout/errors.js +40 -0
  102. package/dist/internal/playout/errors.js.map +1 -0
  103. package/dist/internal/playout/policy.d.ts +559 -0
  104. package/dist/internal/playout/policy.d.ts.map +1 -0
  105. package/dist/internal/playout/policy.js +2338 -0
  106. package/dist/internal/playout/policy.js.map +1 -0
  107. package/dist/internal/playout/runtime.d.ts +28 -0
  108. package/dist/internal/playout/runtime.d.ts.map +1 -0
  109. package/dist/internal/playout/runtime.js +766 -0
  110. package/dist/internal/playout/runtime.js.map +1 -0
  111. package/dist/internal/playout/tag.d.ts +14 -0
  112. package/dist/internal/playout/tag.d.ts.map +1 -0
  113. package/dist/internal/playout/tag.js +43 -0
  114. package/dist/internal/playout/tag.js.map +1 -0
  115. package/dist/internal/proto/reactor_wire/v1/common_pb.d.ts +55 -0
  116. package/dist/internal/proto/reactor_wire/v1/common_pb.d.ts.map +1 -0
  117. package/dist/internal/proto/reactor_wire/v1/common_pb.js +42 -0
  118. package/dist/internal/proto/reactor_wire/v1/common_pb.js.map +1 -0
  119. package/dist/internal/proto/reactor_wire/v1/control_pb.d.ts +160 -0
  120. package/dist/internal/proto/reactor_wire/v1/control_pb.d.ts.map +1 -0
  121. package/dist/internal/proto/reactor_wire/v1/control_pb.js +21 -0
  122. package/dist/internal/proto/reactor_wire/v1/control_pb.js.map +1 -0
  123. package/dist/internal/proto/reactor_wire/v1/data_pb.d.ts +89 -0
  124. package/dist/internal/proto/reactor_wire/v1/data_pb.d.ts.map +1 -0
  125. package/dist/internal/proto/reactor_wire/v1/data_pb.js +20 -0
  126. package/dist/internal/proto/reactor_wire/v1/data_pb.js.map +1 -0
  127. package/dist/internal/proto/reactor_wire/v1/model_pb.d.ts +85 -0
  128. package/dist/internal/proto/reactor_wire/v1/model_pb.d.ts.map +1 -0
  129. package/dist/internal/proto/reactor_wire/v1/model_pb.js +24 -0
  130. package/dist/internal/proto/reactor_wire/v1/model_pb.js.map +1 -0
  131. package/dist/internal/proto/reactor_wire/v1/platform_pb.d.ts +190 -0
  132. package/dist/internal/proto/reactor_wire/v1/platform_pb.d.ts.map +1 -0
  133. package/dist/internal/proto/reactor_wire/v1/platform_pb.js +54 -0
  134. package/dist/internal/proto/reactor_wire/v1/platform_pb.js.map +1 -0
  135. package/dist/internal/proto/reactor_wire/v1/track_pb.d.ts +88 -0
  136. package/dist/internal/proto/reactor_wire/v1/track_pb.d.ts.map +1 -0
  137. package/dist/internal/proto/reactor_wire/v1/track_pb.js +33 -0
  138. package/dist/internal/proto/reactor_wire/v1/track_pb.js.map +1 -0
  139. package/dist/internal/queue.d.ts +4 -0
  140. package/dist/internal/queue.d.ts.map +1 -0
  141. package/dist/{_internal → internal}/queue.js +0 -9
  142. package/dist/internal/queue.js.map +1 -0
  143. package/dist/internal/reactorTest/coordinator.d.ts +5 -0
  144. package/dist/internal/reactorTest/coordinator.d.ts.map +1 -0
  145. package/dist/internal/reactorTest/coordinator.js +178 -0
  146. package/dist/internal/reactorTest/coordinator.js.map +1 -0
  147. package/dist/internal/reactorTest/faults.d.ts +172 -0
  148. package/dist/internal/reactorTest/faults.d.ts.map +1 -0
  149. package/dist/internal/reactorTest/faults.js +23 -0
  150. package/dist/internal/reactorTest/faults.js.map +1 -0
  151. package/dist/internal/reactorTest/h3.d.ts +431 -0
  152. package/dist/internal/reactorTest/h3.d.ts.map +1 -0
  153. package/dist/internal/reactorTest/h3.js +625 -0
  154. package/dist/internal/reactorTest/h3.js.map +1 -0
  155. package/dist/internal/reactorTest/media.d.ts +22 -0
  156. package/dist/internal/reactorTest/media.d.ts.map +1 -0
  157. package/dist/internal/reactorTest/media.js +34 -0
  158. package/dist/internal/reactorTest/media.js.map +1 -0
  159. package/dist/internal/reactorTest/peer.d.ts +318 -0
  160. package/dist/internal/reactorTest/peer.d.ts.map +1 -0
  161. package/dist/internal/reactorTest/peer.js +206 -0
  162. package/dist/internal/reactorTest/peer.js.map +1 -0
  163. package/dist/internal/reactorTest/playout.d.ts +34 -0
  164. package/dist/internal/reactorTest/playout.d.ts.map +1 -0
  165. package/dist/internal/reactorTest/playout.js +340 -0
  166. package/dist/internal/reactorTest/playout.js.map +1 -0
  167. package/dist/internal/reactorTest/references.d.ts +25 -0
  168. package/dist/internal/reactorTest/references.d.ts.map +1 -0
  169. package/dist/{testing/Png.js → internal/reactorTest/references.js} +50 -12
  170. package/dist/internal/reactorTest/references.js.map +1 -0
  171. package/dist/internal/reactorTest/sessions.d.ts +364 -0
  172. package/dist/internal/reactorTest/sessions.d.ts.map +1 -0
  173. package/dist/internal/reactorTest/sessions.js +592 -0
  174. package/dist/internal/reactorTest/sessions.js.map +1 -0
  175. package/dist/internal/reactorTest/timing.d.ts +13 -0
  176. package/dist/internal/reactorTest/timing.d.ts.map +1 -0
  177. package/dist/internal/reactorTest/timing.js +20 -0
  178. package/dist/internal/reactorTest/timing.js.map +1 -0
  179. package/dist/internal/recording.d.ts +52 -0
  180. package/dist/internal/recording.d.ts.map +1 -0
  181. package/dist/internal/recording.js +141 -0
  182. package/dist/internal/recording.js.map +1 -0
  183. package/dist/internal/session/connect.d.ts +34 -0
  184. package/dist/internal/session/connect.d.ts.map +1 -0
  185. package/dist/internal/session/connect.js +502 -0
  186. package/dist/internal/session/connect.js.map +1 -0
  187. package/dist/internal/session/generation.d.ts +35 -0
  188. package/dist/internal/session/generation.d.ts.map +1 -0
  189. package/dist/internal/session/generation.js +185 -0
  190. package/dist/internal/session/generation.js.map +1 -0
  191. package/dist/internal/session/ice.d.ts +23 -0
  192. package/dist/internal/session/ice.d.ts.map +1 -0
  193. package/dist/internal/session/ice.js +57 -0
  194. package/dist/internal/session/ice.js.map +1 -0
  195. package/dist/internal/session/inbound.d.ts +50 -0
  196. package/dist/internal/session/inbound.d.ts.map +1 -0
  197. package/dist/internal/session/inbound.js +202 -0
  198. package/dist/internal/session/inbound.js.map +1 -0
  199. package/dist/internal/session/model.d.ts +163 -0
  200. package/dist/internal/session/model.d.ts.map +1 -0
  201. package/dist/internal/session/model.js +78 -0
  202. package/dist/internal/session/model.js.map +1 -0
  203. package/dist/internal/session/requests.d.ts +71 -0
  204. package/dist/internal/session/requests.d.ts.map +1 -0
  205. package/dist/internal/session/requests.js +240 -0
  206. package/dist/internal/session/requests.js.map +1 -0
  207. package/dist/internal/session/tracks.d.ts +21 -0
  208. package/dist/internal/session/tracks.d.ts.map +1 -0
  209. package/dist/internal/session/tracks.js +166 -0
  210. package/dist/internal/session/tracks.js.map +1 -0
  211. package/dist/internal/session/upload.d.ts +21 -0
  212. package/dist/internal/session/upload.d.ts.map +1 -0
  213. package/dist/internal/session/upload.js +68 -0
  214. package/dist/internal/session/upload.js.map +1 -0
  215. package/dist/internal/session.d.ts +66 -0
  216. package/dist/internal/session.d.ts.map +1 -0
  217. package/dist/internal/session.js +280 -0
  218. package/dist/internal/session.js.map +1 -0
  219. package/dist/{stats.d.ts → internal/stats.d.ts} +30 -16
  220. package/dist/internal/stats.d.ts.map +1 -0
  221. package/dist/internal/stats.js +201 -0
  222. package/dist/internal/stats.js.map +1 -0
  223. package/dist/internal/token.d.ts +22 -0
  224. package/dist/internal/token.d.ts.map +1 -0
  225. package/dist/internal/token.js +65 -0
  226. package/dist/internal/token.js.map +1 -0
  227. package/dist/internal/wire.d.ts +37 -0
  228. package/dist/internal/wire.d.ts.map +1 -0
  229. package/dist/internal/wire.js +49 -0
  230. package/dist/internal/wire.js.map +1 -0
  231. package/notices/bufbuild-protobuf-LICENSE +201 -0
  232. package/package.json +17 -38
  233. package/dist/FetchHttp.d.ts +0 -9
  234. package/dist/FetchHttp.d.ts.map +0 -1
  235. package/dist/FetchHttp.js +0 -63
  236. package/dist/FetchHttp.js.map +0 -1
  237. package/dist/PeerFactory.d.ts +0 -26
  238. package/dist/PeerFactory.d.ts.map +0 -1
  239. package/dist/PeerFactory.js +0 -4
  240. package/dist/PeerFactory.js.map +0 -1
  241. package/dist/PeerTypes.d.ts +0 -64
  242. package/dist/PeerTypes.d.ts.map +0 -1
  243. package/dist/PeerTypes.js +0 -2
  244. package/dist/PeerTypes.js.map +0 -1
  245. package/dist/Sequence.d.ts +0 -85
  246. package/dist/Sequence.d.ts.map +0 -1
  247. package/dist/Sequence.js +0 -210
  248. package/dist/Sequence.js.map +0 -1
  249. package/dist/SessionTypes.d.ts +0 -312
  250. package/dist/SessionTypes.d.ts.map +0 -1
  251. package/dist/SessionTypes.js +0 -19
  252. package/dist/SessionTypes.js.map +0 -1
  253. package/dist/Submission.d.ts.map +0 -1
  254. package/dist/Submission.js.map +0 -1
  255. package/dist/_internal/queue.d.ts +0 -6
  256. package/dist/_internal/queue.d.ts.map +0 -1
  257. package/dist/_internal/queue.js.map +0 -1
  258. package/dist/bytes.d.ts +0 -6
  259. package/dist/bytes.d.ts.map +0 -1
  260. package/dist/bytes.js +0 -32
  261. package/dist/bytes.js.map +0 -1
  262. package/dist/contract.d.ts +0 -149
  263. package/dist/contract.d.ts.map +0 -1
  264. package/dist/contract.js +0 -119
  265. package/dist/contract.js.map +0 -1
  266. package/dist/coordinator/_internal/client.d.ts +0 -215
  267. package/dist/coordinator/_internal/client.d.ts.map +0 -1
  268. package/dist/coordinator/_internal/client.js +0 -563
  269. package/dist/coordinator/_internal/client.js.map +0 -1
  270. package/dist/coordinator/_internal/recording.d.ts +0 -31
  271. package/dist/coordinator/_internal/recording.d.ts.map +0 -1
  272. package/dist/coordinator/_internal/recording.js +0 -123
  273. package/dist/coordinator/_internal/recording.js.map +0 -1
  274. package/dist/coordinator/_internal/response.d.ts +0 -15
  275. package/dist/coordinator/_internal/response.d.ts.map +0 -1
  276. package/dist/coordinator/_internal/response.js +0 -55
  277. package/dist/coordinator/_internal/response.js.map +0 -1
  278. package/dist/coordinator/_internal/schemas.d.ts +0 -73
  279. package/dist/coordinator/_internal/schemas.d.ts.map +0 -1
  280. package/dist/coordinator/_internal/schemas.js +0 -200
  281. package/dist/coordinator/_internal/schemas.js.map +0 -1
  282. package/dist/coordinator/index.d.ts +0 -40
  283. package/dist/coordinator/index.d.ts.map +0 -1
  284. package/dist/coordinator/index.js +0 -21
  285. package/dist/coordinator/index.js.map +0 -1
  286. package/dist/correlation.d.ts +0 -47
  287. package/dist/correlation.d.ts.map +0 -1
  288. package/dist/correlation.js +0 -131
  289. package/dist/correlation.js.map +0 -1
  290. package/dist/duration.d.ts +0 -27
  291. package/dist/duration.d.ts.map +0 -1
  292. package/dist/duration.js +0 -47
  293. package/dist/duration.js.map +0 -1
  294. package/dist/errors.d.ts +0 -1303
  295. package/dist/errors.d.ts.map +0 -1
  296. package/dist/errors.js +0 -745
  297. package/dist/errors.js.map +0 -1
  298. package/dist/h3/_internal/client.d.ts +0 -9
  299. package/dist/h3/_internal/client.d.ts.map +0 -1
  300. package/dist/h3/_internal/client.js +0 -652
  301. package/dist/h3/_internal/client.js.map +0 -1
  302. package/dist/h3/_internal/contracts.d.ts.map +0 -1
  303. package/dist/h3/_internal/contracts.js +0 -123
  304. package/dist/h3/_internal/contracts.js.map +0 -1
  305. package/dist/h3/_internal/deployment.d.ts +0 -8
  306. package/dist/h3/_internal/deployment.d.ts.map +0 -1
  307. package/dist/h3/_internal/deployment.js +0 -149
  308. package/dist/h3/_internal/deployment.js.map +0 -1
  309. package/dist/h3/_internal/evidence.d.ts +0 -17
  310. package/dist/h3/_internal/evidence.d.ts.map +0 -1
  311. package/dist/h3/_internal/evidence.js +0 -48
  312. package/dist/h3/_internal/evidence.js.map +0 -1
  313. package/dist/h3/_internal/operations.d.ts +0 -58
  314. package/dist/h3/_internal/operations.d.ts.map +0 -1
  315. package/dist/h3/_internal/operations.js +0 -268
  316. package/dist/h3/_internal/operations.js.map +0 -1
  317. package/dist/h3/_internal/references.d.ts +0 -24
  318. package/dist/h3/_internal/references.d.ts.map +0 -1
  319. package/dist/h3/_internal/references.js +0 -309
  320. package/dist/h3/_internal/references.js.map +0 -1
  321. package/dist/h3/_internal/request.d.ts +0 -21
  322. package/dist/h3/_internal/request.d.ts.map +0 -1
  323. package/dist/h3/_internal/request.js +0 -97
  324. package/dist/h3/_internal/request.js.map +0 -1
  325. package/dist/h3/_internal/retained.d.ts +0 -22
  326. package/dist/h3/_internal/retained.d.ts.map +0 -1
  327. package/dist/h3/_internal/retained.js +0 -89
  328. package/dist/h3/_internal/retained.js.map +0 -1
  329. package/dist/h3/_internal/state.d.ts +0 -44
  330. package/dist/h3/_internal/state.d.ts.map +0 -1
  331. package/dist/h3/_internal/state.js +0 -283
  332. package/dist/h3/_internal/state.js.map +0 -1
  333. package/dist/h3/index.d.ts +0 -10
  334. package/dist/h3/index.d.ts.map +0 -1
  335. package/dist/h3/index.js +0 -7
  336. package/dist/h3/index.js.map +0 -1
  337. package/dist/h3/messages.d.ts.map +0 -1
  338. package/dist/h3/messages.js.map +0 -1
  339. package/dist/h3/profile.d.ts.map +0 -1
  340. package/dist/h3/profile.js.map +0 -1
  341. package/dist/h3/types.d.ts +0 -291
  342. package/dist/h3/types.d.ts.map +0 -1
  343. package/dist/h3/types.js +0 -2
  344. package/dist/h3/types.js.map +0 -1
  345. package/dist/host.d.ts +0 -26
  346. package/dist/host.d.ts.map +0 -1
  347. package/dist/host.js +0 -20
  348. package/dist/host.js.map +0 -1
  349. package/dist/json.d.ts +0 -25
  350. package/dist/json.d.ts.map +0 -1
  351. package/dist/json.js +0 -143
  352. package/dist/json.js.map +0 -1
  353. package/dist/media-stream.d.ts +0 -10
  354. package/dist/media-stream.d.ts.map +0 -1
  355. package/dist/media-stream.js +0 -18
  356. package/dist/media-stream.js.map +0 -1
  357. package/dist/observation.d.ts +0 -38
  358. package/dist/observation.d.ts.map +0 -1
  359. package/dist/observation.js +0 -113
  360. package/dist/observation.js.map +0 -1
  361. package/dist/orchestration/elapsed.d.ts +0 -9
  362. package/dist/orchestration/elapsed.d.ts.map +0 -1
  363. package/dist/orchestration/elapsed.js +0 -8
  364. package/dist/orchestration/elapsed.js.map +0 -1
  365. package/dist/orchestration/h3-source.d.ts +0 -58
  366. package/dist/orchestration/h3-source.d.ts.map +0 -1
  367. package/dist/orchestration/h3-source.js +0 -362
  368. package/dist/orchestration/h3-source.js.map +0 -1
  369. package/dist/orchestration/index.d.ts +0 -17
  370. package/dist/orchestration/index.d.ts.map +0 -1
  371. package/dist/orchestration/index.js +0 -12
  372. package/dist/orchestration/index.js.map +0 -1
  373. package/dist/orchestration/media-buffer.d.ts +0 -32
  374. package/dist/orchestration/media-buffer.d.ts.map +0 -1
  375. package/dist/orchestration/media-buffer.js +0 -94
  376. package/dist/orchestration/media-buffer.js.map +0 -1
  377. package/dist/orchestration/open-h3.d.ts +0 -101
  378. package/dist/orchestration/open-h3.d.ts.map +0 -1
  379. package/dist/orchestration/open-h3.js +0 -127
  380. package/dist/orchestration/open-h3.js.map +0 -1
  381. package/dist/orchestration/queries.d.ts +0 -10
  382. package/dist/orchestration/queries.d.ts.map +0 -1
  383. package/dist/orchestration/queries.js +0 -43
  384. package/dist/orchestration/queries.js.map +0 -1
  385. package/dist/orchestration/references.d.ts +0 -18
  386. package/dist/orchestration/references.d.ts.map +0 -1
  387. package/dist/orchestration/references.js +0 -79
  388. package/dist/orchestration/references.js.map +0 -1
  389. package/dist/orchestration/renewal-state.d.ts +0 -84
  390. package/dist/orchestration/renewal-state.d.ts.map +0 -1
  391. package/dist/orchestration/renewal-state.js +0 -95
  392. package/dist/orchestration/renewal-state.js.map +0 -1
  393. package/dist/orchestration/renewal.d.ts +0 -103
  394. package/dist/orchestration/renewal.d.ts.map +0 -1
  395. package/dist/orchestration/renewal.js +0 -1088
  396. package/dist/orchestration/renewal.js.map +0 -1
  397. package/dist/orchestration/request.d.ts +0 -65
  398. package/dist/orchestration/request.d.ts.map +0 -1
  399. package/dist/orchestration/request.js +0 -134
  400. package/dist/orchestration/request.js.map +0 -1
  401. package/dist/orchestration/retention.d.ts +0 -100
  402. package/dist/orchestration/retention.d.ts.map +0 -1
  403. package/dist/orchestration/retention.js +0 -134
  404. package/dist/orchestration/retention.js.map +0 -1
  405. package/dist/orchestration/routing.d.ts +0 -21
  406. package/dist/orchestration/routing.d.ts.map +0 -1
  407. package/dist/orchestration/routing.js +0 -93
  408. package/dist/orchestration/routing.js.map +0 -1
  409. package/dist/orchestration/scheduler-key.d.ts +0 -14
  410. package/dist/orchestration/scheduler-key.d.ts.map +0 -1
  411. package/dist/orchestration/scheduler-key.js +0 -62
  412. package/dist/orchestration/scheduler-key.js.map +0 -1
  413. package/dist/orchestration/scheduler-policy.d.ts +0 -222
  414. package/dist/orchestration/scheduler-policy.d.ts.map +0 -1
  415. package/dist/orchestration/scheduler-policy.js +0 -582
  416. package/dist/orchestration/scheduler-policy.js.map +0 -1
  417. package/dist/orchestration/scheduler.d.ts +0 -383
  418. package/dist/orchestration/scheduler.d.ts.map +0 -1
  419. package/dist/orchestration/scheduler.js +0 -2597
  420. package/dist/orchestration/scheduler.js.map +0 -1
  421. package/dist/orchestration/source-slot.d.ts +0 -124
  422. package/dist/orchestration/source-slot.d.ts.map +0 -1
  423. package/dist/orchestration/source-slot.js +0 -348
  424. package/dist/orchestration/source-slot.js.map +0 -1
  425. package/dist/orchestration/types.d.ts +0 -1462
  426. package/dist/orchestration/types.d.ts.map +0 -1
  427. package/dist/orchestration/types.js +0 -135
  428. package/dist/orchestration/types.js.map +0 -1
  429. package/dist/protobuf.d.ts +0 -63
  430. package/dist/protobuf.d.ts.map +0 -1
  431. package/dist/protobuf.js +0 -252
  432. package/dist/protobuf.js.map +0 -1
  433. package/dist/session/_internal/acquire.d.ts +0 -21
  434. package/dist/session/_internal/acquire.d.ts.map +0 -1
  435. package/dist/session/_internal/acquire.js +0 -168
  436. package/dist/session/_internal/acquire.js.map +0 -1
  437. package/dist/session/_internal/cleanup.d.ts +0 -19
  438. package/dist/session/_internal/cleanup.d.ts.map +0 -1
  439. package/dist/session/_internal/cleanup.js +0 -102
  440. package/dist/session/_internal/cleanup.js.map +0 -1
  441. package/dist/session/_internal/lifecycle.d.ts +0 -67
  442. package/dist/session/_internal/lifecycle.d.ts.map +0 -1
  443. package/dist/session/_internal/lifecycle.js +0 -142
  444. package/dist/session/_internal/lifecycle.js.map +0 -1
  445. package/dist/session/_internal/remote.d.ts +0 -28
  446. package/dist/session/_internal/remote.d.ts.map +0 -1
  447. package/dist/session/_internal/remote.js +0 -71
  448. package/dist/session/_internal/remote.js.map +0 -1
  449. package/dist/session/_internal/uploads.d.ts +0 -7
  450. package/dist/session/_internal/uploads.d.ts.map +0 -1
  451. package/dist/session/_internal/uploads.js +0 -31
  452. package/dist/session/_internal/uploads.js.map +0 -1
  453. package/dist/session/commands.d.ts +0 -2
  454. package/dist/session/commands.d.ts.map +0 -1
  455. package/dist/session/commands.js +0 -2
  456. package/dist/session/commands.js.map +0 -1
  457. package/dist/session/files.d.ts +0 -21
  458. package/dist/session/files.d.ts.map +0 -1
  459. package/dist/session/files.js +0 -49
  460. package/dist/session/files.js.map +0 -1
  461. package/dist/session/index.d.ts +0 -84
  462. package/dist/session/index.d.ts.map +0 -1
  463. package/dist/session/index.js +0 -16
  464. package/dist/session/index.js.map +0 -1
  465. package/dist/session/media.d.ts +0 -114
  466. package/dist/session/media.d.ts.map +0 -1
  467. package/dist/session/media.js +0 -2
  468. package/dist/session/media.js.map +0 -1
  469. package/dist/session/recorder.d.ts +0 -24
  470. package/dist/session/recorder.d.ts.map +0 -1
  471. package/dist/session/recorder.js +0 -19
  472. package/dist/session/recorder.js.map +0 -1
  473. package/dist/session.d.ts +0 -105
  474. package/dist/session.d.ts.map +0 -1
  475. package/dist/session.js +0 -976
  476. package/dist/session.js.map +0 -1
  477. package/dist/simulation/_internal/source.d.ts +0 -12
  478. package/dist/simulation/_internal/source.d.ts.map +0 -1
  479. package/dist/simulation/_internal/source.js +0 -513
  480. package/dist/simulation/_internal/source.js.map +0 -1
  481. package/dist/simulation/index.d.ts +0 -13
  482. package/dist/simulation/index.d.ts.map +0 -1
  483. package/dist/simulation/index.js +0 -12
  484. package/dist/simulation/index.js.map +0 -1
  485. package/dist/simulation/types.d.ts +0 -39
  486. package/dist/simulation/types.d.ts.map +0 -1
  487. package/dist/simulation/types.js +0 -2
  488. package/dist/simulation/types.js.map +0 -1
  489. package/dist/stats.d.ts.map +0 -1
  490. package/dist/stats.js +0 -205
  491. package/dist/stats.js.map +0 -1
  492. package/dist/testing/Faults.d.ts +0 -4
  493. package/dist/testing/Faults.d.ts.map +0 -1
  494. package/dist/testing/Faults.js +0 -7
  495. package/dist/testing/Faults.js.map +0 -1
  496. package/dist/testing/Png.d.ts +0 -3
  497. package/dist/testing/Png.d.ts.map +0 -1
  498. package/dist/testing/Png.js.map +0 -1
  499. package/dist/testing/Wav.d.ts +0 -14
  500. package/dist/testing/Wav.d.ts.map +0 -1
  501. package/dist/testing/Wav.js +0 -37
  502. package/dist/testing/Wav.js.map +0 -1
  503. package/dist/testing/index.d.ts +0 -5
  504. package/dist/testing/index.d.ts.map +0 -1
  505. package/dist/testing/index.js +0 -4
  506. package/dist/testing/index.js.map +0 -1
  507. package/dist/wire.d.ts +0 -7
  508. package/dist/wire.d.ts.map +0 -1
  509. package/dist/wire.generated.d.ts +0 -305
  510. package/dist/wire.generated.d.ts.map +0 -1
  511. package/dist/wire.generated.js +0 -1257
  512. package/dist/wire.generated.js.map +0 -1
  513. package/dist/wire.js +0 -5
  514. package/dist/wire.js.map +0 -1
@@ -0,0 +1,592 @@
1
+ /**
2
+ * The simulated coordinator's state: token grants, the sessions they allocate
3
+ * and those sessions' lifetimes, and the test peers signaling binds to them.
4
+ */
5
+ import * as Clock from "effect/Clock";
6
+ import * as Duration from "effect/Duration";
7
+ import * as Effect from "effect/Effect";
8
+ import * as Encoding from "effect/Encoding";
9
+ import * as Exit from "effect/Exit";
10
+ import * as FiberSet from "effect/FiberSet";
11
+ import * as Option from "effect/Option";
12
+ import * as Ref from "effect/Ref";
13
+ import * as Schema from "effect/Schema";
14
+ import * as Scope from "effect/Scope";
15
+ import * as Wire from "../wire.js";
16
+ import * as Faults from "./faults.js";
17
+ import { deployment, documented } from "./h3.js";
18
+ import * as Playout from "./playout.js";
19
+ const Id = Schema.String.check(Schema.isNonEmpty());
20
+ /**
21
+ * A session-scoped token request, as Reactor's authentication docs state it:
22
+ * one `session` entry naming at least one model, optionally sessions to bind,
23
+ * up to 500 sessions and sessions of one second to a day.
24
+ */
25
+ export const Authorization = Schema.Struct({
26
+ type: Schema.Literal("session"),
27
+ resources: Schema.Struct({
28
+ models: Schema.Struct({ match: Schema.NonEmptyArray(Id) }),
29
+ sessions: Schema.optionalKey(Schema.Struct({ bind: Schema.NonEmptyArray(Id) })),
30
+ }),
31
+ constraints: Schema.optionalKey(Schema.Struct({
32
+ max_sessions: Schema.optionalKey(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 500 }))),
33
+ max_session_duration_seconds: Schema.optionalKey(Schema.Int.check(Schema.isBetween({ minimum: 1, maximum: 86_400 }))),
34
+ })),
35
+ });
36
+ /** A request the simulated coordinator refuses, with the status a client sees. */
37
+ export class Refusal extends Schema.TaggedError("reactor-effect-client/ReactorTest/Refusal")("Refusal", {
38
+ status: Schema.Int,
39
+ code: Schema.String,
40
+ reason: Schema.String,
41
+ /** Seconds to wait, sent as `Retry-After` and `retry_after_seconds`. */
42
+ retryAfter: Schema.optionalKey(Schema.Int),
43
+ }) {
44
+ }
45
+ const refuse = (status, code, reason) => Refusal.make({ status, code, reason });
46
+ /** Reactor's ceiling on a token's life: a longer one is clamped without a word. */
47
+ const maxTokenSeconds = 21_600;
48
+ /** Reactor caps a clip of a session's last seconds at five minutes by default. */
49
+ const maxClipSeconds = 300;
50
+ /** A recording's segments, each two seconds of it. */
51
+ const segmentSeconds = 2;
52
+ const segmentBytes = 1_024;
53
+ /** Where a recording's segments are served: another origin, as a CDN's would be. */
54
+ export const clips = "https://clips.reactor.test";
55
+ /** How long Reactor keeps a session that lost its last connection. */
56
+ const reconnectWindowMs = 30_000;
57
+ /** Sessions an account may create back to back before the per-minute rate applies. */
58
+ const burst = 3;
59
+ const withSlot = (state, cid, slot) => ({
60
+ ...state,
61
+ connections: new Map(state.connections).set(cid, slot),
62
+ });
63
+ const anyOpen = (state) => [...state.connections.values()].some((slot) => slot.open);
64
+ const descriptor = (id, phase) => ({
65
+ session_id: id,
66
+ state: phase,
67
+ // Whether hosted Reactor still describes an INACTIVE session's capabilities and
68
+ // transport is unobserved (paid run tokens 83d17eb7 saw only the state), so the
69
+ // most permissive reading is modelled: they stay, and a reconnect can use them.
70
+ ...(phase === "ACTIVE" || phase === "INACTIVE"
71
+ ? {
72
+ capabilities: {
73
+ protocol_version: "1.0",
74
+ tracks: [
75
+ { name: documented.tracks.video, kind: "video", direction: "recvonly" },
76
+ { name: documented.tracks.audio, kind: "audio", direction: "recvonly" },
77
+ ],
78
+ emission_fps: documented.fps,
79
+ },
80
+ selected_transport: { protocol: "webrtc", version: "1.0" },
81
+ }
82
+ : {}),
83
+ });
84
+ export const make = Effect.fnUntraced(function* (options, timing) {
85
+ const scope = yield* Effect.scope;
86
+ const lifetimes = yield* FiberSet.make();
87
+ const faults = yield* Faults.make(options.faults);
88
+ const grants = yield* Ref.make(new Map());
89
+ const sessions = yield* Ref.make(new Map());
90
+ const peers = yield* Ref.make(new Map());
91
+ /** The session and slot each test peer's last offer bound it to. */
92
+ const bindings = yield* Ref.make(new Map());
93
+ const counts = yield* Ref.make({
94
+ grants: 0,
95
+ sessions: 0,
96
+ connections: 1000,
97
+ peers: 0,
98
+ recordings: 0,
99
+ });
100
+ /** The account's session-creation bucket: `burst` at once, refilled at the per-minute rate. */
101
+ const bucket = yield* Ref.make({ tokens: burst, at: 0 });
102
+ const entries = yield* Ref.make([]);
103
+ /** Upload slots handed out and not yet filled, with the session that asked for each. */
104
+ const slots = yield* Ref.make(new Map());
105
+ const uploads = yield* Ref.make(0);
106
+ const recordings = yield* Ref.make(new Map());
107
+ const openapi = yield* Schema.decodeUnknownEffect(Wire.StructJson)(deployment(options.referenceAudio)).pipe(Effect.orDie);
108
+ const count = (key) => Ref.modify(counts, (all) => [all[key] + 1, { ...all, [key]: all[key] + 1 }]);
109
+ const log = (entry) => Effect.flatMap(Playout.monotonic, (at) => Ref.update(entries, (all) => [...all.slice(-9_999), { at, ...entry }]));
110
+ const later = (ms, effect) => FiberSet.run(lifetimes, Effect.sleep(Duration.millis(ms)).pipe(Effect.andThen(effect)));
111
+ const setBinding = (peerId, binding) => Ref.update(bindings, (all) => {
112
+ const next = new Map(all);
113
+ if (binding !== undefined)
114
+ next.set(peerId, binding);
115
+ else
116
+ next.delete(peerId);
117
+ return next;
118
+ });
119
+ /**
120
+ * A session token, or the API key where Reactor takes it as the bearer:
121
+ * reading a session and ending one. Paid run tokens 7bc779d4 read an
122
+ * unknown session with the key (404) and ended a live one (200); Reactor's
123
+ * docs name the key for `DELETE` only, so its other calls refuse it.
124
+ */
125
+ const authorize = (jwt, access) => Effect.gen(function* () {
126
+ if (jwt !== undefined && jwt === options.apiKey && access === "key")
127
+ return "key";
128
+ const grant = jwt === undefined ? undefined : (yield* Ref.get(grants)).get(jwt);
129
+ if (grant === undefined || grant.expiresAt * 1000 <= (yield* Clock.currentTimeMillis))
130
+ return yield* refuse(401, "unauthorized", "a valid session token is required");
131
+ return grant;
132
+ });
133
+ /**
134
+ * A session the bearer may act on. `live` refuses one not ACTIVE, or not
135
+ * ACTIVE or INACTIVE when a connection may return; `key` admits the API key.
136
+ */
137
+ const owned = (jwt, id, live, access = "token") => Effect.gen(function* () {
138
+ const grant = yield* authorize(jwt, access);
139
+ const session = (yield* Ref.get(sessions)).get(id);
140
+ if (session === undefined)
141
+ return yield* refuse(404, "not_found", "no such session");
142
+ if (grant !== "key" && session.creator !== grant.jwt && !grant.bound.has(id))
143
+ return yield* refuse(403, "forbidden", "the token is not bound to this session: authorization_details.resources.sessions.bind");
144
+ const { phase } = yield* Ref.get(session.state);
145
+ const accepted = live === undefined ||
146
+ phase === "ACTIVE" ||
147
+ (live === "connectable" && phase === "INACTIVE");
148
+ if (!accepted)
149
+ return yield* refuse(409, "session_not_active", `the session is ${phase}`);
150
+ return session;
151
+ });
152
+ const connection = (session, cid) => Effect.flatMap(Ref.get(session.state), (state) => {
153
+ const slot = state.connections.get(cid);
154
+ return slot === undefined
155
+ ? Effect.fail(refuse(404, "not_found", "no such connection"))
156
+ : Effect.succeed(slot);
157
+ });
158
+ /** The slot's transport stops carrying the session: it closes, or another replaces it. */
159
+ const unlink = (session, cid, link, reason) => Effect.gen(function* () {
160
+ const unbound = yield* Ref.modify(session.state, (state) => {
161
+ const slot = state.connections.get(cid);
162
+ return slot?.link === link
163
+ ? [true, withSlot(state, cid, { ...slot, link: undefined, open: false })]
164
+ : [false, state];
165
+ });
166
+ if (!unbound)
167
+ return;
168
+ yield* setBinding(link.id, undefined);
169
+ yield* session.playout.disconnect(cid);
170
+ yield* log({ sessionId: session.id, kind: "session", name: reason });
171
+ yield* link.drop(reason);
172
+ if (reason !== "disconnected")
173
+ return;
174
+ // Reactor ends a session 30 s after its last connection drops, unless one returns.
175
+ // Meanwhile hosted Reactor reads it INACTIVE (paid run tokens 83d17eb7), still billing.
176
+ const drops = yield* Ref.modify(session.state, (state) => {
177
+ if (anyOpen(state))
178
+ return [undefined, state];
179
+ const next = {
180
+ ...state,
181
+ phase: state.phase === "ACTIVE" ? "INACTIVE" : state.phase,
182
+ drops: state.drops + 1,
183
+ };
184
+ return [next.drops, next];
185
+ });
186
+ if (drops === undefined)
187
+ return;
188
+ yield* later(reconnectWindowMs, Effect.flatMap(Ref.get(session.state), (state) => !anyOpen(state) && state.drops === drops ? end(session, "abandoned") : Effect.void));
189
+ });
190
+ const end = (session, reason) => Effect.gen(function* () {
191
+ const now = yield* Playout.monotonic;
192
+ const previous = yield* Ref.getAndUpdate(session.state, (state) => state.phase === "CLOSED" ? state : { ...state, phase: "CLOSED", endedAt: now });
193
+ if (previous.phase === "CLOSED")
194
+ return;
195
+ for (const [cid, slot] of previous.connections)
196
+ if (slot.link !== undefined)
197
+ yield* unlink(session, cid, slot.link, "ended");
198
+ yield* log({ sessionId: session.id, kind: "session", name: reason });
199
+ yield* Scope.close(session.scope, Exit.void);
200
+ });
201
+ /**
202
+ * A clip of the session's last `seconds`, or a recording of all of it so
203
+ * far: its playlist is ready as the clip predicts, unless a fault makes it late.
204
+ */
205
+ const record = (session, kind, seconds) => Effect.gen(function* () {
206
+ const now = yield* Playout.monotonic;
207
+ const { activeAt } = yield* Ref.get(session.state);
208
+ const elapsed = activeAt === undefined ? 0 : (now - activeAt) / 1000;
209
+ const start = kind === "snap" ? Math.max(0, elapsed - Math.min(seconds, maxClipSeconds)) : 0;
210
+ const late = yield* faults.trip((fault) => fault._tag === "LateRecording");
211
+ const id = `rec_reactor_test_${yield* count("recordings")}`;
212
+ yield* Ref.update(recordings, (all) => new Map(all).set(id, {
213
+ sessionId: session.id,
214
+ readyAt: now + (late?._tag === "LateRecording" ? Duration.toMillis(late.by) : 0),
215
+ segments: Math.max(1, Math.ceil((elapsed - start) / segmentSeconds)),
216
+ }));
217
+ yield* log({ sessionId: session.id, kind: "session", name: `${kind} ${id}` });
218
+ return {
219
+ sessionId: session.id,
220
+ kind,
221
+ startMarker: start,
222
+ endMarker: elapsed,
223
+ nowMarker: elapsed,
224
+ // A test clock can stand between two milliseconds, and a BigInt takes whole ones.
225
+ predictedReadyAtMs: BigInt(Math.round(yield* Clock.currentTimeMillis)),
226
+ playlistUrl: `/clips/${id}.m3u8`,
227
+ };
228
+ });
229
+ const activate = (session) => Effect.gen(function* () {
230
+ const now = yield* Playout.monotonic;
231
+ const previous = yield* Ref.getAndUpdate(session.state, (state) => state.phase === "PENDING" ? { ...state, phase: "ACTIVE", activeAt: now } : state);
232
+ if (previous.phase !== "PENDING")
233
+ return;
234
+ yield* log({ sessionId: session.id, kind: "session", name: "active" });
235
+ const expire = yield* faults.standing((fault) => fault._tag === "Expire");
236
+ const uncapped = yield* faults.standing((fault) => fault._tag === "IgnoreCap");
237
+ const cap = session.maxSessionSeconds === undefined || uncapped !== undefined
238
+ ? Infinity
239
+ : session.maxSessionSeconds * 1000;
240
+ const early = expire?._tag === "Expire" ? Duration.toMillis(expire.after) : Infinity;
241
+ const lifetime = Math.min(cap, early);
242
+ if (Number.isFinite(lifetime))
243
+ yield* later(lifetime, end(session, "expired"));
244
+ });
245
+ /** Connectivity succeeded for the transport a slot is bound to. */
246
+ const open = (session, cid, link) => Effect.gen(function* () {
247
+ const state = yield* Ref.get(session.state);
248
+ const slot = state.connections.get(cid);
249
+ if (state.phase !== "ACTIVE" || slot?.link !== link)
250
+ return;
251
+ yield* link.open;
252
+ yield* Ref.update(session.state, (current) => withSlot(current, cid, { ...slot, open: true }));
253
+ yield* log({ sessionId: session.id, kind: "session", name: "connected" });
254
+ yield* session.playout.connect(cid, link);
255
+ const fault = yield* faults.trip((candidate) => candidate._tag === "Disconnect");
256
+ if (fault?._tag === "Disconnect")
257
+ yield* later(Duration.toMillis(fault.after), unlink(session, cid, link, "disconnected"));
258
+ });
259
+ return {
260
+ options,
261
+ timing,
262
+ // Test peers
263
+ nextPeer: Effect.map(count("peers"), (n) => `reactor-test-peer-${n}`),
264
+ attach: (link) => Ref.update(peers, (all) => new Map(all).set(link.id, link)),
265
+ detach: (peerId) => Effect.gen(function* () {
266
+ const link = (yield* Ref.get(peers)).get(peerId);
267
+ const bound = (yield* Ref.get(bindings)).get(peerId);
268
+ yield* Ref.update(peers, (all) => new Map([...all].filter(([key]) => key !== peerId)));
269
+ if (link !== undefined && bound !== undefined)
270
+ yield* unlink(bound.session, bound.cid, link, "disconnected");
271
+ }),
272
+ answered: (peerId) => Effect.gen(function* () {
273
+ const link = (yield* Ref.get(peers)).get(peerId);
274
+ const bound = (yield* Ref.get(bindings)).get(peerId);
275
+ if (link !== undefined && bound !== undefined)
276
+ yield* later(yield* timing.delay("connect"), open(bound.session, bound.cid, link));
277
+ }),
278
+ receive: (peerId, channel, bytes) => Effect.gen(function* () {
279
+ const link = (yield* Ref.get(peers)).get(peerId);
280
+ const bound = (yield* Ref.get(bindings)).get(peerId);
281
+ if (link !== undefined && bound !== undefined)
282
+ yield* bound.session.playout.receive(bound.cid, link, channel, bytes);
283
+ }),
284
+ // Coordinator
285
+ pricing: {
286
+ settings: { currency_code: "USD", credits_per_dollar: options.creditsPerDollar },
287
+ models: [
288
+ {
289
+ name: documented.modelName.slice(documented.modelName.lastIndexOf("/") + 1),
290
+ rate: {
291
+ amount_per_sec: options.creditsPerSecond,
292
+ unit: "credits",
293
+ denomination: "second",
294
+ },
295
+ },
296
+ ],
297
+ },
298
+ mint: (key, authorization, expiresAfter) => Effect.gen(function* () {
299
+ if (key !== options.apiKey)
300
+ return yield* refuse(401, "unauthorized", "a valid Reactor-API-Key is required");
301
+ const models = authorization.resources.models.match;
302
+ const bind = authorization.resources.sessions?.bind ?? [];
303
+ const all = yield* Ref.get(sessions);
304
+ for (const id of bind) {
305
+ const session = all.get(id);
306
+ const open = session !== undefined && (yield* Ref.get(session.state)).phase !== "CLOSED";
307
+ // A closed, foreign or unknown id is refused alike, so bind cannot find sessions.
308
+ if (!open || !models.includes(session.model))
309
+ return yield* refuse(403, "forbidden", "a bound session is not open for this token");
310
+ }
311
+ const n = yield* count("grants");
312
+ const issuedAt = Math.floor((yield* Clock.currentTimeMillis) / 1000);
313
+ const asked = authorization.constraints?.max_session_duration_seconds;
314
+ const misgrant = yield* faults.trip((fault) => fault._tag === "OverGrant");
315
+ const how = misgrant?._tag === "OverGrant" ? (misgrant.grant ?? "longer") : undefined;
316
+ // An over-granting token lets its sessions run twice as long as was asked, or uncapped.
317
+ const cap = asked === undefined || how === "uncapped"
318
+ ? undefined
319
+ : asked * (how === "longer" ? 2 : 1);
320
+ const granted = how === "bound" ? [...bind, `sess_reactor_test_unasked_${n}`] : bind;
321
+ const grant = {
322
+ jwt: "",
323
+ models,
324
+ maxSessions: authorization.constraints?.max_sessions ?? (bind.length > 0 ? bind.length : 5),
325
+ maxSessionSeconds: cap,
326
+ expiresAt: how === "expired"
327
+ ? issuedAt - 1
328
+ : issuedAt + Math.min(expiresAfter ?? 3_600, maxTokenSeconds),
329
+ created: 0,
330
+ bound: new Set(granted),
331
+ };
332
+ const echo = {
333
+ type: "session",
334
+ resources: {
335
+ models: { match: models },
336
+ ...(granted.length > 0 ? { sessions: { bind: granted } } : {}),
337
+ },
338
+ constraints: {
339
+ max_sessions: grant.maxSessions,
340
+ ...(cap !== undefined ? { max_session_duration_seconds: cap } : {}),
341
+ ...(how === "uncapped" ? { max_session_duration_seconds: null } : {}),
342
+ },
343
+ };
344
+ // Bound sessions live on the server, not in the token's claims.
345
+ const claims = {
346
+ iss: "reactor-test",
347
+ iat: issuedAt,
348
+ exp: grant.expiresAt,
349
+ jti: `reactor-test-grant-${n}`,
350
+ authorization_details: [{ ...echo, resources: { models: { match: models } } }],
351
+ };
352
+ const jwt = [{ alg: "none", typ: "JWT" }, claims, "reactor-test"]
353
+ .map((part) => Encoding.encodeBase64Url(JSON.stringify(part)))
354
+ .join(".");
355
+ yield* Ref.update(grants, (current) => new Map(current).set(jwt, { ...grant, jwt }));
356
+ return {
357
+ jwt,
358
+ expires_at: grant.expiresAt,
359
+ ...(how === "silent" ? {} : { authorization_details: [echo] }),
360
+ };
361
+ }),
362
+ create: (jwt, model, webrtc, client) => Effect.gen(function* () {
363
+ const grant = yield* authorize(jwt, "token");
364
+ if (grant === "key")
365
+ return yield* refuse(401, "unauthorized", "a session token is required");
366
+ if (model !== documented.modelName)
367
+ return yield* refuse(404, "unknown_model", "ReactorTest serves H3 only");
368
+ if (!grant.models.includes(model))
369
+ return yield* refuse(403, "forbidden", "the token does not grant this model");
370
+ if (!webrtc)
371
+ return yield* refuse(400, "unsupported_transport", "WebRTC 1.0 only");
372
+ if ((yield* faults.trip((fault) => fault._tag === "StallAllocation")) !== undefined)
373
+ return yield* Effect.never;
374
+ const spent = grant.created >= grant.maxSessions - grant.bound.size;
375
+ if (spent &&
376
+ (yield* faults.trip((fault) => fault._tag === "RepeatSession")) !== undefined) {
377
+ const last = [...(yield* Ref.get(sessions)).values()].findLast((session) => session.creator === grant.jwt);
378
+ if (last !== undefined)
379
+ return descriptor(last.id, (yield* Ref.get(last.state)).phase);
380
+ }
381
+ if (spent &&
382
+ (yield* faults.trip((fault) => fault._tag === "IgnoreSessionLimit")) === undefined)
383
+ return yield* refuse(403, "session_limit", "the token's sessions are used");
384
+ const refusal = yield* faults.trip((fault) => fault._tag === "RefuseAllocation");
385
+ if (refusal?._tag === "RefuseAllocation")
386
+ return yield* (refusal.status ?? 403) >= 500
387
+ ? refuse(refusal.status ?? 500, "server_error", "allocation failed")
388
+ : refuse(refusal.status ?? 403, "allocation_refused", "allocation refused");
389
+ const running = yield* Effect.filter([...(yield* Ref.get(sessions)).values()], (session) => Effect.map(Ref.get(session.state), (state) => state.phase !== "CLOSED"));
390
+ if (running.length >= options.concurrentSessions)
391
+ return yield* refuse(429, "concurrent_limit", "too many concurrent sessions");
392
+ // A token bucket: `burst` back to back, then one every minute / sessionsPerMinute.
393
+ const now = yield* Playout.monotonic;
394
+ const refillMs = 60_000 / options.sessionsPerMinute;
395
+ const wait = yield* Ref.modify(bucket, (current) => {
396
+ const tokens = Math.min(burst, current.tokens + (now - current.at) / refillMs);
397
+ return tokens >= 1
398
+ ? [0, { tokens: tokens - 1, at: now }]
399
+ : [Math.ceil(((1 - tokens) * refillMs) / 1000), { tokens, at: now }];
400
+ });
401
+ if (wait > 0)
402
+ return yield* Refusal.make({
403
+ status: 429,
404
+ code: "rate_limited",
405
+ reason: "too many sessions this minute",
406
+ retryAfter: wait,
407
+ });
408
+ yield* Ref.update(grants, (all) => new Map(all).set(grant.jwt, { ...grant, created: grant.created + 1 }));
409
+ const id = `sess_reactor_test_${yield* count("sessions")}`;
410
+ const sessionScope = yield* Scope.fork(scope);
411
+ const session = {
412
+ id,
413
+ model,
414
+ client,
415
+ creator: grant.jwt,
416
+ maxSessionSeconds: grant.maxSessionSeconds,
417
+ expiresAt: grant.expiresAt,
418
+ scope: sessionScope,
419
+ state: yield* Ref.make({
420
+ phase: "PENDING",
421
+ drops: 0,
422
+ activeAt: undefined,
423
+ endedAt: undefined,
424
+ deletes: 0,
425
+ connections: new Map(),
426
+ }),
427
+ playout: yield* Playout.make(id, {
428
+ options,
429
+ faults,
430
+ timing,
431
+ openapi,
432
+ log,
433
+ // The session a moderation verdict ends is this one, built just below. It ends
434
+ // outside its own scope, which the ending closes.
435
+ terminate: Effect.suspend(() => Effect.asVoid(later(0, end(session, "moderated")))),
436
+ record: (kind, seconds) => record(session, kind, seconds),
437
+ }).pipe(Scope.provide(sessionScope)),
438
+ };
439
+ yield* Ref.update(sessions, (all) => new Map(all).set(id, session));
440
+ yield* log({ sessionId: id, kind: "session", name: "created" });
441
+ yield* later(yield* timing.delay("allocation"), activate(session));
442
+ if ((yield* faults.trip((fault) => fault._tag === "UnnamedAllocation")) === undefined)
443
+ return descriptor(id, "PENDING");
444
+ const { session_id: _, ...unnamed } = descriptor(id, "PENDING");
445
+ return unnamed;
446
+ }),
447
+ read: (jwt, id) => Effect.gen(function* () {
448
+ const session = yield* owned(jwt, id, undefined, "key");
449
+ if ((yield* faults.trip((fault) => fault._tag === "MissingSession")) !== undefined)
450
+ return yield* refuse(404, "not_found", "no such session");
451
+ return descriptor(id, (yield* Ref.get(session.state)).phase);
452
+ }),
453
+ upload: (jwt, id, name, size) => Effect.gen(function* () {
454
+ yield* owned(jwt, id, "active");
455
+ const n = yield* Ref.updateAndGet(uploads, (value) => value + 1);
456
+ const slot = `00000000-0000-4000-8000-${n.toString(16).padStart(12, "0")}`;
457
+ yield* Ref.update(slots, (all) => new Map(all).set(slot, id));
458
+ yield* log({ sessionId: id, kind: "upload", name: `allocated ${name} (${size} bytes)` });
459
+ return slot;
460
+ }),
461
+ stored: (slot, bytes) => Effect.gen(function* () {
462
+ const sessionId = (yield* Ref.get(slots)).get(slot);
463
+ if (sessionId === undefined)
464
+ return yield* refuse(404, "not_found", "no such upload");
465
+ yield* log({ sessionId, kind: "upload", name: `stored ${bytes} bytes` });
466
+ }),
467
+ remove: (jwt, id) => Effect.gen(function* () {
468
+ const session = yield* owned(jwt, id, undefined, "key");
469
+ const state = yield* Ref.updateAndGet(session.state, (current) => ({
470
+ ...current,
471
+ deletes: current.deletes + 1,
472
+ }));
473
+ yield* log({ sessionId: id, kind: "session", name: "deleted" });
474
+ if (state.phase === "CLOSED" || state.phase === "STOPPING")
475
+ return;
476
+ if ((yield* faults.standing((fault) => fault._tag === "IgnoreDelete")) !== undefined)
477
+ return;
478
+ const slow = yield* faults.standing((fault) => fault._tag === "SlowDelete");
479
+ if (slow?._tag !== "SlowDelete")
480
+ return yield* end(session, "terminated");
481
+ yield* Ref.update(session.state, (current) => ({ ...current, phase: "STOPPING" }));
482
+ yield* later(Duration.toMillis(slow.for), end(session, "terminated"));
483
+ }),
484
+ iceServers: (jwt, id) => Effect.as(owned(jwt, id), {
485
+ ice_servers: [{ uris: ["stun:stun.reactor.test:3478"], credentials: null }],
486
+ }),
487
+ register: (jwt, id) => Effect.gen(function* () {
488
+ const session = yield* owned(jwt, id, "connectable");
489
+ if ((yield* faults.trip((fault) => fault._tag === "RefuseConnect")) !== undefined)
490
+ return yield* refuse(403, "connect_refused", "connection refused");
491
+ const cid = yield* count("connections");
492
+ yield* Ref.update(session.state, (state) => withSlot(state, cid, { answer: undefined, link: undefined, open: false }));
493
+ return { connection_id: cid };
494
+ }),
495
+ offer: (jwt, id, cid, sdp, replace) => Effect.gen(function* () {
496
+ const session = yield* owned(jwt, id, "connectable");
497
+ const refusal = replace
498
+ ? yield* faults.trip((fault) => fault._tag === "RefuseReconnect")
499
+ : undefined;
500
+ if (refusal?._tag === "RefuseReconnect")
501
+ return yield* Refusal.make({
502
+ status: refusal.status ?? 503,
503
+ code: "reconnect_refused",
504
+ reason: "reconnect refused",
505
+ ...(refusal.retryAfter === undefined
506
+ ? {}
507
+ : { retryAfter: Math.ceil(Duration.toSeconds(refusal.retryAfter)) }),
508
+ });
509
+ const previous = (yield* connection(session, cid)).link;
510
+ const peerId = /^a=ice-ufrag:([\w-]+)\r?$/m.exec(sdp)?.[1] ?? "";
511
+ const link = (yield* Ref.get(peers)).get(peerId);
512
+ if (link === undefined)
513
+ return yield* refuse(400, "invalid_offer", "the offer names no peer");
514
+ if (previous !== undefined && previous !== link)
515
+ yield* unlink(session, cid, previous, "replaced");
516
+ yield* setBinding(peerId, { session, cid });
517
+ const at = (yield* Playout.monotonic) + (yield* timing.delay("negotiation"));
518
+ const answer = `v=0\r\no=- 1 2 IN IP4 127.0.0.1\r\ns=reactor-test\r\na=ice-ufrag:${peerId}\r\n`;
519
+ // A connection returning to an INACTIVE session makes it ACTIVE again.
520
+ yield* Ref.update(session.state, (state) => withSlot({
521
+ ...state,
522
+ phase: state.phase === "INACTIVE" ? "ACTIVE" : state.phase,
523
+ }, cid, { answer: { at, sdp: answer }, link, open: false }));
524
+ }),
525
+ answer: (jwt, id, cid) => Effect.gen(function* () {
526
+ const { answer } = yield* connection(yield* owned(jwt, id), cid);
527
+ const now = yield* Playout.monotonic;
528
+ return answer !== undefined && answer.at <= now
529
+ ? Option.some({ sdp_answer: answer.sdp, connection_id: cid })
530
+ : Option.none();
531
+ }),
532
+ candidates: (jwt, id, cid) => Effect.flatMap(owned(jwt, id), (session) => connection(session, cid)),
533
+ /** A recording's HLS playlist for a bearer that acts on its session; none until it is ready. */
534
+ playlist: (jwt, id) => Effect.gen(function* () {
535
+ const recording = (yield* Ref.get(recordings)).get(id);
536
+ if (recording === undefined)
537
+ return yield* refuse(404, "not_found", "no such clip");
538
+ yield* owned(jwt, recording.sessionId);
539
+ if ((yield* Playout.monotonic) < recording.readyAt)
540
+ return Option.none();
541
+ const media = Array.from({ length: recording.segments }, (_, index) => `#EXTINF:${segmentSeconds.toFixed(3)},\n${clips}/${id}/${index}.m4s`);
542
+ return Option.some([
543
+ "#EXTM3U",
544
+ "#EXT-X-VERSION:7",
545
+ `#EXT-X-TARGETDURATION:${segmentSeconds}`,
546
+ "#EXT-X-PLAYLIST-TYPE:VOD",
547
+ `#EXT-X-MAP:URI="${clips}/${id}/init.mp4"`,
548
+ ...media,
549
+ "#EXT-X-ENDLIST",
550
+ "",
551
+ ].join("\n"));
552
+ }),
553
+ /** A recording's segment, served to whoever holds its URL, as a CDN would. */
554
+ segment: (id, file) => Effect.gen(function* () {
555
+ const recording = (yield* Ref.get(recordings)).get(id);
556
+ const index = file === "init.mp4" ? -1 : Number.parseInt(file, 10);
557
+ if (recording === undefined ||
558
+ (yield* Playout.monotonic) < recording.readyAt ||
559
+ !(file === "init.mp4" || (file === `${index}.m4s` && index < recording.segments)))
560
+ return yield* refuse(404, "not_found", "no such segment");
561
+ // Each segment's bytes name it, so a joined download shows its order.
562
+ const bytes = new Uint8Array(index < 0 ? 8 : segmentBytes).fill(index < 0 ? 255 : index);
563
+ return bytes;
564
+ }),
565
+ // Control
566
+ info: Effect.flatMap(Ref.get(sessions), (all) => Effect.forEach([...all.values()], (session) => Effect.map(Ref.get(session.state), (state) => ({
567
+ id: session.id,
568
+ state: state.phase,
569
+ connected: anyOpen(state),
570
+ deletes: state.deletes,
571
+ grant: {
572
+ maxSessionSeconds: session.maxSessionSeconds,
573
+ expiresAt: session.expiresAt,
574
+ },
575
+ client: session.client,
576
+ })))),
577
+ billing: Effect.gen(function* () {
578
+ const now = yield* Playout.monotonic;
579
+ const billed = yield* Effect.forEach([...(yield* Ref.get(sessions)).values()], (session) => Effect.map(Ref.get(session.state), (state) => state.activeAt === undefined ? 0 : ((state.endedAt ?? now) - state.activeAt) / 1000));
580
+ const seconds = billed.reduce((sum, each) => sum + each, 0);
581
+ return {
582
+ seconds,
583
+ usd: (seconds * options.creditsPerSecond) / options.creditsPerDollar,
584
+ };
585
+ }),
586
+ log: Ref.get(entries),
587
+ /** Adds to the log, as the coordinator does for each request it serves. */
588
+ note: log,
589
+ inject: faults.arm,
590
+ };
591
+ });
592
+ //# sourceMappingURL=sessions.js.map