@wazo/sdk 0.43.6 → 1.0.0-alpha.1

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 (141) hide show
  1. package/dist/wazo-sdk.js +2 -2
  2. package/esm/__tests__/__voice__/call.test.js +72 -0
  3. package/esm/__tests__/__voice__/softphone.test.js +66 -0
  4. package/esm/checker/checks/webrtc.js +2 -1
  5. package/esm/domain/InvalidState.js +8 -0
  6. package/esm/domain/InvalidStateTransition.js +10 -0
  7. package/esm/domain/Phone/WebRTCPhone.js +33 -35
  8. package/esm/index.js +2 -0
  9. package/esm/simple/Phone.js +2 -1
  10. package/esm/simple/index.js +8 -2
  11. package/esm/simple/room/Room.js +2 -1
  12. package/esm/simple/room/SipRoom.js +2 -1
  13. package/esm/state-machine/call-state-machine.js +114 -0
  14. package/esm/state-machine/softphone-state-machine.js +39 -0
  15. package/esm/state-machine/utils.js +40 -0
  16. package/esm/types/__tests__/__voice__/call.test.d.ts +2 -0
  17. package/esm/types/__tests__/__voice__/call.test.d.ts.map +1 -0
  18. package/esm/types/__tests__/__voice__/softphone.test.d.ts +2 -0
  19. package/esm/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
  20. package/esm/types/checker/checks/webrtc.d.ts.map +1 -1
  21. package/esm/types/domain/InvalidState.d.ts +6 -0
  22. package/esm/types/domain/InvalidState.d.ts.map +1 -0
  23. package/esm/types/domain/InvalidStateTransition.d.ts +7 -0
  24. package/esm/types/domain/InvalidStateTransition.d.ts.map +1 -0
  25. package/esm/types/domain/Phone/WebRTCPhone.d.ts +0 -1
  26. package/esm/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  27. package/esm/types/domain/types.d.ts +4 -0
  28. package/esm/types/domain/types.d.ts.map +1 -1
  29. package/esm/types/index.d.ts +2 -0
  30. package/esm/types/index.d.ts.map +1 -1
  31. package/esm/types/simple/Phone.d.ts.map +1 -1
  32. package/esm/types/simple/index.d.ts +7 -2
  33. package/esm/types/simple/index.d.ts.map +1 -1
  34. package/esm/types/simple/room/Room.d.ts.map +1 -1
  35. package/esm/types/simple/room/SipRoom.d.ts +1 -1
  36. package/esm/types/simple/room/SipRoom.d.ts.map +1 -1
  37. package/esm/types/state-machine/call-state-machine.d.ts +43 -0
  38. package/esm/types/state-machine/call-state-machine.d.ts.map +1 -0
  39. package/esm/types/state-machine/softphone-state-machine.d.ts +19 -0
  40. package/esm/types/state-machine/softphone-state-machine.d.ts.map +1 -0
  41. package/esm/types/state-machine/utils.d.ts +9 -0
  42. package/esm/types/state-machine/utils.d.ts.map +1 -0
  43. package/esm/types/utils/sdp.d.ts +6 -0
  44. package/esm/types/utils/sdp.d.ts.map +1 -1
  45. package/esm/types/utils/sip-logger.d.ts +4 -0
  46. package/esm/types/utils/sip-logger.d.ts.map +1 -0
  47. package/esm/types/utils/webrtc.d.ts +40 -0
  48. package/esm/types/utils/webrtc.d.ts.map +1 -1
  49. package/esm/types/voice/call.d.ts +110 -0
  50. package/esm/types/voice/call.d.ts.map +1 -0
  51. package/esm/types/voice/softphone.d.ts +77 -0
  52. package/esm/types/voice/softphone.d.ts.map +1 -0
  53. package/esm/types/web-rtc-client.d.ts +1 -36
  54. package/esm/types/web-rtc-client.d.ts.map +1 -1
  55. package/esm/utils/sdp.js +26 -0
  56. package/esm/utils/sip-logger.js +32 -0
  57. package/esm/utils/webrtc.js +465 -0
  58. package/esm/voice/call.js +527 -0
  59. package/esm/voice/softphone.js +347 -0
  60. package/esm/web-rtc-client.js +86 -584
  61. package/lib/__tests__/__voice__/call.test.js +86 -0
  62. package/lib/__tests__/__voice__/softphone.test.js +80 -0
  63. package/lib/checker/checks/webrtc.js +2 -1
  64. package/lib/domain/InvalidState.js +9 -0
  65. package/lib/domain/InvalidStateTransition.js +10 -0
  66. package/lib/domain/Phone/WebRTCPhone.js +33 -35
  67. package/lib/index.js +6 -2
  68. package/lib/simple/Phone.js +2 -1
  69. package/lib/simple/index.js +6 -0
  70. package/lib/simple/room/Room.js +4 -3
  71. package/lib/simple/room/SipRoom.js +2 -1
  72. package/lib/state-machine/call-state-machine.js +120 -0
  73. package/lib/state-machine/softphone-state-machine.js +42 -0
  74. package/lib/state-machine/utils.js +52 -0
  75. package/lib/types/__tests__/__voice__/call.test.d.ts +2 -0
  76. package/lib/types/__tests__/__voice__/call.test.d.ts.map +1 -0
  77. package/lib/types/__tests__/__voice__/softphone.test.d.ts +2 -0
  78. package/lib/types/__tests__/__voice__/softphone.test.d.ts.map +1 -0
  79. package/lib/types/checker/checks/webrtc.d.ts.map +1 -1
  80. package/lib/types/domain/InvalidState.d.ts +6 -0
  81. package/lib/types/domain/InvalidState.d.ts.map +1 -0
  82. package/lib/types/domain/InvalidStateTransition.d.ts +7 -0
  83. package/lib/types/domain/InvalidStateTransition.d.ts.map +1 -0
  84. package/lib/types/domain/Phone/WebRTCPhone.d.ts +0 -1
  85. package/lib/types/domain/Phone/WebRTCPhone.d.ts.map +1 -1
  86. package/lib/types/domain/types.d.ts +4 -0
  87. package/lib/types/domain/types.d.ts.map +1 -1
  88. package/lib/types/index.d.ts +2 -0
  89. package/lib/types/index.d.ts.map +1 -1
  90. package/lib/types/simple/Phone.d.ts.map +1 -1
  91. package/lib/types/simple/index.d.ts +7 -2
  92. package/lib/types/simple/index.d.ts.map +1 -1
  93. package/lib/types/simple/room/Room.d.ts.map +1 -1
  94. package/lib/types/simple/room/SipRoom.d.ts +1 -1
  95. package/lib/types/simple/room/SipRoom.d.ts.map +1 -1
  96. package/lib/types/state-machine/call-state-machine.d.ts +43 -0
  97. package/lib/types/state-machine/call-state-machine.d.ts.map +1 -0
  98. package/lib/types/state-machine/softphone-state-machine.d.ts +19 -0
  99. package/lib/types/state-machine/softphone-state-machine.d.ts.map +1 -0
  100. package/lib/types/state-machine/utils.d.ts +9 -0
  101. package/lib/types/state-machine/utils.d.ts.map +1 -0
  102. package/lib/types/utils/sdp.d.ts +6 -0
  103. package/lib/types/utils/sdp.d.ts.map +1 -1
  104. package/lib/types/utils/sip-logger.d.ts +4 -0
  105. package/lib/types/utils/sip-logger.d.ts.map +1 -0
  106. package/lib/types/utils/webrtc.d.ts +40 -0
  107. package/lib/types/utils/webrtc.d.ts.map +1 -1
  108. package/lib/types/voice/call.d.ts +110 -0
  109. package/lib/types/voice/call.d.ts.map +1 -0
  110. package/lib/types/voice/softphone.d.ts +77 -0
  111. package/lib/types/voice/softphone.d.ts.map +1 -0
  112. package/lib/types/web-rtc-client.d.ts +1 -36
  113. package/lib/types/web-rtc-client.d.ts.map +1 -1
  114. package/lib/utils/sdp.js +27 -1
  115. package/lib/utils/sip-logger.js +37 -0
  116. package/lib/utils/webrtc.js +521 -1
  117. package/lib/voice/call.js +577 -0
  118. package/lib/voice/softphone.js +375 -0
  119. package/lib/web-rtc-client.js +89 -601
  120. package/package.json +3 -2
  121. package/src/__tests__/__voice__/call.test.ts +90 -0
  122. package/src/__tests__/__voice__/softphone.test.ts +82 -0
  123. package/src/checker/checks/webrtc.ts +2 -1
  124. package/src/domain/InvalidState.ts +13 -0
  125. package/src/domain/InvalidStateTransition.ts +17 -0
  126. package/src/domain/Phone/WebRTCPhone.ts +34 -36
  127. package/src/domain/types.ts +6 -1
  128. package/src/index.ts +2 -0
  129. package/src/simple/Phone.ts +2 -1
  130. package/src/simple/index.ts +9 -2
  131. package/src/simple/room/Room.ts +2 -1
  132. package/src/simple/room/SipRoom.ts +2 -1
  133. package/src/state-machine/call-state-machine.ts +131 -0
  134. package/src/state-machine/softphone-state-machine.ts +49 -0
  135. package/src/state-machine/utils.ts +53 -0
  136. package/src/utils/sdp.ts +36 -0
  137. package/src/utils/sip-logger.ts +40 -0
  138. package/src/utils/webrtc.ts +571 -0
  139. package/src/voice/call.ts +713 -0
  140. package/src/voice/softphone.ts +485 -0
  141. package/src/web-rtc-client.ts +90 -690
@@ -0,0 +1,131 @@
1
+ import { setup, type ActorRefFrom } from 'xstate';
2
+
3
+ import softphone from '../voice/softphone';
4
+ import { States as SoftphoneStates } from './softphone-state-machine';
5
+
6
+ export const States = {
7
+ IDLE: 'idle',
8
+ RINGING: 'ringing',
9
+ IN_PROGRESS: 'progress',
10
+ ESTABLISHED: 'established',
11
+ TERMINATED: 'terminated',
12
+ } as const;
13
+
14
+ export const EstablishedStates = {
15
+ ONGOING: 'ongoing',
16
+ HELD: 'held',
17
+ MUTED: 'muted',
18
+ } as const;
19
+
20
+ export type StateTypes = typeof States[keyof typeof States];
21
+
22
+ export type EstablishedStateTypes = typeof EstablishedStates[keyof typeof EstablishedStates];
23
+
24
+ export const Actions = {
25
+ INCOMING_CALL: 'incomingCall',
26
+ MAKE_CALL: 'makeCall',
27
+ ACCEPT: 'accept',
28
+ REMOTLY_ACCEPTED: 'remotlyAccepted',
29
+ REJECT: 'reject',
30
+ HANGUP: 'hangup',
31
+ } as const;
32
+
33
+ export const EstablishedActions = {
34
+ HOLD: 'hold',
35
+ RESUME: 'resume',
36
+ MUTE: 'mute',
37
+ UN_MUTE: 'unMute',
38
+ } as const;
39
+
40
+ export type ActionTypes = typeof Actions[keyof typeof Actions];
41
+
42
+ export type EstablishedActionTypes = typeof EstablishedActions[keyof typeof EstablishedActions];
43
+
44
+ const callMachine = setup({
45
+ guards: {
46
+ isRegistered: () => softphone.getState() === SoftphoneStates.REGISTERED,
47
+ },
48
+ }).createMachine({
49
+ id: 'call',
50
+ initial: States.IDLE,
51
+ context: {},
52
+ states: {
53
+ [States.IDLE]: {
54
+ on: {
55
+ [Actions.INCOMING_CALL]: {
56
+ // guard: 'isRegistered',
57
+ target: States.RINGING,
58
+ },
59
+ [Actions.MAKE_CALL]: {
60
+ guard: 'isRegistered',
61
+ target: States.IN_PROGRESS,
62
+ },
63
+ },
64
+ },
65
+ [States.IN_PROGRESS]: {
66
+ on: {
67
+ [Actions.HANGUP]: {
68
+ guard: 'isRegistered',
69
+ target: States.TERMINATED,
70
+ },
71
+ [Actions.REMOTLY_ACCEPTED]: {
72
+ guard: 'isRegistered',
73
+ target: States.ESTABLISHED,
74
+ },
75
+ },
76
+ },
77
+ [States.RINGING]: {
78
+ on: {
79
+ [Actions.ACCEPT]: {
80
+ guard: 'isRegistered',
81
+ target: States.ESTABLISHED,
82
+ },
83
+ [Actions.REJECT]: {
84
+ guard: 'isRegistered',
85
+ target: States.TERMINATED,
86
+ },
87
+ },
88
+ },
89
+ [States.ESTABLISHED]: {
90
+ initial: EstablishedStates.ONGOING,
91
+ on: {
92
+ [Actions.HANGUP]: {
93
+ guard: 'isRegistered',
94
+ target: States.TERMINATED,
95
+ },
96
+ [EstablishedActions.HOLD]: {
97
+ guard: 'isRegistered',
98
+ target: `.${EstablishedStates.HELD}`,
99
+ },
100
+ [EstablishedActions.MUTE]: {
101
+ guard: 'isRegistered',
102
+ target: `.${EstablishedStates.MUTED}`,
103
+ },
104
+ },
105
+ states: {
106
+ [EstablishedStates.ONGOING]: { },
107
+ [EstablishedStates.HELD]: {
108
+ on: {
109
+ [EstablishedActions.RESUME]: {
110
+ guard: 'isRegistered',
111
+ target: EstablishedStates.ONGOING,
112
+ },
113
+ },
114
+ },
115
+ [EstablishedStates.MUTED]: {
116
+ on: {
117
+ [EstablishedActions.UN_MUTE]: {
118
+ guard: 'isRegistered',
119
+ target: EstablishedStates.ONGOING,
120
+ },
121
+ },
122
+ },
123
+ },
124
+ },
125
+ [States.TERMINATED]: { },
126
+ },
127
+ });
128
+
129
+ export type CallActorRef = ActorRefFrom<typeof callMachine>;
130
+
131
+ export default callMachine;
@@ -0,0 +1,49 @@
1
+ import { type ActorRefFrom, setup } from 'xstate';
2
+
3
+ export const States = {
4
+ UNREGISTERED: 'unregistered',
5
+ REGISTERING: 'registering',
6
+ REGISTERED: 'registered',
7
+ } as const;
8
+
9
+ export type StateTypes = typeof States[keyof typeof States];
10
+
11
+ export const Actions = {
12
+ REGISTER: 'register',
13
+ REGISTER_DONE: 'registerDone',
14
+ UNREGISTER: 'unregister',
15
+ UNREGISTERED: 'unregistered',
16
+ TRANSPORT_CLOSED: 'transportClosed',
17
+ } as const;
18
+
19
+ export type ActionTypes = typeof Actions[keyof typeof Actions];
20
+
21
+ const softphoneMachine = setup({}).createMachine({
22
+ id: 'softphone',
23
+ initial: States.UNREGISTERED,
24
+ context: {},
25
+ states: {
26
+ [States.UNREGISTERED]: {
27
+ on: {
28
+ [Actions.REGISTER]: States.REGISTERING,
29
+ },
30
+ },
31
+ [States.REGISTERING]: {
32
+ on: {
33
+ [Actions.REGISTER_DONE]: States.REGISTERED,
34
+ [Actions.TRANSPORT_CLOSED]: States.UNREGISTERED,
35
+ [Actions.UNREGISTERED]: States.UNREGISTERED,
36
+ },
37
+ },
38
+ [States.REGISTERED]: {
39
+ on: {
40
+ [Actions.UNREGISTER]: States.UNREGISTERED,
41
+ [Actions.TRANSPORT_CLOSED]: States.UNREGISTERED,
42
+ },
43
+ },
44
+ },
45
+ });
46
+
47
+ export type SoftphoneActorRef = ActorRefFrom<typeof softphoneMachine>;
48
+
49
+ export default softphoneMachine;
@@ -0,0 +1,53 @@
1
+ import { type SnapshotFrom } from 'xstate';
2
+ import { type SoftphoneActorRef, type ActionTypes as SoftphoneActions, type StateTypes as SoftphoneStates } from './softphone-state-machine';
3
+ import { type CallActorRef, type ActionTypes as CallActions, type EstablishedActionTypes as EstablishedCallActions, type StateTypes as CallStates, type EstablishedStateTypes } from './call-state-machine';
4
+
5
+ import InvalidStateTransition from '../domain/InvalidStateTransition';
6
+ import InvalidState from '../domain/InvalidState';
7
+ import IssueReporter from '../service/IssueReporter';
8
+
9
+ const logger = IssueReporter.loggerFor('state-machine');
10
+
11
+ export const getState = (actor: SoftphoneActorRef | CallActorRef): SoftphoneStates | CallStates | EstablishedStateTypes => {
12
+ return actor.getSnapshot().value as SoftphoneStates | CallStates | EstablishedStateTypes;
13
+ };
14
+
15
+ export const can = (actor: SoftphoneActorRef | CallActorRef, action: SoftphoneActions | CallActions | EstablishedCallActions): boolean => {
16
+ return actor.getSnapshot().can({ type: action });
17
+ };
18
+
19
+ export const hasState = (actor: SoftphoneActorRef | CallActorRef, state: SoftphoneStates | CallStates): boolean => {
20
+ return getState(actor) === state;
21
+ };
22
+
23
+ export const waitUntilState = (actor: SoftphoneActorRef | CallActorRef, state: SoftphoneStates | CallStates, timeout = 5000): Promise<unknown> => {
24
+ return Promise.race([
25
+ new Promise(resolve => {
26
+ (actor as SoftphoneActorRef).subscribe((snapshot: SnapshotFrom<SoftphoneActorRef | CallActorRef>) => {
27
+ if (snapshot.value === state) {
28
+ resolve(null);
29
+ }
30
+ });
31
+ }),
32
+ new Promise((_, reject) => setTimeout(() => reject(new Error(`Waited for state ${state} timeout.`)), timeout)),
33
+ ]);
34
+ };
35
+
36
+ export const assertCan = (actor: SoftphoneActorRef | CallActorRef, action: SoftphoneActions | CallActions | EstablishedCallActions): void => {
37
+ if (!can(actor, action)) {
38
+ const currentState = actor.getSnapshot().value;
39
+ const message = `Invalid state transition from ${currentState} with action ${action}`;
40
+ logger.warn(message);
41
+
42
+ throw new InvalidStateTransition(message, action, currentState as string);
43
+ }
44
+ };
45
+
46
+ export const assertState = (actor: SoftphoneActorRef | CallActorRef, state: SoftphoneStates | CallStates): void => {
47
+ if (!hasState(actor, state)) {
48
+ const message = `Invalid state ${state}`;
49
+ logger.warn(message);
50
+
51
+ throw new InvalidState(message, state);
52
+ }
53
+ };
package/src/utils/sdp.ts CHANGED
@@ -1,4 +1,11 @@
1
1
  import sdpParser from 'sdp-transform';
2
+ import { UserAgent } from 'sip.js/lib/api/user-agent';
3
+ import { Inviter } from 'sip.js/lib/api';
4
+ import { URI } from 'sip.js/lib/grammar/uri';
5
+
6
+ import type { SipCall } from '../domain/types';
7
+
8
+ export const SIP_ID_LENGTH = 36;
2
9
 
3
10
  export type Candidate = {
4
11
  foundation: string;
@@ -138,3 +145,32 @@ export const addIcesInAllBundles = (sdp: string) => {
138
145
  }));
139
146
  return sdpParser.write(parsedSdp);
140
147
  };
148
+
149
+ export const getSipSessionId = (sipSession: SipCall | null | undefined): string => {
150
+ if (!sipSession) {
151
+ return '';
152
+ }
153
+
154
+ const message = sipSession.message as Partial<{ callId?: string }>;
155
+ if (message?.callId) {
156
+ return message.callId.substring(0, SIP_ID_LENGTH);
157
+ }
158
+
159
+ // For Inviter
160
+ // @ts-ignore: private
161
+ if (sipSession instanceof Inviter && sipSession.outgoingRequestMessage) {
162
+ // @ts-ignore: private
163
+ return sipSession.outgoingRequestMessage.callId.substring(0, SIP_ID_LENGTH);
164
+ }
165
+
166
+ // For Invitation
167
+ return (sipSession.id || '').substring(0, SIP_ID_LENGTH);
168
+ };
169
+
170
+ // We need to replace 0.0.0.0 to 127.0.0.1 in the sdp to avoid MOH during a createOffer.
171
+ export const replaceLocalIpModifier = (description: Record<string, any>) => Promise.resolve({ // description is immutable... so we have to clone it or the `type` attribute won't be returned.
172
+ ...JSON.parse(JSON.stringify(description)),
173
+ sdp: description.sdp.replace('c=IN IP4 0.0.0.0', 'c=IN IP4 127.0.0.1'),
174
+ });
175
+
176
+ export const makeURI = (target: string, host: string): URI | undefined => UserAgent.makeURI(`sip:${target}@${host}`);
@@ -0,0 +1,40 @@
1
+ import IssueReporter from '../service/IssueReporter';
2
+ import { WebRtcConfig } from '../domain/types';
3
+
4
+ const sipLogger = IssueReporter.loggerFor('sip.js');
5
+ const protocolLogger = IssueReporter.loggerFor('sip');
6
+ const protocolDebugMessages = ['Received WebSocket text message:', 'Sending WebSocket message:'];
7
+
8
+ const logConnector = (level: any, className: string, label: any, content: string): void => {
9
+ const protocolIndex = content && content.indexOf ? protocolDebugMessages.findIndex(prefix => content.indexOf(prefix) !== -1) : -1;
10
+
11
+ if (className === 'sip.Transport' && protocolIndex !== -1) {
12
+ const direction = protocolIndex === 0 ? 'receiving' : 'sending';
13
+ const message = content.replace(`${protocolDebugMessages[protocolIndex]}\n\n`, '').replace('\r\n', '\n');
14
+ protocolLogger.trace(message, {
15
+ className,
16
+ direction,
17
+ });
18
+ } else {
19
+ sipLogger.trace(content, {
20
+ className,
21
+ });
22
+ }
23
+ };
24
+
25
+ const configureLogger = (rawOptions: Partial<WebRtcConfig> = {}): Partial<WebRtcConfig> => {
26
+ const options = rawOptions;
27
+
28
+ if (options.uaConfigOverrides) {
29
+ options.uaConfigOverrides.traceSip = true;
30
+ }
31
+ options.log = options.log || {};
32
+ options.log.builtinEnabled = false;
33
+ options.log.logLevel = 'debug';
34
+
35
+ options.log.connector = logConnector;
36
+
37
+ return options;
38
+ };
39
+
40
+ export default configureLogger;