react-jssip-kit 0.7.8 → 0.7.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -473,6 +473,175 @@ function removeSessionState(state, sessionId) {
473
473
  });
474
474
  }
475
475
 
476
+ // src/jssip-lib/sip/debugLogging.ts
477
+ var describePc = (pc) => ({
478
+ connectionState: pc?.connectionState,
479
+ signalingState: pc?.signalingState,
480
+ iceConnectionState: pc?.iceConnectionState
481
+ });
482
+ var SipDebugLogger = class {
483
+ constructor() {
484
+ this.enabled = false;
485
+ this.statsStops = /* @__PURE__ */ new Map();
486
+ }
487
+ setEnabled(enabled) {
488
+ this.enabled = enabled;
489
+ if (!enabled) {
490
+ this.statsStops.forEach((stop) => stop());
491
+ this.statsStops.clear();
492
+ }
493
+ }
494
+ isEnabled() {
495
+ return this.enabled;
496
+ }
497
+ logLocalAudioError(sessionId, message, pc, extra) {
498
+ if (!this.enabled)
499
+ return;
500
+ console.error(message, {
501
+ sessionId,
502
+ pc: describePc(pc),
503
+ ...extra
504
+ });
505
+ void this.logOutboundStats(sessionId, pc, message);
506
+ }
507
+ logRemoteAudioError(sessionId, message, pc, extra) {
508
+ if (!this.enabled)
509
+ return;
510
+ console.error(message, {
511
+ sessionId,
512
+ pc: describePc(pc),
513
+ ...extra
514
+ });
515
+ void this.logInboundStats(sessionId, pc, message);
516
+ }
517
+ logMicRecoveryDrop(payload) {
518
+ if (!this.enabled)
519
+ return;
520
+ console.error("[sip] microphone dropped", payload);
521
+ }
522
+ logIceReady(sessionId, payload) {
523
+ if (!this.enabled)
524
+ return;
525
+ console.info("[sip] ice ready", { sessionId, ...payload });
526
+ }
527
+ logIceReadyConfig(sessionId, delayMs) {
528
+ if (!this.enabled)
529
+ return;
530
+ console.info("[sip] ice ready config", { sessionId, delayMs });
531
+ }
532
+ startCallStatsLogging(sessionId, session) {
533
+ if (!this.enabled || this.statsStops.has(sessionId))
534
+ return;
535
+ let pc = session?.connection ?? null;
536
+ const onPeer = (data) => {
537
+ pc = data.peerconnection;
538
+ };
539
+ session.on?.("peerconnection", onPeer);
540
+ const intervalMs = 3e3;
541
+ const logStats = async () => {
542
+ if (!this.enabled || !pc?.getStats)
543
+ return;
544
+ try {
545
+ const report = await pc.getStats();
546
+ const { outboundAudio, inboundAudio } = collectAudioStats(report);
547
+ console.info("[sip] call stats", {
548
+ sessionId,
549
+ pc: describePc(pc),
550
+ outboundAudio,
551
+ inboundAudio
552
+ });
553
+ } catch (err) {
554
+ console.error("[sip] call stats failed", { sessionId, error: err });
555
+ }
556
+ };
557
+ const timer = setInterval(() => {
558
+ void logStats();
559
+ }, intervalMs);
560
+ void logStats();
561
+ const stop = () => {
562
+ clearInterval(timer);
563
+ session.off?.("peerconnection", onPeer);
564
+ this.statsStops.delete(sessionId);
565
+ };
566
+ this.statsStops.set(sessionId, stop);
567
+ }
568
+ stopCallStatsLogging(sessionId) {
569
+ const stop = this.statsStops.get(sessionId);
570
+ if (stop)
571
+ stop();
572
+ }
573
+ async logOutboundStats(sessionId, pc, context) {
574
+ if (!pc?.getStats)
575
+ return;
576
+ try {
577
+ const report = await pc.getStats();
578
+ const { outboundAudio } = collectAudioStats(report);
579
+ if (outboundAudio.length) {
580
+ console.info("[sip] outgoing audio stats", {
581
+ sessionId,
582
+ context,
583
+ outboundAudio
584
+ });
585
+ }
586
+ } catch (err) {
587
+ console.error("[sip] outgoing audio stats failed", {
588
+ sessionId,
589
+ context,
590
+ error: err
591
+ });
592
+ }
593
+ }
594
+ async logInboundStats(sessionId, pc, context) {
595
+ if (!pc?.getStats)
596
+ return;
597
+ try {
598
+ const report = await pc.getStats();
599
+ const { inboundAudio } = collectAudioStats(report);
600
+ if (inboundAudio.length) {
601
+ console.error("[sip] incoming audio stats", {
602
+ sessionId,
603
+ context,
604
+ inboundAudio
605
+ });
606
+ }
607
+ } catch (err) {
608
+ console.error("[sip] incoming audio stats failed", {
609
+ sessionId,
610
+ context,
611
+ error: err
612
+ });
613
+ }
614
+ }
615
+ };
616
+ var sipDebugLogger = new SipDebugLogger();
617
+ function collectAudioStats(report) {
618
+ const outboundAudio = [];
619
+ const inboundAudio = [];
620
+ report.forEach((stat) => {
621
+ const kind = stat.kind ?? stat.mediaType;
622
+ if (stat.type === "outbound-rtp" && kind === "audio") {
623
+ outboundAudio.push({
624
+ id: stat.id,
625
+ packetsSent: stat.packetsSent,
626
+ bytesSent: stat.bytesSent,
627
+ jitter: stat.jitter,
628
+ roundTripTime: stat.roundTripTime
629
+ });
630
+ }
631
+ if (stat.type === "inbound-rtp" && kind === "audio") {
632
+ inboundAudio.push({
633
+ id: stat.id,
634
+ packetsReceived: stat.packetsReceived,
635
+ packetsLost: stat.packetsLost,
636
+ bytesReceived: stat.bytesReceived,
637
+ jitter: stat.jitter,
638
+ roundTripTime: stat.roundTripTime
639
+ });
640
+ }
641
+ });
642
+ return { outboundAudio, inboundAudio };
643
+ }
644
+
476
645
  // src/jssip-lib/sip/handlers/sessionHandlers.ts
477
646
  function createSessionHandlers(deps) {
478
647
  const {
@@ -481,8 +650,20 @@ function createSessionHandlers(deps) {
481
650
  rtc,
482
651
  detachSessionHandlers,
483
652
  onSessionFailed,
484
- sessionId
653
+ sessionId,
654
+ iceCandidateReadyDelayMs
485
655
  } = deps;
656
+ let iceReadyCalled = false;
657
+ let iceReadyTimer = null;
658
+ const clearIceReadyTimer = () => {
659
+ if (!iceReadyTimer)
660
+ return;
661
+ clearTimeout(iceReadyTimer);
662
+ iceReadyTimer = null;
663
+ };
664
+ if (typeof iceCandidateReadyDelayMs === "number") {
665
+ sipDebugLogger.logIceReadyConfig(sessionId, iceCandidateReadyDelayMs);
666
+ }
486
667
  return {
487
668
  progress: (e) => {
488
669
  emitter.emit("progress", e);
@@ -505,6 +686,7 @@ function createSessionHandlers(deps) {
505
686
  },
506
687
  ended: (e) => {
507
688
  emitter.emit("ended", e);
689
+ clearIceReadyTimer();
508
690
  detachSessionHandlers();
509
691
  rtc.cleanup();
510
692
  const nextSessions = state.getState().sessions.filter((s) => s.id !== sessionId);
@@ -514,6 +696,7 @@ function createSessionHandlers(deps) {
514
696
  },
515
697
  failed: (e) => {
516
698
  emitter.emit("failed", e);
699
+ clearIceReadyTimer();
517
700
  detachSessionHandlers();
518
701
  rtc.cleanup();
519
702
  const cause = e?.cause || "call failed";
@@ -542,13 +725,55 @@ function createSessionHandlers(deps) {
542
725
  reinvite: (e) => emitter.emit("reinvite", e),
543
726
  update: (e) => emitter.emit("update", e),
544
727
  sdp: (e) => emitter.emit("sdp", e),
545
- icecandidate: (e) => emitter.emit("icecandidate", e),
728
+ icecandidate: (e) => {
729
+ const candidate = e?.candidate;
730
+ const ready = typeof e?.ready === "function" ? e.ready : null;
731
+ const delayMs = typeof iceCandidateReadyDelayMs === "number" ? iceCandidateReadyDelayMs : null;
732
+ if (!iceReadyCalled && ready && delayMs != null) {
733
+ if (candidate?.type === "srflx" && candidate?.relatedAddress != null && candidate?.relatedPort != null) {
734
+ iceReadyCalled = true;
735
+ if (iceReadyTimer) {
736
+ clearTimeout(iceReadyTimer);
737
+ iceReadyTimer = null;
738
+ }
739
+ sipDebugLogger.logIceReady(sessionId, {
740
+ source: "srflx",
741
+ delayMs,
742
+ candidateType: candidate?.type
743
+ });
744
+ ready();
745
+ } else if (!iceReadyTimer && delayMs > 0) {
746
+ iceReadyTimer = setTimeout(() => {
747
+ iceReadyTimer = null;
748
+ if (iceReadyCalled)
749
+ return;
750
+ iceReadyCalled = true;
751
+ sipDebugLogger.logIceReady(sessionId, {
752
+ source: "timer",
753
+ delayMs,
754
+ candidateType: candidate?.type
755
+ });
756
+ ready();
757
+ }, delayMs);
758
+ } else if (delayMs === 0) {
759
+ iceReadyCalled = true;
760
+ sipDebugLogger.logIceReady(sessionId, {
761
+ source: "immediate",
762
+ delayMs,
763
+ candidateType: candidate?.type
764
+ });
765
+ ready();
766
+ }
767
+ }
768
+ emitter.emit("icecandidate", e);
769
+ },
546
770
  refer: (e) => emitter.emit("refer", e),
547
771
  replaces: (e) => emitter.emit("replaces", e),
548
772
  newDTMF: (e) => emitter.emit("newDTMF", e),
549
773
  newInfo: (e) => emitter.emit("newInfo", e),
550
774
  getusermediafailed: (e) => {
551
775
  emitter.emit("getusermediafailed", e);
776
+ clearIceReadyTimer();
552
777
  detachSessionHandlers();
553
778
  rtc.cleanup();
554
779
  onSessionFailed("getUserMedia failed", e);
@@ -558,6 +783,7 @@ function createSessionHandlers(deps) {
558
783
  },
559
784
  "peerconnection:createofferfailed": (e) => {
560
785
  emitter.emit("peerconnection:createofferfailed", e);
786
+ clearIceReadyTimer();
561
787
  detachSessionHandlers();
562
788
  rtc.cleanup();
563
789
  onSessionFailed("peer connection createOffer failed", e);
@@ -567,6 +793,7 @@ function createSessionHandlers(deps) {
567
793
  },
568
794
  "peerconnection:createanswerfailed": (e) => {
569
795
  emitter.emit("peerconnection:createanswerfailed", e);
796
+ clearIceReadyTimer();
570
797
  detachSessionHandlers();
571
798
  rtc.cleanup();
572
799
  onSessionFailed("peer connection createAnswer failed", e);
@@ -576,6 +803,7 @@ function createSessionHandlers(deps) {
576
803
  },
577
804
  "peerconnection:setlocaldescriptionfailed": (e) => {
578
805
  emitter.emit("peerconnection:setlocaldescriptionfailed", e);
806
+ clearIceReadyTimer();
579
807
  detachSessionHandlers();
580
808
  rtc.cleanup();
581
809
  onSessionFailed("peer connection setLocalDescription failed", e);
@@ -585,6 +813,7 @@ function createSessionHandlers(deps) {
585
813
  },
586
814
  "peerconnection:setremotedescriptionfailed": (e) => {
587
815
  emitter.emit("peerconnection:setremotedescriptionfailed", e);
816
+ clearIceReadyTimer();
588
817
  detachSessionHandlers();
589
818
  rtc.cleanup();
590
819
  onSessionFailed("peer connection setRemoteDescription failed", e);
@@ -837,165 +1066,6 @@ var SessionManager = class {
837
1066
  }
838
1067
  };
839
1068
 
840
- // src/jssip-lib/sip/debugLogging.ts
841
- var describePc = (pc) => ({
842
- connectionState: pc?.connectionState,
843
- signalingState: pc?.signalingState,
844
- iceConnectionState: pc?.iceConnectionState
845
- });
846
- var SipDebugLogger = class {
847
- constructor() {
848
- this.enabled = false;
849
- this.statsStops = /* @__PURE__ */ new Map();
850
- }
851
- setEnabled(enabled) {
852
- this.enabled = enabled;
853
- if (!enabled) {
854
- this.statsStops.forEach((stop) => stop());
855
- this.statsStops.clear();
856
- }
857
- }
858
- isEnabled() {
859
- return this.enabled;
860
- }
861
- logLocalAudioError(sessionId, message, pc, extra) {
862
- if (!this.enabled)
863
- return;
864
- console.error(message, {
865
- sessionId,
866
- pc: describePc(pc),
867
- ...extra
868
- });
869
- void this.logOutboundStats(sessionId, pc, message);
870
- }
871
- logRemoteAudioError(sessionId, message, pc, extra) {
872
- if (!this.enabled)
873
- return;
874
- console.error(message, {
875
- sessionId,
876
- pc: describePc(pc),
877
- ...extra
878
- });
879
- void this.logInboundStats(sessionId, pc, message);
880
- }
881
- logMicRecoveryDrop(payload) {
882
- if (!this.enabled)
883
- return;
884
- console.error("[sip] microphone dropped", payload);
885
- }
886
- startCallStatsLogging(sessionId, session) {
887
- if (!this.enabled || this.statsStops.has(sessionId))
888
- return;
889
- let pc = session?.connection ?? null;
890
- const onPeer = (data) => {
891
- pc = data.peerconnection;
892
- };
893
- session.on?.("peerconnection", onPeer);
894
- const intervalMs = 3e3;
895
- const logStats = async () => {
896
- if (!this.enabled || !pc?.getStats)
897
- return;
898
- try {
899
- const report = await pc.getStats();
900
- const { outboundAudio, inboundAudio } = collectAudioStats(report);
901
- console.info("[sip] call stats", {
902
- sessionId,
903
- pc: describePc(pc),
904
- outboundAudio,
905
- inboundAudio
906
- });
907
- } catch (err) {
908
- console.error("[sip] call stats failed", { sessionId, error: err });
909
- }
910
- };
911
- const timer = setInterval(() => {
912
- void logStats();
913
- }, intervalMs);
914
- void logStats();
915
- const stop = () => {
916
- clearInterval(timer);
917
- session.off?.("peerconnection", onPeer);
918
- this.statsStops.delete(sessionId);
919
- };
920
- this.statsStops.set(sessionId, stop);
921
- }
922
- stopCallStatsLogging(sessionId) {
923
- const stop = this.statsStops.get(sessionId);
924
- if (stop)
925
- stop();
926
- }
927
- async logOutboundStats(sessionId, pc, context) {
928
- if (!pc?.getStats)
929
- return;
930
- try {
931
- const report = await pc.getStats();
932
- const { outboundAudio } = collectAudioStats(report);
933
- if (outboundAudio.length) {
934
- console.info("[sip] outgoing audio stats", {
935
- sessionId,
936
- context,
937
- outboundAudio
938
- });
939
- }
940
- } catch (err) {
941
- console.error("[sip] outgoing audio stats failed", {
942
- sessionId,
943
- context,
944
- error: err
945
- });
946
- }
947
- }
948
- async logInboundStats(sessionId, pc, context) {
949
- if (!pc?.getStats)
950
- return;
951
- try {
952
- const report = await pc.getStats();
953
- const { inboundAudio } = collectAudioStats(report);
954
- if (inboundAudio.length) {
955
- console.error("[sip] incoming audio stats", {
956
- sessionId,
957
- context,
958
- inboundAudio
959
- });
960
- }
961
- } catch (err) {
962
- console.error("[sip] incoming audio stats failed", {
963
- sessionId,
964
- context,
965
- error: err
966
- });
967
- }
968
- }
969
- };
970
- var sipDebugLogger = new SipDebugLogger();
971
- function collectAudioStats(report) {
972
- const outboundAudio = [];
973
- const inboundAudio = [];
974
- report.forEach((stat) => {
975
- const kind = stat.kind ?? stat.mediaType;
976
- if (stat.type === "outbound-rtp" && kind === "audio") {
977
- outboundAudio.push({
978
- id: stat.id,
979
- packetsSent: stat.packetsSent,
980
- bytesSent: stat.bytesSent,
981
- jitter: stat.jitter,
982
- roundTripTime: stat.roundTripTime
983
- });
984
- }
985
- if (stat.type === "inbound-rtp" && kind === "audio") {
986
- inboundAudio.push({
987
- id: stat.id,
988
- packetsReceived: stat.packetsReceived,
989
- packetsLost: stat.packetsLost,
990
- bytesReceived: stat.bytesReceived,
991
- jitter: stat.jitter,
992
- roundTripTime: stat.roundTripTime
993
- });
994
- }
995
- });
996
- return { outboundAudio, inboundAudio };
997
- }
998
-
999
1069
  // src/jssip-lib/sip/sessionLifecycle.ts
1000
1070
  var SessionLifecycle = class {
1001
1071
  constructor(deps) {
@@ -1640,9 +1710,11 @@ var SipClient = class extends EventTargetEmitter {
1640
1710
  micRecoveryIntervalMs,
1641
1711
  micRecoveryMaxRetries,
1642
1712
  maxSessionCount,
1713
+ iceCandidateReadyDelayMs,
1643
1714
  ...uaCfg
1644
1715
  } = config;
1645
1716
  this.maxSessionCount = typeof maxSessionCount === "number" ? maxSessionCount : Infinity;
1717
+ this.iceCandidateReadyDelayMs = typeof iceCandidateReadyDelayMs === "number" ? iceCandidateReadyDelayMs : void 0;
1646
1718
  this.micRecovery.configure({
1647
1719
  enabled: Boolean(enableMicRecovery),
1648
1720
  intervalMs: micRecoveryIntervalMs,
@@ -1790,6 +1862,7 @@ var SipClient = class extends EventTargetEmitter {
1790
1862
  emitError: (raw, code, fallback) => this.emitError(raw, code, fallback),
1791
1863
  onSessionFailed: (err, event) => this.onSessionFailed(err, event),
1792
1864
  enableMicrophoneRecovery: (confirmedSessionId) => this.micRecovery.enable(confirmedSessionId),
1865
+ iceCandidateReadyDelayMs: this.iceCandidateReadyDelayMs,
1793
1866
  sessionId
1794
1867
  });
1795
1868
  }