@kaltura-sdk/avatars 1.21.1 → 1.21.3-hotfix.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/index.esm.js
CHANGED
|
@@ -6170,12 +6170,24 @@ const clientConfigurationSchema = z$1.object({
|
|
|
6170
6170
|
isWebSearchEnabled: z$1.boolean(),
|
|
6171
6171
|
isScreenShareEnabled: z$1.boolean(),
|
|
6172
6172
|
isCameraAnalysisEnabled: z$1.boolean(),
|
|
6173
|
-
youtubeUrl: z$1.string(),
|
|
6174
|
-
audioMode: z$1.boolean(),
|
|
6175
|
-
phoneMode: z$1.boolean(),
|
|
6176
6173
|
initialHtml: z$1.string(),
|
|
6177
6174
|
visualPhotos: z$1.array(z$1.unknown()),
|
|
6178
|
-
visualVideos: z$1.array(z$1.unknown())
|
|
6175
|
+
visualVideos: z$1.array(z$1.unknown()),
|
|
6176
|
+
youtubeUrl: z$1.optional(z$1.string()),
|
|
6177
|
+
audioMode: z$1.optional(z$1.boolean()),
|
|
6178
|
+
phoneMode: z$1.optional(z$1.boolean()),
|
|
6179
|
+
forwardLoopMode: z$1.optional(z$1.boolean()),
|
|
6180
|
+
imaginativeAiMode: z$1.optional(z$1.boolean()),
|
|
6181
|
+
interruptionsEnabled: z$1.optional(z$1.boolean()),
|
|
6182
|
+
pauseConversationEnabled: z$1.optional(z$1.boolean()),
|
|
6183
|
+
showTranscription: z$1.optional(z$1.boolean()),
|
|
6184
|
+
shouldAggregateCurrentTurn: z$1.optional(z$1.boolean()),
|
|
6185
|
+
agentPersonaName: z$1.optional(z$1.nullable(z$1.string())),
|
|
6186
|
+
userName: z$1.optional(z$1.nullable(z$1.string())),
|
|
6187
|
+
asr: z$1.optional(z$1.object({
|
|
6188
|
+
provider: z$1.string(),
|
|
6189
|
+
language: z$1.string()
|
|
6190
|
+
}))
|
|
6179
6191
|
})
|
|
6180
6192
|
});
|
|
6181
6193
|
const joinCompleteSchema = z$1.object({});
|
|
@@ -23829,6 +23841,11 @@ const avatarConnectionMachine = setup({
|
|
|
23829
23841
|
});
|
|
23830
23842
|
|
|
23831
23843
|
const ACTIVE_SESSION_STORE_ID = 'agenticExperienceAvatar';
|
|
23844
|
+
const PROBE_STORE_ID = 'agenticExperienceAvatarProbe';
|
|
23845
|
+
const HEARTBEAT_INTERVAL_MS = 2000;
|
|
23846
|
+
const HEARTBEAT_STALE_AFTER_MS = 6000;
|
|
23847
|
+
// A live owner answers within one postMessage round trip; this only has to outlast a busy main thread.
|
|
23848
|
+
const PROBE_TIMEOUT_MS = 1000;
|
|
23832
23849
|
function createId() {
|
|
23833
23850
|
return typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
23834
23851
|
}
|
|
@@ -23837,18 +23854,29 @@ function createId() {
|
|
|
23837
23854
|
* just overwriting the shared record: the previous owner sees an id that is
|
|
23838
23855
|
* no longer its own and ends its own call (a close callback could not cross
|
|
23839
23856
|
* frames).
|
|
23857
|
+
*
|
|
23858
|
+
* `pagehide` can't be trusted to release (crashed/frozen tabs, an iframe the
|
|
23859
|
+
* host removes while the avatar is mid-call), so the owner also refreshes
|
|
23860
|
+
* `heartbeatAt` and everyone else ignores a record that stopped refreshing.
|
|
23861
|
+
* `verifyConflictOwner()` shortcuts that wait: a removed iframe can't answer a ping.
|
|
23840
23862
|
*/
|
|
23841
23863
|
class ActiveSessionCoordinator {
|
|
23842
23864
|
constructor(_options) {
|
|
23843
23865
|
this._options = _options;
|
|
23844
23866
|
this.id = createId();
|
|
23845
23867
|
this._owns = false;
|
|
23868
|
+
// Heartbeats change every record, so only `id`/`preventDisconnections` count as a change.
|
|
23846
23869
|
this._conflict = new TinyDataStore({
|
|
23847
23870
|
initialValue: null,
|
|
23848
|
-
compare:
|
|
23871
|
+
compare: (a, b) => (a === null || a === void 0 ? void 0 : a.id) === (b === null || b === void 0 ? void 0 : b.id) && (a === null || a === void 0 ? void 0 : a.preventDisconnections) === (b === null || b === void 0 ? void 0 : b.preventDisconnections)
|
|
23849
23872
|
});
|
|
23873
|
+
// Owners that failed a probe, keyed to the heartbeat they were last seen with; a newer heartbeat revives them.
|
|
23874
|
+
this._abandoned = new Map();
|
|
23875
|
+
this._pendingProbe = null;
|
|
23850
23876
|
this._win = window;
|
|
23851
23877
|
this._onPageHide = () => this.release();
|
|
23878
|
+
this._heartbeatTimer = null;
|
|
23879
|
+
this._expiryTimer = null;
|
|
23852
23880
|
this._logger = _options.logger;
|
|
23853
23881
|
this._global = getOrCreateGlobalTinyDataStore({
|
|
23854
23882
|
id: ACTIVE_SESSION_STORE_ID,
|
|
@@ -23856,6 +23884,14 @@ class ActiveSessionCoordinator {
|
|
|
23856
23884
|
logger: _options.logger
|
|
23857
23885
|
});
|
|
23858
23886
|
this._unsubscribe = this._global.onChanges(value => this._handle(value));
|
|
23887
|
+
this._probe = getOrCreateGlobalTinyDataStore({
|
|
23888
|
+
id: PROBE_STORE_ID,
|
|
23889
|
+
initialValue: null,
|
|
23890
|
+
logger: _options.logger
|
|
23891
|
+
});
|
|
23892
|
+
this._unsubscribeProbe = this._probe.onChanges(probe => this._handleProbe(probe), {
|
|
23893
|
+
replayLastValue: false
|
|
23894
|
+
});
|
|
23859
23895
|
// A closed iframe must not leave the page locked.
|
|
23860
23896
|
this._win.addEventListener('pagehide', this._onPageHide);
|
|
23861
23897
|
}
|
|
@@ -23875,25 +23911,57 @@ class ActiveSessionCoordinator {
|
|
|
23875
23911
|
preventDisconnections
|
|
23876
23912
|
}
|
|
23877
23913
|
});
|
|
23878
|
-
this.
|
|
23879
|
-
|
|
23880
|
-
preventDisconnections
|
|
23881
|
-
}));
|
|
23914
|
+
this._publish(preventDisconnections);
|
|
23915
|
+
this._startHeartbeat();
|
|
23882
23916
|
}
|
|
23883
23917
|
/** Republishes the flag only while this avatar owns the call; never claims an idle one */
|
|
23884
23918
|
updateFlag(preventDisconnections) {
|
|
23885
23919
|
var _a, _b;
|
|
23886
23920
|
if (this._owns && ((_a = this._global.getData()) === null || _a === void 0 ? void 0 : _a.id) === this.id && ((_b = this._global.getData()) === null || _b === void 0 ? void 0 : _b.preventDisconnections) !== preventDisconnections) {
|
|
23887
|
-
this.
|
|
23888
|
-
id: this.id,
|
|
23889
|
-
preventDisconnections
|
|
23890
|
-
}));
|
|
23921
|
+
this._publish(preventDisconnections);
|
|
23891
23922
|
}
|
|
23892
23923
|
}
|
|
23924
|
+
/**
|
|
23925
|
+
* Pings the avatar that owns the call. No answer within `PROBE_TIMEOUT_MS`
|
|
23926
|
+
* (removed iframe, crashed tab) marks it abandoned so `conflict` clears
|
|
23927
|
+
* instead of waiting for its heartbeat to go stale.
|
|
23928
|
+
*/
|
|
23929
|
+
verifyConflictOwner() {
|
|
23930
|
+
const record = this._global.getData();
|
|
23931
|
+
if (!record || record.id === this.id || this._pendingProbe) {
|
|
23932
|
+
return;
|
|
23933
|
+
}
|
|
23934
|
+
const nonce = createId();
|
|
23935
|
+
const timer = setTimeout(() => {
|
|
23936
|
+
var _a;
|
|
23937
|
+
this._pendingProbe = null;
|
|
23938
|
+
const current = this._global.getData();
|
|
23939
|
+
// A newer heartbeat in the meantime proves the owner is alive.
|
|
23940
|
+
if ((current === null || current === void 0 ? void 0 : current.id) === record.id && current.heartbeatAt === record.heartbeatAt) {
|
|
23941
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.log('active avatar session owner did not answer, treating it as abandoned', {
|
|
23942
|
+
data: {
|
|
23943
|
+
id: record.id
|
|
23944
|
+
}
|
|
23945
|
+
});
|
|
23946
|
+
this._abandoned.set(record.id, record.heartbeatAt);
|
|
23947
|
+
this._handle(current);
|
|
23948
|
+
}
|
|
23949
|
+
}, PROBE_TIMEOUT_MS);
|
|
23950
|
+
this._pendingProbe = {
|
|
23951
|
+
nonce,
|
|
23952
|
+
timer
|
|
23953
|
+
};
|
|
23954
|
+
this._probe.setData(() => ({
|
|
23955
|
+
nonce,
|
|
23956
|
+
ownerId: record.id,
|
|
23957
|
+
reply: false
|
|
23958
|
+
}));
|
|
23959
|
+
}
|
|
23893
23960
|
/** Clears the record only if it is still ours, so a taken-over avatar can't wipe the new owner */
|
|
23894
23961
|
release() {
|
|
23895
23962
|
var _a, _b;
|
|
23896
23963
|
this._owns = false;
|
|
23964
|
+
this._stopHeartbeat();
|
|
23897
23965
|
if (((_a = this._global.getData()) === null || _a === void 0 ? void 0 : _a.id) === this.id) {
|
|
23898
23966
|
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.log('releasing the active avatar session', {
|
|
23899
23967
|
data: {
|
|
@@ -23906,15 +23974,85 @@ class ActiveSessionCoordinator {
|
|
|
23906
23974
|
dispose() {
|
|
23907
23975
|
this.release();
|
|
23908
23976
|
this._unsubscribe();
|
|
23977
|
+
this._unsubscribeProbe();
|
|
23978
|
+
this._clearExpiryTimer();
|
|
23979
|
+
this._clearPendingProbe();
|
|
23909
23980
|
this._win.removeEventListener('pagehide', this._onPageHide);
|
|
23910
23981
|
}
|
|
23911
|
-
|
|
23982
|
+
_publish(preventDisconnections) {
|
|
23983
|
+
this._global.setData(() => ({
|
|
23984
|
+
id: this.id,
|
|
23985
|
+
preventDisconnections,
|
|
23986
|
+
heartbeatAt: Date.now()
|
|
23987
|
+
}));
|
|
23988
|
+
}
|
|
23989
|
+
_startHeartbeat() {
|
|
23990
|
+
if (this._heartbeatTimer) {
|
|
23991
|
+
return;
|
|
23992
|
+
}
|
|
23993
|
+
this._heartbeatTimer = setInterval(() => {
|
|
23994
|
+
const current = this._global.getData();
|
|
23995
|
+
if (this._owns && (current === null || current === void 0 ? void 0 : current.id) === this.id) {
|
|
23996
|
+
this._publish(current.preventDisconnections);
|
|
23997
|
+
}
|
|
23998
|
+
}, HEARTBEAT_INTERVAL_MS);
|
|
23999
|
+
}
|
|
24000
|
+
_stopHeartbeat() {
|
|
24001
|
+
if (this._heartbeatTimer) {
|
|
24002
|
+
clearInterval(this._heartbeatTimer);
|
|
24003
|
+
this._heartbeatTimer = null;
|
|
24004
|
+
}
|
|
24005
|
+
}
|
|
24006
|
+
_clearPendingProbe() {
|
|
24007
|
+
if (this._pendingProbe) {
|
|
24008
|
+
clearTimeout(this._pendingProbe.timer);
|
|
24009
|
+
this._pendingProbe = null;
|
|
24010
|
+
}
|
|
24011
|
+
}
|
|
24012
|
+
_handleProbe(probe) {
|
|
23912
24013
|
var _a;
|
|
23913
|
-
|
|
24014
|
+
if (!probe) {
|
|
24015
|
+
return;
|
|
24016
|
+
}
|
|
24017
|
+
if (!probe.reply && probe.ownerId === this.id && this._owns) {
|
|
24018
|
+
this._probe.setData(() => Object.assign(Object.assign({}, probe), {
|
|
24019
|
+
reply: true
|
|
24020
|
+
}));
|
|
24021
|
+
} else if (probe.reply && probe.nonce === ((_a = this._pendingProbe) === null || _a === void 0 ? void 0 : _a.nonce)) {
|
|
24022
|
+
this._clearPendingProbe();
|
|
24023
|
+
}
|
|
24024
|
+
}
|
|
24025
|
+
_clearExpiryTimer() {
|
|
24026
|
+
if (this._expiryTimer) {
|
|
24027
|
+
clearTimeout(this._expiryTimer);
|
|
24028
|
+
this._expiryTimer = null;
|
|
24029
|
+
}
|
|
24030
|
+
}
|
|
24031
|
+
_handle(value) {
|
|
24032
|
+
var _a, _b, _c;
|
|
24033
|
+
this._clearExpiryTimer();
|
|
24034
|
+
// A record without `heartbeatAt` (older bundle in another frame) never expires.
|
|
24035
|
+
const staleIn = value && typeof value.heartbeatAt === 'number' ? value.heartbeatAt + HEARTBEAT_STALE_AFTER_MS - Date.now() : Infinity;
|
|
24036
|
+
const isAbandoned = !!value && value.heartbeatAt !== undefined && value.heartbeatAt <= ((_a = this._abandoned.get(value.id)) !== null && _a !== void 0 ? _a : -Infinity);
|
|
24037
|
+
const isStale = !!value && (staleIn < 0 || isAbandoned);
|
|
24038
|
+
if (isStale && value.id !== this.id) {
|
|
24039
|
+
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.log('ignoring an abandoned active avatar session', {
|
|
24040
|
+
data: {
|
|
24041
|
+
id: value.id,
|
|
24042
|
+
heartbeatAt: value.heartbeatAt
|
|
24043
|
+
}
|
|
24044
|
+
});
|
|
24045
|
+
}
|
|
24046
|
+
const other = value && value.id !== this.id && !isStale ? value : null;
|
|
23914
24047
|
this._conflict.setData(() => other);
|
|
24048
|
+
if (other && Number.isFinite(staleIn)) {
|
|
24049
|
+
// Nothing is written when the owner dies, so re-evaluate once its record would go stale.
|
|
24050
|
+
this._expiryTimer = setTimeout(() => this._handle(this._global.getData()), staleIn + 1);
|
|
24051
|
+
}
|
|
23915
24052
|
if (other && this._owns) {
|
|
23916
24053
|
this._owns = false;
|
|
23917
|
-
|
|
24054
|
+
this._stopHeartbeat();
|
|
24055
|
+
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.log('active avatar session taken over by another avatar', {
|
|
23918
24056
|
data: {
|
|
23919
24057
|
id: this.id,
|
|
23920
24058
|
by: other.id
|
|
@@ -24237,6 +24375,19 @@ class AvatarClientManager extends ActorManager {
|
|
|
24237
24375
|
preventDisconnections: (_b = (_a = this._config.runtimeSettings.multipleAvatarBehavior) === null || _a === void 0 ? void 0 : _a.preventDisconnections) !== null && _b !== void 0 ? _b : false
|
|
24238
24376
|
};
|
|
24239
24377
|
});
|
|
24378
|
+
// The call that held our connect is gone (ended, abandoned): the user already asked to start, so just start.
|
|
24379
|
+
this._activeSession.conflict.onChanges(conflict => {
|
|
24380
|
+
if (conflict || !this._isTakeoverPending.getData()) {
|
|
24381
|
+
return;
|
|
24382
|
+
}
|
|
24383
|
+
const options = this._pendingTakeoverOptions;
|
|
24384
|
+
this._pendingTakeoverOptions = undefined;
|
|
24385
|
+
this._isTakeoverPending.setData(() => false);
|
|
24386
|
+
this._logger.log('Resuming the held connect: the other avatar call is gone');
|
|
24387
|
+
this.connect(options);
|
|
24388
|
+
}, {
|
|
24389
|
+
replayLastValue: false
|
|
24390
|
+
});
|
|
24240
24391
|
this._status.onChanges(status => {
|
|
24241
24392
|
if (status === AvatarConnectionStatus.NotConnected || status === AvatarConnectionStatus.Disconnected || status === AvatarConnectionStatus.Error) {
|
|
24242
24393
|
this._activeSession.release();
|
|
@@ -24459,6 +24610,8 @@ class AvatarClientManager extends ActorManager {
|
|
|
24459
24610
|
});
|
|
24460
24611
|
this._pendingTakeoverOptions = options;
|
|
24461
24612
|
this._isTakeoverPending.setData(() => true);
|
|
24613
|
+
// An owner that is already gone (removed iframe, crashed tab) can't answer, so don't make the user wait out its heartbeat.
|
|
24614
|
+
this._activeSession.verifyConflictOwner();
|
|
24462
24615
|
return;
|
|
24463
24616
|
}
|
|
24464
24617
|
this._activeSession.claim(this._multipleAvatarBehavior.getData().preventDisconnections);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaltura-sdk/avatars",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.3-hotfix.0",
|
|
4
4
|
"author": "kaltura",
|
|
5
5
|
"license": "AGPL-3.0",
|
|
6
6
|
"repository": {
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
"@kaltura-sdk/rtc-avatar": "^1.33.4",
|
|
13
13
|
"@kaltura-sdk/rtc-core": "^1.26.2",
|
|
14
14
|
"@local/genie-shared-utils": "^1.0.0",
|
|
15
|
-
"@unisphere/core": "^1.101.
|
|
16
|
-
"@unisphere/genie-types": "^1.47.
|
|
15
|
+
"@unisphere/core": "^1.101.2",
|
|
16
|
+
"@unisphere/genie-types": "^1.47.1",
|
|
17
17
|
"@unisphere/notifications-core": "^1.25.0",
|
|
18
18
|
"@unisphere/runtime": "^1.98.0",
|
|
19
19
|
"@unisphere/runtime-js": "^1.98.0",
|
|
@@ -14,12 +14,24 @@ export declare const clientConfigurationSchema: z.ZodMiniObject<{
|
|
|
14
14
|
isWebSearchEnabled: z.ZodMiniBoolean<boolean>;
|
|
15
15
|
isScreenShareEnabled: z.ZodMiniBoolean<boolean>;
|
|
16
16
|
isCameraAnalysisEnabled: z.ZodMiniBoolean<boolean>;
|
|
17
|
-
youtubeUrl: z.ZodMiniString<string>;
|
|
18
|
-
audioMode: z.ZodMiniBoolean<boolean>;
|
|
19
|
-
phoneMode: z.ZodMiniBoolean<boolean>;
|
|
20
17
|
initialHtml: z.ZodMiniString<string>;
|
|
21
18
|
visualPhotos: z.ZodMiniArray<z.ZodMiniUnknown>;
|
|
22
19
|
visualVideos: z.ZodMiniArray<z.ZodMiniUnknown>;
|
|
20
|
+
youtubeUrl: z.ZodMiniOptional<z.ZodMiniString<string>>;
|
|
21
|
+
audioMode: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
22
|
+
phoneMode: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
23
|
+
forwardLoopMode: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
24
|
+
imaginativeAiMode: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
25
|
+
interruptionsEnabled: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
26
|
+
pauseConversationEnabled: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
27
|
+
showTranscription: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
28
|
+
shouldAggregateCurrentTurn: z.ZodMiniOptional<z.ZodMiniBoolean<boolean>>;
|
|
29
|
+
agentPersonaName: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
30
|
+
userName: z.ZodMiniOptional<z.ZodMiniNullable<z.ZodMiniString<string>>>;
|
|
31
|
+
asr: z.ZodMiniOptional<z.ZodMiniObject<{
|
|
32
|
+
provider: z.ZodMiniString<string>;
|
|
33
|
+
language: z.ZodMiniString<string>;
|
|
34
|
+
}, z.core.$strip>>;
|
|
23
35
|
}, z.core.$strip>;
|
|
24
36
|
}, z.core.$strip>;
|
|
25
37
|
export declare const joinCompleteSchema: z.ZodMiniObject<{}, z.core.$strip>;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { UnisphereLoggerType } from '@unisphere/core';
|
|
2
2
|
import type { ActiveAvatarSession } from '@unisphere/genie-types';
|
|
3
3
|
export declare const ACTIVE_SESSION_STORE_ID = "agenticExperienceAvatar";
|
|
4
|
+
export declare const HEARTBEAT_INTERVAL_MS = 2000;
|
|
5
|
+
export declare const HEARTBEAT_STALE_AFTER_MS = 6000;
|
|
6
|
+
export declare const PROBE_TIMEOUT_MS = 1000;
|
|
4
7
|
/**
|
|
5
8
|
* Page-wide "who owns the call" record for one avatar manager. Takeover is
|
|
6
9
|
* just overwriting the shared record: the previous owner sees an id that is
|
|
7
10
|
* no longer its own and ends its own call (a close callback could not cross
|
|
8
11
|
* frames).
|
|
12
|
+
*
|
|
13
|
+
* `pagehide` can't be trusted to release (crashed/frozen tabs, an iframe the
|
|
14
|
+
* host removes while the avatar is mid-call), so the owner also refreshes
|
|
15
|
+
* `heartbeatAt` and everyone else ignores a record that stopped refreshing.
|
|
16
|
+
* `verifyConflictOwner()` shortcuts that wait: a removed iframe can't answer a ping.
|
|
9
17
|
*/
|
|
10
18
|
export declare class ActiveSessionCoordinator {
|
|
11
19
|
private readonly _options;
|
|
@@ -13,10 +21,16 @@ export declare class ActiveSessionCoordinator {
|
|
|
13
21
|
private _owns;
|
|
14
22
|
private readonly _conflict;
|
|
15
23
|
private readonly _global;
|
|
24
|
+
private readonly _probe;
|
|
16
25
|
private readonly _unsubscribe;
|
|
26
|
+
private readonly _unsubscribeProbe;
|
|
27
|
+
private readonly _abandoned;
|
|
28
|
+
private _pendingProbe;
|
|
17
29
|
private readonly _win;
|
|
18
30
|
private readonly _onPageHide;
|
|
19
31
|
private readonly _logger?;
|
|
32
|
+
private _heartbeatTimer;
|
|
33
|
+
private _expiryTimer;
|
|
20
34
|
constructor(_options: {
|
|
21
35
|
onTakenOver: () => void;
|
|
22
36
|
logger?: UnisphereLoggerType;
|
|
@@ -26,8 +40,20 @@ export declare class ActiveSessionCoordinator {
|
|
|
26
40
|
claim(preventDisconnections: boolean): void;
|
|
27
41
|
/** Republishes the flag only while this avatar owns the call; never claims an idle one */
|
|
28
42
|
updateFlag(preventDisconnections: boolean): void;
|
|
43
|
+
/**
|
|
44
|
+
* Pings the avatar that owns the call. No answer within `PROBE_TIMEOUT_MS`
|
|
45
|
+
* (removed iframe, crashed tab) marks it abandoned so `conflict` clears
|
|
46
|
+
* instead of waiting for its heartbeat to go stale.
|
|
47
|
+
*/
|
|
48
|
+
verifyConflictOwner(): void;
|
|
29
49
|
/** Clears the record only if it is still ours, so a taken-over avatar can't wipe the new owner */
|
|
30
50
|
release(): void;
|
|
31
51
|
dispose(): void;
|
|
52
|
+
private _publish;
|
|
53
|
+
private _startHeartbeat;
|
|
54
|
+
private _stopHeartbeat;
|
|
55
|
+
private _clearPendingProbe;
|
|
56
|
+
private _handleProbe;
|
|
57
|
+
private _clearExpiryTimer;
|
|
32
58
|
private _handle;
|
|
33
59
|
}
|