@stream-io/video-client 1.40.2 → 1.41.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.
@@ -57,7 +57,9 @@ describe('CallState', () => {
57
57
  it(`shouldn't emit when primitive (backstage) values didn't change`, () => {
58
58
  const state = new CallState();
59
59
  const updateWith = (value: boolean) => {
60
- state.updateFromCallResponse(fromPartial({ backstage: value }));
60
+ state.updateFromCallResponse(
61
+ fromPartial({ backstage: value, egress: {} }),
62
+ );
61
63
  };
62
64
 
63
65
  updateWith(false);
@@ -138,7 +140,9 @@ describe('CallState', () => {
138
140
  it(`shouldn't emit when string arrays (blockedUserIds) value didn't change`, () => {
139
141
  const state = new CallState();
140
142
  const updateWith = (value: string[]) => {
141
- state.updateFromCallResponse(fromPartial({ blocked_user_ids: value }));
143
+ state.updateFromCallResponse(
144
+ fromPartial({ blocked_user_ids: value, egress: {} }),
145
+ );
142
146
  };
143
147
 
144
148
  updateWith(['a', 'b']);
@@ -387,13 +391,12 @@ describe('CallState', () => {
387
391
  describe('call.live and backstage events', () => {
388
392
  it('handles call.live_started events', () => {
389
393
  const state = new CallState();
390
- state.updateFromEvent({
391
- type: 'call.live_started',
392
- // @ts-expect-error - incomplete data
393
- call: {
394
- backstage: false,
395
- },
396
- });
394
+ state.updateFromEvent(
395
+ fromPartial({
396
+ type: 'call.live_started',
397
+ call: fromPartial({ backstage: false, egress: {} }),
398
+ }),
399
+ );
397
400
  expect(state.backstage).toBe(false);
398
401
  });
399
402
  });
@@ -402,17 +405,15 @@ describe('CallState', () => {
402
405
  describe('call.updated', () => {
403
406
  it(`will update the call's metadata`, () => {
404
407
  const state = new CallState();
405
- const event: CallUpdatedEvent = {
408
+ const event: CallUpdatedEvent = fromPartial({
406
409
  type: 'call.updated',
407
410
  call_cid: 'development:12345',
408
- // @ts-expect-error incomplete data
409
- call: {
411
+ call: fromPartial({
410
412
  cid: 'development:12345',
411
- custom: {
412
- test: 'value',
413
- },
414
- },
415
- };
413
+ egress: {},
414
+ custom: { test: 'value' },
415
+ }),
416
+ });
416
417
 
417
418
  // @ts-expect-error incomplete data
418
419
  state.updateFromEvent(event);
@@ -423,15 +424,10 @@ describe('CallState', () => {
423
424
  describe(`call.accepted`, () => {
424
425
  it(`will update state`, () => {
425
426
  const state = new CallState();
426
- const event: CallAcceptedEvent = {
427
+ const event: CallAcceptedEvent = fromPartial({
427
428
  type: 'call.accepted',
428
- // @ts-expect-error incomplete data
429
- call: {
430
- custom: {
431
- test: 'value',
432
- },
433
- },
434
- };
429
+ call: fromPartial({ egress: {}, custom: { test: 'value' } }),
430
+ });
435
431
  // @ts-expect-error incomplete data
436
432
  state.updateFromEvent(event);
437
433
 
@@ -442,15 +438,10 @@ describe('CallState', () => {
442
438
  describe(`call.rejected`, () => {
443
439
  it(`will update state`, () => {
444
440
  const state = new CallState();
445
- const event: CallEndedEvent = {
441
+ const event: CallEndedEvent = fromPartial({
446
442
  type: 'call.rejected',
447
- // @ts-expect-error incomplete data
448
- call: {
449
- custom: {
450
- test: 'value',
451
- },
452
- },
453
- };
443
+ call: fromPartial({ egress: {}, custom: { test: 'value' } }),
444
+ });
454
445
  // @ts-expect-error incomplete data
455
446
  state.updateFromEvent(event);
456
447
 
@@ -461,15 +452,10 @@ describe('CallState', () => {
461
452
  describe(`call.ended`, () => {
462
453
  it(`will update state`, () => {
463
454
  const state = new CallState();
464
- const event: CallEndedEvent = {
455
+ const event: CallEndedEvent = fromPartial({
465
456
  type: 'call.ended',
466
- // @ts-expect-error incomplete data
467
- call: {
468
- custom: {
469
- test: 'value',
470
- },
471
- },
472
- };
457
+ call: fromPartial({ egress: {}, custom: { test: 'value' } }),
458
+ });
473
459
  // @ts-expect-error incomplete data
474
460
  state.updateFromEvent(event);
475
461
 
@@ -525,7 +511,7 @@ describe('CallState', () => {
525
511
  // @ts-expect-error incomplete data
526
512
  members: [{ user_id: 'user1' }, { user_id: 'user2' }],
527
513
  // @ts-expect-error incomplete data
528
- call: {},
514
+ call: { egress: {} },
529
515
  });
530
516
 
531
517
  const updatedMembers = state.members;
@@ -551,7 +537,7 @@ describe('CallState', () => {
551
537
  type: 'call.member_removed',
552
538
  members: removedMembers,
553
539
  // @ts-expect-error incomplete data
554
- call: {},
540
+ call: { egress: {} },
555
541
  });
556
542
 
557
543
  const updatedMembers = state.members;
@@ -596,7 +582,7 @@ describe('CallState', () => {
596
582
  },
597
583
  ],
598
584
  // @ts-expect-error incomplete data
599
- call: {},
585
+ call: { egress: {} },
600
586
  });
601
587
 
602
588
  const updatedMembers = state.members;
@@ -626,7 +612,7 @@ describe('CallState', () => {
626
612
  // @ts-expect-error incomplete data
627
613
  members: [{ ...user1, user: { name: 'John' } }],
628
614
  // @ts-expect-error incomplete data
629
- call: {},
615
+ call: { egress: {} },
630
616
  });
631
617
 
632
618
  const updatedMembers = state.members;
@@ -691,7 +677,7 @@ describe('CallState', () => {
691
677
  it('handles call.hls_broadcasting_stopped events', () => {
692
678
  const state = new CallState();
693
679
  // @ts-expect-error incomplete data
694
- state.updateFromCallResponse({});
680
+ state.updateFromCallResponse({ egress: {} });
695
681
  // @ts-expect-error incomplete data
696
682
  state.updateFromEvent({
697
683
  type: 'call.hls_broadcasting_stopped',
@@ -709,20 +695,381 @@ describe('CallState', () => {
709
695
  });
710
696
  });
711
697
 
698
+ describe('updateFromRecordingEvent', () => {
699
+ describe('COMPOSITE recording type', () => {
700
+ it('should set recording to true when composite recording starts', () => {
701
+ const state = new CallState();
702
+ state.updateFromEvent(
703
+ fromPartial({
704
+ type: 'call.recording_started',
705
+ recording_type: 'composite',
706
+ }),
707
+ );
708
+ expect(state.recording).toBe(true);
709
+ expect(state.individualRecording).toBe(false);
710
+ expect(state.rawRecording).toBe(false);
711
+ });
712
+
713
+ it('should set recording to false when composite recording stops', () => {
714
+ const state = new CallState();
715
+ state.updateFromEvent(
716
+ fromPartial({
717
+ type: 'call.recording_started',
718
+ recording_type: 'composite',
719
+ }),
720
+ );
721
+ expect(state.recording).toBe(true);
722
+
723
+ state.updateFromEvent(
724
+ fromPartial({
725
+ type: 'call.recording_stopped',
726
+ recording_type: 'composite',
727
+ }),
728
+ );
729
+ expect(state.recording).toBe(false);
730
+ });
731
+
732
+ it('should set recording to false when composite recording fails', () => {
733
+ const state = new CallState();
734
+ state.updateFromEvent(
735
+ fromPartial({
736
+ type: 'call.recording_started',
737
+ recording_type: 'composite',
738
+ }),
739
+ );
740
+ expect(state.recording).toBe(true);
741
+
742
+ state.updateFromEvent(
743
+ fromPartial({
744
+ type: 'call.recording_failed',
745
+ recording_type: 'composite',
746
+ }),
747
+ );
748
+ expect(state.recording).toBe(false);
749
+ });
750
+ });
751
+
752
+ describe('INDIVIDUAL recording type', () => {
753
+ it('should set individualRecording to true when individual recording starts', () => {
754
+ const state = new CallState();
755
+ state.updateFromEvent(
756
+ fromPartial({
757
+ type: 'call.recording_started',
758
+ recording_type: 'individual',
759
+ }),
760
+ );
761
+ expect(state.individualRecording).toBe(true);
762
+ expect(state.recording).toBe(false);
763
+ expect(state.rawRecording).toBe(false);
764
+ });
765
+
766
+ it('should set individualRecording to false when individual recording stops', () => {
767
+ const state = new CallState();
768
+ state.updateFromEvent(
769
+ fromPartial({
770
+ type: 'call.recording_started',
771
+ recording_type: 'individual',
772
+ }),
773
+ );
774
+ expect(state.individualRecording).toBe(true);
775
+
776
+ state.updateFromEvent(
777
+ fromPartial({
778
+ type: 'call.recording_stopped',
779
+ recording_type: 'individual',
780
+ }),
781
+ );
782
+ expect(state.individualRecording).toBe(false);
783
+ });
784
+
785
+ it('should set individualRecording to false when individual recording fails', () => {
786
+ const state = new CallState();
787
+ state.updateFromEvent(
788
+ fromPartial({
789
+ type: 'call.recording_started',
790
+ recording_type: 'individual',
791
+ }),
792
+ );
793
+ expect(state.individualRecording).toBe(true);
794
+
795
+ state.updateFromEvent(
796
+ fromPartial({
797
+ type: 'call.recording_failed',
798
+ recording_type: 'individual',
799
+ }),
800
+ );
801
+ expect(state.individualRecording).toBe(false);
802
+ });
803
+ });
804
+
805
+ describe('RAW recording type', () => {
806
+ it('should set rawRecording to true when raw recording starts', () => {
807
+ const state = new CallState();
808
+ state.updateFromEvent(
809
+ fromPartial({
810
+ type: 'call.recording_started',
811
+ recording_type: 'raw',
812
+ }),
813
+ );
814
+ expect(state.rawRecording).toBe(true);
815
+ expect(state.recording).toBe(false);
816
+ expect(state.individualRecording).toBe(false);
817
+ });
818
+
819
+ it('should set rawRecording to false when raw recording stops', () => {
820
+ const state = new CallState();
821
+ state.updateFromEvent(
822
+ fromPartial({
823
+ type: 'call.recording_started',
824
+ recording_type: 'raw',
825
+ }),
826
+ );
827
+ expect(state.rawRecording).toBe(true);
828
+
829
+ state.updateFromEvent(
830
+ fromPartial({
831
+ type: 'call.recording_stopped',
832
+ recording_type: 'raw',
833
+ }),
834
+ );
835
+ expect(state.rawRecording).toBe(false);
836
+ });
837
+
838
+ it('should set rawRecording to false when raw recording fails', () => {
839
+ const state = new CallState();
840
+ state.updateFromEvent(
841
+ fromPartial({
842
+ type: 'call.recording_started',
843
+ recording_type: 'raw',
844
+ }),
845
+ );
846
+ expect(state.rawRecording).toBe(true);
847
+
848
+ state.updateFromEvent(
849
+ fromPartial({
850
+ type: 'call.recording_failed',
851
+ recording_type: 'raw',
852
+ }),
853
+ );
854
+ expect(state.rawRecording).toBe(false);
855
+ });
856
+ });
857
+
858
+ describe('Legacy (undefined) recording type', () => {
859
+ it('should set recording to true when recording starts with undefined type (legacy)', () => {
860
+ const state = new CallState();
861
+ state.updateFromEvent(
862
+ fromPartial({
863
+ type: 'call.recording_started',
864
+ recording_type: undefined,
865
+ }),
866
+ );
867
+ expect(state.recording).toBe(true);
868
+ expect(state.individualRecording).toBe(false);
869
+ expect(state.rawRecording).toBe(false);
870
+ });
871
+
872
+ it('should set recording to false when recording stops with undefined type (legacy)', () => {
873
+ const state = new CallState();
874
+ state.updateFromEvent(
875
+ fromPartial({
876
+ type: 'call.recording_started',
877
+ recording_type: undefined,
878
+ }),
879
+ );
880
+ expect(state.recording).toBe(true);
881
+
882
+ state.updateFromEvent(
883
+ fromPartial({
884
+ type: 'call.recording_stopped',
885
+ recording_type: undefined,
886
+ }),
887
+ );
888
+ expect(state.recording).toBe(false);
889
+ });
890
+
891
+ it('should set recording to false when recording fails with undefined type (legacy)', () => {
892
+ const state = new CallState();
893
+ state.updateFromEvent(
894
+ fromPartial({
895
+ type: 'call.recording_started',
896
+ recording_type: undefined,
897
+ }),
898
+ );
899
+ expect(state.recording).toBe(true);
900
+
901
+ state.updateFromEvent(
902
+ fromPartial({
903
+ type: 'call.recording_failed',
904
+ recording_type: undefined,
905
+ }),
906
+ );
907
+ expect(state.recording).toBe(false);
908
+ });
909
+ });
910
+
911
+ describe('Recording type isolation', () => {
912
+ it('should not affect other recording types when composite recording changes', () => {
913
+ const state = new CallState();
914
+ // Start all three types
915
+ state.updateFromEvent(
916
+ fromPartial({
917
+ type: 'call.recording_started',
918
+ recording_type: 'composite',
919
+ }),
920
+ );
921
+ state.updateFromEvent(
922
+ fromPartial({
923
+ type: 'call.recording_started',
924
+ recording_type: 'individual',
925
+ }),
926
+ );
927
+ state.updateFromEvent(
928
+ fromPartial({
929
+ type: 'call.recording_started',
930
+ recording_type: 'raw',
931
+ }),
932
+ );
933
+
934
+ expect(state.recording).toBe(true);
935
+ expect(state.individualRecording).toBe(true);
936
+ expect(state.rawRecording).toBe(true);
937
+
938
+ // Stop composite recording only
939
+ state.updateFromEvent(
940
+ fromPartial({
941
+ type: 'call.recording_stopped',
942
+ recording_type: 'composite',
943
+ }),
944
+ );
945
+
946
+ expect(state.recording).toBe(false);
947
+ expect(state.individualRecording).toBe(true);
948
+ expect(state.rawRecording).toBe(true);
949
+ });
950
+
951
+ it('should not affect other recording types when individual recording changes', () => {
952
+ const state = new CallState();
953
+ // Start all three types
954
+ state.updateFromEvent(
955
+ fromPartial({
956
+ type: 'call.recording_started',
957
+ recording_type: 'composite',
958
+ }),
959
+ );
960
+ state.updateFromEvent(
961
+ fromPartial({
962
+ type: 'call.recording_started',
963
+ recording_type: 'individual',
964
+ }),
965
+ );
966
+ state.updateFromEvent(
967
+ fromPartial({
968
+ type: 'call.recording_started',
969
+ recording_type: 'raw',
970
+ }),
971
+ );
972
+
973
+ // Stop individual recording only
974
+ state.updateFromEvent(
975
+ fromPartial({
976
+ type: 'call.recording_stopped',
977
+ recording_type: 'individual',
978
+ }),
979
+ );
980
+
981
+ expect(state.recording).toBe(true);
982
+ expect(state.individualRecording).toBe(false);
983
+ expect(state.rawRecording).toBe(true);
984
+ });
985
+
986
+ it('should not affect other recording types when raw recording changes', () => {
987
+ const state = new CallState();
988
+ // Start all three types
989
+ state.updateFromEvent(
990
+ fromPartial({
991
+ type: 'call.recording_started',
992
+ recording_type: 'composite',
993
+ }),
994
+ );
995
+ state.updateFromEvent(
996
+ fromPartial({
997
+ type: 'call.recording_started',
998
+ recording_type: 'individual',
999
+ }),
1000
+ );
1001
+ state.updateFromEvent(
1002
+ fromPartial({
1003
+ type: 'call.recording_started',
1004
+ recording_type: 'raw',
1005
+ }),
1006
+ );
1007
+
1008
+ // Stop raw recording only
1009
+ state.updateFromEvent(
1010
+ fromPartial({
1011
+ type: 'call.recording_stopped',
1012
+ recording_type: 'raw',
1013
+ }),
1014
+ );
1015
+
1016
+ expect(state.recording).toBe(true);
1017
+ expect(state.individualRecording).toBe(true);
1018
+ expect(state.rawRecording).toBe(false);
1019
+ });
1020
+
1021
+ it('should handle multiple recording types being active simultaneously', () => {
1022
+ const state = new CallState();
1023
+
1024
+ // Start composite and individual
1025
+ state.updateFromEvent(
1026
+ fromPartial({
1027
+ type: 'call.recording_started',
1028
+ recording_type: 'composite',
1029
+ }),
1030
+ );
1031
+ state.updateFromEvent(
1032
+ fromPartial({
1033
+ type: 'call.recording_started',
1034
+ recording_type: 'individual',
1035
+ }),
1036
+ );
1037
+
1038
+ expect(state.recording).toBe(true);
1039
+ expect(state.individualRecording).toBe(true);
1040
+ expect(state.rawRecording).toBe(false);
1041
+
1042
+ // Add raw recording
1043
+ state.updateFromEvent(
1044
+ fromPartial({
1045
+ type: 'call.recording_started',
1046
+ recording_type: 'raw',
1047
+ }),
1048
+ );
1049
+
1050
+ expect(state.recording).toBe(true);
1051
+ expect(state.individualRecording).toBe(true);
1052
+ expect(state.rawRecording).toBe(true);
1053
+ });
1054
+ });
1055
+ });
1056
+
712
1057
  describe('call.session events', () => {
713
1058
  it('should update the call metadata when a session starts', () => {
714
1059
  const state = new CallState();
715
- state.updateFromEvent({
716
- type: 'call.session_started',
717
- call: {
718
- // @ts-expect-error incomplete data
719
- session: {
720
- id: 'session-id',
721
- participants: [],
722
- participants_count_by_role: {},
1060
+ state.updateFromEvent(
1061
+ fromPartial({
1062
+ type: 'call.session_started',
1063
+ call: {
1064
+ egress: {},
1065
+ session: fromPartial({
1066
+ id: 'session-id',
1067
+ participants: [],
1068
+ participants_count_by_role: {},
1069
+ }),
723
1070
  },
724
- },
725
- });
1071
+ }),
1072
+ );
726
1073
 
727
1074
  expect(state.session).toEqual({
728
1075
  id: 'session-id',
@@ -733,17 +1080,19 @@ describe('CallState', () => {
733
1080
 
734
1081
  it('should update the call metadata when a session ends', () => {
735
1082
  const state = new CallState();
736
- state.updateFromEvent({
737
- type: 'call.session_ended',
738
- call: {
739
- // @ts-expect-error incomplete data
740
- session: {
741
- id: 'session-id',
742
- participants: [],
743
- participants_count_by_role: {},
1083
+ state.updateFromEvent(
1084
+ fromPartial({
1085
+ type: 'call.session_ended',
1086
+ call: {
1087
+ egress: {},
1088
+ session: fromPartial({
1089
+ id: 'session-id',
1090
+ participants: [],
1091
+ participants_count_by_role: {},
1092
+ }),
744
1093
  },
745
- },
746
- });
1094
+ }),
1095
+ );
747
1096
  expect(state.session).toEqual({
748
1097
  id: 'session-id',
749
1098
  participants: [],
@@ -753,13 +1102,12 @@ describe('CallState', () => {
753
1102
 
754
1103
  it('should update the call metadata when a participant joins', () => {
755
1104
  const state = new CallState();
756
- state.updateFromCallResponse({
757
- // @ts-expect-error incomplete data
758
- session: {
759
- participants: [],
760
- participants_count_by_role: {},
761
- },
762
- });
1105
+ state.updateFromCallResponse(
1106
+ fromPartial({
1107
+ session: { participants: [], participants_count_by_role: {} },
1108
+ egress: {},
1109
+ }),
1110
+ );
763
1111
  state.updateFromEvent({
764
1112
  type: 'call.session_participant_joined',
765
1113
  participant: {
@@ -781,19 +1129,21 @@ describe('CallState', () => {
781
1129
 
782
1130
  it('should update the call metadata when a participant leaves', () => {
783
1131
  const state = new CallState();
784
- state.updateFromCallResponse({
785
- session: {
786
- participants: [
787
- {
788
- joined_at: '2021-01-01T00:00:00.000Z',
789
- // @ts-expect-error incomplete data
790
- user: { id: 'user-id', role: 'user' },
791
- user_session_id: '123',
792
- },
793
- ],
794
- participants_count_by_role: { user: 1 },
795
- },
796
- });
1132
+ state.updateFromCallResponse(
1133
+ fromPartial({
1134
+ egress: {},
1135
+ session: {
1136
+ participants: [
1137
+ {
1138
+ joined_at: '2021-01-01T00:00:00.000Z',
1139
+ user: { id: 'user-id', role: 'user' },
1140
+ user_session_id: '123',
1141
+ },
1142
+ ],
1143
+ participants_count_by_role: { user: 1 },
1144
+ },
1145
+ }),
1146
+ );
797
1147
  state.updateFromEvent({
798
1148
  type: 'call.session_participant_left',
799
1149
  participant: {
@@ -810,18 +1160,20 @@ describe('CallState', () => {
810
1160
 
811
1161
  it('should update existing participant', () => {
812
1162
  const state = new CallState();
813
- state.updateFromCallResponse({
814
- session: {
815
- participants: [
816
- {
817
- // @ts-expect-error incomplete data
818
- user: { id: 'user-id', role: 'user' },
819
- user_session_id: '123',
820
- },
821
- ],
822
- participants_count_by_role: { user: 1 },
823
- },
824
- });
1163
+ state.updateFromCallResponse(
1164
+ fromPartial({
1165
+ egress: {},
1166
+ session: {
1167
+ participants: [
1168
+ {
1169
+ user: { id: 'user-id', role: 'user' },
1170
+ user_session_id: '123',
1171
+ },
1172
+ ],
1173
+ participants_count_by_role: { user: 1 },
1174
+ },
1175
+ }),
1176
+ );
825
1177
  state.updateFromEvent({
826
1178
  type: 'call.session_participant_joined',
827
1179
  participant: {
@@ -843,10 +1195,12 @@ describe('CallState', () => {
843
1195
 
844
1196
  it('should handle call.session_participant_updated events', () => {
845
1197
  const state = new CallState();
846
- state.updateFromCallResponse({
847
- // @ts-expect-error incomplete data
848
- session: { participants: [], participants_count_by_role: {} },
849
- });
1198
+ state.updateFromCallResponse(
1199
+ fromPartial({
1200
+ session: { participants: [], participants_count_by_role: {} },
1201
+ egress: {},
1202
+ }),
1203
+ );
850
1204
  // @ts-expect-error incomplete data
851
1205
  state.updateFromEvent({
852
1206
  type: 'call.session_participant_count_updated',
@@ -866,10 +1220,12 @@ describe('CallState', () => {
866
1220
 
867
1221
  it('should not update the participant counts when call is joined', () => {
868
1222
  const state = new CallState();
869
- state.updateFromCallResponse({
870
- // @ts-expect-error incomplete data
871
- session: { participants: [], participants_count_by_role: {} },
872
- });
1223
+ state.updateFromCallResponse(
1224
+ fromPartial({
1225
+ session: { participants: [], participants_count_by_role: {} },
1226
+ egress: {},
1227
+ }),
1228
+ );
873
1229
  state.setCallingState(CallingState.JOINED);
874
1230
 
875
1231
  // @ts-expect-error incomplete data