@memberjunction/realtime-runtime 6.2.0-edge.3 → 6.2.0-edge.4
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/README.md +75 -0
- package/dist/channels/base-realtime-channel-client.d.ts +535 -28
- package/dist/channels/base-realtime-channel-client.d.ts.map +1 -1
- package/dist/channels/base-realtime-channel-client.js +864 -10
- package/dist/channels/base-realtime-channel-client.js.map +1 -1
- package/dist/channels/channel-action-dispatcher.d.ts +70 -0
- package/dist/channels/channel-action-dispatcher.d.ts.map +1 -0
- package/dist/channels/channel-action-dispatcher.js +136 -0
- package/dist/channels/channel-action-dispatcher.js.map +1 -0
- package/dist/channels/channel-catalog-note.d.ts +48 -0
- package/dist/channels/channel-catalog-note.d.ts.map +1 -0
- package/dist/channels/channel-catalog-note.js +119 -0
- package/dist/channels/channel-catalog-note.js.map +1 -0
- package/dist/channels/channel-contract-types.d.ts +107 -0
- package/dist/channels/channel-contract-types.d.ts.map +1 -0
- package/dist/channels/channel-contract-types.js +12 -0
- package/dist/channels/channel-contract-types.js.map +1 -0
- package/dist/channels/channel-descriptor-synthesis.d.ts +56 -0
- package/dist/channels/channel-descriptor-synthesis.d.ts.map +1 -0
- package/dist/channels/channel-descriptor-synthesis.js +90 -0
- package/dist/channels/channel-descriptor-synthesis.js.map +1 -0
- package/dist/channels/channel-perception.d.ts +82 -0
- package/dist/channels/channel-perception.d.ts.map +1 -0
- package/dist/channels/channel-perception.js +120 -0
- package/dist/channels/channel-perception.js.map +1 -0
- package/dist/channels/channel-schema-format.d.ts +17 -0
- package/dist/channels/channel-schema-format.d.ts.map +1 -0
- package/dist/channels/channel-schema-format.js +45 -0
- package/dist/channels/channel-schema-format.js.map +1 -0
- package/dist/channels/channel-state-delta.d.ts +59 -0
- package/dist/channels/channel-state-delta.d.ts.map +1 -0
- package/dist/channels/channel-state-delta.js +116 -0
- package/dist/channels/channel-state-delta.js.map +1 -0
- package/dist/channels/channel-surface-placement.d.ts +25 -0
- package/dist/channels/channel-surface-placement.d.ts.map +1 -0
- package/dist/channels/channel-surface-placement.js +29 -0
- package/dist/channels/channel-surface-placement.js.map +1 -0
- package/dist/channels/channel-visual-pump.d.ts +126 -0
- package/dist/channels/channel-visual-pump.d.ts.map +1 -0
- package/dist/channels/channel-visual-pump.js +174 -0
- package/dist/channels/channel-visual-pump.js.map +1 -0
- package/dist/hosts/IRealtimeMediaHost.d.ts +61 -4
- package/dist/hosts/IRealtimeMediaHost.d.ts.map +1 -1
- package/dist/hosts/IRealtimeMediaHost.js +2 -1
- package/dist/hosts/IRealtimeMediaHost.js.map +1 -1
- package/dist/index.d.ts +21 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -1
- package/dist/session/RealtimeSessionRuntime.d.ts +727 -74
- package/dist/session/RealtimeSessionRuntime.d.ts.map +1 -1
- package/dist/session/RealtimeSessionRuntime.js +1732 -242
- package/dist/session/RealtimeSessionRuntime.js.map +1 -1
- package/dist/session/RecordingShardQueue.d.ts +77 -0
- package/dist/session/RecordingShardQueue.d.ts.map +1 -0
- package/dist/session/RecordingShardQueue.js +172 -0
- package/dist/session/RecordingShardQueue.js.map +1 -0
- package/dist/session/app-client-tool-registry.d.ts +76 -0
- package/dist/session/app-client-tool-registry.d.ts.map +1 -0
- package/dist/session/app-client-tool-registry.js +100 -0
- package/dist/session/app-client-tool-registry.js.map +1 -0
- package/dist/session/avatar-notice.d.ts +43 -0
- package/dist/session/avatar-notice.d.ts.map +1 -0
- package/dist/session/avatar-notice.js +60 -0
- package/dist/session/avatar-notice.js.map +1 -0
- package/dist/session/call-end.d.ts +21 -0
- package/dist/session/call-end.d.ts.map +1 -0
- package/dist/session/call-end.js +19 -0
- package/dist/session/call-end.js.map +1 -0
- package/dist/session/channel-exposure-preferences.d.ts +70 -0
- package/dist/session/channel-exposure-preferences.d.ts.map +1 -0
- package/dist/session/channel-exposure-preferences.js +138 -0
- package/dist/session/channel-exposure-preferences.js.map +1 -0
- package/dist/session/channel-session-scope.d.ts +142 -0
- package/dist/session/channel-session-scope.d.ts.map +1 -0
- package/dist/session/channel-session-scope.js +128 -0
- package/dist/session/channel-session-scope.js.map +1 -0
- package/dist/session/client-session-deadline.d.ts +28 -0
- package/dist/session/client-session-deadline.d.ts.map +1 -0
- package/dist/session/client-session-deadline.js +50 -0
- package/dist/session/client-session-deadline.js.map +1 -0
- package/dist/session/realtime-captures.d.ts +236 -0
- package/dist/session/realtime-captures.d.ts.map +1 -0
- package/dist/session/realtime-captures.js +383 -0
- package/dist/session/realtime-captures.js.map +1 -0
- package/dist/session/realtime-microphone.d.ts +50 -0
- package/dist/session/realtime-microphone.d.ts.map +1 -0
- package/dist/session/realtime-microphone.js +51 -0
- package/dist/session/realtime-microphone.js.map +1 -0
- package/dist/session/session-event-hub.d.ts +72 -0
- package/dist/session/session-event-hub.d.ts.map +1 -0
- package/dist/session/session-event-hub.js +151 -0
- package/dist/session/session-event-hub.js.map +1 -0
- package/dist/session/session-launcher.d.ts +128 -0
- package/dist/session/session-launcher.d.ts.map +1 -0
- package/dist/session/session-launcher.js +160 -0
- package/dist/session/session-launcher.js.map +1 -0
- package/package.json +9 -9
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The camera and the screen share of one realtime session: what the user chooses to show the agent
|
|
3
|
+
* besides the channels' own surfaces.
|
|
4
|
+
*
|
|
5
|
+
* A capture starts only when the user asks (a click in the host's UI) and in this order:
|
|
6
|
+
* 0. the session's policy must allow it ({@link RealtimeCapturesOptions.Admit}: the runtime admits a capture only while
|
|
7
|
+
* the channel that fronts it is in the session and lets the agent see pixels);
|
|
8
|
+
* 1. the model must be able to take it: an inbound video track is already live, or one is added now
|
|
9
|
+
* (`AddTrack`; a session never requests video before anyone shares, so it is not billed or limited for it);
|
|
10
|
+
* 2. the device or surface is opened: the camera through the host's `ILocalMediaController`, a screen, window or
|
|
11
|
+
* tab through the host's `RequestDisplayCapture`;
|
|
12
|
+
* 3. frames are sampled at the rate the track negotiated, by the host's sampler or the DOM default, and it becomes a
|
|
13
|
+
* source of the session's video source arbiter, which decides what the model sees and tells it when that changes.
|
|
14
|
+
* A sampler that cannot start fails the capture as `unsupported`: the capture does not report on, and the model is
|
|
15
|
+
* never told it can see it.
|
|
16
|
+
*
|
|
17
|
+
* With the camera check on ({@link RealtimeCapturesOptions.CameraCheck}), the first camera start waits between 2 and 3:
|
|
18
|
+
* the camera is open for the user to look at, and becomes a source only once the user confirms
|
|
19
|
+
* ({@link RealtimeCaptures.ConfirmCamera}).
|
|
20
|
+
*
|
|
21
|
+
* Stopping undoes all three. The track is removed again only when this class added it, no capture is left, and the
|
|
22
|
+
* arbiter has no other source: a video track the session was minted with (a channel's) stays, and so does one a channel
|
|
23
|
+
* started sending on while a capture was on. A camera that goes away (unplugged) or a share the user ends from the
|
|
24
|
+
* browser's own bar stops the capture the same way.
|
|
25
|
+
*
|
|
26
|
+
* @module @memberjunction/realtime-runtime
|
|
27
|
+
*/
|
|
28
|
+
import { BehaviorSubject } from 'rxjs';
|
|
29
|
+
import { FrameSampler, VideoSourceArbiter, } from '@memberjunction/ai-realtime-client';
|
|
30
|
+
/** Neither capture offered: what a host shows outside a call. */
|
|
31
|
+
export const REALTIME_CAPTURE_OFFERS_NONE = Object.freeze({ Camera: false, Screen: false });
|
|
32
|
+
const OFF = { Status: 'off' };
|
|
33
|
+
/** What a capture gets when the session sets no policy. */
|
|
34
|
+
const ADMITTED = { Admitted: true, VisibleToAgent: true };
|
|
35
|
+
/** Both captures off: what a host shows outside a session. */
|
|
36
|
+
export const REALTIME_CAPTURES_OFF = Object.freeze({ Camera: OFF, Screen: OFF });
|
|
37
|
+
/**
|
|
38
|
+
* The ids the camera and the screen share go by on the session's video source arbiter, and so on the runtime's
|
|
39
|
+
* `VideoSources$`; prefixed so they never meet a channel's (`Key#instance`). A host or channel finds a capture's source
|
|
40
|
+
* by its id there, to tell whether the model is being sent its frames (`Enabled` and `Active`).
|
|
41
|
+
*/
|
|
42
|
+
export const REALTIME_CAPTURE_SOURCE_IDS = Object.freeze({
|
|
43
|
+
camera: 'capture:camera',
|
|
44
|
+
screen: 'capture:screen',
|
|
45
|
+
});
|
|
46
|
+
/** The names the model is told it is looking at, and the "agent can see" list shows. A shared panel goes by its own. */
|
|
47
|
+
const LABELS = { camera: 'Camera', screen: 'Shared screen' };
|
|
48
|
+
/** What the user is told when frames cannot be taken from a capture for the agent: its sampler could not start. */
|
|
49
|
+
const CANNOT_SAMPLE = {
|
|
50
|
+
camera: 'This app cannot show the agent your camera.',
|
|
51
|
+
screen: 'This app cannot show the agent your screen.',
|
|
52
|
+
};
|
|
53
|
+
/** What the model and the "agent can see" list call a capture: a named shared panel by its name, as "Whiteboard (shared panel)". */
|
|
54
|
+
function sourceLabel(kind, panelLabel) {
|
|
55
|
+
return panelLabel ? `${panelLabel} (shared panel)` : LABELS[kind];
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The inbound video track a capture needs. No rate: the model's own ceiling applies. The user's click is the
|
|
59
|
+
* consent the track asks for.
|
|
60
|
+
*/
|
|
61
|
+
const CAPTURE_VIDEO_TRACK = {
|
|
62
|
+
Modality: 'video',
|
|
63
|
+
Direction: 'inbound',
|
|
64
|
+
Encoding: 'image/jpeg',
|
|
65
|
+
UsageBasis: ['tokens', 'frames'],
|
|
66
|
+
RequiresConsent: true,
|
|
67
|
+
};
|
|
68
|
+
/** The camera and the screen share of one session. Created once the session is connected; disposed at its end. */
|
|
69
|
+
export class RealtimeCaptures {
|
|
70
|
+
constructor(options) {
|
|
71
|
+
this.options = options;
|
|
72
|
+
this.states = new BehaviorSubject(REALTIME_CAPTURES_OFF);
|
|
73
|
+
this.live = { camera: null, screen: null };
|
|
74
|
+
/** Bumped by every Stop, so a start that resolves after it knows it was cancelled. */
|
|
75
|
+
this.generation = { camera: 0, screen: 0 };
|
|
76
|
+
/** Whether this class added the session's inbound video track, and so may remove it. */
|
|
77
|
+
this.addedVideoTrack = false;
|
|
78
|
+
/** Whether the user has confirmed the camera check; later camera starts skip it. */
|
|
79
|
+
this.cameraChecked = false;
|
|
80
|
+
this.disposed = false;
|
|
81
|
+
/** How each capture is shown to the agent: set at its start, kept up to date by {@link SetVisibleToAgent}. */
|
|
82
|
+
this.showing = {
|
|
83
|
+
camera: { VisibleToAgent: true },
|
|
84
|
+
screen: { VisibleToAgent: true },
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/** Both captures now. */
|
|
88
|
+
get States() {
|
|
89
|
+
return this.states.value;
|
|
90
|
+
}
|
|
91
|
+
/** Both captures, now and on every change. Completes on {@link Dispose}. */
|
|
92
|
+
get States$() {
|
|
93
|
+
return this.states.asObservable();
|
|
94
|
+
}
|
|
95
|
+
async Start(kind, options) {
|
|
96
|
+
const current = this.stateOf(kind);
|
|
97
|
+
if (this.disposed || current.Status === 'starting' || current.Status === 'on') {
|
|
98
|
+
return current;
|
|
99
|
+
}
|
|
100
|
+
const admission = this.options.Admit?.(kind) ?? ADMITTED;
|
|
101
|
+
if (!admission.Admitted) {
|
|
102
|
+
return this.fail(kind, 'policy', admission.Message);
|
|
103
|
+
}
|
|
104
|
+
this.showing[kind] = { ChannelKey: admission.ChannelKey, VisibleToAgent: admission.VisibleToAgent };
|
|
105
|
+
const generation = ++this.generation[kind];
|
|
106
|
+
this.setState(kind, { Status: 'starting' });
|
|
107
|
+
const refusal = this.ensureVideoTrack();
|
|
108
|
+
if (refusal) {
|
|
109
|
+
return this.fail(kind, 'unsupported', refusal);
|
|
110
|
+
}
|
|
111
|
+
const acquired = kind === 'camera' ? await this.openCamera(options) : await this.openScreen(options);
|
|
112
|
+
if (generation !== this.generation[kind]) {
|
|
113
|
+
// Stopped (or disposed) while opening: let go of what just opened.
|
|
114
|
+
if (acquired.Status === 'started') {
|
|
115
|
+
acquired.Release();
|
|
116
|
+
}
|
|
117
|
+
return this.stateOf(kind);
|
|
118
|
+
}
|
|
119
|
+
if (acquired.Status === 'failed') {
|
|
120
|
+
// Failed first, so this capture no longer counts as starting when the track is given back.
|
|
121
|
+
const failed = this.fail(kind, acquired.Failure, acquired.Message);
|
|
122
|
+
this.releaseVideoTrack();
|
|
123
|
+
return failed;
|
|
124
|
+
}
|
|
125
|
+
if (kind === 'camera' && this.options.CameraCheck && !this.cameraChecked) {
|
|
126
|
+
this.live.camera = { Stream: acquired.Stream, Sampler: null, Release: acquired.Release };
|
|
127
|
+
return this.setState('camera', { Status: 'starting', Checking: true, Stream: acquired.Stream, ...this.cameraDevices() });
|
|
128
|
+
}
|
|
129
|
+
const sampler = this.showToAgent(kind, acquired.Stream, sourceLabel(kind, acquired.PanelLabel));
|
|
130
|
+
if (!sampler) {
|
|
131
|
+
return this.failToShow(kind, acquired.Release);
|
|
132
|
+
}
|
|
133
|
+
this.live[kind] = { Stream: acquired.Stream, Sampler: sampler, Release: acquired.Release };
|
|
134
|
+
return this.setState(kind, {
|
|
135
|
+
Status: 'on',
|
|
136
|
+
Stream: acquired.Stream,
|
|
137
|
+
...(acquired.Surface ? { Surface: acquired.Surface } : {}),
|
|
138
|
+
...(acquired.PanelLabel ? { PanelLabel: acquired.PanelLabel } : {}),
|
|
139
|
+
...(kind === 'camera' ? this.cameraDevices() : {}),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* The user checked the camera and turned it on: it is shown to the agent from now on, and later camera starts skip the
|
|
144
|
+
* check. Returns the camera's state; unless the camera is waiting for its check, nothing changes. When frames cannot be
|
|
145
|
+
* taken from the camera, it fails as `unsupported` and the camera is released.
|
|
146
|
+
*/
|
|
147
|
+
ConfirmCamera() {
|
|
148
|
+
const live = this.live.camera;
|
|
149
|
+
if (!live || !this.States.Camera.Checking) {
|
|
150
|
+
return this.States.Camera;
|
|
151
|
+
}
|
|
152
|
+
const sampler = this.showToAgent('camera', live.Stream, LABELS.camera);
|
|
153
|
+
if (!sampler) {
|
|
154
|
+
this.live.camera = null;
|
|
155
|
+
return this.failToShow('camera', live.Release);
|
|
156
|
+
}
|
|
157
|
+
this.cameraChecked = true;
|
|
158
|
+
live.Sampler = sampler;
|
|
159
|
+
return this.setState('camera', { Status: 'on', Stream: live.Stream, ...this.cameraDevices() });
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Moves the open camera (being checked or on) to another of its {@link RealtimeCaptureState.Devices}. The stream stays
|
|
163
|
+
* the same, so the preview and the agent's frames carry on from the new camera. When the new camera cannot open, the
|
|
164
|
+
* host's controller goes back to the one in use; when that fails too, the camera stops. Resolves with the camera's
|
|
165
|
+
* state; nothing changes while the camera is not open.
|
|
166
|
+
*/
|
|
167
|
+
async SwitchCamera(deviceId) {
|
|
168
|
+
const controller = this.options.LocalMedia;
|
|
169
|
+
if (this.live.camera && controller) {
|
|
170
|
+
await controller.SwitchDevice('camera', deviceId);
|
|
171
|
+
}
|
|
172
|
+
return this.States.Camera;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Lets the agent see a capture, or hides it from the agent while it keeps running for the user (the policy of the
|
|
176
|
+
* channel that fronts it changed). Applies to a capture that is still starting too. Quiet: the channel tells the model.
|
|
177
|
+
*/
|
|
178
|
+
SetVisibleToAgent(kind, visible) {
|
|
179
|
+
this.showing[kind] = { ...this.showing[kind], VisibleToAgent: visible };
|
|
180
|
+
if (this.live[kind]) {
|
|
181
|
+
VideoSourceArbiter.ForSink(this.options.Client).SetSourceEnabled(REALTIME_CAPTURE_SOURCE_IDS[kind], visible, false);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/** Stops a capture: no more frames, out of the arbiter, the device or share released. Safe when it is off. */
|
|
185
|
+
Stop(kind) {
|
|
186
|
+
this.generation[kind]++;
|
|
187
|
+
const live = this.live[kind];
|
|
188
|
+
this.live[kind] = null;
|
|
189
|
+
if (live) {
|
|
190
|
+
live.Sampler?.Stop();
|
|
191
|
+
VideoSourceArbiter.ForSink(this.options.Client).UnregisterSource(REALTIME_CAPTURE_SOURCE_IDS[kind]);
|
|
192
|
+
live.Release();
|
|
193
|
+
}
|
|
194
|
+
if (this.stateOf(kind).Status !== 'off') {
|
|
195
|
+
this.setState(kind, OFF);
|
|
196
|
+
}
|
|
197
|
+
this.releaseVideoTrack();
|
|
198
|
+
}
|
|
199
|
+
/** Stops both captures and completes {@link States$}. */
|
|
200
|
+
Dispose() {
|
|
201
|
+
if (this.disposed) {
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
this.Stop('camera');
|
|
205
|
+
this.Stop('screen');
|
|
206
|
+
this.disposed = true;
|
|
207
|
+
this.states.complete();
|
|
208
|
+
}
|
|
209
|
+
stateOf(kind) {
|
|
210
|
+
return kind === 'camera' ? this.states.value.Camera : this.states.value.Screen;
|
|
211
|
+
}
|
|
212
|
+
setState(kind, state) {
|
|
213
|
+
if (!this.disposed) {
|
|
214
|
+
this.states.next(kind === 'camera' ? { ...this.states.value, Camera: state } : { ...this.states.value, Screen: state });
|
|
215
|
+
}
|
|
216
|
+
return state;
|
|
217
|
+
}
|
|
218
|
+
fail(kind, failure, message) {
|
|
219
|
+
return this.setState(kind, { Status: 'failed', Failure: failure, Message: message });
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Fails a capture whose device or share opened but whose frames cannot be taken for the agent (its sampler could not
|
|
223
|
+
* start): lets go of what opened, reports `unsupported`, and gives the track back.
|
|
224
|
+
*/
|
|
225
|
+
failToShow(kind, release) {
|
|
226
|
+
release();
|
|
227
|
+
// Failed first, so this capture no longer counts as starting when the track is given back.
|
|
228
|
+
const failed = this.fail(kind, 'unsupported', CANNOT_SAMPLE[kind]);
|
|
229
|
+
this.releaseVideoTrack();
|
|
230
|
+
return failed;
|
|
231
|
+
}
|
|
232
|
+
/** The camera in use and the cameras to choose from, as the host's controller reports them now. */
|
|
233
|
+
cameraDevices() {
|
|
234
|
+
return cameraDevicesOf(this.options.LocalMedia?.State);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Keeps the open camera's device and list current as the controller reports them: a switch, a camera plugged in or
|
|
238
|
+
* removed, names that appear once the user allows the camera. While a switch is under way the device in use stays.
|
|
239
|
+
*/
|
|
240
|
+
followCamera(media) {
|
|
241
|
+
const current = this.States.Camera;
|
|
242
|
+
const next = cameraDevicesOf(media, current.DeviceID);
|
|
243
|
+
if (next.DeviceID !== current.DeviceID || !sameDevices(next.Devices, current.Devices ?? [])) {
|
|
244
|
+
this.setState('camera', { ...current, ...next });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Makes sure an inbound video track is live: the one the session already has, or one added now.
|
|
249
|
+
*
|
|
250
|
+
* @returns Why the model cannot take video, or `null` when it can.
|
|
251
|
+
*/
|
|
252
|
+
ensureVideoTrack() {
|
|
253
|
+
const client = this.options.Client;
|
|
254
|
+
if (client.IsTrackEstablished('video', 'inbound')) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const track = client.AddTrack(CAPTURE_VIDEO_TRACK);
|
|
258
|
+
if (!track || track.State !== 'live') {
|
|
259
|
+
return track?.Reason ?? 'This model cannot see video.';
|
|
260
|
+
}
|
|
261
|
+
this.addedVideoTrack = true;
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Removes the video track this class added, once no capture is on or starting and the arbiter has no other source.
|
|
266
|
+
* A channel that started sending on the track while a capture was on (one opened mid-call before it had a track of its
|
|
267
|
+
* own) is such a source: removing the track would leave it sending nothing, right after the model was told it can see
|
|
268
|
+
* that channel. The track then stays for the session, as a channel's own does.
|
|
269
|
+
*/
|
|
270
|
+
releaseVideoTrack() {
|
|
271
|
+
const busy = [this.states.value.Camera, this.states.value.Screen].some((s) => s.Status === 'on' || s.Status === 'starting');
|
|
272
|
+
if (!this.addedVideoTrack || busy || VideoSourceArbiter.ForSink(this.options.Client).GetSources().length > 0) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this.addedVideoTrack = false;
|
|
276
|
+
this.options.Client.RemoveTrack(CAPTURE_VIDEO_TRACK);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Samples the capture at the negotiated rate and registers it with the arbiter under `label`, as its channel's and as
|
|
280
|
+
* visible as the policy allows. The sampler starts first: one that cannot start (no DOM, and no sampler from the host)
|
|
281
|
+
* leaves nothing registered, so the model is never told it can see a capture that would send it nothing.
|
|
282
|
+
*
|
|
283
|
+
* @returns The running sampler, or `null` when sampling could not start.
|
|
284
|
+
*/
|
|
285
|
+
showToAgent(kind, stream, label) {
|
|
286
|
+
const arbiter = VideoSourceArbiter.ForSink(this.options.Client);
|
|
287
|
+
const sourceId = REALTIME_CAPTURE_SOURCE_IDS[kind];
|
|
288
|
+
const create = this.options.CreateSampler ?? createDomSampler;
|
|
289
|
+
const sampler = create(stream, {
|
|
290
|
+
Rate: this.options.Client.InboundVideoRate ?? 1,
|
|
291
|
+
OnFrame: (frame) => arbiter.PushFrame(sourceId, frame.Data, frame.MimeType),
|
|
292
|
+
});
|
|
293
|
+
if (!sampler.Start()) {
|
|
294
|
+
sampler.Stop();
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const { ChannelKey, VisibleToAgent } = this.showing[kind];
|
|
298
|
+
arbiter.RegisterSource({ SourceID: sourceId, Label: label, Kind: kind, Enabled: VisibleToAgent, ...(ChannelKey ? { ChannelKey } : {}) });
|
|
299
|
+
return sampler;
|
|
300
|
+
}
|
|
301
|
+
/** Opens the camera through the host's controller, and stops the capture if the camera goes away. */
|
|
302
|
+
async openCamera(options) {
|
|
303
|
+
const controller = this.options.LocalMedia;
|
|
304
|
+
if (!controller) {
|
|
305
|
+
return { Status: 'failed', Failure: 'unsupported', Message: 'This app cannot open a camera.' };
|
|
306
|
+
}
|
|
307
|
+
const result = await controller.Start('camera', options?.DeviceID);
|
|
308
|
+
if (result.Status === 'failed') {
|
|
309
|
+
return { Status: 'failed', Failure: result.Reason, Message: result.Message };
|
|
310
|
+
}
|
|
311
|
+
const watch = controller.State$.subscribe((state) => {
|
|
312
|
+
if (state.Camera.Status === 'off' || state.Camera.Status === 'failed') {
|
|
313
|
+
this.Stop('camera');
|
|
314
|
+
}
|
|
315
|
+
else if (this.live.camera) {
|
|
316
|
+
this.followCamera(state);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
return {
|
|
320
|
+
Status: 'started',
|
|
321
|
+
Stream: result.Stream,
|
|
322
|
+
Release: () => {
|
|
323
|
+
watch.unsubscribe();
|
|
324
|
+
controller.Stop('camera');
|
|
325
|
+
},
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
/** Asks the host for a screen, window, tab or panel, and stops the capture when the share ends. */
|
|
329
|
+
async openScreen(options) {
|
|
330
|
+
const host = this.options.Host;
|
|
331
|
+
if (!host.RequestDisplayCapture) {
|
|
332
|
+
return { Status: 'failed', Failure: 'unsupported', Message: 'This app cannot share a screen.' };
|
|
333
|
+
}
|
|
334
|
+
const result = await host.RequestDisplayCapture(options);
|
|
335
|
+
if (result.Status === 'cancelled') {
|
|
336
|
+
return { Status: 'failed', Failure: 'cancelled', Message: 'Sharing was cancelled.' };
|
|
337
|
+
}
|
|
338
|
+
if (result.Status === 'failed') {
|
|
339
|
+
return { Status: 'failed', Failure: screenFailure(result.Reason), Message: result.Message };
|
|
340
|
+
}
|
|
341
|
+
const capture = result.Capture;
|
|
342
|
+
const stopWatching = capture.OnEnded(() => this.Stop('screen'));
|
|
343
|
+
return {
|
|
344
|
+
Status: 'started',
|
|
345
|
+
Stream: capture.Stream,
|
|
346
|
+
Surface: capture.Surface,
|
|
347
|
+
...(capture.PanelLabel ? { PanelLabel: capture.PanelLabel } : {}),
|
|
348
|
+
Release: () => {
|
|
349
|
+
stopWatching();
|
|
350
|
+
capture.Stop();
|
|
351
|
+
},
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* The camera in use and the cameras among the devices, as a controller reports them. The camera in use is known while
|
|
357
|
+
* the controller reports it on; otherwise it is `inUse`, the one the capture already names.
|
|
358
|
+
*/
|
|
359
|
+
function cameraDevicesOf(media, inUse) {
|
|
360
|
+
const deviceId = (media?.Camera.Status === 'on' ? media.Camera.DeviceID : undefined) ?? inUse;
|
|
361
|
+
return { ...(deviceId ? { DeviceID: deviceId } : {}), Devices: media?.Devices.filter((d) => d.Kind === 'camera') ?? [] };
|
|
362
|
+
}
|
|
363
|
+
/** Whether two lists name the same devices in the same order. */
|
|
364
|
+
function sameDevices(a, b) {
|
|
365
|
+
return a.length === b.length && a.every((d, i) => d.DeviceID === b[i].DeviceID && d.Label === b[i].Label);
|
|
366
|
+
}
|
|
367
|
+
/** The default sampler: a DOM {@link FrameSampler}, whose `Start()` returns `false` without a DOM. */
|
|
368
|
+
function createDomSampler(stream, options) {
|
|
369
|
+
return new FrameSampler(stream, options);
|
|
370
|
+
}
|
|
371
|
+
/** A share failure as a capture failure: a panel the browser cannot narrow to is unsupported; a wrong one an error. */
|
|
372
|
+
function screenFailure(reason) {
|
|
373
|
+
switch (reason) {
|
|
374
|
+
case 'unsupported':
|
|
375
|
+
case 'panel-unsupported':
|
|
376
|
+
return 'unsupported';
|
|
377
|
+
case 'denied':
|
|
378
|
+
return 'denied';
|
|
379
|
+
default:
|
|
380
|
+
return 'error';
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=realtime-captures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-captures.js","sourceRoot":"","sources":["../../src/session/realtime-captures.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The microphone of one realtime session, as a host's device menu shows it: the one the call uses, the
|
|
3
|
+
* ones the user can move it to (`RealtimeSessionRuntime.SwitchMicrophone`), and why none works once the call lost it.
|
|
4
|
+
*
|
|
5
|
+
* The call opens the microphone through the host's camera-and-microphone controller, and the controller reports the
|
|
6
|
+
* device in use and the devices the browser lists. This module reads the microphone's part of that report. It shows
|
|
7
|
+
* nothing outside a call and nothing on a host without a controller, which opens the microphone itself
|
|
8
|
+
* (`IRealtimeMediaHost.AcquireMicrophone`) and so has no list to offer.
|
|
9
|
+
*
|
|
10
|
+
* @module @memberjunction/realtime-runtime
|
|
11
|
+
*/
|
|
12
|
+
import type { LocalMediaFailure, LocalMediaState, MediaDevice } from '@memberjunction/ai-realtime-client';
|
|
13
|
+
/** The call's microphone: the one in use, the ones the user can move it to, and why none works once the call lost it. */
|
|
14
|
+
export interface RealtimeMicrophoneState {
|
|
15
|
+
/**
|
|
16
|
+
* The microphone in use. Kept while a switch is under way, until the new one is on. Absent outside a call, on a host
|
|
17
|
+
* without a controller, and when the controller has no microphone open (it stopped, or no device could open).
|
|
18
|
+
*/
|
|
19
|
+
DeviceID?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The microphones the browser lists while the call is connected: what `SwitchMicrophone` can move to. The call has
|
|
22
|
+
* opened the microphone, so the browser gives their names. Empty outside a call and on a host without a controller.
|
|
23
|
+
*/
|
|
24
|
+
Devices: readonly MediaDevice[];
|
|
25
|
+
/**
|
|
26
|
+
* Why no microphone works: the call lost its microphone (the one in use failed and so did the fallback to the default,
|
|
27
|
+
* or the last one went away), so the agent hears nothing until the user picks one with `SwitchMicrophone`. Kept while
|
|
28
|
+
* the picked one opens, and gone once a microphone is on. Absent while the microphone works or is being switched,
|
|
29
|
+
* outside a call, and on a host without a controller.
|
|
30
|
+
*/
|
|
31
|
+
Failure?: LocalMediaFailure;
|
|
32
|
+
}
|
|
33
|
+
/** No microphone to show or switch: what a host gets outside a call and on a host without a controller. */
|
|
34
|
+
export declare const REALTIME_MICROPHONE_NONE: RealtimeMicrophoneState;
|
|
35
|
+
/**
|
|
36
|
+
* The call's microphone as a controller reports it. The device in use is the controller's while its microphone is on.
|
|
37
|
+
* While the controller is starting a microphone (a switch, a lost device replaced by the default, or a pick after the call
|
|
38
|
+
* lost its microphone), the call keeps what it shows now: the microphone in use, so a device menu keeps showing it until
|
|
39
|
+
* the new one is on, or why none works. Failed, there is none, and the failure says why. Stopped, there is none.
|
|
40
|
+
*
|
|
41
|
+
* @param media The controller's state.
|
|
42
|
+
* @param current What the call shows now.
|
|
43
|
+
*/
|
|
44
|
+
export declare function ReadMicrophoneState(media: LocalMediaState, current?: RealtimeMicrophoneState): RealtimeMicrophoneState;
|
|
45
|
+
/**
|
|
46
|
+
* Whether two states name the same microphone in use, list the same microphones (by id and name, in the same order) and
|
|
47
|
+
* give the same failure.
|
|
48
|
+
*/
|
|
49
|
+
export declare function SameMicrophoneState(a: RealtimeMicrophoneState, b: RealtimeMicrophoneState): boolean;
|
|
50
|
+
//# sourceMappingURL=realtime-microphone.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-microphone.d.ts","sourceRoot":"","sources":["../../src/session/realtime-microphone.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAE1G,yHAAyH;AACzH,MAAM,WAAW,uBAAuB;IACpC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAC;CAC/B;AAED,2GAA2G;AAC3G,eAAO,MAAM,wBAAwB,EAAE,uBAAuE,CAAC;AAE/G;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,EAAE,OAAO,GAAE,uBAAkD,GAAG,uBAAuB,CAiBhJ;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,uBAAuB,EAAE,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAOnG"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The microphone of one realtime session, as a host's device menu shows it: the one the call uses, the
|
|
3
|
+
* ones the user can move it to (`RealtimeSessionRuntime.SwitchMicrophone`), and why none works once the call lost it.
|
|
4
|
+
*
|
|
5
|
+
* The call opens the microphone through the host's camera-and-microphone controller, and the controller reports the
|
|
6
|
+
* device in use and the devices the browser lists. This module reads the microphone's part of that report. It shows
|
|
7
|
+
* nothing outside a call and nothing on a host without a controller, which opens the microphone itself
|
|
8
|
+
* (`IRealtimeMediaHost.AcquireMicrophone`) and so has no list to offer.
|
|
9
|
+
*
|
|
10
|
+
* @module @memberjunction/realtime-runtime
|
|
11
|
+
*/
|
|
12
|
+
/** No microphone to show or switch: what a host gets outside a call and on a host without a controller. */
|
|
13
|
+
export const REALTIME_MICROPHONE_NONE = Object.freeze({ Devices: Object.freeze([]) });
|
|
14
|
+
/**
|
|
15
|
+
* The call's microphone as a controller reports it. The device in use is the controller's while its microphone is on.
|
|
16
|
+
* While the controller is starting a microphone (a switch, a lost device replaced by the default, or a pick after the call
|
|
17
|
+
* lost its microphone), the call keeps what it shows now: the microphone in use, so a device menu keeps showing it until
|
|
18
|
+
* the new one is on, or why none works. Failed, there is none, and the failure says why. Stopped, there is none.
|
|
19
|
+
*
|
|
20
|
+
* @param media The controller's state.
|
|
21
|
+
* @param current What the call shows now.
|
|
22
|
+
*/
|
|
23
|
+
export function ReadMicrophoneState(media, current = REALTIME_MICROPHONE_NONE) {
|
|
24
|
+
const microphone = media.Microphone;
|
|
25
|
+
const devices = media.Devices.filter((d) => d.Kind === 'microphone');
|
|
26
|
+
switch (microphone.Status) {
|
|
27
|
+
case 'on':
|
|
28
|
+
return { ...(microphone.DeviceID ? { DeviceID: microphone.DeviceID } : {}), Devices: devices };
|
|
29
|
+
case 'starting':
|
|
30
|
+
return {
|
|
31
|
+
...(current.DeviceID ? { DeviceID: current.DeviceID } : {}),
|
|
32
|
+
...(current.Failure ? { Failure: current.Failure } : {}),
|
|
33
|
+
Devices: devices,
|
|
34
|
+
};
|
|
35
|
+
case 'failed':
|
|
36
|
+
return { Failure: microphone.Failure ?? 'error', Devices: devices };
|
|
37
|
+
default:
|
|
38
|
+
return { Devices: devices };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Whether two states name the same microphone in use, list the same microphones (by id and name, in the same order) and
|
|
43
|
+
* give the same failure.
|
|
44
|
+
*/
|
|
45
|
+
export function SameMicrophoneState(a, b) {
|
|
46
|
+
return (a.DeviceID === b.DeviceID &&
|
|
47
|
+
a.Failure === b.Failure &&
|
|
48
|
+
a.Devices.length === b.Devices.length &&
|
|
49
|
+
a.Devices.every((d, i) => d.DeviceID === b.Devices[i].DeviceID && d.Label === b.Devices[i].Label));
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=realtime-microphone.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime-microphone.js","sourceRoot":"","sources":["../../src/session/realtime-microphone.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAyBH,2GAA2G;AAC3G,MAAM,CAAC,MAAM,wBAAwB,GAA4B,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;AAE/G;;;;;;;;GAQG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAsB,EAAE,UAAmC,wBAAwB;IACnH,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACpC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;IACrE,QAAQ,UAAU,CAAC,MAAM,EAAE,CAAC;QACxB,KAAK,IAAI;YACL,OAAO,EAAE,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QACnG,KAAK,UAAU;YACX,OAAO;gBACH,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3D,GAAG,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACxD,OAAO,EAAE,OAAO;aACnB,CAAC;QACN,KAAK,QAAQ;YACT,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,IAAI,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QACxE;YACI,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACpC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,CAA0B,EAAE,CAA0B;IACtF,OAAO,CACH,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ;QACzB,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO;QACvB,CAAC,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM;QACrC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CACpG,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import type { Observable } from 'rxjs';
|
|
2
|
+
import type { RealtimeSessionEvent, UnknownRealtimeSessionEvent } from '@memberjunction/ai-core-plus';
|
|
3
|
+
/** An event a session delivers: a known, typed one, or an app-defined one the runtime does not interpret. */
|
|
4
|
+
export type RealtimeSessionStreamEvent = RealtimeSessionEvent | UnknownRealtimeSessionEvent;
|
|
5
|
+
/** The slice of a verification status read the hub needs to recover a missed `identity.verified`. */
|
|
6
|
+
export interface RealtimeSessionVerificationSnapshot {
|
|
7
|
+
VerificationState: 'unverified' | 'pending' | 'verified';
|
|
8
|
+
VerifiedEmail?: string;
|
|
9
|
+
VerifiedAt?: string;
|
|
10
|
+
MaxSessionDeadlineIso?: string;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Where session events come from — the transport seam. `GraphQLRealtimeSessionClient`
|
|
14
|
+
* (`@memberjunction/graphql-dataprovider`) satisfies this structurally; a host on another transport
|
|
15
|
+
* supplies its own.
|
|
16
|
+
*/
|
|
17
|
+
export interface IRealtimeSessionEventSource {
|
|
18
|
+
/** Opens the session's event stream. It completes when the transport recycles (e.g. a token refresh). */
|
|
19
|
+
SubscribeToSessionEvents(agentSessionId: string): Observable<RealtimeSessionStreamEvent>;
|
|
20
|
+
/** Reads the session's verification state once — the durable backstop for an event missed while away. Never throws. */
|
|
21
|
+
GetVerificationStatus(agentSessionId: string): Promise<RealtimeSessionVerificationSnapshot>;
|
|
22
|
+
}
|
|
23
|
+
/** Tuning for {@link RealtimeSessionEventHub}'s reconnect loop. */
|
|
24
|
+
export interface RealtimeSessionEventHubOptions {
|
|
25
|
+
/** Consecutive failed/short-lived subscriptions tolerated before the hub gives up. Default 6. */
|
|
26
|
+
MaxReconnectAttempts?: number;
|
|
27
|
+
/** First reconnect delay; doubles per consecutive attempt up to {@link MaxBackoffMs}. Default 1000. */
|
|
28
|
+
BaseBackoffMs?: number;
|
|
29
|
+
/** Ceiling of the reconnect delay. Default 15000. */
|
|
30
|
+
MaxBackoffMs?: number;
|
|
31
|
+
/** A subscription that lived at least this long counts as healthy and resets the attempt counter. Default 30000. */
|
|
32
|
+
StableAfterMs?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Keeps one session's event stream open for the session's life and delivers every event to a sink.
|
|
36
|
+
*
|
|
37
|
+
* The stream is a long-lived subscription that ends when the transport recycles its connection (token
|
|
38
|
+
* refresh, network blip) — and delivery has no replay, so an event published during the gap is gone.
|
|
39
|
+
* The hub therefore, on every re-subscribe: opens the new subscription FIRST (so nothing published
|
|
40
|
+
* from now on is missed), THEN reads the verification status once, and if the person verified while the
|
|
41
|
+
* hub was away, delivers the `identity.verified` event it missed (marked `Recovered`).
|
|
42
|
+
*
|
|
43
|
+
* Reconnects are capped: a stream that keeps failing is given up on (once, loudly) rather than retried
|
|
44
|
+
* forever; a stream that stayed up long enough counts as healthy and resets the cap.
|
|
45
|
+
*
|
|
46
|
+
* Framework-agnostic and clock-driven by `setTimeout`, so tests drive it with fake timers.
|
|
47
|
+
*/
|
|
48
|
+
export declare class RealtimeSessionEventHub {
|
|
49
|
+
private readonly source;
|
|
50
|
+
private readonly deliver;
|
|
51
|
+
private readonly options;
|
|
52
|
+
private sessionId;
|
|
53
|
+
private subscription;
|
|
54
|
+
private reconnectTimer;
|
|
55
|
+
private attempts;
|
|
56
|
+
private verifiedDelivered;
|
|
57
|
+
/** Bumped by every Start/Stop so a late callback from a previous run can tell it is stale. */
|
|
58
|
+
private generation;
|
|
59
|
+
constructor(source: IRealtimeSessionEventSource, deliver: (event: RealtimeSessionStreamEvent) => void, options?: RealtimeSessionEventHubOptions);
|
|
60
|
+
/** Whether the hub currently has (or is about to re-open) a subscription. */
|
|
61
|
+
get IsRunning(): boolean;
|
|
62
|
+
/** Opens the stream for `agentSessionId`. Replaces any run in progress. */
|
|
63
|
+
Start(agentSessionId: string): void;
|
|
64
|
+
/** Closes the stream and cancels any pending reconnect. Safe to call when not running. */
|
|
65
|
+
Stop(): void;
|
|
66
|
+
private connect;
|
|
67
|
+
private onEvent;
|
|
68
|
+
private onStreamEnded;
|
|
69
|
+
/** Reads the verification status once and delivers the `identity.verified` event the hub missed, if any. */
|
|
70
|
+
private recoverMissedVerification;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=session-event-hub.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-event-hub.d.ts","sourceRoot":"","sources":["../../src/session/session-event-hub.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,MAAM,CAAC;AACrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AAEtG,6GAA6G;AAC7G,MAAM,MAAM,0BAA0B,GAAG,oBAAoB,GAAG,2BAA2B,CAAC;AAE5F,qGAAqG;AACrG,MAAM,WAAW,mCAAmC;IAClD,iBAAiB,EAAE,YAAY,GAAG,SAAS,GAAG,UAAU,CAAC;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,yGAAyG;IACzG,wBAAwB,CAAC,cAAc,EAAE,MAAM,GAAG,UAAU,CAAC,0BAA0B,CAAC,CAAC;IACzF,uHAAuH;IACvH,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,mCAAmC,CAAC,CAAC;CAC7F;AAED,mEAAmE;AACnE,MAAM,WAAW,8BAA8B;IAC7C,iGAAiG;IACjG,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,uGAAuG;IACvG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oHAAoH;IACpH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AASD;;;;;;;;;;;;;GAaG;AACH,qBAAa,uBAAuB;IAWhC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IACnE,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,QAAQ,CAAK;IACrB,OAAO,CAAC,iBAAiB,CAAS;IAClC,8FAA8F;IAC9F,OAAO,CAAC,UAAU,CAAK;gBAGJ,MAAM,EAAE,2BAA2B,EACnC,OAAO,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,IAAI,EACrE,OAAO,GAAE,8BAAmC;IAK9C,6EAA6E;IAC7E,IAAW,SAAS,IAAI,OAAO,CAE9B;IAED,2EAA2E;IACpE,KAAK,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAQ1C,0FAA0F;IACnF,IAAI,IAAI,IAAI;IAWnB,OAAO,CAAC,OAAO;IA2Bf,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,aAAa;IA0BrB,4GAA4G;YAC9F,yBAAyB;CAgCxC"}
|