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.
- package/NOTICE +7 -2
- package/README.md +103 -141
- package/dist/Coordinator.d.ts +382 -0
- package/dist/Coordinator.d.ts.map +1 -0
- package/dist/Coordinator.js +732 -0
- package/dist/Coordinator.js.map +1 -0
- package/dist/H3.d.ts +159 -0
- package/dist/H3.d.ts.map +1 -0
- package/dist/H3.js +21 -0
- package/dist/H3.js.map +1 -0
- package/dist/H3Source.d.ts +93 -0
- package/dist/H3Source.d.ts.map +1 -0
- package/dist/H3Source.js +401 -0
- package/dist/H3Source.js.map +1 -0
- package/dist/LocalSource.d.ts +68 -0
- package/dist/LocalSource.d.ts.map +1 -0
- package/dist/LocalSource.js +354 -0
- package/dist/LocalSource.js.map +1 -0
- package/dist/Media.d.ts +106 -0
- package/dist/Media.d.ts.map +1 -0
- package/dist/Media.js +16 -0
- package/dist/Media.js.map +1 -0
- package/dist/Peer.d.ts +134 -0
- package/dist/Peer.d.ts.map +1 -0
- package/dist/Peer.js +57 -0
- package/dist/Peer.js.map +1 -0
- package/dist/Playout.d.ts +724 -0
- package/dist/Playout.d.ts.map +1 -0
- package/dist/Playout.js +28 -0
- package/dist/Playout.js.map +1 -0
- package/dist/Reactor.d.ts +135 -0
- package/dist/Reactor.d.ts.map +1 -0
- package/dist/Reactor.js +158 -0
- package/dist/Reactor.js.map +1 -0
- package/dist/ReactorError.d.ts +331 -0
- package/dist/ReactorError.d.ts.map +1 -0
- package/dist/ReactorError.js +316 -0
- package/dist/ReactorError.js.map +1 -0
- package/dist/ReactorTest.d.ts +383 -0
- package/dist/ReactorTest.d.ts.map +1 -0
- package/dist/ReactorTest.js +317 -0
- package/dist/ReactorTest.js.map +1 -0
- package/dist/Session.d.ts +314 -0
- package/dist/Session.d.ts.map +1 -0
- package/dist/Session.js +51 -0
- package/dist/Session.js.map +1 -0
- package/dist/index.d.ts +12 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -11
- package/dist/index.js.map +1 -1
- package/dist/internal/correlator.d.ts +46 -0
- package/dist/internal/correlator.d.ts.map +1 -0
- package/dist/internal/correlator.js +150 -0
- package/dist/internal/correlator.js.map +1 -0
- package/dist/internal/deadline.d.ts +24 -0
- package/dist/internal/deadline.d.ts.map +1 -0
- package/dist/internal/deadline.js +44 -0
- package/dist/internal/deadline.js.map +1 -0
- package/dist/{h3/_internal/contracts.d.ts → internal/h3/commands.d.ts} +39 -33
- package/dist/internal/h3/commands.d.ts.map +1 -0
- package/dist/internal/h3/commands.js +128 -0
- package/dist/internal/h3/commands.js.map +1 -0
- package/dist/{h3 → internal/h3}/messages.d.ts +16 -11
- package/dist/internal/h3/messages.d.ts.map +1 -0
- package/dist/{h3 → internal/h3}/messages.js +19 -31
- package/dist/internal/h3/messages.js.map +1 -0
- package/dist/internal/h3/operations.d.ts +145 -0
- package/dist/internal/h3/operations.d.ts.map +1 -0
- package/dist/internal/h3/operations.js +284 -0
- package/dist/internal/h3/operations.js.map +1 -0
- package/dist/{h3 → internal/h3}/profile.d.ts +63 -65
- package/dist/internal/h3/profile.d.ts.map +1 -0
- package/dist/{h3 → internal/h3}/profile.js +39 -39
- package/dist/internal/h3/profile.js.map +1 -0
- package/dist/internal/h3/provider.d.ts +14 -0
- package/dist/internal/h3/provider.d.ts.map +1 -0
- package/dist/internal/h3/provider.js +672 -0
- package/dist/internal/h3/provider.js.map +1 -0
- package/dist/internal/h3/references.d.ts +68 -0
- package/dist/internal/h3/references.d.ts.map +1 -0
- package/dist/internal/h3/references.js +339 -0
- package/dist/internal/h3/references.js.map +1 -0
- package/dist/internal/h3/request.d.ts +56 -0
- package/dist/internal/h3/request.d.ts.map +1 -0
- package/dist/internal/h3/request.js +70 -0
- package/dist/internal/h3/request.js.map +1 -0
- package/dist/internal/h3/state.d.ts +120 -0
- package/dist/internal/h3/state.d.ts.map +1 -0
- package/dist/internal/h3/state.js +270 -0
- package/dist/internal/h3/state.js.map +1 -0
- package/dist/{Submission.d.ts → internal/h3/submission.d.ts} +1 -3
- package/dist/internal/h3/submission.d.ts.map +1 -0
- package/dist/{Submission.js → internal/h3/submission.js} +1 -9
- package/dist/internal/h3/submission.js.map +1 -0
- package/dist/internal/hub.d.ts +32 -0
- package/dist/internal/hub.d.ts.map +1 -0
- package/dist/internal/hub.js +92 -0
- package/dist/internal/hub.js.map +1 -0
- package/dist/internal/playout/errors.d.ts +51 -0
- package/dist/internal/playout/errors.d.ts.map +1 -0
- package/dist/internal/playout/errors.js +40 -0
- package/dist/internal/playout/errors.js.map +1 -0
- package/dist/internal/playout/policy.d.ts +559 -0
- package/dist/internal/playout/policy.d.ts.map +1 -0
- package/dist/internal/playout/policy.js +2338 -0
- package/dist/internal/playout/policy.js.map +1 -0
- package/dist/internal/playout/runtime.d.ts +28 -0
- package/dist/internal/playout/runtime.d.ts.map +1 -0
- package/dist/internal/playout/runtime.js +766 -0
- package/dist/internal/playout/runtime.js.map +1 -0
- package/dist/internal/playout/tag.d.ts +14 -0
- package/dist/internal/playout/tag.d.ts.map +1 -0
- package/dist/internal/playout/tag.js +43 -0
- package/dist/internal/playout/tag.js.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/common_pb.d.ts +55 -0
- package/dist/internal/proto/reactor_wire/v1/common_pb.d.ts.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/common_pb.js +42 -0
- package/dist/internal/proto/reactor_wire/v1/common_pb.js.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/control_pb.d.ts +160 -0
- package/dist/internal/proto/reactor_wire/v1/control_pb.d.ts.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/control_pb.js +21 -0
- package/dist/internal/proto/reactor_wire/v1/control_pb.js.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/data_pb.d.ts +89 -0
- package/dist/internal/proto/reactor_wire/v1/data_pb.d.ts.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/data_pb.js +20 -0
- package/dist/internal/proto/reactor_wire/v1/data_pb.js.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/model_pb.d.ts +85 -0
- package/dist/internal/proto/reactor_wire/v1/model_pb.d.ts.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/model_pb.js +24 -0
- package/dist/internal/proto/reactor_wire/v1/model_pb.js.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/platform_pb.d.ts +190 -0
- package/dist/internal/proto/reactor_wire/v1/platform_pb.d.ts.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/platform_pb.js +54 -0
- package/dist/internal/proto/reactor_wire/v1/platform_pb.js.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/track_pb.d.ts +88 -0
- package/dist/internal/proto/reactor_wire/v1/track_pb.d.ts.map +1 -0
- package/dist/internal/proto/reactor_wire/v1/track_pb.js +33 -0
- package/dist/internal/proto/reactor_wire/v1/track_pb.js.map +1 -0
- package/dist/internal/queue.d.ts +4 -0
- package/dist/internal/queue.d.ts.map +1 -0
- package/dist/{_internal → internal}/queue.js +0 -9
- package/dist/internal/queue.js.map +1 -0
- package/dist/internal/reactorTest/coordinator.d.ts +5 -0
- package/dist/internal/reactorTest/coordinator.d.ts.map +1 -0
- package/dist/internal/reactorTest/coordinator.js +178 -0
- package/dist/internal/reactorTest/coordinator.js.map +1 -0
- package/dist/internal/reactorTest/faults.d.ts +172 -0
- package/dist/internal/reactorTest/faults.d.ts.map +1 -0
- package/dist/internal/reactorTest/faults.js +23 -0
- package/dist/internal/reactorTest/faults.js.map +1 -0
- package/dist/internal/reactorTest/h3.d.ts +431 -0
- package/dist/internal/reactorTest/h3.d.ts.map +1 -0
- package/dist/internal/reactorTest/h3.js +625 -0
- package/dist/internal/reactorTest/h3.js.map +1 -0
- package/dist/internal/reactorTest/media.d.ts +22 -0
- package/dist/internal/reactorTest/media.d.ts.map +1 -0
- package/dist/internal/reactorTest/media.js +34 -0
- package/dist/internal/reactorTest/media.js.map +1 -0
- package/dist/internal/reactorTest/peer.d.ts +318 -0
- package/dist/internal/reactorTest/peer.d.ts.map +1 -0
- package/dist/internal/reactorTest/peer.js +206 -0
- package/dist/internal/reactorTest/peer.js.map +1 -0
- package/dist/internal/reactorTest/playout.d.ts +34 -0
- package/dist/internal/reactorTest/playout.d.ts.map +1 -0
- package/dist/internal/reactorTest/playout.js +340 -0
- package/dist/internal/reactorTest/playout.js.map +1 -0
- package/dist/internal/reactorTest/references.d.ts +25 -0
- package/dist/internal/reactorTest/references.d.ts.map +1 -0
- package/dist/{testing/Png.js → internal/reactorTest/references.js} +50 -12
- package/dist/internal/reactorTest/references.js.map +1 -0
- package/dist/internal/reactorTest/sessions.d.ts +364 -0
- package/dist/internal/reactorTest/sessions.d.ts.map +1 -0
- package/dist/internal/reactorTest/sessions.js +592 -0
- package/dist/internal/reactorTest/sessions.js.map +1 -0
- package/dist/internal/reactorTest/timing.d.ts +13 -0
- package/dist/internal/reactorTest/timing.d.ts.map +1 -0
- package/dist/internal/reactorTest/timing.js +20 -0
- package/dist/internal/reactorTest/timing.js.map +1 -0
- package/dist/internal/recording.d.ts +52 -0
- package/dist/internal/recording.d.ts.map +1 -0
- package/dist/internal/recording.js +141 -0
- package/dist/internal/recording.js.map +1 -0
- package/dist/internal/session/connect.d.ts +34 -0
- package/dist/internal/session/connect.d.ts.map +1 -0
- package/dist/internal/session/connect.js +502 -0
- package/dist/internal/session/connect.js.map +1 -0
- package/dist/internal/session/generation.d.ts +35 -0
- package/dist/internal/session/generation.d.ts.map +1 -0
- package/dist/internal/session/generation.js +185 -0
- package/dist/internal/session/generation.js.map +1 -0
- package/dist/internal/session/ice.d.ts +23 -0
- package/dist/internal/session/ice.d.ts.map +1 -0
- package/dist/internal/session/ice.js +57 -0
- package/dist/internal/session/ice.js.map +1 -0
- package/dist/internal/session/inbound.d.ts +50 -0
- package/dist/internal/session/inbound.d.ts.map +1 -0
- package/dist/internal/session/inbound.js +202 -0
- package/dist/internal/session/inbound.js.map +1 -0
- package/dist/internal/session/model.d.ts +163 -0
- package/dist/internal/session/model.d.ts.map +1 -0
- package/dist/internal/session/model.js +78 -0
- package/dist/internal/session/model.js.map +1 -0
- package/dist/internal/session/requests.d.ts +71 -0
- package/dist/internal/session/requests.d.ts.map +1 -0
- package/dist/internal/session/requests.js +240 -0
- package/dist/internal/session/requests.js.map +1 -0
- package/dist/internal/session/tracks.d.ts +21 -0
- package/dist/internal/session/tracks.d.ts.map +1 -0
- package/dist/internal/session/tracks.js +166 -0
- package/dist/internal/session/tracks.js.map +1 -0
- package/dist/internal/session/upload.d.ts +21 -0
- package/dist/internal/session/upload.d.ts.map +1 -0
- package/dist/internal/session/upload.js +68 -0
- package/dist/internal/session/upload.js.map +1 -0
- package/dist/internal/session.d.ts +66 -0
- package/dist/internal/session.d.ts.map +1 -0
- package/dist/internal/session.js +280 -0
- package/dist/internal/session.js.map +1 -0
- package/dist/{stats.d.ts → internal/stats.d.ts} +30 -16
- package/dist/internal/stats.d.ts.map +1 -0
- package/dist/internal/stats.js +201 -0
- package/dist/internal/stats.js.map +1 -0
- package/dist/internal/token.d.ts +22 -0
- package/dist/internal/token.d.ts.map +1 -0
- package/dist/internal/token.js +65 -0
- package/dist/internal/token.js.map +1 -0
- package/dist/internal/wire.d.ts +37 -0
- package/dist/internal/wire.d.ts.map +1 -0
- package/dist/internal/wire.js +49 -0
- package/dist/internal/wire.js.map +1 -0
- package/notices/bufbuild-protobuf-LICENSE +201 -0
- package/package.json +17 -38
- package/dist/FetchHttp.d.ts +0 -9
- package/dist/FetchHttp.d.ts.map +0 -1
- package/dist/FetchHttp.js +0 -63
- package/dist/FetchHttp.js.map +0 -1
- package/dist/PeerFactory.d.ts +0 -26
- package/dist/PeerFactory.d.ts.map +0 -1
- package/dist/PeerFactory.js +0 -4
- package/dist/PeerFactory.js.map +0 -1
- package/dist/PeerTypes.d.ts +0 -64
- package/dist/PeerTypes.d.ts.map +0 -1
- package/dist/PeerTypes.js +0 -2
- package/dist/PeerTypes.js.map +0 -1
- package/dist/Sequence.d.ts +0 -85
- package/dist/Sequence.d.ts.map +0 -1
- package/dist/Sequence.js +0 -210
- package/dist/Sequence.js.map +0 -1
- package/dist/SessionTypes.d.ts +0 -312
- package/dist/SessionTypes.d.ts.map +0 -1
- package/dist/SessionTypes.js +0 -19
- package/dist/SessionTypes.js.map +0 -1
- package/dist/Submission.d.ts.map +0 -1
- package/dist/Submission.js.map +0 -1
- package/dist/_internal/queue.d.ts +0 -6
- package/dist/_internal/queue.d.ts.map +0 -1
- package/dist/_internal/queue.js.map +0 -1
- package/dist/bytes.d.ts +0 -6
- package/dist/bytes.d.ts.map +0 -1
- package/dist/bytes.js +0 -32
- package/dist/bytes.js.map +0 -1
- package/dist/contract.d.ts +0 -149
- package/dist/contract.d.ts.map +0 -1
- package/dist/contract.js +0 -119
- package/dist/contract.js.map +0 -1
- package/dist/coordinator/_internal/client.d.ts +0 -215
- package/dist/coordinator/_internal/client.d.ts.map +0 -1
- package/dist/coordinator/_internal/client.js +0 -563
- package/dist/coordinator/_internal/client.js.map +0 -1
- package/dist/coordinator/_internal/recording.d.ts +0 -31
- package/dist/coordinator/_internal/recording.d.ts.map +0 -1
- package/dist/coordinator/_internal/recording.js +0 -123
- package/dist/coordinator/_internal/recording.js.map +0 -1
- package/dist/coordinator/_internal/response.d.ts +0 -15
- package/dist/coordinator/_internal/response.d.ts.map +0 -1
- package/dist/coordinator/_internal/response.js +0 -55
- package/dist/coordinator/_internal/response.js.map +0 -1
- package/dist/coordinator/_internal/schemas.d.ts +0 -73
- package/dist/coordinator/_internal/schemas.d.ts.map +0 -1
- package/dist/coordinator/_internal/schemas.js +0 -200
- package/dist/coordinator/_internal/schemas.js.map +0 -1
- package/dist/coordinator/index.d.ts +0 -40
- package/dist/coordinator/index.d.ts.map +0 -1
- package/dist/coordinator/index.js +0 -21
- package/dist/coordinator/index.js.map +0 -1
- package/dist/correlation.d.ts +0 -47
- package/dist/correlation.d.ts.map +0 -1
- package/dist/correlation.js +0 -131
- package/dist/correlation.js.map +0 -1
- package/dist/duration.d.ts +0 -27
- package/dist/duration.d.ts.map +0 -1
- package/dist/duration.js +0 -47
- package/dist/duration.js.map +0 -1
- package/dist/errors.d.ts +0 -1303
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -745
- package/dist/errors.js.map +0 -1
- package/dist/h3/_internal/client.d.ts +0 -9
- package/dist/h3/_internal/client.d.ts.map +0 -1
- package/dist/h3/_internal/client.js +0 -652
- package/dist/h3/_internal/client.js.map +0 -1
- package/dist/h3/_internal/contracts.d.ts.map +0 -1
- package/dist/h3/_internal/contracts.js +0 -123
- package/dist/h3/_internal/contracts.js.map +0 -1
- package/dist/h3/_internal/deployment.d.ts +0 -8
- package/dist/h3/_internal/deployment.d.ts.map +0 -1
- package/dist/h3/_internal/deployment.js +0 -149
- package/dist/h3/_internal/deployment.js.map +0 -1
- package/dist/h3/_internal/evidence.d.ts +0 -17
- package/dist/h3/_internal/evidence.d.ts.map +0 -1
- package/dist/h3/_internal/evidence.js +0 -48
- package/dist/h3/_internal/evidence.js.map +0 -1
- package/dist/h3/_internal/operations.d.ts +0 -58
- package/dist/h3/_internal/operations.d.ts.map +0 -1
- package/dist/h3/_internal/operations.js +0 -268
- package/dist/h3/_internal/operations.js.map +0 -1
- package/dist/h3/_internal/references.d.ts +0 -24
- package/dist/h3/_internal/references.d.ts.map +0 -1
- package/dist/h3/_internal/references.js +0 -309
- package/dist/h3/_internal/references.js.map +0 -1
- package/dist/h3/_internal/request.d.ts +0 -21
- package/dist/h3/_internal/request.d.ts.map +0 -1
- package/dist/h3/_internal/request.js +0 -97
- package/dist/h3/_internal/request.js.map +0 -1
- package/dist/h3/_internal/retained.d.ts +0 -22
- package/dist/h3/_internal/retained.d.ts.map +0 -1
- package/dist/h3/_internal/retained.js +0 -89
- package/dist/h3/_internal/retained.js.map +0 -1
- package/dist/h3/_internal/state.d.ts +0 -44
- package/dist/h3/_internal/state.d.ts.map +0 -1
- package/dist/h3/_internal/state.js +0 -283
- package/dist/h3/_internal/state.js.map +0 -1
- package/dist/h3/index.d.ts +0 -10
- package/dist/h3/index.d.ts.map +0 -1
- package/dist/h3/index.js +0 -7
- package/dist/h3/index.js.map +0 -1
- package/dist/h3/messages.d.ts.map +0 -1
- package/dist/h3/messages.js.map +0 -1
- package/dist/h3/profile.d.ts.map +0 -1
- package/dist/h3/profile.js.map +0 -1
- package/dist/h3/types.d.ts +0 -291
- package/dist/h3/types.d.ts.map +0 -1
- package/dist/h3/types.js +0 -2
- package/dist/h3/types.js.map +0 -1
- package/dist/host.d.ts +0 -26
- package/dist/host.d.ts.map +0 -1
- package/dist/host.js +0 -20
- package/dist/host.js.map +0 -1
- package/dist/json.d.ts +0 -25
- package/dist/json.d.ts.map +0 -1
- package/dist/json.js +0 -143
- package/dist/json.js.map +0 -1
- package/dist/media-stream.d.ts +0 -10
- package/dist/media-stream.d.ts.map +0 -1
- package/dist/media-stream.js +0 -18
- package/dist/media-stream.js.map +0 -1
- package/dist/observation.d.ts +0 -38
- package/dist/observation.d.ts.map +0 -1
- package/dist/observation.js +0 -113
- package/dist/observation.js.map +0 -1
- package/dist/orchestration/elapsed.d.ts +0 -9
- package/dist/orchestration/elapsed.d.ts.map +0 -1
- package/dist/orchestration/elapsed.js +0 -8
- package/dist/orchestration/elapsed.js.map +0 -1
- package/dist/orchestration/h3-source.d.ts +0 -58
- package/dist/orchestration/h3-source.d.ts.map +0 -1
- package/dist/orchestration/h3-source.js +0 -362
- package/dist/orchestration/h3-source.js.map +0 -1
- package/dist/orchestration/index.d.ts +0 -17
- package/dist/orchestration/index.d.ts.map +0 -1
- package/dist/orchestration/index.js +0 -12
- package/dist/orchestration/index.js.map +0 -1
- package/dist/orchestration/media-buffer.d.ts +0 -32
- package/dist/orchestration/media-buffer.d.ts.map +0 -1
- package/dist/orchestration/media-buffer.js +0 -94
- package/dist/orchestration/media-buffer.js.map +0 -1
- package/dist/orchestration/open-h3.d.ts +0 -101
- package/dist/orchestration/open-h3.d.ts.map +0 -1
- package/dist/orchestration/open-h3.js +0 -127
- package/dist/orchestration/open-h3.js.map +0 -1
- package/dist/orchestration/queries.d.ts +0 -10
- package/dist/orchestration/queries.d.ts.map +0 -1
- package/dist/orchestration/queries.js +0 -43
- package/dist/orchestration/queries.js.map +0 -1
- package/dist/orchestration/references.d.ts +0 -18
- package/dist/orchestration/references.d.ts.map +0 -1
- package/dist/orchestration/references.js +0 -79
- package/dist/orchestration/references.js.map +0 -1
- package/dist/orchestration/renewal-state.d.ts +0 -84
- package/dist/orchestration/renewal-state.d.ts.map +0 -1
- package/dist/orchestration/renewal-state.js +0 -95
- package/dist/orchestration/renewal-state.js.map +0 -1
- package/dist/orchestration/renewal.d.ts +0 -103
- package/dist/orchestration/renewal.d.ts.map +0 -1
- package/dist/orchestration/renewal.js +0 -1088
- package/dist/orchestration/renewal.js.map +0 -1
- package/dist/orchestration/request.d.ts +0 -65
- package/dist/orchestration/request.d.ts.map +0 -1
- package/dist/orchestration/request.js +0 -134
- package/dist/orchestration/request.js.map +0 -1
- package/dist/orchestration/retention.d.ts +0 -100
- package/dist/orchestration/retention.d.ts.map +0 -1
- package/dist/orchestration/retention.js +0 -134
- package/dist/orchestration/retention.js.map +0 -1
- package/dist/orchestration/routing.d.ts +0 -21
- package/dist/orchestration/routing.d.ts.map +0 -1
- package/dist/orchestration/routing.js +0 -93
- package/dist/orchestration/routing.js.map +0 -1
- package/dist/orchestration/scheduler-key.d.ts +0 -14
- package/dist/orchestration/scheduler-key.d.ts.map +0 -1
- package/dist/orchestration/scheduler-key.js +0 -62
- package/dist/orchestration/scheduler-key.js.map +0 -1
- package/dist/orchestration/scheduler-policy.d.ts +0 -222
- package/dist/orchestration/scheduler-policy.d.ts.map +0 -1
- package/dist/orchestration/scheduler-policy.js +0 -582
- package/dist/orchestration/scheduler-policy.js.map +0 -1
- package/dist/orchestration/scheduler.d.ts +0 -383
- package/dist/orchestration/scheduler.d.ts.map +0 -1
- package/dist/orchestration/scheduler.js +0 -2597
- package/dist/orchestration/scheduler.js.map +0 -1
- package/dist/orchestration/source-slot.d.ts +0 -124
- package/dist/orchestration/source-slot.d.ts.map +0 -1
- package/dist/orchestration/source-slot.js +0 -348
- package/dist/orchestration/source-slot.js.map +0 -1
- package/dist/orchestration/types.d.ts +0 -1462
- package/dist/orchestration/types.d.ts.map +0 -1
- package/dist/orchestration/types.js +0 -135
- package/dist/orchestration/types.js.map +0 -1
- package/dist/protobuf.d.ts +0 -63
- package/dist/protobuf.d.ts.map +0 -1
- package/dist/protobuf.js +0 -252
- package/dist/protobuf.js.map +0 -1
- package/dist/session/_internal/acquire.d.ts +0 -21
- package/dist/session/_internal/acquire.d.ts.map +0 -1
- package/dist/session/_internal/acquire.js +0 -168
- package/dist/session/_internal/acquire.js.map +0 -1
- package/dist/session/_internal/cleanup.d.ts +0 -19
- package/dist/session/_internal/cleanup.d.ts.map +0 -1
- package/dist/session/_internal/cleanup.js +0 -102
- package/dist/session/_internal/cleanup.js.map +0 -1
- package/dist/session/_internal/lifecycle.d.ts +0 -67
- package/dist/session/_internal/lifecycle.d.ts.map +0 -1
- package/dist/session/_internal/lifecycle.js +0 -142
- package/dist/session/_internal/lifecycle.js.map +0 -1
- package/dist/session/_internal/remote.d.ts +0 -28
- package/dist/session/_internal/remote.d.ts.map +0 -1
- package/dist/session/_internal/remote.js +0 -71
- package/dist/session/_internal/remote.js.map +0 -1
- package/dist/session/_internal/uploads.d.ts +0 -7
- package/dist/session/_internal/uploads.d.ts.map +0 -1
- package/dist/session/_internal/uploads.js +0 -31
- package/dist/session/_internal/uploads.js.map +0 -1
- package/dist/session/commands.d.ts +0 -2
- package/dist/session/commands.d.ts.map +0 -1
- package/dist/session/commands.js +0 -2
- package/dist/session/commands.js.map +0 -1
- package/dist/session/files.d.ts +0 -21
- package/dist/session/files.d.ts.map +0 -1
- package/dist/session/files.js +0 -49
- package/dist/session/files.js.map +0 -1
- package/dist/session/index.d.ts +0 -84
- package/dist/session/index.d.ts.map +0 -1
- package/dist/session/index.js +0 -16
- package/dist/session/index.js.map +0 -1
- package/dist/session/media.d.ts +0 -114
- package/dist/session/media.d.ts.map +0 -1
- package/dist/session/media.js +0 -2
- package/dist/session/media.js.map +0 -1
- package/dist/session/recorder.d.ts +0 -24
- package/dist/session/recorder.d.ts.map +0 -1
- package/dist/session/recorder.js +0 -19
- package/dist/session/recorder.js.map +0 -1
- package/dist/session.d.ts +0 -105
- package/dist/session.d.ts.map +0 -1
- package/dist/session.js +0 -976
- package/dist/session.js.map +0 -1
- package/dist/simulation/_internal/source.d.ts +0 -12
- package/dist/simulation/_internal/source.d.ts.map +0 -1
- package/dist/simulation/_internal/source.js +0 -513
- package/dist/simulation/_internal/source.js.map +0 -1
- package/dist/simulation/index.d.ts +0 -13
- package/dist/simulation/index.d.ts.map +0 -1
- package/dist/simulation/index.js +0 -12
- package/dist/simulation/index.js.map +0 -1
- package/dist/simulation/types.d.ts +0 -39
- package/dist/simulation/types.d.ts.map +0 -1
- package/dist/simulation/types.js +0 -2
- package/dist/simulation/types.js.map +0 -1
- package/dist/stats.d.ts.map +0 -1
- package/dist/stats.js +0 -205
- package/dist/stats.js.map +0 -1
- package/dist/testing/Faults.d.ts +0 -4
- package/dist/testing/Faults.d.ts.map +0 -1
- package/dist/testing/Faults.js +0 -7
- package/dist/testing/Faults.js.map +0 -1
- package/dist/testing/Png.d.ts +0 -3
- package/dist/testing/Png.d.ts.map +0 -1
- package/dist/testing/Png.js.map +0 -1
- package/dist/testing/Wav.d.ts +0 -14
- package/dist/testing/Wav.d.ts.map +0 -1
- package/dist/testing/Wav.js +0 -37
- package/dist/testing/Wav.js.map +0 -1
- package/dist/testing/index.d.ts +0 -5
- package/dist/testing/index.d.ts.map +0 -1
- package/dist/testing/index.js +0 -4
- package/dist/testing/index.js.map +0 -1
- package/dist/wire.d.ts +0 -7
- package/dist/wire.d.ts.map +0 -1
- package/dist/wire.generated.d.ts +0 -305
- package/dist/wire.generated.d.ts.map +0 -1
- package/dist/wire.generated.js +0 -1257
- package/dist/wire.generated.js.map +0 -1
- package/dist/wire.js +0 -5
- 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
|