@q9labsai/chalk-client 5.2.5 → 5.3.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/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/dist/{chunk-FD25AMLG.js → chunk-3TGVTP6Z.js} +364 -79
- package/dist/{chunk-BUNSEDXL.js → chunk-DHUHTIU7.js} +213 -64
- package/dist/{chunk-UMAE5GCV.js → chunk-GOSWSXTH.js} +21 -1
- package/dist/{chunk-LKNT6XFD.js → chunk-O26BHHMI.js} +1 -1
- package/dist/{chunk-5YPQNMJE.js → chunk-XSB2GDRW.js} +32 -8
- package/dist/client.d.ts +2 -0
- package/dist/connection/dependencies.d.ts +2 -1
- package/dist/connection/production.d.ts +1 -1
- package/dist/effect.js +4 -4
- package/dist/generated/http-api.d.ts +2 -0
- package/dist/generated/schemas.d.ts +3 -0
- package/dist/generated/sync.d.ts +45 -0
- package/dist/index.js +5 -5
- package/dist/media/client.d.ts +6 -5
- package/dist/media/index.js +1 -1
- package/dist/media/plane.d.ts +9 -3
- package/dist/media/receive-stall.d.ts +43 -0
- package/dist/media/types.d.ts +4 -1
- package/dist/public-invites/index.js +2 -2
- package/dist/server/index.cjs +1 -0
- package/dist/server/index.js +1 -0
- package/dist/server/types.d.cts +2 -0
- package/dist/server/types.d.ts +2 -0
- package/dist/space-client/promise-facade.d.ts +5 -5
- package/dist/space-client/same-identity.d.ts +6 -0
- package/dist/space-client/types.d.ts +6 -0
- package/dist/sync/index.js +1 -1
- package/dist/sync/v1-types.d.ts +6 -0
- package/package.json +2 -1
|
@@ -6,11 +6,11 @@ import {
|
|
|
6
6
|
describeMediaCaptureError,
|
|
7
7
|
grantedMediaFromStream,
|
|
8
8
|
markMediaFailure
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-3TGVTP6Z.js";
|
|
10
10
|
import {
|
|
11
11
|
AccessGrantError,
|
|
12
12
|
requireParsedAccessGrant
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-GOSWSXTH.js";
|
|
14
14
|
import {
|
|
15
15
|
ChatAttachmentMimeTypeSchema,
|
|
16
16
|
ChatReactionSetFrameSchema,
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
formatSyncConnectDiagnostic,
|
|
25
25
|
scheduleSyncDialDeadline,
|
|
26
26
|
syncConnectFailureCode
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-XSB2GDRW.js";
|
|
28
28
|
|
|
29
29
|
// src/access/manager.ts
|
|
30
30
|
import { Clock, Context, Data, Deferred, Effect, Layer, SynchronizedRef } from "effect";
|
|
@@ -2699,6 +2699,7 @@ function createSFUConnectionMediaClient(client) {
|
|
|
2699
2699
|
function bindConnectionMediaClient(client, restart) {
|
|
2700
2700
|
return {
|
|
2701
2701
|
setLocalPublicationTarget: client.setLocalPublicationTarget.bind(client),
|
|
2702
|
+
...client.pauseLocalCapture ? { pauseLocalCapture: client.pauseLocalCapture.bind(client) } : {},
|
|
2702
2703
|
...client.setLocalSourceIntent ? { setLocalSourceIntent: client.setLocalSourceIntent.bind(client) } : {},
|
|
2703
2704
|
...client.closeForcedLocalPublication ? { closeForcedLocalPublication: client.closeForcedLocalPublication.bind(client) } : {},
|
|
2704
2705
|
observeLocalPublications: client.observeLocalPublications.bind(client),
|
|
@@ -2852,7 +2853,7 @@ var ConnectionDiagnostics = class {
|
|
|
2852
2853
|
// src/connection/lifecycle.ts
|
|
2853
2854
|
var MEDIA_RECOVERY_TIMEOUT_MS = 6e4;
|
|
2854
2855
|
var START_TIMEOUT_MS = 3e4;
|
|
2855
|
-
var LEAVE_TIMEOUT_MS =
|
|
2856
|
+
var LEAVE_TIMEOUT_MS = 4e4;
|
|
2856
2857
|
var RECOVERY_BUDGET_MS = MEDIA_RECOVERY_TIMEOUT_MS;
|
|
2857
2858
|
var MAX_RECOVERY_ATTEMPTS = 3;
|
|
2858
2859
|
var REFRESH_RETRY_MS = 5e3;
|
|
@@ -2998,7 +2999,7 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
|
|
|
2998
2999
|
yield* publish();
|
|
2999
3000
|
});
|
|
3000
3001
|
const enqueue = (effect, join = false) => Effect2.gen(function* () {
|
|
3001
|
-
if (model.closed) return yield* Effect2.fail(lifecycleFailure("invalid_state", false, "The Connection scope is closed"));
|
|
3002
|
+
if (model.closed) return yield* Effect2.fail(lifecycleFailure("invalid_state", false, "The Connection scope is closed. Rejoin the call."));
|
|
3002
3003
|
const deferred = yield* Deferred2.make();
|
|
3003
3004
|
yield* Queue.offer(queue, { effect, deferred, join });
|
|
3004
3005
|
return yield* Deferred2.await(deferred);
|
|
@@ -3290,6 +3291,12 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
|
|
|
3290
3291
|
if (model.failure) return yield* Effect2.fail(lifecycleFailure("leave_unconfirmed", false, model.failure.message));
|
|
3291
3292
|
return;
|
|
3292
3293
|
}
|
|
3294
|
+
yield* Effect2.sync(() => {
|
|
3295
|
+
for (const { track } of model.media?.getSnapshot().localTracks ?? []) {
|
|
3296
|
+
track.enabled = false;
|
|
3297
|
+
track.stop();
|
|
3298
|
+
}
|
|
3299
|
+
});
|
|
3293
3300
|
yield* transition("leaving");
|
|
3294
3301
|
const confirmed = yield* leaveIsConfirmed();
|
|
3295
3302
|
yield* stopPorts();
|
|
@@ -3328,7 +3335,7 @@ var makeConnectionLifecycleLayerFromServices = (options) => Layer3.effect(
|
|
|
3328
3335
|
yield* scheduleRefresh();
|
|
3329
3336
|
})
|
|
3330
3337
|
);
|
|
3331
|
-
const requireOpen = Effect2.suspend(() => model.closed ? Effect2.fail(lifecycleFailure("invalid_state", false, "The Connection scope is closed")) : Effect2.void);
|
|
3338
|
+
const requireOpen = Effect2.suspend(() => model.closed ? Effect2.fail(lifecycleFailure("invalid_state", false, "The Connection scope is closed. Rejoin the call.")) : Effect2.void);
|
|
3332
3339
|
const withFreshAccess = (operation) => Effect2.gen(function* () {
|
|
3333
3340
|
yield* requireOpen;
|
|
3334
3341
|
if (yield* access.requiresRefresh) {
|
|
@@ -4876,6 +4883,20 @@ function chatError(message) {
|
|
|
4876
4883
|
return new SpaceClientError({ code: "chat.payload_invalid", recoverable: false, message });
|
|
4877
4884
|
}
|
|
4878
4885
|
|
|
4886
|
+
// src/space-client/same-identity.ts
|
|
4887
|
+
function otherDevices(participants, participantId, ownIdentityId) {
|
|
4888
|
+
const identityId = ownIdentityId ?? participants.find((item) => item.participantId === participantId)?.identityId;
|
|
4889
|
+
return identityId ? participants.filter((item) => item.participantId !== participantId && item.identityId === identityId) : [];
|
|
4890
|
+
}
|
|
4891
|
+
function otherDeviceHasMicrophone(snapshot, participants) {
|
|
4892
|
+
if (snapshot.control?.status === "ended") return false;
|
|
4893
|
+
const admitted = snapshot.control ? new Set(snapshot.control.participants.map((item) => item.participantId)) : null;
|
|
4894
|
+
const devices = new Set(
|
|
4895
|
+
otherDevices(participants, snapshot.participantId, snapshot.participantIdentityId).filter((item) => !admitted || admitted.has(item.participantId)).map((item) => item.participantId)
|
|
4896
|
+
);
|
|
4897
|
+
return snapshot.media?.items.some((item) => devices.has(item.participantId) && item.source === "microphone" && item.enabled) ?? false;
|
|
4898
|
+
}
|
|
4899
|
+
|
|
4879
4900
|
// src/space-client/media-controller.ts
|
|
4880
4901
|
import { Clock as Clock3, Context as Context6, Deferred as Deferred3, Effect as Effect5, Exit as Exit2, Layer as Layer6, Semaphore } from "effect";
|
|
4881
4902
|
|
|
@@ -4949,7 +4970,7 @@ var MediaProjectionMonitor = class {
|
|
|
4949
4970
|
var MEDIA_SOURCES = ["microphone", "camera", "screen"];
|
|
4950
4971
|
var MEDIA_READY_TIMEOUT_MS = 1e4;
|
|
4951
4972
|
function sourceWorker(enabled) {
|
|
4952
|
-
return { desired: { enabled, revision: 0, controlRevision: -1, previousEnabled: enabled }, result: { revision: 0, status: "confirmed" }, initialRevision: 0, localEnabled: enabled, pause: null, serverSequence: -1, forcedRevision: null, pausedPublicationId: null };
|
|
4973
|
+
return { desired: { enabled, revision: 0, controlRevision: -1, previousEnabled: enabled }, result: { revision: 0, status: "confirmed" }, initialRevision: 0, localEnabled: enabled, pause: null, serverSequence: -1, forcedRevision: null, pausedPublicationId: null, capturePause: "none" };
|
|
4953
4974
|
}
|
|
4954
4975
|
var MediaControllerService = class extends Context6.Service()("@chalk/client/MediaController") {
|
|
4955
4976
|
};
|
|
@@ -4992,6 +5013,7 @@ var MediaControllerRuntime = class {
|
|
|
4992
5013
|
#tracks = /* @__PURE__ */ new Map();
|
|
4993
5014
|
#sources = { microphone: sourceWorker(true), camera: sourceWorker(true), screen: sourceWorker(false) };
|
|
4994
5015
|
#syncLive = false;
|
|
5016
|
+
#disposed = false;
|
|
4995
5017
|
#recovering = null;
|
|
4996
5018
|
#pendingSources = /* @__PURE__ */ new Map();
|
|
4997
5019
|
#pendingCaptures = /* @__PURE__ */ new Set();
|
|
@@ -5010,6 +5032,7 @@ var MediaControllerRuntime = class {
|
|
|
5010
5032
|
#unsubscribePorts = null;
|
|
5011
5033
|
#microphoneWatch = null;
|
|
5012
5034
|
#projection;
|
|
5035
|
+
#microphoneMutedByOtherDevice = false;
|
|
5013
5036
|
constructor(connection, store, selection, gates, now, fork) {
|
|
5014
5037
|
this.#connection = connection;
|
|
5015
5038
|
this.#store = store;
|
|
@@ -5024,7 +5047,7 @@ var MediaControllerRuntime = class {
|
|
|
5024
5047
|
changed: () => this.#publish(),
|
|
5025
5048
|
report: (event) => connection.recordTransportDiagnostic(event),
|
|
5026
5049
|
pause: (source, revision) => {
|
|
5027
|
-
if (this.#ports) this.#pausePublication(this.#ports, source, revision);
|
|
5050
|
+
if (this.#ports) this.#pausePublication(this.#ports, source, revision, "projection_mismatch");
|
|
5028
5051
|
}
|
|
5029
5052
|
});
|
|
5030
5053
|
this.#unsubscribeConnection = connection.subscribe(() => this.#handleConnectionChange());
|
|
@@ -5035,7 +5058,10 @@ var MediaControllerRuntime = class {
|
|
|
5035
5058
|
if (intent.microphone !== void 0) this.#recordIntent("microphone", intent.microphone);
|
|
5036
5059
|
if (intent.camera !== void 0) this.#recordIntent("camera", intent.camera);
|
|
5037
5060
|
}
|
|
5038
|
-
setMicrophoneEnabled = (enabled) =>
|
|
5061
|
+
setMicrophoneEnabled = (enabled) => Effect5.sync(() => {
|
|
5062
|
+
this.#microphoneMutedByOtherDevice = false;
|
|
5063
|
+
this.#publish();
|
|
5064
|
+
}).pipe(Effect5.andThen(this.#set("microphone", enabled)));
|
|
5039
5065
|
setCameraEnabled = (enabled) => this.#set("camera", enabled);
|
|
5040
5066
|
captureCameraPreview = () => this.#cameraSource("preview").pipe(Effect5.mapError(normalizeMediaError));
|
|
5041
5067
|
setScreenShareEnabled = (enabled, options) => enabled ? this.#mediaAction(true, this.#startScreen(options)) : this.#stopScreen();
|
|
@@ -5130,8 +5156,9 @@ var MediaControllerRuntime = class {
|
|
|
5130
5156
|
this.#fork(this.#refreshDevices().pipe(Effect5.ignore));
|
|
5131
5157
|
}
|
|
5132
5158
|
dispose() {
|
|
5159
|
+
this.#disposed = true;
|
|
5133
5160
|
this.#projection.reset();
|
|
5134
|
-
for (const check of this.#readinessChecks) check(normalizeMediaError(cancelledCaptureError()));
|
|
5161
|
+
for (const check of this.#readinessChecks) check(normalizeMediaError(this.#cancelledCaptureError()));
|
|
5135
5162
|
this.#cancelCaptures();
|
|
5136
5163
|
for (const unsubscribe of [this.#unsubscribeMedia, this.#unsubscribeRequests, this.#unsubscribePause, this.#unsubscribeSync]) unsubscribe?.();
|
|
5137
5164
|
this.#unsubscribeConnection?.();
|
|
@@ -5161,7 +5188,7 @@ var MediaControllerRuntime = class {
|
|
|
5161
5188
|
const check = (failure2) => {
|
|
5162
5189
|
if (failure2) return resume(Effect5.fail(failure2));
|
|
5163
5190
|
if (this.#sources[source].desired.revision !== revision) return resume(Effect5.void);
|
|
5164
|
-
if (ports && !this.#canUsePorts(ports)) return resume(Effect5.fail(normalizeMediaError(cancelledCaptureError())));
|
|
5191
|
+
if (ports && !this.#canUsePorts(ports)) return resume(Effect5.fail(normalizeMediaError(this.#cancelledCaptureError())));
|
|
5165
5192
|
const state = this.#connection.getSnapshot().state;
|
|
5166
5193
|
const phases = [state, this.#ports?.sync.getSnapshot().connection.phase, this.#store.getSnapshot().connection.status];
|
|
5167
5194
|
if (phases.every((phase) => phase === "live")) return resume(Effect5.void);
|
|
@@ -5210,6 +5237,7 @@ var MediaControllerRuntime = class {
|
|
|
5210
5237
|
const controlRevision = Math.max(state.serverSequence, this.#ports?.sync.getSnapshot().control?.revision ?? -1);
|
|
5211
5238
|
const desired = { enabled, revision: state.desired.revision + 1, controlRevision, options, previousEnabled: state.localEnabled };
|
|
5212
5239
|
state.desired = desired;
|
|
5240
|
+
state.capturePause = "none";
|
|
5213
5241
|
if (!enabled) {
|
|
5214
5242
|
state.localEnabled = false;
|
|
5215
5243
|
this.#pauseTracks(source);
|
|
@@ -5219,8 +5247,8 @@ var MediaControllerRuntime = class {
|
|
|
5219
5247
|
}
|
|
5220
5248
|
const ports = this.#ports;
|
|
5221
5249
|
if (ports) {
|
|
5222
|
-
ports.media.setLocalSourceIntent?.(source, false);
|
|
5223
|
-
this.#retainPause(state, this.#beginLocalPause(ports, source));
|
|
5250
|
+
ports.media.setLocalSourceIntent?.(source, false, "user_pause");
|
|
5251
|
+
this.#retainPause(state, this.#beginLocalPause(ports, source, "user_pause"));
|
|
5224
5252
|
}
|
|
5225
5253
|
}
|
|
5226
5254
|
for (const check of this.#readinessChecks) check();
|
|
@@ -5316,11 +5344,11 @@ var MediaControllerRuntime = class {
|
|
|
5316
5344
|
if (state.desired.revision !== intent.revision) return;
|
|
5317
5345
|
state.result = { revision: intent.revision, status: "failed", cause, cleanup: !intent.enabled || !previous };
|
|
5318
5346
|
if (state.result.cleanup) {
|
|
5319
|
-
if (intent.enabled && source !== "screen") this.#pausePublication(ports, source, intent.revision);
|
|
5347
|
+
if (intent.enabled && source !== "screen") this.#pausePublication(ports, source, intent.revision, "enable_failure");
|
|
5320
5348
|
else {
|
|
5321
5349
|
state.localEnabled = false;
|
|
5322
5350
|
this.#pauseTracks(source);
|
|
5323
|
-
ports.media.setLocalSourceIntent?.(source, false);
|
|
5351
|
+
ports.media.setLocalSourceIntent?.(source, false, "enable_failure");
|
|
5324
5352
|
}
|
|
5325
5353
|
}
|
|
5326
5354
|
});
|
|
@@ -5434,7 +5462,7 @@ var MediaControllerRuntime = class {
|
|
|
5434
5462
|
}
|
|
5435
5463
|
#assertCurrentIntent(source, revision, enabling = true) {
|
|
5436
5464
|
const state = this.#sources[source];
|
|
5437
|
-
if (state.desired.revision !== revision || enabling && state.forcedRevision === revision) throw cancelledCaptureError();
|
|
5465
|
+
if (state.desired.revision !== revision || enabling && state.forcedRevision === revision) throw this.#cancelledCaptureError();
|
|
5438
5466
|
}
|
|
5439
5467
|
#hasConsent(source) {
|
|
5440
5468
|
const state = this.#sources[source];
|
|
@@ -5458,19 +5486,36 @@ var MediaControllerRuntime = class {
|
|
|
5458
5486
|
}
|
|
5459
5487
|
}
|
|
5460
5488
|
startInitial(media, stream) {
|
|
5489
|
+
const waitForSync = Effect5.callback((resume) => {
|
|
5490
|
+
const sync = this.#ports?.sync;
|
|
5491
|
+
if (!sync) return resume(Effect5.void);
|
|
5492
|
+
const check = () => {
|
|
5493
|
+
if (sync.getSnapshot().connection.phase === "live" || media.getSnapshot().connection.phase === "stopped") resume(Effect5.void);
|
|
5494
|
+
};
|
|
5495
|
+
const unsubscribe = sync.subscribe(check);
|
|
5496
|
+
const unsubscribeMedia = media.subscribe(check);
|
|
5497
|
+
check();
|
|
5498
|
+
return Effect5.sync(() => {
|
|
5499
|
+
unsubscribe();
|
|
5500
|
+
unsubscribeMedia();
|
|
5501
|
+
});
|
|
5502
|
+
});
|
|
5461
5503
|
const publish = foreign2(async () => {
|
|
5462
5504
|
const revisions = { microphone: this.#sources.microphone.initialRevision, camera: this.#sources.camera.initialRevision };
|
|
5463
|
-
const tracks = stream.getTracks()
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
|
|
5467
|
-
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5505
|
+
const tracks = this.#consentedTracks(stream.getTracks());
|
|
5506
|
+
await media.start(
|
|
5507
|
+
streamFromTracks(tracks),
|
|
5508
|
+
() => Effect5.runPromise(
|
|
5509
|
+
waitForSync.pipe(
|
|
5510
|
+
Effect5.map(() => {
|
|
5511
|
+
const snapshot = this.#ports?.sync.getSnapshot();
|
|
5512
|
+
this.#microphoneMutedByOtherDevice = Boolean(snapshot && this.#hasConsent("microphone") && otherDeviceHasMicrophone(snapshot, this.#store.getSnapshot().participants.roster));
|
|
5513
|
+
if (this.#microphoneMutedByOtherDevice) this.#recordIntent("microphone", false);
|
|
5514
|
+
return streamFromTracks(this.#consentedTracks(tracks));
|
|
5515
|
+
})
|
|
5516
|
+
)
|
|
5517
|
+
)
|
|
5518
|
+
);
|
|
5474
5519
|
for (const source of ["microphone", "camera"]) {
|
|
5475
5520
|
const state = this.#sources[source];
|
|
5476
5521
|
if (state.desired.revision === revisions[source]) state.result = { revision: revisions[source], status: "confirmed" };
|
|
@@ -5478,6 +5523,18 @@ var MediaControllerRuntime = class {
|
|
|
5478
5523
|
});
|
|
5479
5524
|
return this.#serialize("microphone", this.#serialize("camera", publish));
|
|
5480
5525
|
}
|
|
5526
|
+
#consentedTracks(tracks) {
|
|
5527
|
+
return tracks.filter((track) => {
|
|
5528
|
+
const source = track.kind === "audio" ? "microphone" : "camera";
|
|
5529
|
+
const enabled = this.#hasConsent(source);
|
|
5530
|
+
track.enabled = enabled;
|
|
5531
|
+
if (!enabled) {
|
|
5532
|
+
this.#deleteTrack(source);
|
|
5533
|
+
if (source === "microphone") track.stop();
|
|
5534
|
+
}
|
|
5535
|
+
return enabled;
|
|
5536
|
+
});
|
|
5537
|
+
}
|
|
5481
5538
|
#send(ports, source, intent, enabled) {
|
|
5482
5539
|
const command = this.#mediaAction(
|
|
5483
5540
|
enabled,
|
|
@@ -5519,9 +5576,9 @@ var MediaControllerRuntime = class {
|
|
|
5519
5576
|
)
|
|
5520
5577
|
);
|
|
5521
5578
|
}
|
|
5522
|
-
#beginLocalPause(ports, source) {
|
|
5579
|
+
#beginLocalPause(ports, source, cause) {
|
|
5523
5580
|
this.#sources[source].pausedPublicationId = ports.media.getSnapshot().localTracks.find((item) => item.source === source)?.publicationId ?? null;
|
|
5524
|
-
const pause = ports.media.closeForcedLocalPublication ? Promise.resolve().then(() => ports.media.closeForcedLocalPublication(source)) : Effect5.runPromise(this.#localTarget(ports, source, false));
|
|
5581
|
+
const pause = ports.media.closeForcedLocalPublication ? Promise.resolve().then(() => ports.media.closeForcedLocalPublication(source, cause)) : Effect5.runPromise(this.#localTarget(ports, source, false));
|
|
5525
5582
|
void pause.catch(() => void 0);
|
|
5526
5583
|
return pause;
|
|
5527
5584
|
}
|
|
@@ -5594,15 +5651,15 @@ var MediaControllerRuntime = class {
|
|
|
5594
5651
|
return;
|
|
5595
5652
|
}
|
|
5596
5653
|
const state = this.#camera;
|
|
5597
|
-
if (!("owner" in state)) throw cancelledCaptureError();
|
|
5598
|
-
if (state.status === "live" && state.track !== track) throw cancelledCaptureError();
|
|
5654
|
+
if (!("owner" in state)) throw this.#cancelledCaptureError();
|
|
5655
|
+
if (state.status === "live" && state.track !== track) throw this.#cancelledCaptureError();
|
|
5599
5656
|
const onEnded = () => this.#handleCameraEnded(track);
|
|
5600
5657
|
this.#transitionCamera({ status: "live", owner: state.owner, track, publication: this.#ports?.media ?? "initial", onEnded });
|
|
5601
5658
|
}
|
|
5602
5659
|
#handleCameraEnded(track) {
|
|
5603
5660
|
const state = this.#camera;
|
|
5604
5661
|
if (state.status !== "live" || state.track !== track) return;
|
|
5605
|
-
if (this.#ports && state.publication === this.#ports.media) this.#pausePublication(this.#ports, "camera", this.#sources.camera.desired.revision);
|
|
5662
|
+
if (this.#ports && state.publication === this.#ports.media) this.#pausePublication(this.#ports, "camera", this.#sources.camera.desired.revision, "capture_cleanup");
|
|
5606
5663
|
this.#transitionCamera({ status: "ended" });
|
|
5607
5664
|
}
|
|
5608
5665
|
#watchMicrophone(track) {
|
|
@@ -5615,7 +5672,7 @@ var MediaControllerRuntime = class {
|
|
|
5615
5672
|
if (track.muted || track.readyState === "ended") {
|
|
5616
5673
|
track.enabled = false;
|
|
5617
5674
|
const state = this.#sources.microphone;
|
|
5618
|
-
if (this.#ports) this.#pausePublication(this.#ports, "microphone", state.desired.revision);
|
|
5675
|
+
if (this.#ports) this.#pausePublication(this.#ports, "microphone", state.desired.revision, "capture_cleanup");
|
|
5619
5676
|
else {
|
|
5620
5677
|
state.forcedRevision = state.desired.revision;
|
|
5621
5678
|
state.localEnabled = false;
|
|
@@ -5694,7 +5751,7 @@ var MediaControllerRuntime = class {
|
|
|
5694
5751
|
references: 0,
|
|
5695
5752
|
cancel: () => {
|
|
5696
5753
|
abort.abort();
|
|
5697
|
-
Deferred3.doneUnsafe(owner.result, Effect5.fail(cancelledCaptureError()));
|
|
5754
|
+
Deferred3.doneUnsafe(owner.result, Effect5.fail(this.#cancelledCaptureError()));
|
|
5698
5755
|
}
|
|
5699
5756
|
};
|
|
5700
5757
|
this.#transitionCamera({ status: "capturing", owner });
|
|
@@ -5706,13 +5763,13 @@ var MediaControllerRuntime = class {
|
|
|
5706
5763
|
const state = this.#camera;
|
|
5707
5764
|
if (abort.signal.aborted || !("owner" in state) || state.owner !== owner) {
|
|
5708
5765
|
track.stop();
|
|
5709
|
-
throw cancelledCaptureError();
|
|
5766
|
+
throw this.#cancelledCaptureError();
|
|
5710
5767
|
}
|
|
5711
5768
|
const onEnded = () => this.#handleCameraEnded(track);
|
|
5712
5769
|
this.#transitionCamera({ status: "live", owner, track, publication: state.status === "live" ? state.publication : null, onEnded });
|
|
5713
5770
|
if (track.readyState !== "live") {
|
|
5714
5771
|
this.#transitionCamera({ status: "ended" });
|
|
5715
|
-
throw cancelledCaptureError();
|
|
5772
|
+
throw this.#cancelledCaptureError();
|
|
5716
5773
|
}
|
|
5717
5774
|
},
|
|
5718
5775
|
catch: (cause) => cause
|
|
@@ -5735,7 +5792,7 @@ var MediaControllerRuntime = class {
|
|
|
5735
5792
|
if (owner.references === 0 && "owner" in current && current.owner === owner && (current.status !== "live" || current.publication === null)) this.#transitionCamera({ status: "ended" });
|
|
5736
5793
|
};
|
|
5737
5794
|
return Deferred3.await(owner.result).pipe(
|
|
5738
|
-
Effect5.flatMap((track) => track.readyState === "live" ? Effect5.succeed({ track, release }) : Effect5.fail(cancelledCaptureError())),
|
|
5795
|
+
Effect5.flatMap((track) => track.readyState === "live" ? Effect5.succeed({ track, release }) : Effect5.fail(this.#cancelledCaptureError())),
|
|
5739
5796
|
Effect5.onExit(
|
|
5740
5797
|
(exit) => Effect5.sync(() => {
|
|
5741
5798
|
if (Exit2.isFailure(exit)) release();
|
|
@@ -5768,25 +5825,25 @@ var MediaControllerRuntime = class {
|
|
|
5768
5825
|
const cancel = () => {
|
|
5769
5826
|
cancelled = true;
|
|
5770
5827
|
stopStream(captured);
|
|
5771
|
-
Deferred3.doneUnsafe(cancellation, Effect5.fail(cancelledCaptureError()));
|
|
5828
|
+
Deferred3.doneUnsafe(cancellation, Effect5.fail(this.#cancelledCaptureError()));
|
|
5772
5829
|
};
|
|
5773
5830
|
this.#pendingCaptures.add(cancel);
|
|
5774
5831
|
signal?.addEventListener("abort", cancel, { once: true });
|
|
5775
5832
|
if (signal?.aborted) cancel();
|
|
5776
5833
|
return foreign2(async () => {
|
|
5777
|
-
if (cancelled) throw cancelledCaptureError();
|
|
5834
|
+
if (cancelled) throw this.#cancelledCaptureError();
|
|
5778
5835
|
const stream = await operation();
|
|
5779
5836
|
captured = stream;
|
|
5780
5837
|
if (cancelled || this.#ports?.media.getSnapshot().connection.phase === "stopped") {
|
|
5781
5838
|
stopStream(stream);
|
|
5782
|
-
throw cancelledCaptureError();
|
|
5839
|
+
throw this.#cancelledCaptureError();
|
|
5783
5840
|
}
|
|
5784
5841
|
return stream;
|
|
5785
5842
|
}).pipe(
|
|
5786
5843
|
Effect5.mapError(onCaptureError),
|
|
5787
5844
|
Effect5.flatMap(use),
|
|
5788
5845
|
Effect5.raceFirst(Deferred3.await(cancellation)),
|
|
5789
|
-
Effect5.tap(() => cancelled ? Effect5.fail(cancelledCaptureError()) : Effect5.void),
|
|
5846
|
+
Effect5.tap(() => cancelled ? Effect5.fail(this.#cancelledCaptureError()) : Effect5.void),
|
|
5790
5847
|
Effect5.onExit(
|
|
5791
5848
|
(exit) => Effect5.sync(() => {
|
|
5792
5849
|
this.#pendingCaptures.delete(cancel);
|
|
@@ -5860,23 +5917,48 @@ var MediaControllerRuntime = class {
|
|
|
5860
5917
|
const failure2 = isPermissionDenied(input.cause) || captureWasUnavailable ? captureError(input.cause, input.action, input.permissionMessage) : input.cause;
|
|
5861
5918
|
return rollback.pipe(Effect5.andThen(Effect5.sync(() => this.#publish())), Effect5.andThen(Effect5.fail(failure2)));
|
|
5862
5919
|
}
|
|
5920
|
+
#cancelledCaptureError() {
|
|
5921
|
+
return new ConnectionError({ code: "invalid_state", action: null, recoverable: !this.#disposed, message: this.#disposed ? "This media client has been disposed. Rejoin the call." : "Media capture was cancelled before its result could be used" });
|
|
5922
|
+
}
|
|
5863
5923
|
#canUsePorts(ports) {
|
|
5864
5924
|
return this.#ports?.sync === ports.sync && this.#ports.media === ports.media && ports.media.getSnapshot().connection.phase !== "stopped";
|
|
5865
5925
|
}
|
|
5866
5926
|
#assertActivePorts(ports, action) {
|
|
5867
5927
|
if (this.#connection.getSnapshot().state === "live" && this.#canUsePorts(ports)) return;
|
|
5868
|
-
throw new ConnectionError({ code: "invalid_state", action, recoverable:
|
|
5928
|
+
throw new ConnectionError({ code: "invalid_state", action, recoverable: !this.#disposed, message: this.#disposed ? "This media client has been disposed. Rejoin the call." : `${action} belongs to an inactive connection` });
|
|
5869
5929
|
}
|
|
5870
|
-
#pausePublication(ports, source, revision) {
|
|
5930
|
+
#pausePublication(ports, source, revision, cause) {
|
|
5871
5931
|
const state = this.#sources[source];
|
|
5872
5932
|
if (state.desired.revision !== revision || state.forcedRevision === revision || ports.sync.getSnapshot().control?.status === "ended") return;
|
|
5873
5933
|
state.forcedRevision = revision;
|
|
5874
5934
|
state.localEnabled = false;
|
|
5875
5935
|
this.#pauseTracks(source);
|
|
5876
|
-
ports.media.
|
|
5877
|
-
|
|
5936
|
+
if (cause === "capture_cleanup" && source !== "screen" && ports.media.pauseLocalCapture) {
|
|
5937
|
+
state.capturePause = "pending";
|
|
5938
|
+
this.#notifyCapturePauses(ports);
|
|
5939
|
+
this.#retainPause(state, ports.media.pauseLocalCapture(source));
|
|
5940
|
+
} else {
|
|
5941
|
+
ports.media.setLocalSourceIntent?.(source, false, cause);
|
|
5942
|
+
this.#retainPause(state, this.#beginLocalPause(ports, source, cause));
|
|
5943
|
+
}
|
|
5878
5944
|
this.#publish();
|
|
5879
5945
|
}
|
|
5946
|
+
#notifyCapturePauses(ports) {
|
|
5947
|
+
for (const source of ["microphone", "camera"]) {
|
|
5948
|
+
const state = this.#sources[source];
|
|
5949
|
+
if (state.capturePause === "pending") this.#retainPause(state, this.#announceCapturePause(ports, source));
|
|
5950
|
+
}
|
|
5951
|
+
}
|
|
5952
|
+
async #announceCapturePause(ports, source) {
|
|
5953
|
+
const snapshot = ports.sync.getSnapshot();
|
|
5954
|
+
if (snapshot.connection.phase !== "live") return;
|
|
5955
|
+
const owned = ports.media.getSnapshot().localTracks.find((item) => item.source === source);
|
|
5956
|
+
const projected = snapshot.media?.items.find((item) => item.participantId === snapshot.participantId && item.source === source);
|
|
5957
|
+
if (!owned?.publicationId || !projected?.enabled || projected.publicationId !== owned.publicationId) return;
|
|
5958
|
+
this.#sources[source].capturePause = "announced";
|
|
5959
|
+
const send = source === "microphone" ? ports.sync.setMicrophoneEnabled : ports.sync.setCameraEnabled;
|
|
5960
|
+
await send.call(ports.sync, false);
|
|
5961
|
+
}
|
|
5880
5962
|
#bind(ports) {
|
|
5881
5963
|
if (ports?.media.getSnapshot().connection.phase === "stopped") this.#cancelCaptures();
|
|
5882
5964
|
if (ports && this.#ports?.sync === ports.sync && this.#ports.media === ports.media) return;
|
|
@@ -5914,10 +5996,11 @@ var MediaControllerRuntime = class {
|
|
|
5914
5996
|
const state = this.#sources[pause.source];
|
|
5915
5997
|
if (pause.revision <= state.serverSequence) return;
|
|
5916
5998
|
state.serverSequence = pause.revision;
|
|
5917
|
-
if (pause.revision > state.desired.controlRevision) this.#pausePublication(ports, pause.source, state.desired.revision);
|
|
5999
|
+
if (pause.revision > state.desired.controlRevision) this.#pausePublication(ports, pause.source, state.desired.revision, "moderation_pause");
|
|
5918
6000
|
}) ?? null;
|
|
5919
6001
|
}
|
|
5920
6002
|
#rememberRecoveryIntents() {
|
|
6003
|
+
for (const state of Object.values(this.#sources)) if (state.capturePause === "announced") state.capturePause = "pending";
|
|
5921
6004
|
if (!this.#ports) return;
|
|
5922
6005
|
this.#recovering ??= new Map(MEDIA_SOURCES.filter((source) => this.#sources[source].desired.enabled).map((source) => [source, this.#sources[source].desired.revision]));
|
|
5923
6006
|
}
|
|
@@ -5926,7 +6009,7 @@ var MediaControllerRuntime = class {
|
|
|
5926
6009
|
this.#recovering = null;
|
|
5927
6010
|
for (const [source, revision] of recovering ?? []) {
|
|
5928
6011
|
const current = snapshot.media?.items.find((item) => item.participantId === snapshot.participantId && item.source === source);
|
|
5929
|
-
if (!current?.enabled) this.#pausePublication(ports, source, revision);
|
|
6012
|
+
if (!current?.enabled) this.#pausePublication(ports, source, revision, "sync_recovery_loss");
|
|
5930
6013
|
}
|
|
5931
6014
|
}
|
|
5932
6015
|
#subscribeSync(ports) {
|
|
@@ -5934,6 +6017,7 @@ var MediaControllerRuntime = class {
|
|
|
5934
6017
|
this.#removeRequestsFromMissingParticipants(snapshot);
|
|
5935
6018
|
this.#applyRemoteMediaState(ports, snapshot);
|
|
5936
6019
|
this.#reconcileIntents();
|
|
6020
|
+
this.#notifyCapturePauses(ports);
|
|
5937
6021
|
this.#publish();
|
|
5938
6022
|
});
|
|
5939
6023
|
}
|
|
@@ -6029,8 +6113,8 @@ var MediaControllerRuntime = class {
|
|
|
6029
6113
|
const state = this.#sources[source];
|
|
6030
6114
|
if (!this.#hasConsent(source) && publication?.enabled && state.pausedPublicationId !== publication.publicationId) {
|
|
6031
6115
|
this.#pauseTracks(source);
|
|
6032
|
-
ports.media.setLocalSourceIntent?.(source, false);
|
|
6033
|
-
this.#retainPause(state, this.#beginLocalPause(ports, source));
|
|
6116
|
+
ports.media.setLocalSourceIntent?.(source, false, "consent_revoked");
|
|
6117
|
+
this.#retainPause(state, this.#beginLocalPause(ports, source, "consent_revoked"));
|
|
6034
6118
|
}
|
|
6035
6119
|
}
|
|
6036
6120
|
#observeProjection(ports, source) {
|
|
@@ -6070,6 +6154,7 @@ var MediaControllerRuntime = class {
|
|
|
6070
6154
|
const remote = Object.freeze((snapshot?.remoteTracks ?? []).map((track) => Object.freeze({ participantId: track.participantId, source: track.source, publicationId: track.publicationId, track: track.track })));
|
|
6071
6155
|
const media = Object.freeze({
|
|
6072
6156
|
...current.media,
|
|
6157
|
+
microphoneMutedByOtherDevice: this.#microphoneMutedByOtherDevice,
|
|
6073
6158
|
local,
|
|
6074
6159
|
remote,
|
|
6075
6160
|
screenShare: {
|
|
@@ -6094,9 +6179,6 @@ function confirmedLocalMediaState(track, desired, pending, confirmed, live, conv
|
|
|
6094
6179
|
if (!live && track && liveTrack(track)) return "requesting";
|
|
6095
6180
|
return confirmed ? "enabled" : pending ? "requesting" : "disabled";
|
|
6096
6181
|
}
|
|
6097
|
-
function cancelledCaptureError() {
|
|
6098
|
-
return new ConnectionError({ code: "invalid_state", action: null, recoverable: false, message: "Media capture was cancelled before its result could be used" });
|
|
6099
|
-
}
|
|
6100
6182
|
function foreign2(operation) {
|
|
6101
6183
|
return Effect5.tryPromise({ try: operation, catch: (cause) => cause });
|
|
6102
6184
|
}
|
|
@@ -6463,6 +6545,12 @@ var ParticipantsControllerRuntime = class {
|
|
|
6463
6545
|
#unsubscribePorts = null;
|
|
6464
6546
|
#unsubscribe = null;
|
|
6465
6547
|
#unsubscribeMedia = null;
|
|
6548
|
+
#deviceScope = null;
|
|
6549
|
+
// Retain verified seats while the local Sync replica is recovering, and for older servers
|
|
6550
|
+
// whose negotiated hints disappear with sockets. New servers send every admitted seat.
|
|
6551
|
+
#devices = /* @__PURE__ */ new Map();
|
|
6552
|
+
#presenceCursor = null;
|
|
6553
|
+
#invalidatedDevices = /* @__PURE__ */ new Set();
|
|
6466
6554
|
constructor(connection, store, offline) {
|
|
6467
6555
|
this.#offline = offline;
|
|
6468
6556
|
this.#connection = connection;
|
|
@@ -6516,6 +6604,8 @@ var ParticipantsControllerRuntime = class {
|
|
|
6516
6604
|
return ({ sync }) => foreign3(() => sync.setDisplayName(displayName));
|
|
6517
6605
|
});
|
|
6518
6606
|
dispose() {
|
|
6607
|
+
this.#devices.clear();
|
|
6608
|
+
this.#invalidatedDevices.clear();
|
|
6519
6609
|
this.#unsubscribePorts?.();
|
|
6520
6610
|
this.#unsubscribe?.();
|
|
6521
6611
|
this.#unsubscribeMedia?.();
|
|
@@ -6535,6 +6625,10 @@ var ParticipantsControllerRuntime = class {
|
|
|
6535
6625
|
this.#unsubscribe = null;
|
|
6536
6626
|
this.#unsubscribeMedia = null;
|
|
6537
6627
|
if (!ports) {
|
|
6628
|
+
if (["idle", "leaving", "left", "failed"].includes(this.#connection.getSnapshot().state)) {
|
|
6629
|
+
this.#devices.clear();
|
|
6630
|
+
this.#invalidatedDevices.clear();
|
|
6631
|
+
}
|
|
6538
6632
|
this.#publish(null, {});
|
|
6539
6633
|
return;
|
|
6540
6634
|
}
|
|
@@ -6543,11 +6637,57 @@ var ParticipantsControllerRuntime = class {
|
|
|
6543
6637
|
this.#unsubscribeMedia = ports.media.subscribe(publish);
|
|
6544
6638
|
publish();
|
|
6545
6639
|
}
|
|
6640
|
+
#syncDeviceScope() {
|
|
6641
|
+
const subject = this.#connection.getSnapshot().subject;
|
|
6642
|
+
const scope = subject ? JSON.stringify([subject.tenantId, subject.spaceId, subject.episodeId, subject.participantId, subject.participantGeneration]) : null;
|
|
6643
|
+
if (scope !== this.#deviceScope) {
|
|
6644
|
+
this.#devices.clear();
|
|
6645
|
+
this.#invalidatedDevices.clear();
|
|
6646
|
+
}
|
|
6647
|
+
this.#deviceScope = scope;
|
|
6648
|
+
}
|
|
6649
|
+
#rememberDevices(snapshot) {
|
|
6650
|
+
this.#syncDeviceScope();
|
|
6651
|
+
const control = snapshot?.control;
|
|
6652
|
+
if (!control) return;
|
|
6653
|
+
this.#pruneDevices(control, snapshot.presence);
|
|
6654
|
+
const retained = /* @__PURE__ */ new Map();
|
|
6655
|
+
if (control.status !== "ended") {
|
|
6656
|
+
for (const participant of control.participants) {
|
|
6657
|
+
const device = this.#deviceFor(participant, snapshot.presence?.items ?? []);
|
|
6658
|
+
if (device) retained.set(participant.participantId, device);
|
|
6659
|
+
}
|
|
6660
|
+
}
|
|
6661
|
+
this.#devices = retained;
|
|
6662
|
+
}
|
|
6663
|
+
#pruneDevices(control, presence) {
|
|
6664
|
+
const cursor = presence ? JSON.stringify([presence.projectionId, presence.sequence]) : null;
|
|
6665
|
+
if (cursor !== this.#presenceCursor) this.#invalidatedDevices.clear();
|
|
6666
|
+
this.#presenceCursor = cursor;
|
|
6667
|
+
for (const [id, device] of this.#devices) {
|
|
6668
|
+
if (!control.participants.some((participant) => participant.participantId === id && participant.admissionRevision === device.admissionRevision)) this.#invalidatedDevices.add(id);
|
|
6669
|
+
}
|
|
6670
|
+
}
|
|
6671
|
+
#deviceFor(participant, presence) {
|
|
6672
|
+
const previous = this.#devices.get(participant.participantId);
|
|
6673
|
+
const known = previous && previous.admissionRevision === participant.admissionRevision ? previous : {};
|
|
6674
|
+
const hint = this.#hintFor(participant, presence) ?? {};
|
|
6675
|
+
const identityId = hint.identityId ?? known.identityId;
|
|
6676
|
+
return identityId ? { admissionRevision: participant.admissionRevision, identityId, deviceType: hint.deviceType ?? known.deviceType } : void 0;
|
|
6677
|
+
}
|
|
6678
|
+
#hintFor(participant, presence) {
|
|
6679
|
+
return presence.find((item) => {
|
|
6680
|
+
if (item.participantId !== participant.participantId) return false;
|
|
6681
|
+
if (item.admissionRevision !== void 0) return item.admissionRevision === participant.admissionRevision;
|
|
6682
|
+
return !this.#invalidatedDevices.has(participant.participantId);
|
|
6683
|
+
});
|
|
6684
|
+
}
|
|
6546
6685
|
#publish(snapshot, collaborationCapabilities, media) {
|
|
6686
|
+
this.#rememberDevices(snapshot);
|
|
6547
6687
|
if (snapshot && !controlFor(snapshot)) return;
|
|
6548
6688
|
const audioParticipants = new Set(media?.remoteTracks.filter((track) => track.source === "screen_audio" && track.track.readyState === "live").map((track) => track.participantId));
|
|
6549
6689
|
if (snapshot?.participantId && media?.localTracks.some((track) => track.source === "screen_audio" && track.enabled && track.track.readyState === "live")) audioParticipants.add(snapshot.participantId);
|
|
6550
|
-
const projected = participantsFor(snapshot, collaborationCapabilities);
|
|
6690
|
+
const projected = participantsFor(snapshot, collaborationCapabilities, this.#devices);
|
|
6551
6691
|
const participants = Object.freeze({
|
|
6552
6692
|
...projected,
|
|
6553
6693
|
roster: Object.freeze(projected.roster.map((participant) => Object.freeze({ ...participant, media: Object.freeze({ ...participant.media, screenShareHasAudio: participant.media.screenShare === "active" && audioParticipants.has(participant.participantId) }) })))
|
|
@@ -6573,10 +6713,10 @@ function invalid(message) {
|
|
|
6573
6713
|
function directed(result) {
|
|
6574
6714
|
return Object.freeze({ status: result.result, requestId: result.request_id });
|
|
6575
6715
|
}
|
|
6576
|
-
function participantsFor(snapshot, collaborationCapabilities) {
|
|
6716
|
+
function participantsFor(snapshot, collaborationCapabilities, devices) {
|
|
6577
6717
|
const control = controlFor(snapshot);
|
|
6578
|
-
if (!control) return EMPTY_PARTICIPANTS;
|
|
6579
|
-
return Object.freeze({ roster: rosterFor(control.participants, collaborationCapabilities, mediaFor(snapshot), presenceFor(snapshot)), admissionQueue: admissionQueueFor(control.admissionRequests) });
|
|
6718
|
+
if (!control || control.status === "ended") return EMPTY_PARTICIPANTS;
|
|
6719
|
+
return Object.freeze({ roster: rosterFor(control.participants, collaborationCapabilities, mediaFor(snapshot), presenceFor(snapshot), devices), admissionQueue: admissionQueueFor(control.admissionRequests) });
|
|
6580
6720
|
}
|
|
6581
6721
|
function controlFor(snapshot) {
|
|
6582
6722
|
if (!snapshot) return null;
|
|
@@ -6590,11 +6730,13 @@ function mediaFor(snapshot) {
|
|
|
6590
6730
|
function presenceFor(snapshot) {
|
|
6591
6731
|
return snapshot?.presence ?? null;
|
|
6592
6732
|
}
|
|
6593
|
-
function rosterFor(participants, collaborationCapabilities, media, presence) {
|
|
6733
|
+
function rosterFor(participants, collaborationCapabilities, media, presence, devices) {
|
|
6594
6734
|
return Object.freeze(
|
|
6595
6735
|
participants.map(
|
|
6596
6736
|
(participant) => Object.freeze({
|
|
6597
6737
|
participantId: participant.participantId,
|
|
6738
|
+
identityId: devices.get(participant.participantId)?.identityId,
|
|
6739
|
+
deviceType: devices.get(participant.participantId)?.deviceType,
|
|
6598
6740
|
displayName: participant.displayName,
|
|
6599
6741
|
role: participant.role,
|
|
6600
6742
|
eligibleRoles: Object.freeze([...participant.eligibleRoles]),
|
|
@@ -6616,13 +6758,20 @@ function admissionQueueFor(requests) {
|
|
|
6616
6758
|
function selfFor(snapshot, roster) {
|
|
6617
6759
|
const participantId = snapshot?.participantId ?? null;
|
|
6618
6760
|
const participant = roster.find((candidate) => candidate.participantId === participantId);
|
|
6619
|
-
|
|
6761
|
+
const identityId = selfIdentityFor(snapshot, participant);
|
|
6762
|
+
if (!participant) return emptySelf(participantId, identityId);
|
|
6620
6763
|
const capabilities = participant.capabilities;
|
|
6621
6764
|
const allowed = new Set(capabilities);
|
|
6622
|
-
return Object.freeze({ participantId, displayName: participant.displayName, role: participant.role, capabilities, handRaised: participant.handRaised, can: (capability) => allowed.has(capability) });
|
|
6765
|
+
return Object.freeze({ participantId, identityId, displayName: participant.displayName, role: participant.role, capabilities, handRaised: participant.handRaised, can: (capability) => allowed.has(capability) });
|
|
6766
|
+
}
|
|
6767
|
+
function selfIdentityFor(snapshot, participant) {
|
|
6768
|
+
if (!snapshot) return void 0;
|
|
6769
|
+
if (snapshot.connection.phase === "terminal") return void 0;
|
|
6770
|
+
if (snapshot.control?.status === "ended") return void 0;
|
|
6771
|
+
return snapshot.participantIdentityId ?? participant?.identityId;
|
|
6623
6772
|
}
|
|
6624
|
-
function emptySelf(participantId) {
|
|
6625
|
-
return Object.freeze({ participantId, displayName: null, role: null, capabilities: Object.freeze([]), handRaised: false, can: () => false });
|
|
6773
|
+
function emptySelf(participantId, identityId) {
|
|
6774
|
+
return Object.freeze({ participantId, identityId, displayName: null, role: null, capabilities: Object.freeze([]), handRaised: false, can: () => false });
|
|
6626
6775
|
}
|
|
6627
6776
|
function capabilitiesFor(control, collaboration) {
|
|
6628
6777
|
const capabilities = /* @__PURE__ */ new Set();
|
|
@@ -6656,10 +6805,10 @@ function sameParticipantMedia(left, right) {
|
|
|
6656
6805
|
return left.media.microphone === right.media.microphone && left.media.camera === right.media.camera && left.media.screenShare === right.media.screenShare && left.media.screenShareHasAudio === right.media.screenShareHasAudio;
|
|
6657
6806
|
}
|
|
6658
6807
|
function sameParticipantPresence(left, right) {
|
|
6659
|
-
return left.presence.state === right.presence.state && left.presence.speaking === right.presence.speaking && left.presence.activeSpeaker === right.presence.activeSpeaker;
|
|
6808
|
+
return left.identityId === right.identityId && left.deviceType === right.deviceType && left.presence.state === right.presence.state && left.presence.speaking === right.presence.speaking && left.presence.activeSpeaker === right.presence.activeSpeaker;
|
|
6660
6809
|
}
|
|
6661
6810
|
function sameSelf(left, right) {
|
|
6662
|
-
return left.participantId === right.participantId && left.displayName === right.displayName && left.role === right.role && left.handRaised === right.handRaised && sameValues(left.capabilities, right.capabilities);
|
|
6811
|
+
return left.participantId === right.participantId && left.identityId === right.identityId && left.displayName === right.displayName && left.role === right.role && left.handRaised === right.handRaised && sameValues(left.capabilities, right.capabilities);
|
|
6663
6812
|
}
|
|
6664
6813
|
function sameArray(left, right, equal) {
|
|
6665
6814
|
return left.length === right.length && left.every((value, index) => equal(value, right[index]));
|
|
@@ -7193,7 +7342,7 @@ var SpaceClientCore = class {
|
|
|
7193
7342
|
}
|
|
7194
7343
|
}
|
|
7195
7344
|
#requireActive() {
|
|
7196
|
-
if (this.#disposed) throw new SpaceClientError({ code: "connection.invalid_state", recoverable: false, message: "The SpaceClient has been disposed" });
|
|
7345
|
+
if (this.#disposed) throw new SpaceClientError({ code: "connection.invalid_state", recoverable: false, message: "The SpaceClient has been disposed. Rejoin the call." });
|
|
7197
7346
|
}
|
|
7198
7347
|
};
|
|
7199
7348
|
function liveClock(dependencies) {
|