@absolutejs/voice 0.0.22-beta.655 → 0.0.22-beta.657
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/dist/angular/index.js +204 -70
- package/dist/client/connection.d.ts +3 -0
- package/dist/client/htmxBootstrap.js +204 -70
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +206 -71
- package/dist/core/types.d.ts +4 -0
- package/dist/embed/index.js +204 -70
- package/dist/embed/voice-widget.js +8 -8
- package/dist/react/index.js +204 -70
- package/dist/svelte/index.js +204 -70
- package/dist/testing/index.js +205 -71
- package/dist/vue/index.js +204 -70
- package/package.json +1 -1
package/dist/vue/index.js
CHANGED
|
@@ -10838,11 +10838,19 @@ var DEFAULT_MAX_RECONNECT_ATTEMPTS = 15;
|
|
|
10838
10838
|
var DEFAULT_PING_INTERVAL = 30000;
|
|
10839
10839
|
var DEFAULT_RECONNECT_RESET_AFTER_MS = 30000;
|
|
10840
10840
|
var DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS = 5000;
|
|
10841
|
+
var DEFAULT_PREPARE_RECONNECT_TIMEOUT_MS = 1e4;
|
|
10841
10842
|
var MAX_PENDING_CONTROL_MESSAGES = 100;
|
|
10842
10843
|
var RECONNECT_BASE_DELAY_MS = 500;
|
|
10843
10844
|
var DEFAULT_RECONNECT_MAX_DELAY_MS = 8000;
|
|
10844
10845
|
var computeVoiceReconnectDelayMs = (attempt, baseMs, maxDelayMs) => Math.min(maxDelayMs, baseMs * 2 ** (Math.max(1, attempt) - 1));
|
|
10845
10846
|
var DEFAULT_SCENARIO_QUERY_PARAM = "scenarioId";
|
|
10847
|
+
|
|
10848
|
+
class VoiceReconnectRejectedError extends Error {
|
|
10849
|
+
constructor(message = "Voice authorization could not be renewed.") {
|
|
10850
|
+
super(message);
|
|
10851
|
+
this.name = "VoiceReconnectRejectedError";
|
|
10852
|
+
}
|
|
10853
|
+
}
|
|
10846
10854
|
var noop = () => {};
|
|
10847
10855
|
var noopAsync = () => Promise.resolve();
|
|
10848
10856
|
var noopUnsubscribe = () => noop;
|
|
@@ -10922,6 +10930,9 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
10922
10930
|
const reconnectMaxDelayMs = options.reconnectMaxDelayMs ?? DEFAULT_RECONNECT_MAX_DELAY_MS;
|
|
10923
10931
|
const pingInterval = options.pingInterval ?? DEFAULT_PING_INTERVAL;
|
|
10924
10932
|
const reconnectResetAfterMs = options.reconnectResetAfterMs ?? DEFAULT_RECONNECT_RESET_AFTER_MS;
|
|
10933
|
+
let lifecycleGeneration = 0;
|
|
10934
|
+
let disposed = false;
|
|
10935
|
+
let reconnectPreparationAbort = null;
|
|
10925
10936
|
const computeReconnectDelayMs = (attempt) => computeVoiceReconnectDelayMs(attempt, RECONNECT_BASE_DELAY_MS, reconnectMaxDelayMs);
|
|
10926
10937
|
const state = {
|
|
10927
10938
|
isConnected: false,
|
|
@@ -10985,6 +10996,9 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
10985
10996
|
pendingCallControls.clear();
|
|
10986
10997
|
};
|
|
10987
10998
|
const scheduleReconnect = () => {
|
|
10999
|
+
if (disposed)
|
|
11000
|
+
return;
|
|
11001
|
+
const scheduledGeneration = lifecycleGeneration;
|
|
10988
11002
|
state.reconnectAttempts += 1;
|
|
10989
11003
|
const delayMs = computeReconnectDelayMs(state.reconnectAttempts);
|
|
10990
11004
|
const nextAttemptAt = Date.now() + delayMs;
|
|
@@ -10999,18 +11013,44 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
10999
11013
|
type: "connection"
|
|
11000
11014
|
});
|
|
11001
11015
|
state.reconnectTimeout = setTimeout(async () => {
|
|
11016
|
+
if (disposed || lifecycleGeneration !== scheduledGeneration)
|
|
11017
|
+
return;
|
|
11002
11018
|
if (state.reconnectAttempts > maxReconnectAttempts) {
|
|
11003
11019
|
emitTerminalFailure("Voice connection could not be restored.");
|
|
11004
11020
|
return;
|
|
11005
11021
|
}
|
|
11006
11022
|
try {
|
|
11007
|
-
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11012
|
-
|
|
11013
|
-
|
|
11023
|
+
if (options.prepareReconnect) {
|
|
11024
|
+
const preparationAbort = new AbortController;
|
|
11025
|
+
reconnectPreparationAbort = preparationAbort;
|
|
11026
|
+
const timeout = setTimeout(() => preparationAbort.abort("Voice reconnect preparation timed out"), options.prepareReconnectTimeoutMs ?? DEFAULT_PREPARE_RECONNECT_TIMEOUT_MS);
|
|
11027
|
+
try {
|
|
11028
|
+
await Promise.race([
|
|
11029
|
+
options.prepareReconnect({
|
|
11030
|
+
attempt: state.reconnectAttempts,
|
|
11031
|
+
path,
|
|
11032
|
+
scenarioId: state.scenarioId,
|
|
11033
|
+
sessionId: state.sessionId,
|
|
11034
|
+
signal: preparationAbort.signal
|
|
11035
|
+
}),
|
|
11036
|
+
new Promise((_, reject) => {
|
|
11037
|
+
preparationAbort.signal.addEventListener("abort", () => reject(new Error("Voice reconnect preparation timed out.")), { once: true });
|
|
11038
|
+
})
|
|
11039
|
+
]);
|
|
11040
|
+
} finally {
|
|
11041
|
+
clearTimeout(timeout);
|
|
11042
|
+
if (reconnectPreparationAbort === preparationAbort) {
|
|
11043
|
+
reconnectPreparationAbort = null;
|
|
11044
|
+
}
|
|
11045
|
+
}
|
|
11046
|
+
}
|
|
11047
|
+
} catch (error) {
|
|
11048
|
+
if (disposed || lifecycleGeneration !== scheduledGeneration)
|
|
11049
|
+
return;
|
|
11050
|
+
if (error instanceof VoiceReconnectRejectedError) {
|
|
11051
|
+
emitTerminalFailure(error.message);
|
|
11052
|
+
return;
|
|
11053
|
+
}
|
|
11014
11054
|
if (state.reconnectAttempts < maxReconnectAttempts) {
|
|
11015
11055
|
scheduleReconnect();
|
|
11016
11056
|
} else {
|
|
@@ -11018,10 +11058,14 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11018
11058
|
}
|
|
11019
11059
|
return;
|
|
11020
11060
|
}
|
|
11061
|
+
if (disposed || lifecycleGeneration !== scheduledGeneration)
|
|
11062
|
+
return;
|
|
11021
11063
|
connect();
|
|
11022
11064
|
}, delayMs);
|
|
11023
11065
|
};
|
|
11024
11066
|
const connect = () => {
|
|
11067
|
+
if (disposed)
|
|
11068
|
+
return;
|
|
11025
11069
|
const ws = new WebSocket(buildWsUrl(path, state.sessionId, state.scenarioId, options.query ?? {}));
|
|
11026
11070
|
ws.binaryType = "arraybuffer";
|
|
11027
11071
|
ws.onopen = () => {
|
|
@@ -11092,6 +11136,7 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11092
11136
|
state.ws = null;
|
|
11093
11137
|
state.isConnected = false;
|
|
11094
11138
|
clearTimers();
|
|
11139
|
+
rejectPendingCallControls("Voice connection interrupted");
|
|
11095
11140
|
const reconnectable = shouldReconnect && event.code !== WS_NORMAL_CLOSURE && event.code !== WS_UNAUTHORIZED && event.code !== WS_FORBIDDEN && state.reconnectAttempts < maxReconnectAttempts;
|
|
11096
11141
|
const authorizationFailure = event.code === WS_UNAUTHORIZED || event.code === WS_FORBIDDEN;
|
|
11097
11142
|
if (authorizationFailure) {
|
|
@@ -11122,6 +11167,8 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11122
11167
|
state.ws = ws;
|
|
11123
11168
|
};
|
|
11124
11169
|
const sendSerialized = (value) => {
|
|
11170
|
+
if (disposed)
|
|
11171
|
+
return;
|
|
11125
11172
|
if (state.ws?.readyState === WS_OPEN) {
|
|
11126
11173
|
state.ws.send(value);
|
|
11127
11174
|
return;
|
|
@@ -11157,6 +11204,9 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11157
11204
|
send({ type: "end_turn" });
|
|
11158
11205
|
};
|
|
11159
11206
|
const callControl = (message) => {
|
|
11207
|
+
if (state.ws?.readyState !== WS_OPEN) {
|
|
11208
|
+
return Promise.reject(new Error(`${message.action} is unavailable while disconnected`));
|
|
11209
|
+
}
|
|
11160
11210
|
const requestId = crypto.randomUUID();
|
|
11161
11211
|
const { promise, reject, resolve } = Promise.withResolvers();
|
|
11162
11212
|
const timeout = setTimeout(() => {
|
|
@@ -11164,16 +11214,21 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11164
11214
|
reject(new Error(`${message.action} acknowledgement timed out`));
|
|
11165
11215
|
}, DEFAULT_CALL_CONTROL_ACK_TIMEOUT_MS);
|
|
11166
11216
|
pendingCallControls.set(requestId, { reject, resolve, timeout });
|
|
11167
|
-
send({
|
|
11217
|
+
state.ws.send(JSON.stringify({
|
|
11168
11218
|
...message,
|
|
11169
11219
|
requestId,
|
|
11170
11220
|
type: "call_control"
|
|
11171
|
-
});
|
|
11221
|
+
}));
|
|
11172
11222
|
return promise;
|
|
11173
11223
|
};
|
|
11174
11224
|
const close = (reason = "client-close") => {
|
|
11225
|
+
disposed = true;
|
|
11226
|
+
reconnectPreparationAbort?.abort();
|
|
11227
|
+
reconnectPreparationAbort = null;
|
|
11228
|
+
lifecycleGeneration += 1;
|
|
11175
11229
|
clearTimers();
|
|
11176
11230
|
rejectPendingCallControls("Voice connection closed");
|
|
11231
|
+
state.pendingMessages.length = 0;
|
|
11177
11232
|
if (state.ws) {
|
|
11178
11233
|
const ws = state.ws;
|
|
11179
11234
|
state.ws = null;
|
|
@@ -11186,8 +11241,13 @@ var createVoiceConnection = (path, options = {}) => {
|
|
|
11186
11241
|
listeners.clear();
|
|
11187
11242
|
};
|
|
11188
11243
|
const disconnect = () => {
|
|
11244
|
+
disposed = true;
|
|
11245
|
+
reconnectPreparationAbort?.abort();
|
|
11246
|
+
reconnectPreparationAbort = null;
|
|
11247
|
+
lifecycleGeneration += 1;
|
|
11189
11248
|
clearTimers();
|
|
11190
11249
|
rejectPendingCallControls("Voice connection disconnected");
|
|
11250
|
+
state.pendingMessages.length = 0;
|
|
11191
11251
|
if (state.ws) {
|
|
11192
11252
|
const ws = state.ws;
|
|
11193
11253
|
state.ws = null;
|
|
@@ -11435,7 +11495,10 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
11435
11495
|
getSessionId: () => options.browserMedia ? options.browserMedia.getSessionId?.() ?? connection.getSessionId() : connection.getSessionId()
|
|
11436
11496
|
}) : null;
|
|
11437
11497
|
const subscribers = new Set;
|
|
11498
|
+
let disposed = false;
|
|
11438
11499
|
const start = (input) => Promise.resolve().then(() => {
|
|
11500
|
+
if (disposed)
|
|
11501
|
+
return;
|
|
11439
11502
|
if (!input?.sessionId && !input?.scenarioId) {
|
|
11440
11503
|
return;
|
|
11441
11504
|
}
|
|
@@ -11494,6 +11557,7 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
11494
11557
|
return connection.callControl(message);
|
|
11495
11558
|
},
|
|
11496
11559
|
close(reason) {
|
|
11560
|
+
disposed = true;
|
|
11497
11561
|
unsubscribeConnection();
|
|
11498
11562
|
browserMediaReporter?.close();
|
|
11499
11563
|
connection.close(reason);
|
|
@@ -11501,6 +11565,7 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
11501
11565
|
notify();
|
|
11502
11566
|
},
|
|
11503
11567
|
disconnect() {
|
|
11568
|
+
disposed = true;
|
|
11504
11569
|
unsubscribeConnection();
|
|
11505
11570
|
browserMediaReporter?.close();
|
|
11506
11571
|
connection.disconnect();
|
|
@@ -11719,48 +11784,89 @@ var createMicrophoneCapture = (options) => {
|
|
|
11719
11784
|
let sourceNode = null;
|
|
11720
11785
|
let processorNode = null;
|
|
11721
11786
|
let mediaStream = null;
|
|
11722
|
-
|
|
11723
|
-
|
|
11724
|
-
|
|
11725
|
-
|
|
11726
|
-
|
|
11727
|
-
|
|
11728
|
-
|
|
11729
|
-
|
|
11730
|
-
|
|
11731
|
-
|
|
11732
|
-
|
|
11733
|
-
|
|
11734
|
-
|
|
11735
|
-
|
|
11736
|
-
|
|
11787
|
+
let lifecycleGeneration = 0;
|
|
11788
|
+
let startPromise = null;
|
|
11789
|
+
const release = (resources) => {
|
|
11790
|
+
resources.processorNode?.disconnect();
|
|
11791
|
+
resources.sourceNode?.disconnect();
|
|
11792
|
+
resources.mediaStream?.getTracks().forEach((track) => track.stop());
|
|
11793
|
+
resources.audioContext?.close();
|
|
11794
|
+
};
|
|
11795
|
+
const start = () => {
|
|
11796
|
+
if (mediaStream)
|
|
11797
|
+
return Promise.resolve();
|
|
11798
|
+
if (startPromise)
|
|
11799
|
+
return startPromise;
|
|
11800
|
+
const generation = ++lifecycleGeneration;
|
|
11801
|
+
const operation = async () => {
|
|
11802
|
+
if (!options.stream && (typeof navigator === "undefined" || !navigator.mediaDevices?.getUserMedia)) {
|
|
11803
|
+
throw new Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");
|
|
11804
|
+
}
|
|
11805
|
+
const AudioContextCtor = (typeof window !== "undefined" ? window.AudioContext ?? window.webkitAudioContext : undefined) ?? AudioContext;
|
|
11806
|
+
if (!AudioContextCtor) {
|
|
11807
|
+
throw new Error("Browser microphone capture requires AudioContext support.");
|
|
11808
|
+
}
|
|
11809
|
+
let pendingAudioContext = null;
|
|
11810
|
+
let pendingMediaStream = null;
|
|
11811
|
+
let pendingProcessorNode = null;
|
|
11812
|
+
let pendingSourceNode = null;
|
|
11813
|
+
try {
|
|
11814
|
+
pendingMediaStream = options.stream ?? await navigator.mediaDevices.getUserMedia({
|
|
11815
|
+
audio: {
|
|
11816
|
+
autoGainControl: true,
|
|
11817
|
+
channelCount: options.channelCount ?? 1,
|
|
11818
|
+
echoCancellation: true,
|
|
11819
|
+
noiseSuppression: true
|
|
11820
|
+
}
|
|
11821
|
+
});
|
|
11822
|
+
if (generation !== lifecycleGeneration) {
|
|
11823
|
+
throw new Error("Microphone capture stopped during startup.");
|
|
11737
11824
|
}
|
|
11738
|
-
|
|
11739
|
-
|
|
11740
|
-
|
|
11741
|
-
|
|
11825
|
+
pendingAudioContext = new AudioContextCtor;
|
|
11826
|
+
if (pendingAudioContext.state === "suspended") {
|
|
11827
|
+
await pendingAudioContext.resume();
|
|
11828
|
+
}
|
|
11829
|
+
if (generation !== lifecycleGeneration) {
|
|
11830
|
+
throw new Error("Microphone capture stopped during startup.");
|
|
11831
|
+
}
|
|
11832
|
+
pendingSourceNode = pendingAudioContext.createMediaStreamSource(pendingMediaStream);
|
|
11833
|
+
pendingProcessorNode = pendingAudioContext.createScriptProcessor(4096, 1, 1);
|
|
11834
|
+
pendingProcessorNode.onaudioprocess = (event) => {
|
|
11835
|
+
const channel = event.inputBuffer.getChannelData(0);
|
|
11836
|
+
const downsampled = downsampleBuffer(channel, pendingAudioContext?.sampleRate ?? 48000, options.sampleRateHz ?? 16000);
|
|
11837
|
+
const pcm = floatTo16BitPCM(downsampled);
|
|
11838
|
+
options.onLevel?.(getPcmLevel(pcm));
|
|
11839
|
+
options.onAudio(pcm);
|
|
11840
|
+
};
|
|
11841
|
+
pendingSourceNode.connect(pendingProcessorNode);
|
|
11842
|
+
pendingProcessorNode.connect(pendingAudioContext.destination);
|
|
11843
|
+
if (generation !== lifecycleGeneration) {
|
|
11844
|
+
throw new Error("Microphone capture stopped during startup.");
|
|
11845
|
+
}
|
|
11846
|
+
audioContext = pendingAudioContext;
|
|
11847
|
+
mediaStream = pendingMediaStream;
|
|
11848
|
+
processorNode = pendingProcessorNode;
|
|
11849
|
+
sourceNode = pendingSourceNode;
|
|
11850
|
+
} catch (error) {
|
|
11851
|
+
release({
|
|
11852
|
+
audioContext: pendingAudioContext,
|
|
11853
|
+
mediaStream: pendingMediaStream,
|
|
11854
|
+
processorNode: pendingProcessorNode,
|
|
11855
|
+
sourceNode: pendingSourceNode
|
|
11856
|
+
});
|
|
11857
|
+
throw error;
|
|
11742
11858
|
}
|
|
11743
|
-
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11747
|
-
|
|
11748
|
-
|
|
11749
|
-
|
|
11750
|
-
options.onAudio(pcm);
|
|
11751
|
-
};
|
|
11752
|
-
sourceNode.connect(processorNode);
|
|
11753
|
-
processorNode.connect(audioContext.destination);
|
|
11754
|
-
} catch (error) {
|
|
11755
|
-
stop();
|
|
11756
|
-
throw error;
|
|
11757
|
-
}
|
|
11859
|
+
};
|
|
11860
|
+
const pending = operation().finally(() => {
|
|
11861
|
+
if (startPromise === pending)
|
|
11862
|
+
startPromise = null;
|
|
11863
|
+
});
|
|
11864
|
+
startPromise = pending;
|
|
11865
|
+
return pending;
|
|
11758
11866
|
};
|
|
11759
11867
|
const stop = () => {
|
|
11760
|
-
|
|
11761
|
-
sourceNode
|
|
11762
|
-
mediaStream?.getTracks().forEach((track) => track.stop());
|
|
11763
|
-
audioContext?.close();
|
|
11868
|
+
lifecycleGeneration += 1;
|
|
11869
|
+
release({ audioContext, mediaStream, processorNode, sourceNode });
|
|
11764
11870
|
options.onLevel?.(0);
|
|
11765
11871
|
audioContext = null;
|
|
11766
11872
|
mediaStream = null;
|
|
@@ -12116,6 +12222,9 @@ var createVoiceController = (path, options = {}) => {
|
|
|
12116
12222
|
...options.connection
|
|
12117
12223
|
});
|
|
12118
12224
|
let capture = null;
|
|
12225
|
+
let captureGeneration = 0;
|
|
12226
|
+
let disposed = false;
|
|
12227
|
+
let startRecordingPromise = null;
|
|
12119
12228
|
let state = createInitialState2(stream);
|
|
12120
12229
|
const subscribers = new Set;
|
|
12121
12230
|
const notify = () => {
|
|
@@ -12172,6 +12281,7 @@ var createVoiceController = (path, options = {}) => {
|
|
|
12172
12281
|
return capture;
|
|
12173
12282
|
};
|
|
12174
12283
|
const stopRecording = () => {
|
|
12284
|
+
captureGeneration += 1;
|
|
12175
12285
|
capture?.stop();
|
|
12176
12286
|
capture = null;
|
|
12177
12287
|
state = {
|
|
@@ -12181,39 +12291,63 @@ var createVoiceController = (path, options = {}) => {
|
|
|
12181
12291
|
notify();
|
|
12182
12292
|
};
|
|
12183
12293
|
const startRecording = async () => {
|
|
12294
|
+
if (disposed) {
|
|
12295
|
+
throw new Error("Voice controller is closed.");
|
|
12296
|
+
}
|
|
12184
12297
|
if (state.isRecording) {
|
|
12185
12298
|
return;
|
|
12186
12299
|
}
|
|
12187
|
-
|
|
12188
|
-
|
|
12189
|
-
|
|
12190
|
-
|
|
12191
|
-
|
|
12192
|
-
|
|
12193
|
-
|
|
12194
|
-
|
|
12195
|
-
|
|
12196
|
-
|
|
12197
|
-
|
|
12198
|
-
|
|
12199
|
-
|
|
12200
|
-
|
|
12201
|
-
|
|
12202
|
-
|
|
12203
|
-
|
|
12204
|
-
|
|
12205
|
-
|
|
12206
|
-
|
|
12207
|
-
|
|
12208
|
-
|
|
12209
|
-
|
|
12300
|
+
if (startRecordingPromise)
|
|
12301
|
+
return startRecordingPromise;
|
|
12302
|
+
const generation = ++captureGeneration;
|
|
12303
|
+
const activeCapture = ensureCapture();
|
|
12304
|
+
const operation = (async () => {
|
|
12305
|
+
try {
|
|
12306
|
+
state = {
|
|
12307
|
+
...state,
|
|
12308
|
+
recordingError: null
|
|
12309
|
+
};
|
|
12310
|
+
notify();
|
|
12311
|
+
await activeCapture.start();
|
|
12312
|
+
if (disposed || generation !== captureGeneration || capture !== activeCapture) {
|
|
12313
|
+
activeCapture.stop();
|
|
12314
|
+
return;
|
|
12315
|
+
}
|
|
12316
|
+
state = {
|
|
12317
|
+
...state,
|
|
12318
|
+
isRecording: true
|
|
12319
|
+
};
|
|
12320
|
+
notify();
|
|
12321
|
+
} catch (error) {
|
|
12322
|
+
activeCapture.stop();
|
|
12323
|
+
if (generation !== captureGeneration || capture !== activeCapture) {
|
|
12324
|
+
throw error;
|
|
12325
|
+
}
|
|
12326
|
+
capture = null;
|
|
12327
|
+
state = {
|
|
12328
|
+
...state,
|
|
12329
|
+
isRecording: false,
|
|
12330
|
+
recordingError: error instanceof Error ? error.message : String(error)
|
|
12331
|
+
};
|
|
12332
|
+
notify();
|
|
12333
|
+
throw error;
|
|
12334
|
+
}
|
|
12335
|
+
})();
|
|
12336
|
+
const pending = operation.finally(() => {
|
|
12337
|
+
if (startRecordingPromise === pending)
|
|
12338
|
+
startRecordingPromise = null;
|
|
12339
|
+
});
|
|
12340
|
+
startRecordingPromise = pending;
|
|
12341
|
+
return startRecordingPromise;
|
|
12210
12342
|
};
|
|
12211
12343
|
const close = (reason) => {
|
|
12344
|
+
disposed = true;
|
|
12212
12345
|
unsubscribeStream();
|
|
12213
12346
|
stopRecording();
|
|
12214
12347
|
stream.close(reason);
|
|
12215
12348
|
};
|
|
12216
12349
|
const disconnect = () => {
|
|
12350
|
+
disposed = true;
|
|
12217
12351
|
unsubscribeStream();
|
|
12218
12352
|
stopRecording();
|
|
12219
12353
|
stream.disconnect();
|