matrix-js-sdk 41.0.0 → 41.1.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.
Files changed (93) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/@types/event.d.ts +1 -1
  3. package/lib/@types/event.d.ts.map +1 -1
  4. package/lib/@types/event.js +1 -1
  5. package/lib/@types/event.js.map +1 -1
  6. package/lib/client.d.ts.map +1 -1
  7. package/lib/client.js +250 -246
  8. package/lib/client.js.map +1 -1
  9. package/lib/crypto-api/index.d.ts +13 -2
  10. package/lib/crypto-api/index.d.ts.map +1 -1
  11. package/lib/crypto-api/index.js +11 -0
  12. package/lib/crypto-api/index.js.map +1 -1
  13. package/lib/logger.d.ts +5 -5
  14. package/lib/logger.d.ts.map +1 -1
  15. package/lib/logger.js.map +1 -1
  16. package/lib/matrixrtc/CallMembership.d.ts +49 -145
  17. package/lib/matrixrtc/CallMembership.d.ts.map +1 -1
  18. package/lib/matrixrtc/CallMembership.js +157 -265
  19. package/lib/matrixrtc/CallMembership.js.map +1 -1
  20. package/lib/matrixrtc/EncryptionManager.d.ts +1 -85
  21. package/lib/matrixrtc/EncryptionManager.d.ts.map +1 -1
  22. package/lib/matrixrtc/EncryptionManager.js +0 -317
  23. package/lib/matrixrtc/EncryptionManager.js.map +1 -1
  24. package/lib/matrixrtc/MatrixRTCSession.d.ts +18 -22
  25. package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -1
  26. package/lib/matrixrtc/MatrixRTCSession.js +48 -76
  27. package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
  28. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts +2 -1
  29. package/lib/matrixrtc/MatrixRTCSessionManager.d.ts.map +1 -1
  30. package/lib/matrixrtc/MatrixRTCSessionManager.js +3 -2
  31. package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -1
  32. package/lib/matrixrtc/MembershipManager.d.ts +10 -4
  33. package/lib/matrixrtc/MembershipManager.d.ts.map +1 -1
  34. package/lib/matrixrtc/MembershipManager.js +10 -4
  35. package/lib/matrixrtc/MembershipManager.js.map +1 -1
  36. package/lib/matrixrtc/RTCEncryptionManager.d.ts +6 -7
  37. package/lib/matrixrtc/RTCEncryptionManager.d.ts.map +1 -1
  38. package/lib/matrixrtc/RTCEncryptionManager.js +4 -7
  39. package/lib/matrixrtc/RTCEncryptionManager.js.map +1 -1
  40. package/lib/matrixrtc/index.d.ts +1 -0
  41. package/lib/matrixrtc/index.d.ts.map +1 -1
  42. package/lib/matrixrtc/index.js.map +1 -1
  43. package/lib/matrixrtc/membershipData/common.d.ts +8 -0
  44. package/lib/matrixrtc/membershipData/common.d.ts.map +1 -0
  45. package/lib/matrixrtc/membershipData/common.js +26 -0
  46. package/lib/matrixrtc/membershipData/common.js.map +1 -0
  47. package/lib/matrixrtc/membershipData/index.d.ts +4 -0
  48. package/lib/matrixrtc/membershipData/index.d.ts.map +1 -0
  49. package/lib/matrixrtc/membershipData/index.js +20 -0
  50. package/lib/matrixrtc/membershipData/index.js.map +1 -0
  51. package/lib/matrixrtc/membershipData/rtc.d.ts +33 -0
  52. package/lib/matrixrtc/membershipData/rtc.d.ts.map +1 -0
  53. package/lib/matrixrtc/membershipData/rtc.js +137 -0
  54. package/lib/matrixrtc/membershipData/rtc.js.map +1 -0
  55. package/lib/matrixrtc/membershipData/session.d.ts +77 -0
  56. package/lib/matrixrtc/membershipData/session.d.ts.map +1 -0
  57. package/lib/matrixrtc/membershipData/session.js +62 -0
  58. package/lib/matrixrtc/membershipData/session.js.map +1 -0
  59. package/lib/matrixrtc/types.d.ts +23 -0
  60. package/lib/matrixrtc/types.d.ts.map +1 -1
  61. package/lib/matrixrtc/types.js +9 -1
  62. package/lib/matrixrtc/types.js.map +1 -1
  63. package/lib/matrixrtc/utils.d.ts +11 -1
  64. package/lib/matrixrtc/utils.d.ts.map +1 -1
  65. package/lib/matrixrtc/utils.js +24 -1
  66. package/lib/matrixrtc/utils.js.map +1 -1
  67. package/lib/rust-crypto/rust-crypto.d.ts.map +1 -1
  68. package/lib/rust-crypto/rust-crypto.js +2 -2
  69. package/lib/rust-crypto/rust-crypto.js.map +1 -1
  70. package/package.json +5 -7
  71. package/src/@types/event.ts +2 -2
  72. package/src/client.ts +5 -3
  73. package/src/crypto-api/index.ts +17 -2
  74. package/src/logger.ts +5 -5
  75. package/src/matrixrtc/CallMembership.ts +159 -373
  76. package/src/matrixrtc/EncryptionManager.ts +1 -417
  77. package/src/matrixrtc/MatrixRTCSession.ts +82 -122
  78. package/src/matrixrtc/MatrixRTCSessionManager.ts +5 -3
  79. package/src/matrixrtc/MembershipManager.ts +14 -17
  80. package/src/matrixrtc/RTCEncryptionManager.ts +7 -10
  81. package/src/matrixrtc/index.ts +1 -0
  82. package/src/matrixrtc/membershipData/common.ts +27 -0
  83. package/src/matrixrtc/membershipData/index.ts +19 -0
  84. package/src/matrixrtc/membershipData/rtc.ts +156 -0
  85. package/src/matrixrtc/membershipData/session.ts +146 -0
  86. package/src/matrixrtc/types.ts +27 -1
  87. package/src/matrixrtc/utils.ts +24 -2
  88. package/src/rust-crypto/rust-crypto.ts +4 -1
  89. package/lib/matrixrtc/RoomKeyTransport.d.ts +0 -25
  90. package/lib/matrixrtc/RoomKeyTransport.d.ts.map +0 -1
  91. package/lib/matrixrtc/RoomKeyTransport.js +0 -152
  92. package/lib/matrixrtc/RoomKeyTransport.js.map +0 -1
  93. package/src/matrixrtc/RoomKeyTransport.ts +0 -189
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2023 - 2024 The Matrix.org Foundation C.I.C.
2
+ Copyright 2023 - 2026 The Matrix.org Foundation C.I.C.
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -25,7 +25,7 @@ import { type ISendEventResponse } from "../@types/requests.ts";
25
25
  import { CallMembership } from "./CallMembership.ts";
26
26
  import { RoomStateEvent } from "../models/room-state.ts";
27
27
  import { MembershipManager, StickyEventMembershipManager } from "./MembershipManager.ts";
28
- import { type CallMembershipIdentityParts, EncryptionManager, type IEncryptionManager } from "./EncryptionManager.ts";
28
+ import { type CallMembershipIdentityParts, type IEncryptionManager } from "./EncryptionManager.ts";
29
29
  import { logDurationSync } from "../utils.ts";
30
30
  import type {
31
31
  Statistics,
@@ -34,6 +34,7 @@ import type {
34
34
  IRTCNotificationContent,
35
35
  RTCCallIntent,
36
36
  Transport,
37
+ SlotDescription,
37
38
  } from "./types.ts";
38
39
  import {
39
40
  MembershipManagerEvent,
@@ -45,7 +46,7 @@ import { ToDeviceKeyTransport } from "./ToDeviceKeyTransport.ts";
45
46
  import { TypedReEmitter } from "../ReEmitter.ts";
46
47
  import { type IContent, type MatrixEvent } from "../models/event.ts";
47
48
  import { RoomStickyEventsEvent, type RoomStickyEventsMap } from "../models/room-sticky-events.ts";
48
- import { RoomKeyTransport } from "./RoomKeyTransport.ts";
49
+ import { computeSlotId } from "./utils.ts";
49
50
 
50
51
  /**
51
52
  * Events emitted by MatrixRTCSession
@@ -96,21 +97,6 @@ export interface SessionConfig {
96
97
  callIntent?: RTCCallIntent;
97
98
  }
98
99
 
99
- /**
100
- * The session description is used to identify a session. Used in the state event.
101
- */
102
- export interface SlotDescription {
103
- id: string;
104
- application: string;
105
- }
106
- export function slotIdToDescription(slotId: string): SlotDescription {
107
- const [application, id] = slotId.split("#");
108
- return { application, id };
109
- }
110
- export function slotDescriptionToId(slotDescription: SlotDescription): string {
111
- return `${slotDescription.application}#${slotDescription.id}`;
112
- }
113
-
114
100
  // The names follow these principles:
115
101
  // - we use the technical term delay if the option is related to delayed events.
116
102
  // - we use delayedLeaveEvent if the option is related to the delayed leave event.
@@ -165,11 +151,6 @@ export interface MembershipConfig {
165
151
  */
166
152
  networkErrorRetryMs?: number;
167
153
 
168
- /**
169
- * If true, use the new to-device transport for sending encryption keys.
170
- */
171
- useExperimentalToDeviceTransport?: boolean;
172
-
173
154
  /**
174
155
  * The time (in milliseconds) after which a we consider a delayed event restart http request to have failed.
175
156
  * Setting this to a lower value will result in more frequent retries but also a higher chance of failiour.
@@ -275,6 +256,16 @@ export class MatrixRTCSession extends TypedEventEmitter<
275
256
 
276
257
  public memberships: CallMembership[] = [];
277
258
 
259
+ /**
260
+ * Resolves when the session has calculated the initial membership of the session.
261
+ */
262
+ public readonly initialMembershipCalculated: Promise<void>;
263
+ /**
264
+ * Does membership need to be recalculated? This is set to false upon
265
+ * recalculation.
266
+ */
267
+ private membershipNeedsRecalculation = false;
268
+
278
269
  /**
279
270
  * The statistics for this session.
280
271
  */
@@ -315,7 +306,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
315
306
  * The slotId is the property that, per definition, groups memberships into one call.
316
307
  */
317
308
  public get slotId(): string | undefined {
318
- return slotDescriptionToId(this.slotDescription);
309
+ return computeSlotId(this.slotDescription);
319
310
  }
320
311
 
321
312
  /**
@@ -326,18 +317,20 @@ export class MatrixRTCSession extends TypedEventEmitter<
326
317
  */
327
318
  public static async sessionMembershipsForSlot(
328
319
  room: Pick<Room, "getLiveTimeline" | "roomId" | "hasMembershipState" | "_unstable_getStickyEvents">,
329
- slotId: string,
320
+ slotDescription: SlotDescription,
330
321
  // default both true this implied we combine sticky and state events for the final call state
331
322
  // (prefer sticky events in case of a duplicate)
332
323
  options: SessionMembershipsForSlotOpts = DEFAULT_SESSION_MEMBERSHIPS_FOR_SLOT_OPTS,
333
324
  ): Promise<CallMembership[]> {
334
- const logger = rootLogger.getChild(`[MatrixRTCSession ${room.roomId}]`);
325
+ const logger = rootLogger.getChild(
326
+ `[MatrixRTCSession ${room.roomId} ${slotDescription.application}#${slotDescription.id}]`,
327
+ );
335
328
  const callMemberEvents = collectMembersEvents(room, options, logger);
336
329
 
337
330
  const callMemberships = await computeBackendIdentityAndVerifyMemberEvents(
338
331
  room,
339
332
  callMemberEvents,
340
- slotId,
333
+ slotDescription,
341
334
  logger,
342
335
  );
343
336
 
@@ -389,6 +382,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
389
382
  * @param slotDescription The slot description is a virtual address where participants are allowed to meet.
390
383
  * This session will only manage memberships that match this slot description.Sessions are distinct if any of
391
384
  * those properties are distinct: `roomSubset.roomId`, `slotDescription.application`, `slotDescription.id`.
385
+ * @param calculateMembershipsOpts - Options to configure how memberships are calculated for this session.
392
386
  */
393
387
  public constructor(
394
388
  private readonly client: Pick<
@@ -412,21 +406,27 @@ export class MatrixRTCSession extends TypedEventEmitter<
412
406
  >,
413
407
  private roomSubset: Pick<
414
408
  Room,
415
- "getLiveTimeline" | "roomId" | "getVersion" | "hasMembershipState" | "on" | "off"
409
+ | "getLiveTimeline"
410
+ | "roomId"
411
+ | "getVersion"
412
+ | "hasMembershipState"
413
+ | "on"
414
+ | "off"
415
+ | "_unstable_getStickyEvents"
416
416
  >,
417
417
 
418
418
  public readonly slotDescription: SlotDescription,
419
419
  private readonly calculateMembershipsOpts?: SessionMembershipsForSlotOpts,
420
420
  ) {
421
421
  super();
422
- this.logger = rootLogger.getChild(`[MatrixRTCSession ${roomSubset.roomId}]`);
422
+ this.logger = rootLogger.getChild(
423
+ `[MatrixRTCSession ${roomSubset.roomId} ${slotDescription.application}#${slotDescription.id}]`,
424
+ );
423
425
 
424
426
  this.roomSubset.on(RoomStateEvent.Members, this.onRoomMemberUpdate);
425
427
  this.roomSubset.on(RoomStickyEventsEvent.Update, this.onStickyEventUpdate);
426
428
 
427
- // We can ignore this promise because `recalculateSessionMembers` will emit
428
- // `MatrixRTCSessionEvent.MembershipsChanged` once it has completed.
429
- this.ensureRecalculateSessionMembers();
429
+ this.initialMembershipCalculated = this.ensureRecalculateSessionMembers();
430
430
  this.setExpiryTimer();
431
431
  }
432
432
  /*
@@ -500,57 +500,28 @@ export class MatrixRTCSession extends TypedEventEmitter<
500
500
  MembershipManagerEvent.DelayIdChanged,
501
501
  ]);
502
502
  // Create Encryption manager
503
- let transport;
504
- if (joinConfig?.useExperimentalToDeviceTransport) {
505
- this.logger.info("Using experimental to-device transport for encryption keys");
506
- this.logger.info("Using to-device with room fallback transport for encryption keys");
507
- const [room, client, statistics] = [this.roomSubset, this.client, this.statistics];
508
- const transport = new ToDeviceKeyTransport(ownMembershipIdentity, room.roomId, client, statistics);
509
- this.encryptionManager = new RTCEncryptionManager(
510
- ownMembershipIdentity,
511
- () => this.memberships,
512
- transport,
513
- this.statistics,
514
- (
515
- keyBin: Uint8Array<ArrayBuffer>,
516
- encryptionKeyIndex: number,
517
- membership: CallMembershipIdentityParts,
518
- rtcBackendIdentity: string,
519
- ) => {
520
- this.emit(
521
- MatrixRTCSessionEvent.EncryptionKeyChanged,
522
- keyBin,
523
- encryptionKeyIndex,
524
- membership,
525
- rtcBackendIdentity,
526
- );
527
- },
528
- this.logger,
529
- );
530
- } else {
531
- // TODO REMOVE ME!
532
- transport = new RoomKeyTransport(this.roomSubset, this.client, this.statistics);
533
- this.encryptionManager = new EncryptionManager(
534
- ownMembershipIdentity,
535
- () => this.memberships,
536
- transport,
537
- this.statistics,
538
- (
539
- keyBin: Uint8Array<ArrayBuffer>,
540
- encryptionKeyIndex: number,
541
- membership: CallMembershipIdentityParts,
542
- rtcBackendIdentity: string,
543
- ) => {
544
- this.emit(
545
- MatrixRTCSessionEvent.EncryptionKeyChanged,
546
- keyBin,
547
- encryptionKeyIndex,
548
- membership,
549
- rtcBackendIdentity,
550
- );
551
- },
552
- );
553
- }
503
+ const [room, client, statistics] = [this.roomSubset, this.client, this.statistics];
504
+ const transport = new ToDeviceKeyTransport(ownMembershipIdentity, room.roomId, client, statistics);
505
+ this.encryptionManager = new RTCEncryptionManager(
506
+ ownMembershipIdentity,
507
+ () => this.memberships,
508
+ transport,
509
+ (
510
+ keyBin: Uint8Array<ArrayBuffer>,
511
+ encryptionKeyIndex: number,
512
+ membership: CallMembershipIdentityParts,
513
+ rtcBackendIdentity: string,
514
+ ) => {
515
+ this.emit(
516
+ MatrixRTCSessionEvent.EncryptionKeyChanged,
517
+ keyBin,
518
+ encryptionKeyIndex,
519
+ membership,
520
+ rtcBackendIdentity,
521
+ );
522
+ },
523
+ this.logger,
524
+ );
554
525
  }
555
526
 
556
527
  this.joinConfig = joinConfig;
@@ -620,13 +591,6 @@ export class MatrixRTCSession extends TypedEventEmitter<
620
591
  return oldestMembership?.getTransport(oldestMembership);
621
592
  }
622
593
 
623
- /**
624
- * The used focusActive of the oldest membership (to find out the selection type multi-sfu or oldest membership active focus)
625
- * @deprecated does not work with m.rtc.member. Do not rely on it.
626
- */
627
- public getActiveFocus(): Transport | undefined {
628
- return this.getOldestMembership()?.getFocusActive();
629
- }
630
594
  public getOldestMembership(): CallMembership | undefined {
631
595
  return this.memberships[0];
632
596
  }
@@ -695,7 +659,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
695
659
  }
696
660
 
697
661
  if (soonestExpiry != undefined) {
698
- this.expiryTimeout = setTimeout(this.ensureRecalculateSessionMembers.bind(this), soonestExpiry);
662
+ this.expiryTimeout = setTimeout(() => void this.ensureRecalculateSessionMembers(), soonestExpiry);
699
663
  }
700
664
  }
701
665
 
@@ -747,7 +711,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
747
711
  * Call this when the Matrix room members have changed.
748
712
  */
749
713
  private readonly onRoomMemberUpdate = (): void => {
750
- this.ensureRecalculateSessionMembers();
714
+ void this.ensureRecalculateSessionMembers();
751
715
  };
752
716
 
753
717
  /**
@@ -763,7 +727,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
763
727
  (e) => e.getType() === EventType.RTCMembership,
764
728
  )
765
729
  ) {
766
- this.ensureRecalculateSessionMembers();
730
+ void this.ensureRecalculateSessionMembers();
767
731
  }
768
732
  };
769
733
 
@@ -777,22 +741,24 @@ export class MatrixRTCSession extends TypedEventEmitter<
777
741
  };
778
742
 
779
743
  // helper variables to make sure we do not have parallel running recalculations.
744
+ private recalculateSessionMembersPromise: Promise<void> = Promise.resolve();
780
745
 
781
- private recalculateSessionMembersDirty = false;
782
- private recalculateSessionMembersPromise: Promise<void> | undefined = undefined;
783
-
784
- private ensureRecalculateSessionMembers(): void {
785
- if (this.recalculateSessionMembersPromise === undefined) {
786
- this.recalculateSessionMembersPromise = this.recalculateSessionMembers().then(() => {
787
- this.recalculateSessionMembersPromise = undefined;
788
- if (this.recalculateSessionMembersDirty) {
789
- this.ensureRecalculateSessionMembers();
790
- this.recalculateSessionMembersDirty = false;
791
- }
792
- });
793
- } else {
794
- this.recalculateSessionMembersDirty = true;
746
+ /**
747
+ * Ensures that membership is recalculated when the state of the session may have changed.
748
+ * Also ensures that only one recalculation is made at a time.
749
+ * @returns A promise resolving when the state has been recalculated.
750
+ */
751
+ private ensureRecalculateSessionMembers(): Promise<void> {
752
+ if (this.membershipNeedsRecalculation) {
753
+ // We have already requested recalcuation, don't attempt a new one.
754
+ return this.recalculateSessionMembersPromise;
795
755
  }
756
+ this.membershipNeedsRecalculation = true;
757
+ // Chain the recalculation.
758
+ this.recalculateSessionMembersPromise = this.recalculateSessionMembersPromise
759
+ .finally()
760
+ .then(() => this.recalculateSessionMembers());
761
+ return this.recalculateSessionMembersPromise;
796
762
  }
797
763
 
798
764
  /**
@@ -803,11 +769,13 @@ export class MatrixRTCSession extends TypedEventEmitter<
803
769
  * This function should be called when the room members or call memberships might have changed.
804
770
  */
805
771
  private readonly recalculateSessionMembers = async (): Promise<void> => {
772
+ // Clear the flag.
773
+ this.membershipNeedsRecalculation = false;
806
774
  const oldMemberships = this.memberships;
807
775
 
808
776
  this.memberships = await MatrixRTCSession.sessionMembershipsForSlot(
809
777
  this.room,
810
- slotDescriptionToId(this.slotDescription),
778
+ this.slotDescription,
811
779
  this.calculateMembershipsOpts,
812
780
  );
813
781
 
@@ -857,7 +825,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
857
825
  async function computeBackendIdentityAndVerifyMemberEvents(
858
826
  room: Pick<Room, "hasMembershipState">,
859
827
  callMemberEvents: MatrixEvent[],
860
- slotId: string,
828
+ slotDescription: SlotDescription,
861
829
  logger: Logger,
862
830
  ): Promise<CallMembership[]> {
863
831
  const callMemberships: CallMembership[] = [];
@@ -866,22 +834,14 @@ async function computeBackendIdentityAndVerifyMemberEvents(
866
834
  const content = memberEvent.getContent();
867
835
 
868
836
  // Quick filter to avoid unneeded processing of invalid events or left events.
869
- // A more thorough validation will be done later with CallMembership.membershipDataFromMatrixEvent.
870
837
  if (!quickFilterNonRelevantContents(content, logger)) {
871
838
  continue;
872
839
  }
873
840
 
874
841
  try {
875
- const membershipData = CallMembership.membershipDataFromMatrixEvent(memberEvent);
876
-
877
- const membership = new CallMembership(
878
- memberEvent,
879
- membershipData,
880
- await CallMembership.computeRtcBackendIdentity(memberEvent, membershipData),
881
- logger,
882
- );
842
+ const membership = await CallMembership.parseFromEvent(memberEvent);
883
843
 
884
- if (isValidMembership(membership, room, slotId, logger)) {
844
+ if (isValidMembership(membership, room, slotDescription, logger)) {
885
845
  callMemberships.push(membership);
886
846
  }
887
847
  } catch (e) {
@@ -914,12 +874,12 @@ function quickFilterNonRelevantContents(content: IContent, logger: Logger): bool
914
874
  function isValidMembership(
915
875
  membership: CallMembership,
916
876
  room: Pick<Room, "hasMembershipState">,
917
- slotId: string,
877
+ slotDescription: SlotDescription,
918
878
  logger: Logger,
919
879
  ): boolean {
920
- if (membership.slotId !== slotId) {
880
+ if (membership.slotDescription.id !== slotDescription.id) {
921
881
  logger.info(
922
- `Ignoring membership of user ${membership.userId} for a different slot: user: ${JSON.stringify(membership.slotDescription)}, slotId: ${slotId})`,
882
+ `Ignoring membership of user ${membership.userId} for a different slot. Theirs: ${JSON.stringify(membership.slotDescription)}, Expected: ${JSON.stringify(slotDescription)}`,
923
883
  );
924
884
  return false;
925
885
  }
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2023 The Matrix.org Foundation C.I.C.
2
+ Copyright 2023-2026 The Matrix.org Foundation C.I.C.
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -20,8 +20,10 @@ import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
20
20
  import { type Room } from "../models/room.ts";
21
21
  import { RoomStateEvent } from "../models/room-state.ts";
22
22
  import { type MatrixEvent } from "../models/event.ts";
23
- import { MatrixRTCSession, type SlotDescription } from "./MatrixRTCSession.ts";
23
+ import { MatrixRTCSession } from "./MatrixRTCSession.ts";
24
24
  import { EventType } from "../@types/event.ts";
25
+ import { type SlotDescription } from "./types.ts";
26
+ import { computeSlotId } from "./utils.ts";
25
27
 
26
28
  export enum MatrixRTCSessionManagerEvents {
27
29
  // A member has joined the MatrixRTC session, creating an active session in a room where there wasn't previously
@@ -59,7 +61,7 @@ export class MatrixRTCSessionManager extends TypedEventEmitter<MatrixRTCSessionM
59
61
  private readonly slotDescription: SlotDescription = { application: "m.call", id: "ROOM" }, // Default to the Matrix Call application
60
62
  ) {
61
63
  super();
62
- this.logger = rootLogger.getChild("[MatrixRTCSessionManager]");
64
+ this.logger = rootLogger.getChild(`[MatrixRTCSessionManager ${computeSlotId(slotDescription)}]`);
63
65
  }
64
66
 
65
67
  public start(): void {
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 The Matrix.org Foundation C.I.C.
2
+ Copyright 2025-2026 The Matrix.org Foundation C.I.C.
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -22,19 +22,9 @@ import type { MatrixClient } from "../client.ts";
22
22
  import { ConnectionError, HTTPError, MatrixError } from "../http-api/errors.ts";
23
23
  import { type Logger, logger as rootLogger } from "../logger.ts";
24
24
  import { type Room } from "../models/room.ts";
25
- import {
26
- type CallMembership,
27
- DEFAULT_EXPIRE_DURATION,
28
- type RtcMembershipData,
29
- type SessionMembershipData,
30
- } from "./CallMembership.ts";
31
- import { type Transport, isMyMembership, type RTCCallIntent, Status } from "./types.ts";
32
- import {
33
- type SlotDescription,
34
- type MembershipConfig,
35
- type SessionConfig,
36
- slotDescriptionToId,
37
- } from "./MatrixRTCSession.ts";
25
+ import { type CallMembership, DEFAULT_EXPIRE_DURATION } from "./CallMembership.ts";
26
+ import { type Transport, isMyMembership, type RTCCallIntent, Status, type SlotDescription } from "./types.ts";
27
+ import { type MembershipConfig, type SessionConfig } from "./MatrixRTCSession.ts";
38
28
  import { ActionScheduler, type ActionUpdate } from "./MembershipManagerActionScheduler.ts";
39
29
  import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
40
30
  import { UnsupportedDelayedEventsEndpointError } from "../errors.ts";
@@ -43,6 +33,8 @@ import {
43
33
  type IMembershipManager,
44
34
  type MembershipManagerEventHandlerMap,
45
35
  } from "./IMembershipManager.ts";
36
+ import { type RtcMembershipData, type SessionMembershipData } from "./membershipData/index.ts";
37
+ import { computeSlotId } from "./utils.ts";
46
38
  import { isLivekitTransportConfig } from "./LivekitTransport.ts";
47
39
 
48
40
  /* MembershipActionTypes:
@@ -789,13 +781,14 @@ export class MembershipManager
789
781
 
790
782
  /**
791
783
  * Constructs our own membership
784
+ * @returns Only returns `SessionMembershipData`
792
785
  */
793
786
  protected makeMyMembership(expires: number): SessionMembershipData | RtcMembershipData {
794
787
  const ownMembership = this.ownMembership;
795
788
  const needsEmptyStringRoomFix =
796
789
  this.slotDescription.application === "m.call" && this.slotDescription.id === "ROOM";
797
790
 
798
- const focusObjects =
791
+ const focusObjects: Pick<SessionMembershipData, "foci_preferred" | "focus_active"> =
799
792
  this.rtcTransport === undefined
800
793
  ? {
801
794
  focus_active: { type: "livekit", focus_selection: "oldest_membership" } as const,
@@ -1099,7 +1092,11 @@ export class StickyEventMembershipManager extends MembershipManager {
1099
1092
  return super.actionUpdateFromErrors(e, t, StickyEventMembershipManager.nameMap.get(m) ?? "unknown");
1100
1093
  }
1101
1094
 
1102
- protected makeMyMembership(expires: number): SessionMembershipData | RtcMembershipData {
1095
+ /**
1096
+ *
1097
+ * @returns Only returns `RtcMembershipData`
1098
+ */
1099
+ protected makeMyMembership(): RtcMembershipData {
1103
1100
  const ownMembership = this.ownMembership;
1104
1101
 
1105
1102
  const livekitTransport = isLivekitTransportConfig(this.rtcTransport) ? this.rtcTransport : undefined;
@@ -1111,7 +1108,7 @@ export class StickyEventMembershipManager extends MembershipManager {
1111
1108
  type: this.slotDescription.application,
1112
1109
  ...(this.callIntent ? { "m.call.intent": this.callIntent } : {}),
1113
1110
  },
1114
- slot_id: slotDescriptionToId(this.slotDescription),
1111
+ slot_id: computeSlotId(this.slotDescription),
1115
1112
  // Make sure we do not add the alias to the transport.
1116
1113
  // It is not needed in matrix2.0. The additional session information will be used to find the right alias on the sfu.
1117
1114
  rtc_transports: livekitTransport
@@ -1,5 +1,5 @@
1
1
  /*
2
- Copyright 2025 The Matrix.org Foundation C.I.C.
2
+ Copyright 2025-2026 The Matrix.org Foundation C.I.C.
3
3
 
4
4
  Licensed under the Apache License, Version 2.0 (the "License");
5
5
  you may not use this file except in compliance with the License.
@@ -20,7 +20,7 @@ import {
20
20
  type IEncryptionManager,
21
21
  } from "./EncryptionManager.ts";
22
22
  import { type EncryptionConfig, type MembershipConfig } from "./MatrixRTCSession.ts";
23
- import { CallMembership } from "./CallMembership.ts";
23
+ import type { CallMembership } from "./CallMembership.ts";
24
24
  import { decodeBase64, encodeBase64 } from "../base64.ts";
25
25
  import { type IKeyTransport, type KeyTransportEventListener, KeyTransportEvents } from "./IKeyTransport.ts";
26
26
  import { type Logger } from "../logger.ts";
@@ -30,9 +30,9 @@ import {
30
30
  type InboundEncryptionSession,
31
31
  type OutboundEncryptionSession,
32
32
  type ParticipantDeviceInfo,
33
- type Statistics,
34
33
  } from "./types.ts";
35
34
  import { OutdatedKeyFilter } from "./utils.ts";
35
+ import { computeRtcIdentityRaw } from "./membershipData/rtc.ts";
36
36
 
37
37
  /**
38
38
  * RTCEncryptionManager is used to manage the encryption keys for a call.
@@ -58,7 +58,7 @@ export class RTCEncryptionManager implements IEncryptionManager {
58
58
  * The encryption manager stores the keys because the application layer might not be ready yet to handle the keys.
59
59
  * The keys are stored and can be retrieved later when the application layer is ready {@link RTCEncryptionManager#getEncryptionKeys}.
60
60
  */
61
- private participantKeyRings = new Map<
61
+ private readonly participantKeyRings = new Map<
62
62
  EncryptionKeyMapKey,
63
63
  Array<{
64
64
  key: Uint8Array<ArrayBuffer>;
@@ -111,7 +111,7 @@ export class RTCEncryptionManager implements IEncryptionManager {
111
111
 
112
112
  private logger: Logger | undefined = undefined;
113
113
 
114
- private rtcIdentityProvider: (userId: string, deviceId: string, memberId: string) => Promise<string>;
114
+ private readonly rtcIdentityProvider: (userId: string, deviceId: string, memberId: string) => Promise<string>;
115
115
 
116
116
  /**
117
117
  *
@@ -124,10 +124,9 @@ export class RTCEncryptionManager implements IEncryptionManager {
124
124
  * @param rtcBackendIdProvider - A function to compute the rtc backend identity, exposed for testing purposes
125
125
  */
126
126
  public constructor(
127
- private ownMembership: CallMembershipIdentityParts,
127
+ private readonly ownMembership: CallMembershipIdentityParts,
128
128
  private getMemberships: () => CallMembership[],
129
129
  private transport: IKeyTransport,
130
- private statistics: Statistics,
131
130
  // Callback to notify the media layer of new keys
132
131
  private onEncryptionKeysChanged: (
133
132
  keyBin: Uint8Array<ArrayBuffer>,
@@ -139,7 +138,7 @@ export class RTCEncryptionManager implements IEncryptionManager {
139
138
  rtcBackendIdProvider?: (userId: string, deviceId: string, memberId: string) => Promise<string>,
140
139
  ) {
141
140
  this.logger = parentLogger?.getChild(`[EncryptionManager]`);
142
- this.rtcIdentityProvider = rtcBackendIdProvider ?? CallMembership.computeRtcIdentityRaw;
141
+ this.rtcIdentityProvider = rtcBackendIdProvider ?? computeRtcIdentityRaw;
143
142
  }
144
143
 
145
144
  private async getOwnRtcBackendIdentity(): Promise<string> {
@@ -296,7 +295,6 @@ export class RTCEncryptionManager implements IEncryptionManager {
296
295
  candidateInboundSession.keyIndex,
297
296
  candidateInboundSession.membership,
298
297
  );
299
- this.statistics.counters.roomEventEncryptionKeysReceived += 1;
300
298
  } else {
301
299
  this.logger?.info(
302
300
  `Received an out of order key for ${membership.userId}:${membership.deviceId}, dropping it`,
@@ -410,7 +408,6 @@ export class RTCEncryptionManager implements IEncryptionManager {
410
408
  try {
411
409
  this.logger?.trace(`Sending key...`);
412
410
  await this.transport.sendKey(encodeBase64(outboundKey.key), outboundKey.keyId, toDistributeTo);
413
- this.statistics.counters.roomEventEncryptionKeysSent += 1;
414
411
  outboundKey.sharedWith.push(...toDistributeTo);
415
412
  this.logger?.trace(
416
413
  `key index:${outboundKey.keyId} sent to ${outboundKey.sharedWith.map((m) => `${m.userId}:${m.deviceId}`).join(",")}`,
@@ -19,5 +19,6 @@ export * from "./LivekitTransport.ts";
19
19
  export * from "./MatrixRTCSession.ts";
20
20
  export * from "./MatrixRTCSessionManager.ts";
21
21
  export type * from "./types.ts";
22
+ export { type SessionMembershipData, type RtcMembershipData } from "./membershipData/index.ts";
22
23
  export { Status, parseCallNotificationContent, isMyMembership } from "./types.ts";
23
24
  export { MembershipManagerEvent } from "./IMembershipManager.ts";
@@ -0,0 +1,27 @@
1
+ /*
2
+ Copyright 2026 The Matrix.org Foundation C.I.C.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ /**
18
+ * Thrown when an event is not valid for use with MatrixRTC.
19
+ */
20
+ export class MatrixRTCMembershipParseError extends AggregateError {
21
+ public constructor(
22
+ public readonly type: string,
23
+ errors: string[],
24
+ ) {
25
+ super(errors, `Does not match ${type}:\n${errors.join("\n")}`);
26
+ }
27
+ }
@@ -0,0 +1,19 @@
1
+ /*
2
+ Copyright 2026 The Matrix.org Foundation C.I.C.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ export { type SessionMembershipData, checkSessionsMembershipData } from "./session.ts";
18
+ export { type RtcMembershipData, computeRtcIdentityRaw, checkRtcMembershipData } from "./rtc.ts";
19
+ export { MatrixRTCMembershipParseError } from "./common.ts";