diff --git a/.eslintrc b/.eslintrc index dd2169e3..4c09cd1d 100644 --- a/.eslintrc +++ b/.eslintrc @@ -89,4 +89,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/__tests__/api-client.test.ts b/src/__tests__/api-client.test.ts index 4c5c8ea3..10b3adaf 100644 --- a/src/__tests__/api-client.test.ts +++ b/src/__tests__/api-client.test.ts @@ -88,8 +88,7 @@ client.setToken(token); describe('With correct API results', () => { beforeEach(() => { jest.resetModules(); - // @ts-expect-error - global.fetch = jest.fn(() => Promise.resolve(mockedJson)); + global.fetch = jest.fn(() => Promise.resolve(mockedJson)) as any; }); describe('logIn test', () => { @@ -102,13 +101,12 @@ describe('With correct API results', () => { Authorization: `Basic ${Base64.encode(`${username}:${password}`)}`, 'Content-Type': 'application/json', }; - // @ts-expect-error + const result = await client.auth.logIn({ username, password, - }); + } as any) as any; expect(result).toBeInstanceOf(Session); - // @ts-expect-error expect(result.token).toBe(1); expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token`, { method: 'post', @@ -123,8 +121,7 @@ describe('With correct API results', () => { describe('logOut test', () => { it('should delete the specified token', async () => { const oldToken = 123; - // @ts-expect-error - await client.auth.logOut(oldToken); + await client.auth.logOut(oldToken as any); expect(global.fetch).toBeCalledWith(`https://${server}/api/auth/${authVersion}/token/${oldToken}`, { method: 'delete', body: null, @@ -139,8 +136,7 @@ describe('With correct API results', () => { describe('With unAuthorized API results', () => { beforeEach(() => { jest.resetModules(); - // @ts-expect-error - global.fetch = jest.fn(() => Promise.resolve(mockedUnAuthorized)); + global.fetch = jest.fn(() => Promise.resolve(mockedUnAuthorized)) as any; }); describe('checkLogin test', () => { @@ -162,8 +158,7 @@ describe('With unAuthorized API results', () => { describe('With not found API results', () => { beforeEach(() => { jest.resetModules(); - // @ts-expect-error - global.fetch = jest.fn(() => Promise.resolve(mockedNotFound)); + global.fetch = jest.fn(() => Promise.resolve(mockedNotFound)) as any; }); describe('fetchVoicemail test', () => { @@ -198,19 +193,17 @@ describe('With not found API results', () => { describe('With erroneous text API results', () => { beforeEach(() => { jest.resetModules(); - // @ts-expect-error - global.fetch = jest.fn(() => Promise.resolve(mockedTextError)); + global.fetch = jest.fn(() => Promise.resolve(mockedTextError)) as any; }); it('throw an exception when the response is >= 500', async () => { let error = null; try { - // @ts-expect-error await client.auth.logIn({ username, password, - }); + } as any); } catch (e: any) { error = e; } @@ -225,19 +218,17 @@ describe('With erroneous text API results', () => { describe('With erroneous json API results', () => { beforeEach(() => { jest.resetModules(); - // @ts-expect-error - global.fetch = jest.fn(() => Promise.resolve(mockedJsonError)); + global.fetch = jest.fn(() => Promise.resolve(mockedJsonError)) as any; }); it('throw an exception when the response is >= 500', async () => { let error = null; try { - // @ts-expect-error await client.auth.logIn({ username, password, - }); + } as any); } catch (e: any) { error = e; } diff --git a/src/__tests__/web-rtc-client.test.ts b/src/__tests__/web-rtc-client.test.ts index bc1e8de0..7f5cef49 100644 --- a/src/__tests__/web-rtc-client.test.ts +++ b/src/__tests__/web-rtc-client.test.ts @@ -4,18 +4,15 @@ jest.mock('sip.js/lib/platform/web/transport'); jest.mock('sip.js/lib/api/user-agent', () => ({ start: () => {}, UserAgent: class UserAgent { - // @ts-ignore - static makeURI = () => {}; + static makeURI: () => null = () => null; - // @ts-ignore start = () => {}; transport = {}; }, })); -// @ts-expect-error -const client = new WebRTCClient({}); +const client = new WebRTCClient({} as any, null, undefined); describe('WebRTC client', () => { it('should compute muted/unmuted state', async () => { @@ -65,14 +62,11 @@ describe('WebRTC client', () => { }, }, }; - // @ts-expect-error - expect(client.isAudioMuted(mutedSession)).toBeTruthy(); - // @ts-expect-error - expect(client.isAudioMuted(oldKindMuted)).toBeTruthy(); - // @ts-expect-error - expect(client.isAudioMuted(unMutedSession)).toBeFalsy(); - // @ts-expect-error - expect(client.isAudioMuted(oldKindUnmuted)).toBeFalsy(); + + expect(client.isAudioMuted(mutedSession as any)).toBeTruthy(); + expect(client.isAudioMuted(oldKindMuted as any)).toBeTruthy(); + expect(client.isAudioMuted(unMutedSession as any)).toBeFalsy(); + expect(client.isAudioMuted(oldKindUnmuted as any)).toBeFalsy(); }); }); describe('changeAudioInputDevice', () => { @@ -131,27 +125,21 @@ describe('changeAudioInputDevice', () => { }, }); it('should change the audio input track if the provided id is different', async () => { - // @ts-expect-error - client.setMediaConstraints(constraints); + client.setMediaConstraints(constraints as any); expect(client.getAudioDeviceId()).toBe(defaultId); - // @ts-expect-error - const result = await client.changeAudioInputDevice(deviceId, session); + const result = await client.changeAudioInputDevice(deviceId, session as any); expect(result).toBeTruthy(); }); it('should NOT change the audio input track if the provided id is the same', async () => { - // @ts-expect-error - client.setMediaConstraints(constraints); + client.setMediaConstraints(constraints as any); expect(client.getAudioDeviceId()).toBe(defaultId); - // @ts-expect-error - const result = await client.changeAudioInputDevice(defaultId, session); + const result = await client.changeAudioInputDevice(defaultId, session as any); expect(result).toBeFalsy(); }); it('should change the audio input track if the provided id is the same and force param is TRUE', async () => { - // @ts-expect-error - client.setMediaConstraints(constraints); + client.setMediaConstraints(constraints as any); expect(client.getAudioDeviceId()).toBe(defaultId); - // @ts-expect-error - const result = await client.changeAudioInputDevice(defaultId, session, true); + const result = await client.changeAudioInputDevice(defaultId, session as any, true); expect(result).toBeTruthy(); }); describe('setVideoInputDevice', () => { diff --git a/src/api/chatd.ts b/src/api/chatd.ts index af4726e0..a1dcbcb7 100644 --- a/src/api/chatd.ts +++ b/src/api/chatd.ts @@ -68,9 +68,8 @@ export default ((client: ApiRequester, baseUrl: string): ChatD => ({ const uuids: Array = contactUuids || []; if (uuids.length > MAX_PRESENCE_FETCHED) { - const requests = uuids.reduce((acc, _, i) => { + const requests = uuids.reduce((acc: Promise[], _, i) => { if (i % MAX_PRESENCE_FETCHED === 0) { - // @ts-ignore acc.push(this.getMultipleLineState(uuids.slice(i, i + MAX_PRESENCE_FETCHED))); } return acc; diff --git a/src/api/dird.ts b/src/api/dird.ts index b4853b2a..acdac6ba 100644 --- a/src/api/dird.ts +++ b/src/api/dird.ts @@ -16,12 +16,9 @@ type PhoneBookSearchQueryParams = { const getContactPayload = (contact: NewContact | Contact) => ({ email: contact.email, - // @ts-ignore - firstname: contact.firstName ? contact.firstName : '', - // @ts-ignore - lastname: contact.lastName ? contact.lastName : '', - // @ts-ignore - number: contact.phoneNumber ? contact.phoneNumber : '', + firstname: (contact as any).firstName ? (contact as any).firstName : '', + lastname: (contact as any).lastName ? (contact as any).lastName : '', + number: (contact as any).phoneNumber ? (contact as any).phoneNumber : '', entreprise: contact.entreprise ? contact.entreprise : '', birthday: contact.birthday ? contact.birthday : '', address: contact.address ? contact.address : '', diff --git a/src/checker/checks/ice-ipv4.ts b/src/checker/checks/ice-ipv4.ts index eccc869f..72bde2ce 100644 --- a/src/checker/checks/ice-ipv4.ts +++ b/src/checker/checks/ice-ipv4.ts @@ -66,8 +66,7 @@ export default { } if (ips.every(checkIsIPV4)) { - // @ts-ignore - resolve(); + resolve(null); } else { const nonIPV4 = ips.find(ip => !checkIsIPV4(ip)); reject(new Error(`Non IPv4 ice candidate found : ${nonIPV4}.`)); diff --git a/src/checker/checks/webrtc-transport.ts b/src/checker/checks/webrtc-transport.ts index 34b87afe..c59411e3 100644 --- a/src/checker/checks/webrtc-transport.ts +++ b/src/checker/checks/webrtc-transport.ts @@ -1,9 +1,9 @@ -import { Invitation, Inviter } from 'sip.js'; import WebRTCClient from '../../web-rtc-client'; +import { Session as WazoSession } from '../../domain/types'; export default { name: 'WebRTC Transport (WS) ~30s', - check: (server: string, session: Inviter | Invitation): Promise => new Promise((resolve, reject) => { + check: (server: string, session: WazoSession): Promise => new Promise((resolve, reject) => { const [host, port = 443] = server.split(':'); const client = new WebRTCClient({ diff --git a/src/domain/AdHocAPIConference.ts b/src/domain/AdHocAPIConference.ts index c0ea0268..361aaa68 100644 --- a/src/domain/AdHocAPIConference.ts +++ b/src/domain/AdHocAPIConference.ts @@ -76,7 +76,7 @@ export default class AdHocAPIConference { async start() { this.started = true; - // @ts-ignore + // @ts-ignore: date / number this.answerTime = Object.values(this.participants).length ? Object.values(this.participants)[0].answerTime : null; const participantIds = Object.keys(this.participants); const conference = await getApiClient().calld.createAdHocConference(this.host.callId, participantIds); diff --git a/src/domain/Agent.ts b/src/domain/Agent.ts index bff9e4b9..01c68528 100644 --- a/src/domain/Agent.ts +++ b/src/domain/Agent.ts @@ -8,7 +8,7 @@ export type AgentResponse = { paused_reason: string; }; -type AgentArguments = { +export type AgentArguments = { context: string; extension: string; id: number; diff --git a/src/domain/CallSession.ts b/src/domain/CallSession.ts index 4872ef84..68b96896 100644 --- a/src/domain/CallSession.ts +++ b/src/domain/CallSession.ts @@ -1,9 +1,8 @@ import { SessionState } from 'sip.js/lib/api/session-state'; -import type { Invitation } from 'sip.js/lib/api'; -import { Inviter } from 'sip.js/lib/api'; import Call from './Call'; import newFrom from '../utils/new-from'; import updateFrom from '../utils/update-from'; +import { Session as WazoSession } from './types'; type CallSessionArguments = { answered: boolean; @@ -33,7 +32,7 @@ type CallSessionArguments = { screensharing: boolean; recording: boolean; recordingPaused: boolean; - sipSession?: Inviter | Invitation; + sipSession?: WazoSession; conference: boolean; }; export default class CallSession { @@ -97,7 +96,7 @@ export default class CallSession { recordingPaused: boolean; - sipSession: Inviter | Invitation | undefined; + sipSession: WazoSession | undefined; conference: boolean; diff --git a/src/domain/Contact.ts b/src/domain/Contact.ts index 77c1a97b..5bc1c897 100644 --- a/src/domain/Contact.ts +++ b/src/domain/Contact.ts @@ -771,7 +771,7 @@ export default class Contact { merge(old: Contact): Contact { Object.keys(old).filter(key => key !== 'lineState').forEach(key => { - // @ts-ignore + // @ts-ignore: keys this[key] = old[key] || this[key]; }); diff --git a/src/domain/Phone/WebRTCPhone.ts b/src/domain/Phone/WebRTCPhone.ts index a21fb329..e60b70bf 100644 --- a/src/domain/Phone/WebRTCPhone.ts +++ b/src/domain/Phone/WebRTCPhone.ts @@ -4,12 +4,14 @@ import type { Message } from 'sip.js/lib/api/message'; import { SessionState } from 'sip.js/lib/api/session-state'; import type { IncomingRequestMessage } from 'sip.js/lib/core/messages/incoming-request-message'; import { OutgoingInviteRequest } from 'sip.js/lib/core'; -import { Inviter, Invitation, Session } from 'sip.js/lib/api'; +import { Inviter, Invitation, Session, SessionDescriptionHandlerOptions } from 'sip.js/lib/api'; +import { SessionDescriptionHandler } from 'sip.js/lib/platform/web'; import CallSession from '../CallSession'; import type { Phone, AvailablePhoneOptions } from './Phone'; import WazoWebRTCClient from '../../web-rtc-client'; import Emitter from '../../utils/Emitter'; import IssueReporter from '../../service/IssueReporter'; +import { PeerConnection, Session as WazoSession } from '../types'; export const ON_USER_AGENT = 'onUserAgent'; export const ON_REGISTERED = 'onRegistered'; @@ -66,7 +68,7 @@ export default class WebRTCPhone extends Emitter implements Phone { incomingSessions: string[]; - currentSipSession: Invitation | Inviter | undefined; + currentSipSession: WazoSession | undefined; currentCallSession: CallSession | null | undefined; @@ -311,8 +313,7 @@ export default class WebRTCPhone extends Emitter implements Phone { const shouldScreenShare = constraints && !!constraints.screen; const desktop = constraints && constraints.desktop; const options = sipSession.sessionDescriptionHandlerOptionsReInvite; - // @ts-ignore - const wasAudioOnly = options?.audioOnly; + const wasAudioOnly = (options as SessionDescriptionHandlerOptions & { audioOnly: boolean })?.audioOnly; const newStream = await this.client.upgradeToVideo(sipSession, constraints, isConference); if (callSession) { @@ -363,7 +364,7 @@ export default class WebRTCPhone extends Emitter implements Phone { }, 2500); } - _bindEvents(sipSession: Invitation | Inviter) { + _bindEvents(sipSession: WazoSession) { const sipSessionId = this.getSipSessionId(sipSession); if (sipSession instanceof Invitation) { @@ -431,10 +432,13 @@ export default class WebRTCPhone extends Emitter implements Phone { } // Video events - const { - // @ts-ignore - peerConnection, - } = sipSession.sessionDescriptionHandler; + const sdh = sipSession.sessionDescriptionHandler as SessionDescriptionHandler; + const peerConnection = sdh.peerConnection as PeerConnection; + + if (!peerConnection) { + logger.info('null peer connection'); + return; + } peerConnection.ontrack = (rawEvent: any) => { const event = rawEvent; @@ -497,7 +501,7 @@ export default class WebRTCPhone extends Emitter implements Phone { sender.replaceTrack(screenTrack); } - this._onScreenSharing(screenShareStream, sipSession as Inviter | Invitation, callSession, hadVideo, constraints.desktop); + this._onScreenSharing(screenShareStream, sipSession as WazoSession, callSession, hadVideo, constraints.desktop); return screenShareStream; } @@ -551,7 +555,7 @@ export default class WebRTCPhone extends Emitter implements Phone { return reinvited; } - _onScreenSharing(screenStream: MediaStream, sipSession: Invitation | Inviter, callSession: CallSession | null | undefined, hadVideo: boolean, desktop?: boolean | null | undefined) { + _onScreenSharing(screenStream: MediaStream, sipSession: WazoSession, callSession: CallSession | null | undefined, hadVideo: boolean, desktop?: boolean | null | undefined) { const screenTrack = screenStream.getVideoTracks()[0]; const sipSessionId = this.getSipSessionId(sipSession); const pc = this.client.getPeerConnection(sipSessionId); @@ -586,7 +590,7 @@ export default class WebRTCPhone extends Emitter implements Phone { }), screenStream); } - _onCallAccepted(sipSession: Inviter | Invitation, cameraEnabled: boolean): CallSession { + _onCallAccepted(sipSession: WazoSession, cameraEnabled: boolean): CallSession { logger.info('WebRTC phone - on call accepted', { sipId: sipSession.id, cameraEnabled, @@ -677,7 +681,7 @@ export default class WebRTCPhone extends Emitter implements Phone { return this.currentSipSession && this.client.changeSessionVideoInputDevice(id, this.currentSipSession); } - _onCallTerminated(sipSession: Invitation | Inviter): boolean { + _onCallTerminated(sipSession: WazoSession): boolean { const sipSessionId = this.getSipSessionId(sipSession); logger.info('WebRTC phone - on call terminated', { @@ -743,7 +747,7 @@ export default class WebRTCPhone extends Emitter implements Phone { return false; } - // @ts-ignore + // @ts-ignore: does not exist if (!sipSession.isCanceled) { setTimeout(() => { this.eventEmitter.emit(ON_PLAY_HANGUP_SOUND, this.audioOutputDeviceId, this.audioOutputVolume, callSession); @@ -777,11 +781,9 @@ export default class WebRTCPhone extends Emitter implements Phone { return false; } - const { - // @ts-ignore - peerConnection, - } = sipSession.sessionDescriptionHandler; - const remoteStream: MediaStream = peerConnection.getRemoteStreams().find((stream: MediaStream) => !!stream.getVideoTracks().length); + const sdh = sipSession.sessionDescriptionHandler as SessionDescriptionHandler; + const peerConnection = sdh.peerConnection as PeerConnection; + const remoteStream: MediaStream = peerConnection.getRemoteStreams().find((stream: MediaStream) => !!stream.getVideoTracks().length) as MediaStream; return remoteStream && remoteStream.getVideoTracks().some(track => !track.muted); } @@ -981,7 +983,7 @@ export default class WebRTCPhone extends Emitter implements Phone { return promise; } - async holdSipSession(sipSession: Inviter | Invitation, callSession: CallSession | null | undefined, withEvent = true): Promise { + async holdSipSession(sipSession: WazoSession, callSession: CallSession | null | undefined, withEvent = true): Promise { if (!sipSession) { return new Promise((resolve, reject) => reject(new Error('No session to hold'))); } @@ -1014,7 +1016,7 @@ export default class WebRTCPhone extends Emitter implements Phone { return promise; } - async unholdSipSession(sipSession: Inviter | Invitation, callSession: CallSession | null | undefined, withEvent = true): Promise { + async unholdSipSession(sipSession: WazoSession, callSession: CallSession | null | undefined, withEvent = true): Promise { if (!sipSession) { return new Promise((resolve, reject) => reject(new Error('No session to unhold'))); } @@ -1208,7 +1210,7 @@ export default class WebRTCPhone extends Emitter implements Phone { }); } - let sipSession: Inviter | Invitation; + let sipSession: WazoSession; try { sipSession = this.client.call(number, this.allowVideo ? cameraEnabled : false, audioOnly, conference); @@ -1235,9 +1237,9 @@ export default class WebRTCPhone extends Emitter implements Phone { this.eventEmitter.emit(ON_CALL_OUTGOING, callSession); // If an invite promise exists, catch exceptions on it to trigger error like OverConstraintsError. - // @ts-ignore + // @ts-ignore: does not exist if (sipSession.invitePromise) { - // @ts-ignore + // @ts-ignore: does not exist sipSession.invitePromise.catch(error => { this.eventEmitter.emit(ON_CALL_ERROR, error, callSession); }); @@ -1336,13 +1338,13 @@ export default class WebRTCPhone extends Emitter implements Phone { } forceCancel(sipSession: Inviter): void { - // @ts-ignore + // @ts-ignore: private if (!sipSession || !sipSession.outgoingInviteRequest) { return; } try { - // @ts-ignore + // @ts-ignore: private sipSession.outgoingInviteRequest.cancel(); } catch (e) { logger.info('force cancel, error', e); @@ -1437,7 +1439,7 @@ export default class WebRTCPhone extends Emitter implements Phone { } // eslint-disable-next-line @typescript-eslint/default-param-last - sendMessage(sipSession: Invitation | Inviter | null = null, body: string, contentType = 'text/plain'): void { + sendMessage(sipSession: WazoSession | null = null, body: string, contentType = 'text/plain'): void { return this.client.sendMessage(sipSession, body, contentType); } @@ -1489,7 +1491,7 @@ export default class WebRTCPhone extends Emitter implements Phone { bindClientEvents() { this.client.unbind(); - this.client.on(this.client.INVITE, (sipSession: Invitation | Inviter, wantsToDoVideo: boolean) => { + this.client.on(this.client.INVITE, (sipSession: WazoSession, wantsToDoVideo: boolean) => { const autoAnswer = sipSession.request.getHeader('Answer-Mode') === 'Auto'; const withVideo = this.allowVideo ? wantsToDoVideo : false; logger.info('WebRTC invite received', { @@ -1537,7 +1539,7 @@ export default class WebRTCPhone extends Emitter implements Phone { this.eventEmitter.emit.apply(this.eventEmitter, [this.client.ON_REINVITE, ...args]); }, 2000); }); - this.client.on(this.client.ACCEPTED, (sipSession: Inviter | Invitation) => { + this.client.on(this.client.ACCEPTED, (sipSession: WazoSession) => { const sessionId = this.getSipSessionId(sipSession); const hasSession = (sessionId in this.callSessions); logger.info('WebRTC call accepted', { @@ -1604,8 +1606,7 @@ export default class WebRTCPhone extends Emitter implements Phone { // send a reinvite to renegociate ICE with new IP if (this.shouldSendReinvite && this.currentSipSession && this.currentSipSession.state === SessionState.Established) { this.shouldSendReinvite = false; - // @ts-ignore - const pc = this.currentSipSession?.sessionDescriptionHandler?.peerConnection; + const pc = (this.currentSipSession?.sessionDescriptionHandler as SessionDescriptionHandler)?.peerConnection as PeerConnection; const isConference = pc ? pc.sfu : false; const hasVideo = this.currentCallSession && this.currentCallSession.cameraEnabled; @@ -1652,7 +1653,7 @@ export default class WebRTCPhone extends Emitter implements Phone { this.eventEmitter.emit(ON_NETWORK_STATS, callSession, stats, previousStats); }); // Used when upgrading directly in screenshare mode - this.client.on(this.client.ON_SCREEN_SHARING_REINVITE, (sipSession: Inviter | Invitation, response: any, desktop: boolean) => { + this.client.on(this.client.ON_SCREEN_SHARING_REINVITE, (sipSession: WazoSession, response: any, desktop: boolean) => { const sipSessionId = this.getSipSessionId(sipSession); const localStream = this.client.getLocalStream(sipSessionId); const callSession = this.callSessions[sipSessionId]; @@ -1666,7 +1667,7 @@ export default class WebRTCPhone extends Emitter implements Phone { } // Find a corresponding sipSession from a CallSession - findSipSession(callSession: CallSession | null | undefined): Invitation | Inviter | null | undefined { + findSipSession(callSession: CallSession | null | undefined): WazoSession | null | undefined { const keys = this.client.getSipSessionIds(); const keyIndex = keys.findIndex(sessionId => callSession && callSession.isId(sessionId)); @@ -1682,7 +1683,7 @@ export default class WebRTCPhone extends Emitter implements Phone { return this.callSessions[sipSessionId]; } - getSipSessionId(sipSession: Inviter | Invitation): string { + getSipSessionId(sipSession: WazoSession): string { return this.client.getSipSessionId(sipSession); } @@ -1699,8 +1700,7 @@ export default class WebRTCPhone extends Emitter implements Phone { } } - _onMessage(message: Message): void { - // @ts-ignore + _onMessage(message: Message & { method?: string, body: string }): void { if (!message || message.method !== 'MESSAGE') { return; } @@ -1708,7 +1708,6 @@ export default class WebRTCPhone extends Emitter implements Phone { let body; try { - // @ts-ignore body = JSON.parse(message.body); } catch (e: any) { return; @@ -1734,7 +1733,7 @@ export default class WebRTCPhone extends Emitter implements Phone { } } - _createIncomingCallSession(sipSession: Invitation | Inviter, cameraEnabled: boolean, fromSession?: CallSession | null | undefined, autoAnswer = false): CallSession { + _createIncomingCallSession(sipSession: WazoSession, cameraEnabled: boolean, fromSession?: CallSession | null | undefined, autoAnswer = false): CallSession { return this._createCallSession(sipSession, fromSession, { incoming: true, ringing: true, @@ -1743,7 +1742,7 @@ export default class WebRTCPhone extends Emitter implements Phone { }); } - _createOutgoingCallSession(sipSession: Invitation | Inviter, cameraEnabled: boolean, fromSession?: CallSession): CallSession { + _createOutgoingCallSession(sipSession: WazoSession, cameraEnabled: boolean, fromSession?: CallSession): CallSession { return this._createCallSession(sipSession, fromSession, { incoming: false, ringing: true, @@ -1751,40 +1750,40 @@ export default class WebRTCPhone extends Emitter implements Phone { }); } - _createAcceptedCallSession(sipSession: Invitation | Inviter, cameraEnabled?: boolean, fromSession?: CallSession): CallSession { + _createAcceptedCallSession(sipSession: WazoSession, cameraEnabled?: boolean, fromSession?: CallSession): CallSession { return this._createCallSession(sipSession, fromSession, { cameraEnabled: cameraEnabled !== undefined ? cameraEnabled : false, }); } - _createMutedCallSession(sipSession: Invitation | Inviter, fromSession?: CallSession): CallSession { + _createMutedCallSession(sipSession: WazoSession, fromSession?: CallSession): CallSession { return this._createCallSession(sipSession, fromSession, { muted: true, }); } - _createUnmutedCallSession(sipSession: Invitation | Inviter, fromSession?: CallSession): CallSession { + _createUnmutedCallSession(sipSession: WazoSession, fromSession?: CallSession): CallSession { return this._createCallSession(sipSession, fromSession, { muted: false, }); } - _createCameraResumedCallSession(sipSession: Invitation | Inviter, fromSession?: CallSession): CallSession { + _createCameraResumedCallSession(sipSession: WazoSession, fromSession?: CallSession): CallSession { return this._createCallSession(sipSession, fromSession, { videoMuted: false, }); } - _createCameraDisabledCallSession(sipSession: Invitation | Inviter, fromSession?: CallSession): CallSession { + _createCameraDisabledCallSession(sipSession: WazoSession, fromSession?: CallSession): CallSession { return this._createCallSession(sipSession, fromSession, { videoMuted: true, }); } - _createCallSession(sipSession: Invitation | Inviter, fromSession?: CallSession | null | undefined, extra: Record = {}): CallSession { + _createCallSession(sipSession: WazoSession, fromSession?: CallSession | null | undefined, extra: Record = {}): CallSession { // eslint-disable-next-line const identity = sipSession ? sipSession.remoteIdentity || sipSession.assertedIdentity : null; - // @ts-ignore + // @ts-ignore: private const number = identity ? identity.uri._normal.user : null; const { state, @@ -1796,7 +1795,7 @@ export default class WebRTCPhone extends Emitter implements Phone { sipCallId: sessionId, sipStatus: state, displayName: identity ? identity.displayName || number : number, - // @ts-ignore: @HEADSUP: startTime should be a number, not a date + // @ts-ignore: date / number startTime: fromSession ? fromSession.startTime : new Date(), creationTime: fromSession ? fromSession.creationTime : null, answerTime: fromSession ? fromSession.answerTime : null, diff --git a/src/domain/Session.ts b/src/domain/Session.ts index 9304b426..e3515431 100644 --- a/src/domain/Session.ts +++ b/src/domain/Session.ts @@ -108,7 +108,7 @@ export default class Session { if (token) { const isValid = jws.JWS.verifyJWT(token, swarmKey, { alg: ['RS256'], - // @ts-ignore + // @ts-ignore: date / number verifyAt: new Date(), }); diff --git a/src/domain/__tests__/Agent.test.ts b/src/domain/__tests__/Agent.test.ts index faa03df2..850816d2 100644 --- a/src/domain/__tests__/Agent.test.ts +++ b/src/domain/__tests__/Agent.test.ts @@ -1,4 +1,4 @@ -import Agent, { AgentResponse } from '../Agent'; +import Agent, { AgentArguments, AgentResponse } from '../Agent'; const fieldMap: Record = { context: 'context', @@ -27,14 +27,12 @@ Object.keys(fieldMap).forEach((field: string) => { }); describe('Call Center Agent domain', () => { it('should keep its values', () => { - // @ts-expect-error - const agent = new Agent(formatted) as any; + const agent = new Agent(formatted as AgentArguments) as any; expect(agent).toBeInstanceOf(Agent); Object.keys(fieldMap).map(key => expect(agent[key]).toBe(formatted[key])); }); it('can parse a plain call center agent to domain', () => { const agent = Agent.parse(plain as AgentResponse); - // @ts-expect-error - expect(agent).toEqual(new Agent(formatted)); + expect(agent).toEqual(new Agent(formatted as AgentArguments)); }); }); diff --git a/src/domain/__tests__/Call.test.ts b/src/domain/__tests__/Call.test.ts index d0de1328..2d27e5fd 100644 --- a/src/domain/__tests__/Call.test.ts +++ b/src/domain/__tests__/Call.test.ts @@ -1,12 +1,11 @@ -// @ts-nocheck import Call from '../Call'; describe('Call domain', () => { it('can return the elapsed time since its creation in seconds', () => { - Date.now = jest.fn(() => new Date(2012, 7, 28, 13, 0, 0)); + Date.now = jest.fn(() => new Date(2012, 7, 28, 13, 0, 0)) as any; const call = new Call({ startingTime: new Date(2012, 7, 28, 12, 30, 0), - }); + } as any); const elapsedTimeInSeconds = call.getElapsedTimeInSeconds(); expect(elapsedTimeInSeconds).toBe(30 * 60); }); @@ -14,14 +13,14 @@ describe('Call domain', () => { const call = new Call({ calleeName: 'John Doe', calleeNumber: '911', - }); + } as any); expect(call.hasNumber('911')).toBeTruthy(); }); it('does not have the number provided it is a different number', () => { const call = new Call({ calleeName: 'John Doe', calleeNumber: '911', - }); + } as any); expect(call.hasNumber('418-222-5555')).toBeFalsy(); }); }); diff --git a/src/domain/__tests__/CallLog.test.ts b/src/domain/__tests__/CallLog.test.ts index 51efe98c..7811ac16 100644 --- a/src/domain/__tests__/CallLog.test.ts +++ b/src/domain/__tests__/CallLog.test.ts @@ -1,5 +1,3 @@ -// @ts-nocheck - import moment from 'moment'; import CallLog from '../CallLog'; import Recording from '../Recording'; @@ -34,7 +32,7 @@ describe('CallLog domain', () => { }], total: 233, }; - const logs = CallLog.parseMany(response); + const logs = CallLog.parseMany(response as any); expect(logs).toEqual([new CallLog({ answer: moment('2017-08-07T13:51:18.892002+00:00').toDate(), answered: true, diff --git a/src/domain/__tests__/CallSession.test.ts b/src/domain/__tests__/CallSession.test.ts index 18b04e93..fcbce0b9 100644 --- a/src/domain/__tests__/CallSession.test.ts +++ b/src/domain/__tests__/CallSession.test.ts @@ -1,7 +1,6 @@ -// @ts-nocheck import CallSession from '../CallSession'; -const stringify = cs => JSON.parse(JSON.stringify(cs)); +const stringify = (cs: any) => JSON.parse(JSON.stringify(cs)); describe('CallSession domain', () => { it('should update from another CallSession without data loss', () => { @@ -12,14 +11,14 @@ describe('CallSession domain', () => { number: '8001', isCaller: true, dialedExtension: null, - }); + } as any); const anotherCallSession = new CallSession({ callId: 345, callerNumber: '8008', number: '8008', isCaller: false, dialedExtension: undefined, - }); + } as any); callSession.updateFrom(anotherCallSession); expect(callSession.callId).toBe(345); expect(callSession.sipCallId).toBe(123); @@ -34,22 +33,22 @@ describe('CallSession domain', () => { const cs1 = new CallSession({ callId: 123, sipCallId: 456, - }); + } as any); const cs2 = new CallSession({ callId: 123, sipCallId: null, - }); + } as any); expect(cs1.is(cs2)).toBeTruthy(); expect(cs2.is(cs1)).toBeTruthy(); }); it('should set answerTime when setting answered to true', () => { - const cs1 = new CallSession({}); + const cs1 = new CallSession({} as any); cs1.answered = true; - const cs2 = new CallSession({}); + const cs2 = new CallSession({} as any); cs2.answer(); const cs3 = new CallSession({ answered: true, - }); + } as any); expect(cs1.answered).toEqual(true); expect(cs1.answerTime).not.toBeNull(); expect(cs2.answered).toEqual(true); @@ -64,11 +63,11 @@ describe('CallSession domain', () => { expect(cs6.answered).toEqual(true); }); it('should reset answerTime when setting answered to false', () => { - const cs1 = new CallSession({}); + const cs1 = new CallSession({} as any); cs1.answered = false; const cs2 = new CallSession({ answered: false, - }); + } as any); expect(cs1.answered).toEqual(false); expect(cs1.answerTime).toBeNull(); expect(cs2.answered).toEqual(false); diff --git a/src/domain/__tests__/Contact.test.ts b/src/domain/__tests__/Contact.test.ts index 7c3dfd2a..76088667 100644 --- a/src/domain/__tests__/Contact.test.ts +++ b/src/domain/__tests__/Contact.test.ts @@ -309,13 +309,11 @@ describe('Contact domain', () => { const noBirthdayContact = new Contact({ uuid: 'uuid-12345', lastActivity: 'yesterday', - // @ts-expect-error - birthday: null, + birthday: undefined, }); const noLastActivityContact = new Contact({ uuid: 'uuid-12345', - // @ts-expect-error - lastActivity: null, + lastActivity: undefined, birthday: 'tomorrow', }); const result = noBirthdayContact.merge(noLastActivityContact); diff --git a/src/domain/__tests__/IndirectTransfer.test.ts b/src/domain/__tests__/IndirectTransfer.test.ts index e7119d03..6a5e2a99 100644 --- a/src/domain/__tests__/IndirectTransfer.test.ts +++ b/src/domain/__tests__/IndirectTransfer.test.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import CallSession from '../CallSession'; import IndirectTransfer from '../IndirectTransfer'; @@ -9,9 +8,9 @@ describe('Indirect transfer', () => { const destinationId = 'destination-id'; const indirectTransfer = IndirectTransfer.parseFromCallSession(new CallSession({ callId: sourceId, - }), new CallSession({ + } as any), new CallSession({ callId: destinationId, - })); + } as any)); expect(indirectTransfer).toEqual({ sourceId, destinationId, @@ -27,7 +26,7 @@ describe('Indirect transfer', () => { }); const isDestination = indirectTransfer.destinationIs(new CallSession({ callId: destinationId, - })); + } as any)); expect(isDestination).toBeTruthy(); }); it('should NOT match when destination is the different', async () => { @@ -38,7 +37,7 @@ describe('Indirect transfer', () => { }); const isDestination = indirectTransfer.destinationIs(new CallSession({ callId: destinationId, - })); + } as any)); expect(isDestination).toBeFalsy(); }); }); @@ -51,7 +50,7 @@ describe('Indirect transfer', () => { }); const isSource = indirectTransfer.sourceIs(new CallSession({ callId: sourceId, - })); + } as any)); expect(isSource).toBeTruthy(); }); it('should NOT match when source is the different', async () => { @@ -62,7 +61,7 @@ describe('Indirect transfer', () => { }); const isSource = indirectTransfer.sourceIs(new CallSession({ callId: sourceId, - })); + } as any)); expect(isSource).toBeFalsy(); }); }); diff --git a/src/domain/__tests__/Profile.test.ts b/src/domain/__tests__/Profile.test.ts index ae809e47..2c9eb5d8 100644 --- a/src/domain/__tests__/Profile.test.ts +++ b/src/domain/__tests__/Profile.test.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import Profile from '../Profile'; import Line from '../Line'; import ForwardOption, { FORWARD_KEYS } from '../ForwardOption'; @@ -18,12 +17,12 @@ describe('Profile domain', () => { context: 'default', }], endpoint_custom: null, - })], + } as any)], username: 'john.doe', forwards: [], mobileNumber: '123', }; - const oldProfile = new Profile(attributes); + const oldProfile = new Profile(attributes as any); const newProfile = Profile.newFrom(oldProfile); expect(newProfile).toBeInstanceOf(Profile); expect(newProfile.firstName).toBe(attributes.firstName); @@ -88,7 +87,7 @@ describe('Profile domain', () => { }], online_call_record_enabled: true, }; - const profile = Profile.parse(plain); + const profile = Profile.parse(plain as any); expect(profile).toEqual(new Profile({ id: 'xxx-xxx-xxx-xx', firstName: 'John', diff --git a/src/domain/__tests__/Room.test.ts b/src/domain/__tests__/Room.test.ts index 96534ea0..b02754fd 100644 --- a/src/domain/__tests__/Room.test.ts +++ b/src/domain/__tests__/Room.test.ts @@ -8,10 +8,9 @@ describe('Room', () => { const number = 'some-number'; const room = new Room({ id: 'some-id', - // @ts-expect-error connectedCallSession: new CallSession({ number, - }), + } as any), participants: [], }); const extension = room.getExtension(); @@ -32,14 +31,12 @@ describe('Room', () => { }); describe('on connect call', () => { it('should add call to room', async () => { - // @ts-expect-error const room = new Room({ id: 'some-id', - }); - // @ts-expect-error + } as any); const callSession = new CallSession({ callId: 'some-call-id', - }); + } as any); const connectedRoom = room.connect(callSession); expect(connectedRoom.connectedCallSession).toBe(callSession); }); @@ -47,42 +44,35 @@ describe('Room', () => { describe('on room has call', () => { describe('given no connected call', () => { it('should be false', async () => { - // @ts-expect-error const room = new Room({ connectedCallSession: null, - }); - // @ts-expect-error - const callSession = new CallSession({}); + } as any); + const callSession = new CallSession({} as any); const roomHasCall = room.has(callSession); expect(roomHasCall).toBeFalsy(); }); }); describe('given a connected call', () => { it('should be true if same calls', async () => { - // @ts-expect-error const callSession = new CallSession({ callId: 'some-call-id', - }); - // @ts-expect-error + } as any); const room = new Room({ connectedCallSession: callSession, - }); + } as any); const roomHasCall = room.has(callSession); expect(roomHasCall).toBeTruthy(); }); it('should be false if different calls', async () => { - // @ts-expect-error const call1 = new CallSession({ callId: 'some-call-id', - }); - // @ts-expect-error + } as any); const call2 = new CallSession({ callId: 'some-other-call-id', - }); - // @ts-expect-error + } as any); const room = new Room({ connectedCallSession: call1, - }); + } as any); const roomHasCall = room.has(call2); expect(roomHasCall).toBeFalsy(); }); @@ -93,9 +83,8 @@ describe('Room', () => { const participants = [{}]; const room = new Room({ connectedCallSession: null, - // @ts-expect-error participants, - }); + } as any); const participantUuid = 'some-uuid'; const participantExtension = 'some-extension'; const updatedRoom = room.addParticipant(participantUuid, participantExtension); @@ -106,10 +95,9 @@ describe('Room', () => { describe('on room has call with id', () => { describe('given no connected call', () => { it('should be false', async () => { - // @ts-expect-error const room = new Room({ connectedCallSession: null, - }); + } as any); const callId = 'some-call-id'; const roomHasCall = room.hasCallWithId(callId); expect(roomHasCall).toBeFalsy(); @@ -118,25 +106,21 @@ describe('Room', () => { describe('given a connected call', () => { it('should be true if same calls', async () => { const callId = 'some-call-id'; - // @ts-expect-error const room = new Room({ - // @ts-expect-error connectedCallSession: new CallSession({ callId, - }), - }); + } as any), + } as any); const roomHasCall = room.hasCallWithId(callId); expect(roomHasCall).toBeTruthy(); }); it('should be false if different calls', async () => { const callId = 'some-call-id'; - // @ts-expect-error const room = new Room({ - // @ts-expect-error connectedCallSession: new CallSession({ callId: 'some-other-call-id', - }), - }); + } as any), + } as any); const roomHasCall = room.hasCallWithId(callId); expect(roomHasCall).toBeFalsy(); }); @@ -146,12 +130,11 @@ describe('Room', () => { it('should updated participant with corresponding UUID', async () => { const uuid = 'some-uuid'; const room = new Room({ - // @ts-expect-error participants: [{ uuid, talking: true, }], - }); + } as any); expect(room.participants[0].talking).toBeTruthy(); const updatedRoom = room.updateParticipant(uuid, { talking: false, @@ -160,10 +143,9 @@ describe('Room', () => { }); it('should add participant when not found', async () => { const uuid = 'some-uuid'; - // @ts-expect-error const room = new Room({ participants: [], - }); + } as any); const updatedRoom = room.updateParticipant(uuid, { talking: false, }, true); @@ -172,16 +154,14 @@ describe('Room', () => { }); describe('on updated participant by extension', () => { it('should updated participant with corresponding extension', async () => { - const extension = 1234; + const extension = '1234'; const room = new Room({ participants: [{ - // @ts-expect-error extension, talking: false, }], - }); + } as any); expect(room.participants[0].talking).toBeFalsy(); - // @ts-expect-error const updatedRoom = room.updateParticipantByExtension(extension, { talking: true, }); @@ -190,11 +170,9 @@ describe('Room', () => { }); describe('on disconnect', () => { it('should destroy connected call', async () => { - // @ts-expect-error const room = new Room({ - // @ts-expect-error - connectedCallSession: new CallSession({}), - }); + connectedCallSession: new CallSession({} as any), + } as any); expect(room.connectedCallSession).not.toBeNull(); const updatedRoom = room.disconnect(); expect(updatedRoom.connectedCallSession).toBeNull(); @@ -204,11 +182,10 @@ describe('Room', () => { it('should remove participant with corresponding UUID', async () => { const uuid = 'some-uuid'; const room = new Room({ - // @ts-expect-error participants: [{ uuid, }], - }); + } as any); expect(room.participants.some(participant => participant.uuid === uuid)).toBeTruthy(); const updatedRoom = room.removeParticipantWithUUID(uuid); expect(updatedRoom.participants.some(participant => participant.uuid === uuid)).toBeFalsy(); @@ -218,11 +195,10 @@ describe('Room', () => { it('should remove participant with corresponding UUID', async () => { const extension = 'some-extension'; const room = new Room({ - // @ts-expect-error participants: [{ extension, }], - }); + } as any); expect(room.participants.some(participant => participant.extension === extension)).toBeTruthy(); const updatedRoom = room.removeParticipantWithExtension(extension); expect(updatedRoom.participants.some(participant => participant.extension === extension)).toBeFalsy(); diff --git a/src/domain/__tests__/Session.test.ts b/src/domain/__tests__/Session.test.ts index 9b52eb00..09a0424c 100644 --- a/src/domain/__tests__/Session.test.ts +++ b/src/domain/__tests__/Session.test.ts @@ -27,8 +27,7 @@ describe('Session domain', () => { xivo_user_uuid: null, }, }; - // @ts-ignore - const session = Session.parse(plain); + const session = Session.parse(plain as any); expect(session).toEqual(new Session({ token: 'b93ae6bd-08d7-4001-9e61-057e72bbc4b3', refreshToken: null, @@ -47,13 +46,13 @@ describe('Session domain', () => { const session = new Session({ token: 'ref-12345', uuid: '1234', - // @ts-expect-error + // profile: new Profile({ voicemail: { id: 1234, name: 'inbox', }, - }), + } as any), expiresAt: A_DATE, }); expect(session.hasAccessToVoicemail()).toBeTruthy(); @@ -64,7 +63,7 @@ describe('Session domain', () => { const session = new Session({ token: 'ref-12345', uuid: '1234', - // @ts-expect-error + // profile: new Profile({ lines: [new Line({ id: 9012, @@ -81,7 +80,7 @@ describe('Session domain', () => { context: 'internal', }], })], - }), + } as any), expiresAt: A_DATE, }); expect(session.allNumbers().length).toBeGreaterThan(1); @@ -92,7 +91,7 @@ describe('Session domain', () => { const session = new Session({ token: 'ref-12345', uuid: '1234', - // @ts-expect-error + // profile: new Profile({ lines: [new Line({ id: 9012, @@ -102,7 +101,7 @@ describe('Session domain', () => { context: 'default', }], })], - }), + } as any), expiresAt: A_DATE, }); expect(session.allNumbers().length).toEqual(1); @@ -113,10 +112,10 @@ describe('Session domain', () => { const session = new Session({ token: 'ref-12345', uuid: '1234', - // @ts-expect-error + // profile: new Profile({ voicemail: undefined, - }), + } as any), expiresAt: A_DATE, }); expect(session.hasAccessToVoicemail()).toBeFalsy(); @@ -164,7 +163,7 @@ describe('Session domain', () => { const session = new Session({ token: 'ref-12345', uuid: '1234', - // @ts-expect-error + // profile: new Profile({ lines: [new Line({ id: 9012, @@ -181,7 +180,7 @@ describe('Session domain', () => { context: 'internal', }], })], - }), + } as any), expiresAt: new Date(9999, 0, 1), }); it('should return true given it owns the extension', () => { @@ -226,10 +225,10 @@ describe('Session domain', () => { describe('and NO lines', () => { beforeEach(() => { A_SESSION = new Session({ ...A_SESSION, - // @ts-expect-error + // profile: new Profile({ lines: [], - }), + } as any), }); }); it('should return default context', async () => { @@ -246,13 +245,13 @@ describe('Session domain', () => { exten: '1', }], id: 1, - // @ts-expect-error + // exten: 1, - }); - // @ts-expect-error + } as any); + // const profile = new Profile({ lines: [line], - }); + } as any); A_SESSION = new Session({ ...A_SESSION, profile, }); @@ -272,10 +271,10 @@ describe('Session domain', () => { }); describe('and NO lines', () => { beforeEach(() => { - // @ts-expect-error + // const profile = new Profile({ lines: [], - }); + } as any); A_SESSION = new Session({ ...A_SESSION, profile, }); @@ -294,13 +293,13 @@ describe('Session domain', () => { exten: '1', }], id: 1, - // @ts-expect-error + // exten: 1, - }); - // @ts-expect-error + } as any); + // const profile = new Profile({ lines: [line], - }); + } as any); A_SESSION = new Session({ ...A_SESSION, profile, engineVersion: '18.08', @@ -329,13 +328,13 @@ describe('Session domain', () => { exten: '1', }], id: 1, - // @ts-expect-error + // exten: 1, - }); - // @ts-expect-error + } as any); + // const profile = new Profile({ lines: [line], - }); + } as any); A_SESSION = new Session({ ...A_SESSION, profile, }); diff --git a/src/domain/__tests__/SipLine.test.ts b/src/domain/__tests__/SipLine.test.ts index 2d940bf3..9283dd67 100644 --- a/src/domain/__tests__/SipLine.test.ts +++ b/src/domain/__tests__/SipLine.test.ts @@ -1,12 +1,11 @@ -// @ts-nocheck import SipLine from '../SipLine'; describe('SipLine domain', () => { describe('Endpoint (20.13 and higher)', () => { it('can tell if we have videoConference capabilities', () => { - const getSipLine = endpointSectionOptions => new SipLine({ + const getSipLine = (endpointSectionOptions: any) => new SipLine({ endpointSectionOptions, - }); + } as any); expect(getSipLine([['max_audio_streams', '1'], ['max_video_streams', '2']]).hasVideoConference()).toBe(true); expect(getSipLine([['max_audio_streams', '1'], ['max_video_streams', '1']]).hasVideoConference()).toBe(false); @@ -17,9 +16,9 @@ describe('SipLine domain', () => { }); describe('Old Endpoint', () => { it('can tell if we have videoConference capabilities', () => { - const getSipLine = options => new SipLine({ + const getSipLine = (options: any) => new SipLine({ options, - }); + } as any); expect(getSipLine([['max_audio_streams', '1'], ['max_video_streams', '2']]).hasVideoConference()).toBe(true); expect(getSipLine([['max_audio_streams', '1'], ['max_video_streams', '1']]).hasVideoConference()).toBe(false); diff --git a/src/domain/types.ts b/src/domain/types.ts index 70170ccc..6aa49a92 100644 --- a/src/domain/types.ts +++ b/src/domain/types.ts @@ -1,5 +1,7 @@ -import type { Session, UserAgentOptions as sipJsUserAgentOptions } from 'sip.js'; +import type { Invitation, Inviter, Session as SipSession, UserAgentOptions as sipJsUserAgentOptions } from 'sip.js'; import { SessionDescriptionHandlerFactoryOptions } from 'sip.js/lib/platform/web'; +import type { IncomingResponse as SipIncomingResponse } from 'sip.js/lib/core/messages/incoming-response'; +import { Transport } from 'sip.js/lib/api'; import WazoSessionDescriptionHandler from '../lib/WazoSessionDescriptionHandler'; // @TODO: stand-in for empty object types. Was `type Something = {};` in JS @@ -217,7 +219,7 @@ export type CTITransfer = { }; export type UserAgentOptions = sipJsUserAgentOptions & { peerConnectionOptions?: Record, - sessionDescriptionHandlerFactory: (session: Session, options: SessionDescriptionHandlerFactoryOptions) => WazoSessionDescriptionHandler, + sessionDescriptionHandlerFactory: (session: SipSession, options: SessionDescriptionHandlerFactoryOptions) => WazoSessionDescriptionHandler, }; export type UserAgentConfigOverrides = Partial MediaStream[], + getLocalStreams: () => MediaStream[], + onremovestream: (func: any) => void, + sfu: any +}; + +export type Session = (Invitation | Inviter) & { remoteTag?: any, callId?: string }; + +export type WazoTransport = Transport & { + configuration: Record, + connectPromise: Promise, + disconnectPromise: Promise, + disconnectResolve: any, + transitionState: any, + transitioningState: any; + _ws: any; +}; diff --git a/src/lib/WazoSessionDescriptionHandler.ts b/src/lib/WazoSessionDescriptionHandler.ts index 888094fe..7b9c964d 100644 --- a/src/lib/WazoSessionDescriptionHandler.ts +++ b/src/lib/WazoSessionDescriptionHandler.ts @@ -5,7 +5,7 @@ import type { MediaStreamFactory } from 'sip.js/lib/platform/web/session-descrip import type { SessionDescriptionHandlerConfiguration } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler-configuration'; import { SessionDescriptionHandler } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler'; import { SessionDescriptionHandlerOptions } from 'sip.js/lib/platform/web/session-description-handler/session-description-handler-options'; -import { Inviter, Invitation } from 'sip.js/lib/api'; +import { PeerConnection, Session as WazoSession } from '../domain/types'; import IssueReporter from '../service/IssueReporter'; import { addIcesInAllBundles, fixSdp, parseCandidate } from '../utils/sdp'; @@ -30,16 +30,16 @@ export const wazoMediaStreamFactory = (constraints: Record): Promis }; class WazoSessionDescriptionHandler extends SessionDescriptionHandler { - gatheredCandidates: Array; + gatheredCandidates: Array | string | null | undefined>; eventEmitter: EventEmitter; isWeb: boolean; - session: Inviter | Invitation; + session: WazoSession; // eslint-disable-next-line @typescript-eslint/default-param-last - constructor(logger: Logger, mediaStreamFactory: MediaStreamFactory, sessionDescriptionHandlerConfiguration: SessionDescriptionHandlerConfiguration, isWeb: boolean, session: Inviter | Invitation) { + constructor(logger: Logger, mediaStreamFactory: MediaStreamFactory, sessionDescriptionHandlerConfiguration: SessionDescriptionHandlerConfiguration, isWeb: boolean, session: WazoSession) { super(logger, mediaStreamFactory, sessionDescriptionHandlerConfiguration); this.eventEmitter = new EventEmitter(); this.isWeb = isWeb; @@ -74,7 +74,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { } // Callback on data channel creation - // @ts-ignore + // @ts-ignore: private this.onDataChannel = options.onDataChannel; // ICE will restart upon applying an offer created with the iceRestart option const iceRestart = options.offerOptions ? options.offerOptions.iceRestart : false; @@ -83,7 +83,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { // We should wait for ice when iceRestart (reinvite) or for the first invite // We shouldn't wait for ice when holding or resuming the call // We shouldn't wait for ice when receiving a reinvite (eg: pendingReinviteAck = true) - // @ts-ignore + // @ts-ignore: private const shouldWaitForIce = !this.session.pendingReinviteAck && (iceRestart || 'constraints' in options); // ICE gathering timeout may be set on a per call basis, otherwise the configured default is used const iceTimeout = options.iceGatheringTimeout === undefined ? this.sessionDescriptionHandlerConfiguration?.iceGatheringTimeout : options.iceGatheringTimeout; @@ -105,7 +105,6 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { }); if (event.candidate) { - // @ts-ignore @REEVALUATE this.gatheredCandidates.push(parseCandidate(event.candidate.candidate)); // When receiving a `srflx` or a `relay` candidate, consider the negotiation done. @@ -153,8 +152,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { return { type: description.type, // Fix sdp only when no candidates - // @ts-ignore - sdp: fixSdp(sdp, this.gatheredCandidates, options && options.constraints ? options.constraints.video : false), + sdp: fixSdp(sdp, this.gatheredCandidates as Record[], options && options.constraints ? options.constraints.video : false), }; }) .then((sessionDescription: any) => this.applyModifiers(sessionDescription, modifiers)) @@ -209,18 +207,15 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { } // Closing senders via getLocalStreams, `getLocalStreams` is deprecated, we have to check if it exists. - // @ts-ignore - if (this.peerConnection?.getLocalStreams) { - // @ts-ignore - this.peerConnection?.getLocalStreams().forEach(stream => { + if ((this.peerConnection as PeerConnection)?.getLocalStreams) { + (this.peerConnection as PeerConnection)?.getLocalStreams().forEach(stream => { stream.getTracks() .filter((track: MediaStreamTrack) => track.enabled) .forEach((track: MediaStreamTrack) => track.stop()); }); // Closing receivers via getRemoteStreams - // @ts-ignore - this.peerConnection?.getRemoteStreams().forEach(stream => { + (this.peerConnection as PeerConnection)?.getRemoteStreams().forEach(stream => { stream.getTracks() .filter((track: MediaStreamTrack) => track.enabled) .forEach((track: MediaStreamTrack) => track.stop()); @@ -365,8 +360,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { // set the transceiver direction to the answer direction this._peerConnection.getTransceivers().forEach(transceiver => { - // @ts-ignore - if (transceiver.stopped) { + if ((transceiver as RTCRtpTransceiver & { stopped: boolean }).stopped) { return; } @@ -445,12 +439,12 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { if (oldTrack) { oldTrack.stop(); localStream.removeTrack(oldTrack); - // @ts-ignore + // @ts-ignore: private SessionDescriptionHandler.dispatchRemoveTrackEvent(localStream, oldTrack); } localStream.addTrack(newTrack); - // @ts-ignore + // @ts-ignore: private SessionDescriptionHandler.dispatchAddTrackEvent(localStream, newTrack); }).catch((error: Error) => { this.logger.error(`SessionDescriptionHandler.setLocalMediaStream - failed to replace sender ${kind} track`); @@ -475,7 +469,7 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { } localStream.addTrack(newTrack); - // @ts-ignore + // @ts-ignore: private SessionDescriptionHandler.dispatchAddTrackEvent(localStream, newTrack); })); } @@ -510,10 +504,10 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { } : {}; // if we already have a local media stream... - // @ts-ignore + // @ts-ignore: private if (this.localMediaStreamConstraints) { // if constraints have not changed, do not get a new media stream - // @ts-ignore + // @ts-ignore: private if (JSON.stringify(this.localMediaStreamConstraints.audio) === JSON.stringify(constraints.audio) && JSON.stringify(this.localMediaStreamConstraints.video) === JSON.stringify(constraints.video)) { return Promise.resolve(); } @@ -524,9 +518,8 @@ class WazoSessionDescriptionHandler extends SessionDescriptionHandler { }; } - // @ts-ignore + // @ts-ignore: private this.localMediaStreamConstraints = constraints; - // @ts-ignore return this.mediaStreamFactory(constraints, this).then(mediaStream => { this.setLocalMediaStream(mediaStream); return mediaStream; diff --git a/src/service/IssueReporter.ts b/src/service/IssueReporter.ts index 98d2f981..7d55ed29 100644 --- a/src/service/IssueReporter.ts +++ b/src/service/IssueReporter.ts @@ -168,8 +168,7 @@ class IssueReporter { errorMessage: lastArg.message, errorStack: lastArg.stack, errorType: lastArg.constructor.name, - // @ts-ignore - skipSendToRemote: lastArg.skipSendToRemote, + skipSendToRemote: (lastArg as Error & { skipSendToRemote: boolean }).skipSendToRemote, }; } else { extra = lastArg; @@ -263,7 +262,7 @@ class IssueReporter { this.oldConsoleMethods = {}; CONSOLE_METHODS.forEach((methodName: string) => { if (this.oldConsoleMethods) { - // @ts-ignore + // @ts-ignore: keys // eslint-disable-next-line this.oldConsoleMethods[methodName] = console[methodName]; } @@ -391,8 +390,7 @@ class IssueReporter { 'Content-Type': 'application/json', }, body, - }).catch((e: Error) => { - // @ts-ignore + }).catch((e: Error & { skipSendToRemote?: boolean }) => { e.skipSendToRemote = true; this.log('error', this._makeCategory('grafana'), 'Sending log to grafana, error', e); // wait at least 1 second, at most 50 seconds diff --git a/src/service/__tests__/IssueReporter.test.ts b/src/service/__tests__/IssueReporter.test.ts index bb7747d6..ae0dadc1 100644 --- a/src/service/__tests__/IssueReporter.test.ts +++ b/src/service/__tests__/IssueReporter.test.ts @@ -25,8 +25,7 @@ describe('IssueReporter', () => { }); beforeEach(() => { jest.resetAllMocks(); - // @ts-expect-error - realFetch.mockImplementation(() => () => ({ + (realFetch as any).mockImplementation(() => () => ({ catch: () => {}, })); }); diff --git a/src/simple/Phone.ts b/src/simple/Phone.ts index 2e210cdc..43626856 100644 --- a/src/simple/Phone.ts +++ b/src/simple/Phone.ts @@ -1,5 +1,4 @@ import { SessionState } from 'sip.js/lib/api/session-state'; -import { Inviter, Invitation } from 'sip.js/lib/api'; import { OutgoingInviteRequest } from 'sip.js/lib/core'; import type SipLine from '../domain/SipLine'; import type Session from '../domain/Session'; @@ -12,7 +11,7 @@ import IssueReporter from '../service/IssueReporter'; import Emitter, { IEmitter } from '../utils/Emitter'; import Wazo from './index'; import SFUNotAvailableError from '../domain/SFUNotAvailableError'; -import { ConnectionOptions } from '../domain/types'; +import { ConnectionOptions, Session as WazoSession } from '../domain/types'; const logger = IssueReporter.loggerFor('simple-phone'); const sipLogger = IssueReporter.loggerFor('sip.js'); @@ -91,10 +90,10 @@ export interface IPhone extends IEmitter { getStats: (callSession: CallSession) => Promise; startNetworkMonitoring: (callSession: CallSession, interval: number) => void; stopNetworkMonitoring: (callSession: CallSession) => void; - getSipSessionId: (sipSession: Invitation | Inviter) => string | null | undefined; - sendMessage: (body: string, sipSession?: Inviter | Invitation, contentType?: string) => void; - sendChat: (content: string, sipSession?: Inviter | Invitation) => void; - sendSignal: (content: any, sipSession?: Inviter | Invitation) => void ; + getSipSessionId: (sipSession: WazoSession) => string | null | undefined; + sendMessage: (body: string, sipSession?: WazoSession, contentType?: string) => void; + sendChat: (content: string, sipSession?: WazoSession) => void; + sendSignal: (content: any, sipSession?: WazoSession) => void ; turnCameraOff: (callSession: CallSession) => void; turnCameraOn: (callSession: CallSession) => void; startScreenSharing: (constraints: Record, callSession?: CallSession) => Promise; @@ -114,7 +113,7 @@ export interface IPhone extends IEmitter { getRemoteVideoStreamFromPc: (callSession: CallSession) => MediaStream | null; hasVideo: (callSession: CallSession) => boolean; hasAVideoTrack: (callSession: CallSession) => boolean; - getCurrentSipSession: () => Invitation | Inviter | null; + getCurrentSipSession: () => WazoSession | null; getPrimaryWebRtcLine: () => SipLine | null; getOutputDevice: () => string | null ; getPrimaryLine: () => SipLine | null; @@ -228,7 +227,7 @@ class Phone extends Emitter implements IPhone { super(); // Sugar syntax for `Wazo.Phone.EVENT_NAME` Object.keys(PHONE_EVENTS).forEach((key: string) => { - // @ts-ignore + // @ts-ignore: keys this[key] = PHONE_EVENTS[key]; }); this.SessionState = SessionState; @@ -435,14 +434,14 @@ class Phone extends Emitter implements IPhone { return this.phone ? this.phone.stopNetworkMonitoring(callSession) : null; } - getSipSessionId(sipSession: Invitation | Inviter): string | null | undefined { + getSipSessionId(sipSession: WazoSession): string | null | undefined { if (!sipSession || !this.phone) { return null; } return this.phone.getSipSessionId(sipSession); } - sendMessage(body: string, sipSession?: Inviter | Invitation, contentType = 'text/plain'): void { + sendMessage(body: string, sipSession?: WazoSession, contentType = 'text/plain'): void { const toSipSession = sipSession || this.getCurrentSipSession(); if (!toSipSession || !this.phone) { @@ -452,14 +451,14 @@ class Phone extends Emitter implements IPhone { this.phone.sendMessage(toSipSession, body, contentType); } - sendChat(content: string, sipSession?: Inviter | Invitation): void { + sendChat(content: string, sipSession?: WazoSession): void { this.sendMessage(JSON.stringify({ type: MESSAGE_TYPE_CHAT, content, }), sipSession, 'application/json'); } - sendSignal(content: any, sipSession?: Inviter | Invitation): void { + sendSignal(content: any, sipSession?: WazoSession): void { this.sendMessage(JSON.stringify({ type: MESSAGE_TYPE_SIGNAL, content, @@ -552,7 +551,7 @@ class Phone extends Emitter implements IPhone { return this.phone ? this.phone.hasAVideoTrack(callSession) : false; } - getCurrentSipSession(): Invitation | Inviter | null { + getCurrentSipSession(): WazoSession | null { return this.phone?.currentSipSession || null; } diff --git a/src/simple/Websocket.ts b/src/simple/Websocket.ts index a3ba818d..34f66294 100644 --- a/src/simple/Websocket.ts +++ b/src/simple/Websocket.ts @@ -46,11 +46,11 @@ class Websocket extends Emitter implements IWebsocket { super(); // Sugar syntax for `Wazo.WebSocket.EVENT_NAME` Object.keys(OTHER_EVENTS).forEach(key => { - // @ts-ignore + // @ts-ignore: keys this[key] = OTHER_EVENTS[key]; }); Object.keys(SOCKET_EVENTS).forEach(key => { - // @ts-ignore + // @ts-ignore: keys this[key] = SOCKET_EVENTS[key]; }); this.eventLists = WazoWebSocketClient.eventLists; diff --git a/src/simple/index.ts b/src/simple/index.ts index ddf0789f..0b9289c1 100644 --- a/src/simple/index.ts +++ b/src/simple/index.ts @@ -138,7 +138,6 @@ const Wazo = { }; if (window) { - // @ts-ignore window.Wazo = Wazo; } diff --git a/src/simple/room/Room.ts b/src/simple/room/Room.ts index c5aa1f03..784d8b77 100644 --- a/src/simple/room/Room.ts +++ b/src/simple/room/Room.ts @@ -1,6 +1,6 @@ import type { Message } from 'sip.js/lib/api/message'; import sdpParser from 'sdp-transform'; -import { Invitation, Inviter } from 'sip.js'; +import { SessionDescriptionHandler } from 'sip.js/lib/platform/web'; import type CallSession from '../../domain/CallSession'; import getApiClient from '../../service/getApiClient'; import Emitter from '../../utils/Emitter'; @@ -9,6 +9,7 @@ import Participant, { RawParticipant } from './Participant'; import RemoteParticipant from './RemoteParticipant'; import IssueReporter from '../../service/IssueReporter'; import LocalParticipant from './LocalParticipant'; +import { PeerConnection, Session } from '../../domain/types'; export const SIGNAL_TYPE_PARTICIPANT_UPDATE = 'signal/PARTICIPANT_UPDATE'; export const SIGNAL_TYPE_PARTICIPANT_REQUEST = 'signal/PARTICIPANT_REQUEST'; @@ -467,8 +468,7 @@ class Room extends Emitter { } // Retrieve mapping - // @ts-ignore - Wazo.Phone.phone.currentSipSession.sessionDescriptionHandler.on('setDescription', ({ + (Wazo.Phone.phone.currentSipSession.sessionDescriptionHandler as SessionDescriptionHandler & { on: (input: string, options?: Record) => void })?.on('setDescription', ({ type, sdp: rawSdp, }: any) => { @@ -486,7 +486,7 @@ class Room extends Emitter { this.audioStream = stream; if (!this.roomAudioElement) { - const sessionId = Wazo.Phone.phone?.getSipSessionId(Wazo.Phone.phone?.currentSipSession as Inviter | Invitation); + const sessionId = Wazo.Phone.phone?.getSipSessionId(Wazo.Phone.phone?.currentSipSession as Session); this.roomAudioElement = Wazo.Phone.phone?.createAudioElementFor(sessionId as string); this.roomAudioElement.srcObject = stream; } else { @@ -586,8 +586,7 @@ class Room extends Emitter { [this.ON_AUDIO_STREAM, this.ON_VIDEO_STREAM, this.ON_REMOVE_STREAM].forEach(event => Wazo.Phone.on(event, (...args) => this.eventEmitter.emit.apply(this.eventEmitter, [event, ...args]))); } - _onMessage(message: Message): Record | null | undefined { - // @ts-ignore + _onMessage(message: Message & { method?: string, body?: string }): Record | null | undefined { if (message.method !== 'MESSAGE') { return null; } @@ -595,8 +594,7 @@ class Room extends Emitter { let body: any; try { - // @ts-ignore - body = JSON.parse(message.body); + body = JSON.parse(message.body as string); } catch (e: any) { return null; } @@ -839,8 +837,8 @@ class Room extends Emitter { trackId, streamId, } = this._callIdStreamIdMap[newParticipant.callId] || {}; - // @ts-ignore - const pc = Wazo.Phone.phone.currentSipSession.sessionDescriptionHandler.peerConnection; + + const pc = (Wazo.Phone.phone?.currentSipSession?.sessionDescriptionHandler as SessionDescriptionHandler)?.peerConnection as PeerConnection; // Can't use `getReceivers` here because on FF we make the mapping based on the streamId const stream = pc.getRemoteStreams().find((someStream: any) => someStream.id === streamId || someStream.getTracks().some((track: any) => track.id === trackId)); diff --git a/src/simple/room/SipRoom.ts b/src/simple/room/SipRoom.ts index 93bdea2e..59b52f06 100644 --- a/src/simple/room/SipRoom.ts +++ b/src/simple/room/SipRoom.ts @@ -1,9 +1,9 @@ -import { Invitation, Inviter } from 'sip.js'; import type { Message } from 'sip.js/lib/api/message'; import CallSession from '../../domain/CallSession'; import IssueReporter from '../../service/IssueReporter'; import Wazo from '../index'; import Room from './Room'; +import { Session } from '../../domain/types'; const logger = IssueReporter.loggerFor('sdk-sip-room'); @@ -54,7 +54,7 @@ class SipRoom extends Room { } getLocalGuestName(): string | null { - // @ts-ignore + // @ts-ignore: private return Wazo.Phone.phone?.client.userAgent?.options.displayName || null; } @@ -156,7 +156,7 @@ class SipRoom extends Room { } _getCurrentSipCallIs() { - return Wazo.Phone.getSipSessionId(Wazo.Phone.phone?.currentSipSession as Invitation | Inviter); + return Wazo.Phone.getSipSessionId(Wazo.Phone.phone?.currentSipSession as Session); } } diff --git a/src/simple/room/Stream.ts b/src/simple/room/Stream.ts index 92336204..af8b7218 100644 --- a/src/simple/room/Stream.ts +++ b/src/simple/room/Stream.ts @@ -34,9 +34,8 @@ class Stream { element.onloadedmetadata = () => { const tracks = this.htmlStream ? this.htmlStream.getVideoTracks() : []; - tracks.forEach(track => { + tracks.forEach((track: MediaStreamTrack & { loaded?: boolean }) => { track.enabled = true; - // @ts-ignore track.loaded = true; }); }; diff --git a/src/simple/room/__tests__/Participant.test.ts b/src/simple/room/__tests__/Participant.test.ts index 76028e98..32b6ec7e 100644 --- a/src/simple/room/__tests__/Participant.test.ts +++ b/src/simple/room/__tests__/Participant.test.ts @@ -26,8 +26,7 @@ describe('Participant', () => { const room = new Room(); room.setMeetingUuid(meetingUuid); const banMeetingParticipant = jest.fn(); - // @ts-expect-error - getApiClient.mockImplementation(() => ({ + (getApiClient as any).mockImplementation(() => ({ calld: { banMeetingParticipant, }, @@ -52,8 +51,7 @@ describe('Participant', () => { const room = new Room(); room.setMeetingUuid(meetingUuid); const banMeetingParticipant = jest.fn(); - // @ts-expect-error - getApiClient.mockImplementation(() => ({ + (getApiClient as any).mockImplementation(() => ({ calld: { banMeetingParticipant, }, diff --git a/src/simple/room/__tests__/RemoteParticipant.test.ts b/src/simple/room/__tests__/RemoteParticipant.test.ts index e9ff4d76..9978ae78 100644 --- a/src/simple/room/__tests__/RemoteParticipant.test.ts +++ b/src/simple/room/__tests__/RemoteParticipant.test.ts @@ -1,14 +1,15 @@ // Using directly Wazo to avoid issues with require cycle import Wazo from '../../index'; +import { RawParticipant } from '../Participant'; +import Room from '../Room'; describe('RemoteParticipant', () => { it('should fall back on number when name is ', () => { const number = '1234'; - // @ts-expect-error - const participant = new Wazo.RemoteParticipant(null, { + const participant = new Wazo.RemoteParticipant({} as Room, { caller_id_name: '', caller_id_number: number, - }); + } as RawParticipant); expect(participant.name).toEqual(number); }); }); diff --git a/src/utils/PhoneNumberUtil.ts b/src/utils/PhoneNumberUtil.ts index 28a7ba27..41e85297 100644 --- a/src/utils/PhoneNumberUtil.ts +++ b/src/utils/PhoneNumberUtil.ts @@ -59,7 +59,7 @@ const getDisplayableNumber = (rawNumber: string, country: string, asYouType = fa const parsePhoneNumber = (phoneNumber: string): string => phoneNumber.replace(EXTRA_CHAR_REGEXP, ''); -const getCallableNumber = (number: string, country: string | null | undefined): string | null | undefined => { +const getCallableNumber = (number: string, country?: string | null): string | null | undefined => { try { if (country) { return getDisplayableNumber(number, country).replace(EXTRA_CHAR_REGEXP, ''); diff --git a/src/utils/__tests__/PhoneNumberUtil.test.ts b/src/utils/__tests__/PhoneNumberUtil.test.ts index becf3c3a..cceaba29 100644 --- a/src/utils/__tests__/PhoneNumberUtil.test.ts +++ b/src/utils/__tests__/PhoneNumberUtil.test.ts @@ -44,17 +44,11 @@ describe('getCallableNumber', () => { expect(getCallableNumber('9000', 'US')).toBe('9000'); }); it('works without a country', () => { - // @ts-expect-error expect(getCallableNumber('06 75 45')).toBe('067545'); - // @ts-expect-error expect(getCallableNumber('067-545')).toBe('067545'); - // @ts-expect-error expect(getCallableNumber('8008')).toBe('8008'); - // @ts-expect-error expect(getCallableNumber('80.08')).toBe('8008'); - // @ts-expect-error expect(getCallableNumber('*10')).toBe('*10'); - // @ts-expect-error expect(getCallableNumber('9000')).toBe('9000'); }); }); diff --git a/src/utils/__tests__/api-requester.test.ts b/src/utils/__tests__/api-requester.test.ts index 054f4c72..4297d4af 100644 --- a/src/utils/__tests__/api-requester.test.ts +++ b/src/utils/__tests__/api-requester.test.ts @@ -81,13 +81,12 @@ describe('Calling fetch', () => { it('should call fetch without body but query string in get method', async () => { jest.mock('node-fetch/src/index', () => {}); - // @ts-ignore global.fetch = jest.fn(() => Promise.resolve({ json: () => Promise.resolve({}), headers: { get: () => '', }, - })); + })) as any; await new ApiRequester({ server, @@ -111,7 +110,7 @@ describe('With a refresh token', () => { it('should retry the call with a new token', async () => { jest.mock('node-fetch/src/index', () => {}); let calls = 0; - // @ts-ignore + global.fetch = jest.fn(() => { calls++; return Promise.resolve({ @@ -121,7 +120,7 @@ describe('With a refresh token', () => { status: calls === 1 ? 401 : 200, json: () => Promise.resolve({}), }); - }); + }) as any; const requester = new ApiRequester({ server, agent: null, diff --git a/src/utils/__tests__/sdp.test.ts b/src/utils/__tests__/sdp.test.ts index bfd275a0..713a7b71 100644 --- a/src/utils/__tests__/sdp.test.ts +++ b/src/utils/__tests__/sdp.test.ts @@ -92,19 +92,15 @@ describe('SDP utils', () => { }); describe('Parsing candidate', () => { it('should parse a single candidate', () => { - const parsed = parseCandidate(candidate); - // @ts-expect-error + const parsed = parseCandidate(candidate) as Record; expect(parsed.type).toBe('relay'); - // @ts-expect-error expect(parsed.ip).toBe('14.72.2.1'); }); }); describe('Validating candidates', () => { it('should parse a single candidate', () => { - // @ts-expect-error - expect(areCandidateValid([null])).toBeFalsy(); - // @ts-expect-error - expect(areCandidateValid([parseCandidate(candidate)])).toBeTruthy(); + expect(areCandidateValid([])).toBeFalsy(); + expect(areCandidateValid([parseCandidate(candidate) as Record])).toBeTruthy(); }); }); describe('Validating sdp', () => { @@ -116,15 +112,12 @@ describe('SDP utils', () => { }); describe('Fixing sdp', () => { it('should fix a SDP without candidate or IN ip', () => { - const candidates = [parseCandidate(candidate)]; - // @ts-expect-error + const candidates = [parseCandidate(candidate)] as Record[]; const fixedSdp = fixSdp(badMobileSdp, candidates); const parsed = sdpParser.parse(fixedSdp); - // @ts-expect-error - expect(parsed.media[0].candidates.length).toBe(1); - expect(parsed.media[0].port).toBe(57021); - // @ts-expect-error - expect(parsed.origin.address).toBe('14.72.2.1'); + expect((parsed.media[0] as any).candidates.length).toBe(1); + expect((parsed.media[0] as any).port).toBe(57021); + expect((parsed.origin as any).address).toBe('14.72.2.1'); expect(fixedSdp.indexOf('IN 0.0.0.0')).toBe(-1); }); }); @@ -132,8 +125,7 @@ describe('SDP utils', () => { it('should set a bundle for each m section', async () => { const invalid = fixBundle(invalidBundle); const parsed = sdpParser.parse(invalid); - // @ts-expect-error - expect(parsed.groups[0].mids).toBe('0 1'); + expect((parsed.groups?.[0] as any).mids).toBe('0 1'); }); }); describe('Deactivate video', () => { @@ -160,14 +152,10 @@ a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host a=candidate:3996450952 1 udp 41819903 14.72.21.2 65092 typ relay raddr 0.0.0.0 rport 0 generation 0 network-id 3 network-cost 10 `; const fixedAudioWithoutCandidate = sdpParser.parse(addIcesInAllBundles(audioWithoutCandidate)); - // @ts-expect-error - expect(fixedAudioWithoutCandidate.media[0].candidates[0].type).toBe('host'); - // @ts-expect-error - expect(fixedAudioWithoutCandidate.media[0].candidates[1].type).toBe('relay'); - // @ts-expect-error - expect(fixedAudioWithoutCandidate.media[1].candidates[0].type).toBe('host'); - // @ts-expect-error - expect(fixedAudioWithoutCandidate.media[1].candidates[1].type).toBe('relay'); + expect((fixedAudioWithoutCandidate.media[0] as any).candidates[0].type).toBe('host'); + expect((fixedAudioWithoutCandidate.media[0] as any).candidates[1].type).toBe('relay'); + expect((fixedAudioWithoutCandidate.media[1] as any).candidates[0].type).toBe('host'); + expect((fixedAudioWithoutCandidate.media[1] as any).candidates[1].type).toBe('relay'); // It should not add candidate if already present const audioWithCandidate = ` c=IN IP4 203.0.113.1 @@ -178,10 +166,8 @@ m=video 54400 RTP/SAVPF 0 96 a=candidate:0 1 UDP 2113667327 203.0.113.1 54400 typ host `; const fixedAudioWithCandidate = sdpParser.parse(addIcesInAllBundles(audioWithCandidate)); - // @ts-expect-error - expect(fixedAudioWithCandidate.media[0].candidates[0].type).toBe('srflx'); - // @ts-expect-error - expect(fixedAudioWithCandidate.media[1].candidates[0].type).toBe('host'); + expect((fixedAudioWithCandidate.media?.[0] as any).candidates[0].type).toBe('srflx'); + expect((fixedAudioWithCandidate.media?.[1] as any).candidates[0].type).toBe('host'); }); }); }); diff --git a/src/web-rtc-client.ts b/src/web-rtc-client.ts index 6876baf4..abe90d32 100644 --- a/src/web-rtc-client.ts +++ b/src/web-rtc-client.ts @@ -3,7 +3,6 @@ /* global window, document, navigator */ import 'webrtc-adapter'; import type { InviterInviteOptions } from 'sip.js/lib/api/inviter-invite-options'; -import type { IncomingResponse } from 'sip.js/lib/core/messages/incoming-response'; import type { InvitationRejectOptions } from 'sip.js/lib/api/invitation-reject-options'; import type { InviterCancelOptions } from 'sip.js/lib/api/inviter-cancel-options'; import type { SessionByeOptions } from 'sip.js/lib/api/session-bye-options'; @@ -28,7 +27,7 @@ import { defaultPeerConnectionConfiguration } from 'sip.js/lib/platform/web/sess import getStats from 'getstats'; import { OutgoingByeRequest, OutgoingInviteRequest, OutgoingRequest } from 'sip.js/lib/core'; -import { Inviter, Invitation, Registerer, Session } from 'sip.js/lib/api'; +import { Inviter, Invitation, Registerer, Session, SessionDescriptionHandlerOptions } from 'sip.js/lib/api'; import WazoSessionDescriptionHandler, { wazoMediaStreamFactory } from './lib/WazoSessionDescriptionHandler'; import Emitter from './utils/Emitter'; import ApiClient from './api-client'; @@ -36,7 +35,7 @@ import IssueReporter from './service/IssueReporter'; import Heartbeat from './utils/Heartbeat'; import { getVideoDirection, hasAnActiveVideo } from './utils/sdp'; import { lastIndexOf } from './utils/array'; -import type { MediaConfig, UserAgentConfigOverrides, WebRtcConfig, UserAgentOptions } from './domain/types'; +import type { MediaConfig, UserAgentConfigOverrides, WebRtcConfig, UserAgentOptions, IncomingResponse, PeerConnection, Session as WazoSession, WazoTransport } from './domain/types'; // We need to replace 0.0.0.0 to 127.0.0.1 in the sdp to avoid MOH during a createOffer. export const replaceLocalIpModifier = (description: Record) => Promise.resolve({ // description is immutable... so we have to clone it or the `type` attribute won't be returned. @@ -88,11 +87,11 @@ export default class WebRTCClient extends Emitter { hasAudio: boolean; - audio: MediaTrackConstraints | boolean | undefined; + audio: MediaTrackConstraintSet | boolean | undefined; - audioElements: Record; + audioElements: Record void }>; - video: MediaTrackConstraints | boolean | undefined; + video: MediaTrackConstraintSet | boolean | undefined; audioStreams: Record; @@ -114,7 +113,7 @@ export default class WebRTCClient extends Emitter { statsIntervals: Record; - sipSessions: Record; + sipSessions: Record; conferences: Record; @@ -180,7 +179,7 @@ export default class WebRTCClient extends Emitter { return []; } - constructor(config: WebRtcConfig, session: Invitation | Inviter | null | undefined, uaConfigOverrides?: UserAgentConfigOverrides) { + constructor(config: WebRtcConfig, session: WazoSession | null | undefined, uaConfigOverrides?: UserAgentConfigOverrides) { super(); // For debug purpose @@ -266,8 +265,7 @@ export default class WebRTCClient extends Emitter { method: 'delegate.onInvite', clientId: this.clientId, id: invitation.id, - // @ts-ignore - remoteURI: invitation.remoteURI, + remoteURI: (invitation as any).remoteURI, }); this._setupSession(invitation); @@ -319,9 +317,7 @@ export default class WebRTCClient extends Emitter { logger.info('sdk webrtc connected', { method: 'delegate.onConnect', clientId: this.clientId }); this.eventEmitter.emit(CONNECTED); - // @ts-ignore if (!this.isRegistered() && this.registerer?.waiting) { - // @ts-ignore this.registerer.waitingToggle(false); } @@ -337,9 +333,7 @@ export default class WebRTCClient extends Emitter { if (this.isRegistered()) { await this.unregister(); - // @ts-ignore if (this.registerer?.waiting) { - // @ts-ignore this.registerer.waitingToggle(false); } @@ -354,7 +348,6 @@ export default class WebRTCClient extends Emitter { registered: this.isRegistered(), connectionPromise: !!this.connectionPromise, registerer: !!this.registerer, - // @ts-ignore waiting: this.registerer && this.registerer.waiting, tries, skipRegister: this.skipRegister, @@ -378,7 +371,6 @@ export default class WebRTCClient extends Emitter { return Promise.resolve(); } - // @ts-ignore if (this.connectionPromise || this.registerer?.waiting) { logger.info('sdk webrtc registering aborted due to a registration in progress.', { clientId: this.clientId }); return Promise.resolve(); @@ -402,9 +394,7 @@ export default class WebRTCClient extends Emitter { this.connectionPromise = null; - // @ts-ignore if (this.registerer && this.registerer.waiting) { - // @ts-ignore this.registerer.waitingToggle(false); } @@ -463,14 +453,10 @@ export default class WebRTCClient extends Emitter { return; } - // @ts-ignore const oldWaitingToggle = registerer.waitingToggle.bind(registerer); - // @ts-ignore const oldUnregistered = registerer.unregistered.bind(registerer); - // @ts-ignore registerer.waitingToggle = (waiting: boolean) => { - // @ts-ignore if (!registerer || registerer.waiting === waiting) { return; } @@ -478,7 +464,6 @@ export default class WebRTCClient extends Emitter { oldWaitingToggle(waiting); }; - // @ts-ignore registerer.unregistered = () => { if (!registerer || registerer.state === RegistererState.Terminated) { return; @@ -548,7 +533,7 @@ export default class WebRTCClient extends Emitter { }); } - call(number: string, enableVideo?: boolean, audioOnly = false, conference = false): Inviter | Invitation { + call(number: string, enableVideo?: boolean, audioOnly = false, conference = false): WazoSession { logger.info('sdk webrtc creating call', { clientId: this.clientId, number, @@ -593,19 +578,16 @@ export default class WebRTCClient extends Emitter { if (session.sessionDescriptionHandler?.peerConnection) { session.sessionDescriptionHandler.peerConnection.sfu = conference; } - // @ts-ignore this._onAccepted(session, response.session, true); }, - onProgress: payload => { + onProgress: (payload: IncomingResponse) => { if (payload.message.statusCode === 183) { - // @ts-ignore this._onEarlyProgress(payload.session); } }, onReject: (response: IncomingResponse) => { logger.info('on call rejected', { id: session.id, - // @ts-ignore fromTag: session.fromTag, }); @@ -619,8 +601,7 @@ export default class WebRTCClient extends Emitter { }; if (inviteOptions.sessionDescriptionHandlerOptions) { - // @ts-ignore - inviteOptions.sessionDescriptionHandlerOptions.audioOnly = audioOnly; + (inviteOptions.sessionDescriptionHandlerOptions as SessionDescriptionHandlerOptions & { audioOnly?: boolean }).audioOnly = audioOnly; } inviteOptions.sessionDescriptionHandlerModifiers = [replaceLocalIpModifier]; @@ -653,7 +634,7 @@ export default class WebRTCClient extends Emitter { sessionDescriptionHandlerOptions: this.getMediaConfiguration(enableVideo || false), }; return this._accept(session, options).then(() => { - // @ts-ignore + // @ts-ignore: private if (session.isCanceled) { const message = 'accepted a canceled session (or was canceled during the accept phase).'; logger.error(message, { @@ -672,7 +653,7 @@ export default class WebRTCClient extends Emitter { }); } - async hangup(session: Invitation | Inviter): Promise { + async hangup(session: WazoSession): Promise { const { state, id }: any = session; logger.info('sdk webrtc hangup call', { clientId: this.clientId, id, state }); @@ -705,9 +686,8 @@ export default class WebRTCClient extends Emitter { return Promise.resolve(null); } - async getStats(session: Invitation | Inviter): Promise { - // @ts-ignore - const pc: RTCPeerConnection = session.sessionDescriptionHandler.peerConnection; + async getStats(session: WazoSession): Promise { + const pc = (session.sessionDescriptionHandler as SessionDescriptionHandler)?.peerConnection as RTCPeerConnection; if (!pc) { return null; @@ -717,7 +697,7 @@ export default class WebRTCClient extends Emitter { } // Fetch and emit an event at `interval` with session network stats - startNetworkMonitoring(session: Inviter | Invitation, interval = 1000): void { + startNetworkMonitoring(session: WazoSession, interval = 1000): void { const sessionId = this.getSipSessionId(session); logger.info('starting network inspection', { id: sessionId, @@ -727,7 +707,7 @@ export default class WebRTCClient extends Emitter { this.networkMonitoringInterval[sessionId] = setInterval(() => this._fetchNetworkStats(sessionId), interval); } - stopNetworkMonitoring(session: Inviter | Invitation): void { + stopNetworkMonitoring(session: WazoSession): void { const sessionId = this.getSipSessionId(session); const exists = (sessionId in this.networkMonitoringInterval); logger.info('stopping network inspection', { @@ -743,7 +723,7 @@ export default class WebRTCClient extends Emitter { } } - async reject(session: Invitation | Inviter): Promise { + async reject(session: WazoSession): Promise { logger.info('sdk webrtc reject call', { clientId: this.clientId, id: session.id, @@ -788,7 +768,7 @@ export default class WebRTCClient extends Emitter { if (this.userAgent) { this.userAgent.delegate = undefined; } - // @ts-ignore: this is odd, may want to validate + // @ts-ignore: removeAllListeners does not exist this.userAgent.stateChange.removeAllListeners(); await this._disconnectTransport(force); @@ -796,8 +776,7 @@ export default class WebRTCClient extends Emitter { try { // Prevent `Connect aborted.` error when disconnecting - // @ts-ignore - this.userAgent.transport.connectReject = () => {}; + (this.userAgent.transport as WazoTransport & { connectReject: () => void }).connectReject = () => {}; // Don't wait here, It can take ~30s to stop ... this.userAgent.stop().catch(console.error); } catch (_) { // Avoid to raise exception when trying to close with hanged-up sessions remaining @@ -813,11 +792,11 @@ export default class WebRTCClient extends Emitter { return null; } - // @ts-ignore + // @ts-ignore: private return session.remoteIdentity.uri._normal.user; } - mute(session: Inviter | Invitation): void { + mute(session: WazoSession): void { logger.info('sdk webrtc mute', { id: session.id, }); @@ -825,7 +804,7 @@ export default class WebRTCClient extends Emitter { this._toggleAudio(session, true); } - unmute(session: Inviter | Invitation): void { + unmute(session: WazoSession): void { logger.info('sdk webrtc unmute', { id: session.id, }); @@ -833,14 +812,13 @@ export default class WebRTCClient extends Emitter { this._toggleAudio(session, false); } - isAudioMuted(session: Inviter | Invitation): boolean { + isAudioMuted(session: WazoSession): boolean { if (!session || !session.sessionDescriptionHandler) { return false; } let muted = true; - // @ts-ignore - const pc = session.sessionDescriptionHandler.peerConnection; + const pc = (session.sessionDescriptionHandler as any).peerConnection; if (!pc) { return false; @@ -871,7 +849,7 @@ export default class WebRTCClient extends Emitter { return muted; } - toggleCameraOn(session: Inviter | Invitation): void { + toggleCameraOn(session: WazoSession): void { logger.info('sdk webrtc toggle camera on', { id: session.id, }); @@ -879,7 +857,7 @@ export default class WebRTCClient extends Emitter { this._toggleVideo(session, false); } - toggleCameraOff(session: Inviter | Invitation): void { + toggleCameraOff(session: WazoSession): void { logger.info('sdk webrtc toggle camera off', { id: session.id, }); @@ -887,13 +865,13 @@ export default class WebRTCClient extends Emitter { this._toggleVideo(session, true); } - hold(session: Inviter | Invitation, isConference = false, hadVideo = false): Promise { + hold(session: WazoSession, isConference = false, hadVideo = false): Promise { const sessionId = this.getSipSessionId(session); const hasVideo = hadVideo || this.hasLocalVideo(sessionId); logger.info('sdk webrtc hold', { sessionId, keys: Object.keys(this.heldSessions), - // @ts-ignore + // @ts-ignore: private pendingReinvite: !!session.pendingReinvite, isConference, hasVideo, @@ -903,7 +881,7 @@ export default class WebRTCClient extends Emitter { return Promise.resolve(); } - // @ts-ignore + // @ts-ignore: private if (session.pendingReinvite) { return Promise.resolve(); } @@ -918,10 +896,9 @@ export default class WebRTCClient extends Emitter { this.mute(session); session.sessionDescriptionHandlerOptionsReInvite = { - // @ts-ignore hold: true, conference: isConference, - }; + } as SessionDescriptionHandlerOptions; const options = this.getMediaConfiguration(false, isConference); if (!this._isWeb()) { @@ -936,8 +913,8 @@ export default class WebRTCClient extends Emitter { // Avoid sdh to create a new stream if (session.sessionDescriptionHandler) { - // @ts-ignore - session.sessionDescriptionHandler.localMediaStreamConstraints = options.constraints; + // @ts-ignore: private + (session.sessionDescriptionHandler as SessionDescriptionHandler).localMediaStreamConstraints = options.constraints; } // Send re-INVITE @@ -946,19 +923,19 @@ export default class WebRTCClient extends Emitter { }); } - unhold(session: Inviter | Invitation, isConference = false): Promise { + unhold(session: WazoSession, isConference = false): Promise { const sessionId = this.getSipSessionId(session); const hasVideo = sessionId in this.heldSessions && this.heldSessions[sessionId].hasVideo; logger.info('sdk webrtc unhold', { sessionId, keys: Object.keys(this.heldSessions), - // @ts-ignore + // @ts-ignore: private pendingReinvite: !!session.pendingReinvite, isConference, hasVideo, }); - // @ts-ignore + // @ts-ignore: private if (session.pendingReinvite) { return Promise.resolve(); } @@ -967,10 +944,9 @@ export default class WebRTCClient extends Emitter { delete this.heldSessions[this.getSipSessionId(session)]; session.sessionDescriptionHandlerOptionsReInvite = { - // @ts-ignore hold: false, conference: isConference, - }; + } as SessionDescriptionHandlerOptions; const options = this.getMediaConfiguration(false, isConference); if (!this._isWeb()) { @@ -991,15 +967,14 @@ export default class WebRTCClient extends Emitter { } // Returns true if a re-INVITE is required - async upgradeToVideo(session: Inviter | Invitation, constraints: Record, isConference: boolean): Promise { - // @ts-ignore - const pc = session.sessionDescriptionHandler.peerConnection; + async upgradeToVideo(session: WazoSession, constraints: Record, isConference: boolean): Promise { + const pc = (session.sessionDescriptionHandler as SessionDescriptionHandler)?.peerConnection; // Check if a video sender already exists let videoSender; if (isConference) { // We search for the last transceiver without `video-` in the mid (video- means remote transceiver) - const transceivers = pc.getTransceivers(); + const transceivers = pc?.getTransceivers() || []; const transceiverIdx = lastIndexOf(transceivers, transceiver => transceiver.sender.track === null && transceiver.mid && transceiver.mid.indexOf('video') === -1); videoSender = transceiverIdx !== -1 ? transceivers[transceiverIdx].sender : null; } else { @@ -1021,8 +996,7 @@ export default class WebRTCClient extends Emitter { // Add previous local audio track if (constraints && !constraints.audio) { - // @ts-ignore - const localVideoStream: MediaStream = session.sessionDescriptionHandler.localMediaStream; + const localVideoStream: MediaStream = (session.sessionDescriptionHandler as SessionDescriptionHandler)?.localMediaStream; const localAudioTrack = localVideoStream.getTracks().find(track => track.kind === 'audio'); if (localAudioTrack) { @@ -1040,16 +1014,15 @@ export default class WebRTCClient extends Emitter { return newStream; } - downgradeToAudio(session: Invitation | Inviter): void { + downgradeToAudio(session: WazoSession): void { // Release local video stream when downgrading to audio - // @ts-ignore - const localStream = session.sessionDescriptionHandler.localMediaStream; - // @ts-ignore - const pc = session.sessionDescriptionHandler.peerConnection; + const sessionDescriptionHandler = session.sessionDescriptionHandler as SessionDescriptionHandler; + const localStream = sessionDescriptionHandler?.localMediaStream; + const pc = sessionDescriptionHandler?.peerConnection; const videoTracks = localStream.getVideoTracks(); // Remove video senders - if (pc.getSenders) { + if (pc?.getSenders) { pc.getSenders().filter((sender: any) => sender.track && sender.track.kind === 'video').forEach((videoSender: any) => { videoSender.replaceTrack(null); }); @@ -1067,7 +1040,7 @@ export default class WebRTCClient extends Emitter { const { constraints: newConstraints, } = this.getMediaConfiguration(video, conference, constraints); - let newStream; + let newStream: MediaStream & { local?: boolean } | null = null; try { newStream = await wazoMediaStreamFactory(newConstraints); @@ -1078,7 +1051,6 @@ export default class WebRTCClient extends Emitter { return null; } - // @ts-ignore newStream.local = true; return newStream; } @@ -1087,7 +1059,7 @@ export default class WebRTCClient extends Emitter { return this.heldSessions[sessionId]; } - isCallHeld(session: Inviter | Invitation): boolean { + isCallHeld(session: WazoSession): boolean { return this.getSipSessionId(session) in this.heldSessions; } @@ -1103,7 +1075,7 @@ export default class WebRTCClient extends Emitter { return videoDirection === 'sendonly'; } - sendDTMF(session: Inviter | Invitation, tone: string): boolean { + sendDTMF(session: WazoSession, tone: string): boolean { if (!session.sessionDescriptionHandler) { return false; } @@ -1125,7 +1097,7 @@ export default class WebRTCClient extends Emitter { messager.message(); } - transfer(session: Inviter | Invitation, target: string): void { + transfer(session: WazoSession, target: string): void { this.hold(session); logger.info('Transfering a session', { id: this.getSipSessionId(session), @@ -1149,7 +1121,7 @@ export default class WebRTCClient extends Emitter { } // check https://sipjs.com/api/0.12.0/refer/referClientContext/ - atxfer(session: Inviter | Invitation): Record { + atxfer(session: WazoSession): Record { this.hold(session); logger.info('webrtc transfer started', { id: this.getSipSessionId(session), @@ -1179,7 +1151,7 @@ export default class WebRTCClient extends Emitter { } // eslint-disable-next-line @typescript-eslint/default-param-last - sendMessage(sipSession: Inviter | Invitation | null = null, body: string, contentType = 'text/plain'): void { + sendMessage(sipSession: WazoSession | null = null, body: string, contentType = 'text/plain'): void { if (!sipSession) { return; } @@ -1228,8 +1200,8 @@ export default class WebRTCClient extends Emitter { } getState(): UserAgentState { - // @ts-ignore - return this.userAgent ? states[this.userAgent.state] : UserAgentState.Stopped; + // @ts-ignore: something fishy here: `states` content doesn't match UserAgentState at all + return this.userAgent ? states[this.userAgent.state] as UserAgentState : UserAgentState.Stopped; } getContactIdentifier(): string | null { @@ -1263,15 +1235,13 @@ export default class WebRTCClient extends Emitter { // audioElement is an array of HTMLAudioElements, and HTMLAudioElement inherits the method from HTMLMediaElement // https://developer.mozilla.org/en-US/docs/Web/API/HTMLAudioElement // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/setSinkId - // @ts-ignore if (audioElement.setSinkId) { - // @ts-ignore audioElement.setSinkId(id); } }); } - async changeAudioInputDevice(id: string, session: Inviter | Invitation | null | undefined, force: boolean | null | undefined): Promise { + async changeAudioInputDevice(id: string, session: WazoSession | null | undefined, force?: boolean): Promise { const currentId = this.getAudioDeviceId(); logger.info('setting audio input device', { id, @@ -1315,9 +1285,8 @@ export default class WebRTCClient extends Emitter { } if (session && navigator.mediaDevices) { - const sdh = session.sessionDescriptionHandler; - // @ts-ignore - const pc = sdh.peerConnection; + const sdh = session.sessionDescriptionHandler as SessionDescriptionHandler; + const pc = sdh?.peerConnection; const constraints = { audio: { deviceId: { @@ -1343,7 +1312,7 @@ export default class WebRTCClient extends Emitter { return null; } - async changeVideoInputDevice(id: string, session?: Inviter | Invitation): Promise { + async changeVideoInputDevice(id: string, session?: WazoSession): Promise { this.setVideoInputDevice(id); if (session) { @@ -1371,13 +1340,12 @@ export default class WebRTCClient extends Emitter { }; } - changeSessionVideoInputDevice(id: string | null | undefined, session: Inviter | Invitation): Promise { + changeSessionVideoInputDevice(id: string | null | undefined, session: WazoSession): Promise { if (!this.sessionWantsToDoVideo(session)) { return Promise.resolve(); } - const sdh = session.sessionDescriptionHandler; - // @ts-ignore + const sdh = session.sessionDescriptionHandler as SessionDescriptionHandler; const pc = sdh.peerConnection; const sessionId = this.getSipSessionId(session); const localStream = this.getLocalStream(sessionId); @@ -1429,21 +1397,19 @@ export default class WebRTCClient extends Emitter { } getAudioDeviceId(): string | null | undefined { - // @ts-ignore - return this.audio && typeof this.audio === 'object' && 'deviceId' in this.audio ? this.audio.deviceId.exact : undefined; + return this.audio && typeof this.audio === 'object' && 'deviceId' in this.audio ? (this.audio.deviceId as any)?.exact : undefined; } getVideoDeviceId(): string | null { - // @ts-ignore - return this.video && typeof this.video === 'object' && 'deviceId' in this.video ? this.video.deviceId.exact : undefined; + return this.video && typeof this.video === 'object' && 'deviceId' in this.video ? (this.video.deviceId as any)?.exact : undefined; } - reinvite(sipSession: Inviter | Invitation, newConstraints: Record | null | undefined = null, conference = false, audioOnly = false, iceRestart = false): Promise { + reinvite(sipSession: WazoSession, newConstraints: Record | null | undefined = null, conference = false, audioOnly = false, iceRestart = false): Promise { if (!sipSession) { return Promise.resolve(); } - // @ts-ignore + // @ts-ignore: private if (sipSession.pendingReinvite) { return Promise.resolve(); } @@ -1471,10 +1437,9 @@ export default class WebRTCClient extends Emitter { } sipSession.sessionDescriptionHandlerOptionsReInvite = { ...sipSession.sessionDescriptionHandlerOptionsReInvite, - // @ts-ignore conference, audioOnly, - }; + } as SessionDescriptionHandlerOptions; const { constraints, } = this.getMediaConfiguration(shouldDoVideo, conference, newConstraints); @@ -1483,12 +1448,12 @@ export default class WebRTCClient extends Emitter { onAccept: (response: IncomingResponse) => { // Update the SDP body to be able to call sessionWantsToDoVideo correctly in `_setup[Local|Remote]Media`. // Can't set directly sipSession.body because it's a getter. - // @ts-ignore + // @ts-ignore: private if (sipSession instanceof Inviter && sipSession.outgoingRequestMessage.body) { - // @ts-ignore + // @ts-ignore: private sipSession.outgoingRequestMessage.body.body = response.message.body; } else if (sipSession instanceof Invitation) { - // @ts-ignore + // @ts-ignore: private sipSession.incomingInviteRequest.message.body = response.message.body; } @@ -1503,7 +1468,6 @@ export default class WebRTCClient extends Emitter { this.mute(sipSession); } - // @ts-ignore this._onAccepted(sipSession, response.session, false, false); if (shouldDoScreenSharing) { @@ -1519,13 +1483,12 @@ export default class WebRTCClient extends Emitter { }, sessionDescriptionHandlerOptions: { constraints, - // @ts-ignore conference, audioOnly, offerOptions: { iceRestart, }, - }, + } as SessionDescriptionHandlerOptions, }); } @@ -1544,15 +1507,13 @@ export default class WebRTCClient extends Emitter { return null; } - // @ts-ignore - return sipSession.sessionDescriptionHandler ? sipSession.sessionDescriptionHandler.peerConnection : null; + return sipSession.sessionDescriptionHandler ? (sipSession.sessionDescriptionHandler as SessionDescriptionHandler).peerConnection : null; } // Local streams getLocalStream(sessionId: string): MediaStream | null { const sipSession = this.sipSessions[sessionId]; - // @ts-ignore - return sipSession?.sessionDescriptionHandler?.localMediaStream || null; + return (sipSession?.sessionDescriptionHandler as SessionDescriptionHandler)?.localMediaStream || null; } getLocalTracks(sessionId: string): MediaStreamTrack[] { @@ -1581,8 +1542,7 @@ export default class WebRTCClient extends Emitter { // Remote streams getRemoteStream(sessionId: string): MediaStream | null { const sipSession = this.sipSessions[sessionId]; - // @ts-ignore - return sipSession?.sessionDescriptionHandler?.remoteMediaStream || null; + return (sipSession?.sessionDescriptionHandler as SessionDescriptionHandler)?.remoteMediaStream || null; } getRemoteTracks(sessionId: string): MediaStreamTrack[] { @@ -1614,7 +1574,7 @@ export default class WebRTCClient extends Emitter { // Useful in a react-native environment when remoteMediaStream is not updated getRemoteVideoStreamFromPc(sessionId: string): MediaStream | null | undefined { - const pc = this.getPeerConnection(sessionId); + const pc = this.getPeerConnection(sessionId) as PeerConnection; if (!pc) { return null; @@ -1631,21 +1591,20 @@ export default class WebRTCClient extends Emitter { return this.hasALocalVideoTrack(sessionId) || this.hasARemoteVideoTrack(sessionId); } - getSipSessionId(sipSession: Inviter | Invitation | null | undefined): string { + getSipSessionId(sipSession: WazoSession | null | undefined): string { if (!sipSession) { return ''; } - // @ts-ignore - if (sipSession.message?.callId) { - // @ts-ignore - return sipSession.message.callId.substring(0, SIP_ID_LENGTH); + const message = sipSession.message as Partial<{ callId?: string }>; + if (message?.callId) { + return message.callId.substring(0, SIP_ID_LENGTH); } // For Inviter - // @ts-ignore + // @ts-ignore: private if (sipSession instanceof Inviter && sipSession.outgoingRequestMessage) { - // @ts-ignore + // @ts-ignore: private return sipSession.outgoingRequestMessage.callId.substring(0, SIP_ID_LENGTH); } @@ -1658,7 +1617,7 @@ export default class WebRTCClient extends Emitter { } // eslint-disable-next-line no-unused-vars - sessionWantsToDoVideo(session: Inviter | Invitation): boolean { + sessionWantsToDoVideo(session: WazoSession): boolean { if (!session) { return false; } @@ -1701,7 +1660,7 @@ export default class WebRTCClient extends Emitter { this.heartbeatCb = cb; } - onCallEnded(session: Inviter | Invitation): void { + onCallEnded(session: WazoSession): void { this._cleanupMedia(session); delete this.sipSessions[this.getSipSessionId(session)]; @@ -1718,18 +1677,18 @@ export default class WebRTCClient extends Emitter { return; } - // @ts-ignore + // @ts-ignore: private this.userAgent.attemptReconnection(); } - storeSipSession(session: Invitation | Inviter): void { + storeSipSession(session: WazoSession): void { const id = this.getSipSessionId(session); logger.info('storing sip session', { id, clientId: this.clientId }); this.sipSessions[id] = session; } - getSipSession(id: string): Invitation | Inviter | null | undefined { + getSipSession(id: string): WazoSession | null | undefined { return id in this.sipSessions ? this.sipSessions[id.substring(0, SIP_ID_LENGTH)] : null; } @@ -1750,7 +1709,7 @@ export default class WebRTCClient extends Emitter { }); if (sipSession.sessionDescriptionHandler) { // eslint-disable-next-line no-underscore-dangle - // @ts-ignore + // @ts-ignore: protected sipSession.sessionDescriptionHandler._localMediaStream = newStream; } } @@ -1884,15 +1843,14 @@ export default class WebRTCClient extends Emitter { } if (this.userAgent?.transport) { - // @ts-ignore - this.userAgent.transport.configuration.traceSip = true; + (this.userAgent.transport as WazoTransport).configuration.traceSip = true; } - // @ts-ignore + // @ts-ignore: private this.userAgent.loggerFactory.builtinEnabled = true; - // @ts-ignore + // @ts-ignore: private this.userAgent.loggerFactory.level = 3; // debug - // @ts-ignore + // @ts-ignore: private this.userAgent.loggerFactory.connector = logConnector; } @@ -1925,8 +1883,9 @@ export default class WebRTCClient extends Emitter { if (this.userAgent && this.userAgent.transport) { // Disconnect from WS and triggers events, but do not trigger disconnect if already disconnecting... - // @ts-ignore - if (!this.userAgent.transport.transitioningState && !this.userAgent.transport.disconnectPromise) { + const transport = this.userAgent.transport as WazoTransport; + // @HEADS UP + if (!transport.transitioningState && !transport.disconnectPromise) { try { await this.userAgent.transport.disconnect(); } catch (e: any) { @@ -1952,7 +1911,7 @@ export default class WebRTCClient extends Emitter { } _getAudioConstraints(): MediaTrackConstraints | boolean { - // @ts-ignore + // @ts-ignore: media constraints return this.audio?.deviceId?.exact ? this.audio : true; } @@ -1961,7 +1920,7 @@ export default class WebRTCClient extends Emitter { return false; } - // @ts-ignore + // @ts-ignore: media constraints return this.video?.deviceId?.exact ? this.video : true; } @@ -1980,8 +1939,7 @@ export default class WebRTCClient extends Emitter { if (this.isConnecting()) { logger.info('webrtc sdk, already connecting...'); - // @ts-ignore - this.connectionPromise = this.userAgent.transport.connectPromise; + this.connectionPromise = (this.userAgent.transport as WazoTransport).connectPromise; return Promise.resolve(this.connectionPromise); } @@ -1994,20 +1952,19 @@ export default class WebRTCClient extends Emitter { // Force UA to reconnect if (this.userAgent && this.userAgent.state !== UserAgentState.Stopped) { - // @ts-ignore + // @ts-ignore: private this.userAgent.transitionState(UserAgentState.Stopped); } if (this.userAgent.transport && this.userAgent.transport.state !== TransportState.Disconnected) { - // @ts-ignore - this.userAgent.transport.transitionState(TransportState.Disconnected); + (this.userAgent.transport as WazoTransport).transitionState(TransportState.Disconnected); } this.connectionPromise = this.userAgent.start().catch(console.error); return this.connectionPromise; } - _buildConfig(config: WebRtcConfig, session: Invitation | Inviter | null | undefined): Promise { + _buildConfig(config: WebRtcConfig, session: WazoSession | null | undefined): Promise { // If no session provided, return the configuration directly if (!session) { return new Promise(resolve => resolve(config)); @@ -2016,13 +1973,10 @@ export default class WebRTCClient extends Emitter { const client = new ApiClient({ server: `${config.host}:${String(config.port || 443)}`, }); - // @ts-ignore - client.setToken(session.token); - // @ts-ignore - client.setRefreshToken(session.refreshToken); + client.setToken((session as any).token); + client.setRefreshToken((session as any).refreshToken); - // @ts-ignore - return client.confd.getUserLineSipFromToken(session.uuid).then(sipLine => ({ + return client.confd.getUserLineSipFromToken((session as any).uuid).then(sipLine => ({ authorizationUser: sipLine.username, password: sipLine.secret, uri: `${sipLine.username}@${config.host}`, @@ -2065,15 +2019,14 @@ export default class WebRTCClient extends Emitter { const isWeb = this._isWeb(); - // @ts-ignore - const iceGatheringTimeout = 'peerConnectionOptions' in options ? options.peerConnectionOptions.iceGatheringTimeout || DEFAULT_ICE_TIMEOUT : DEFAULT_ICE_TIMEOUT; + const iceGatheringTimeout = 'peerConnectionOptions' in options ? (options.peerConnectionOptions as any).iceGatheringTimeout || DEFAULT_ICE_TIMEOUT : DEFAULT_ICE_TIMEOUT; const sdhOptions: SessionDescriptionHandlerConfiguration = { ...options, iceGatheringTimeout, peerConnectionConfiguration: { ...defaultPeerConnectionConfiguration(), ...(options.peerConnectionConfiguration || {}), }, }; - return new WazoSessionDescriptionHandler(uaLogger, wazoMediaStreamFactory, sdhOptions, isWeb, session as Inviter | Invitation); + return new WazoSessionDescriptionHandler(uaLogger, wazoMediaStreamFactory, sdhOptions, isWeb, session as WazoSession); }, transportOptions: { traceSip: uaOptionsOverrides?.traceSip || false, @@ -2124,7 +2077,7 @@ export default class WebRTCClient extends Emitter { } // Invitation and Inviter extends Session - _setupSession(session: Inviter | Invitation): void { + _setupSession(session: WazoSession): void { const sipSessionId = this.getSipSessionId(session); // When receiving an Invitation, the delegate is not defined. @@ -2132,9 +2085,8 @@ export default class WebRTCClient extends Emitter { session.delegate = {}; } - session.delegate.onSessionDescriptionHandler = (sdh: SessionDescriptionHandler) => { - // @ts-ignore - sdh.on('error', e => { + session.delegate.onSessionDescriptionHandler = (sdh: SessionDescriptionHandler & { on: (type: string, e: any) => void }) => { + sdh.on('error', (e: any) => { this.eventEmitter.emit(ON_ERROR, e); }); sdh.peerConnectionDelegate = { @@ -2150,13 +2102,13 @@ export default class WebRTCClient extends Emitter { }; }; - // @ts-ignore + // @ts-ignore: protected const oldInviteRequest = session.onInviteRequest.bind(session); let hadRemoteVideo = false; // Monkey patch `onInviteRequest` to be able to know if there was a remote video stream before `onInvite` is called // Because when `onInvite` is called we already got the video track - // @ts-ignore + // @ts-ignore: protected session.onInviteRequest = request => { hadRemoteVideo = this.hasARemoteVideoTrack(sipSessionId); oldInviteRequest(request); @@ -2167,7 +2119,7 @@ export default class WebRTCClient extends Emitter { let updatedNumber = null; if (session.assertedIdentity) { - // @ts-ignore + // @ts-ignore: private updatedNumber = session.assertedIdentity.uri.normal.user; updatedCalleeName = session.assertedIdentity.displayName || updatedNumber; } @@ -2182,11 +2134,11 @@ export default class WebRTCClient extends Emitter { // Update SDP // Remote video is handled by the `track` event. Here we're dealing with video stream removal. if (session instanceof Invitation) { - // @ts-ignore + // @ts-ignore: private session.incomingInviteRequest.message.body = request.body; - // @ts-ignore + // @ts-ignore: private } else if (session instanceof Inviter && session.outgoingInviteRequest.message.body) { - // @ts-ignore + // @ts-ignore: private session.outgoingInviteRequest.message.body.body = request.body; } @@ -2209,11 +2161,10 @@ export default class WebRTCClient extends Emitter { this.eventEmitter.emit(ON_EARLY_MEDIA, session); } - _onAccepted(session: Inviter | Invitation, sessionDialog?: SessionDialog, withEvent = true, initAllTracks = true): void { + _onAccepted(session: WazoSession, sessionDialog?: SessionDialog, withEvent = true, initAllTracks = true): void { logger.info('on call accepted', { id: session.id, clientId: this.clientId, - // @ts-ignore remoteTag: session.remoteTag, }); this.storeSipSession(session); @@ -2221,8 +2172,7 @@ export default class WebRTCClient extends Emitter { this._setupMedias(session); this.updateRemoteStream(this.getSipSessionId(session), initAllTracks); - // @ts-ignore - const pc = session.sessionDescriptionHandler?.peerConnection; + const pc = (session.sessionDescriptionHandler as SessionDescriptionHandler)?.peerConnection; const onTrack = (event: any) => { const isAudioOnly = this._isAudioOnly(session); @@ -2251,14 +2201,12 @@ export default class WebRTCClient extends Emitter { this.eventEmitter.emit(ON_TRACK, session, event); }; - // @ts-ignore - if (session.sessionDescriptionHandler.peerConnection) { - // @ts-ignore - session.sessionDescriptionHandler.peerConnection.addEventListener('track', onTrack); + const sessionDescriptionHandler = session.sessionDescriptionHandler as SessionDescriptionHandler; + if (sessionDescriptionHandler?.peerConnection) { + sessionDescriptionHandler.peerConnection.addEventListener('track', onTrack); } - // @ts-ignore - session.sessionDescriptionHandler.remoteMediaStream.onaddtrack = onTrack; + sessionDescriptionHandler.remoteMediaStream.onaddtrack = onTrack; if (pc) { pc.oniceconnectionstatechange = () => { @@ -2279,11 +2227,11 @@ export default class WebRTCClient extends Emitter { this._startSendingStats(session); } - _isAudioOnly(session: Inviter | Invitation): boolean { + _isAudioOnly(session: WazoSession): boolean { return Boolean(session.sessionDescriptionHandlerModifiersReInvite.find(modifier => modifier === stripVideo)); } - _setupMedias(session: Inviter | Invitation, newStream: MediaStream | null | undefined = null): void { + _setupMedias(session: WazoSession, newStream: MediaStream | null | undefined = null): void { if (!this._isWeb()) { logger.info('Setup media on mobile, no need to setup html element, bailing'); return; @@ -2305,11 +2253,10 @@ export default class WebRTCClient extends Emitter { } const audioElement = this.audioElements[sessionId]; - // @ts-ignore - const sipSession = this.sipSessions[session.callId]; + const sipSession = this.sipSessions[session.callId as string]; const removeStream = this.getRemoteStream(sessionId); - // @ts-ignore - const earlyStream = sipSession && sipSession.sessionDescriptionHandler ? sipSession.sessionDescriptionHandler.remoteMediaStream : null; + const sdh = sipSession.sessionDescriptionHandler as SessionDescriptionHandler; + const earlyStream = sdh ? sdh.remoteMediaStream : null; const stream = newStream || removeStream || earlyStream; if (!stream) { @@ -2345,7 +2292,7 @@ export default class WebRTCClient extends Emitter { audioElement.play().catch(() => {}); } - _cleanupMedia(session?: Inviter | Invitation): void { + _cleanupMedia(session?: WazoSession): void { const sessionId = this.getSipSessionId(session); const localStream = this.getLocalStream(sessionId); @@ -2383,9 +2330,9 @@ export default class WebRTCClient extends Emitter { stream.getTracks().filter(track => track.enabled).forEach(track => track.stop()); } - _toggleAudio(session: Inviter | Invitation, muteAudio: boolean): void { - // @ts-ignore - const pc = session.sessionDescriptionHandler ? session.sessionDescriptionHandler.peerConnection : null; + _toggleAudio(session: WazoSession, muteAudio: boolean): void { + const sdh = session.sessionDescriptionHandler as SessionDescriptionHandler; + const pc = (sdh?.peerConnection || null) as PeerConnection; if (!pc) { return; @@ -2408,11 +2355,11 @@ export default class WebRTCClient extends Emitter { } } - _toggleVideo(session: Inviter | Invitation, muteCamera: boolean): void { - // @ts-ignore - const pc = session.sessionDescriptionHandler?.peerConnection; + _toggleVideo(session: WazoSession, muteCamera: boolean): void { + const sdh = session.sessionDescriptionHandler as SessionDescriptionHandler; + const pc = sdh?.peerConnection as PeerConnection; - if (pc.getSenders) { + if (pc?.getSenders) { pc.getSenders().forEach((sender: any) => { if (sender && sender.track && sender.track.kind === 'video') { // eslint-disable-next-line @@ -2456,15 +2403,15 @@ export default class WebRTCClient extends Emitter { _cleanupRegister(): void { if (this.registerer) { - // @ts-ignore + // @ts-ignore: removeAllListeners does not exist this.registerer.stateChange.removeAllListeners(); this.registerer = null; } } - _startSendingStats(session: Inviter | Invitation): void { - // @ts-ignore - const pc = session.sessionDescriptionHandler.peerConnection; + _startSendingStats(session: WazoSession): void { + const sdh = session.sessionDescriptionHandler as SessionDescriptionHandler; + const pc = sdh.peerConnection; if (!pc) { return; @@ -2486,7 +2433,7 @@ export default class WebRTCClient extends Emitter { }, SEND_STATS_DELAY); } - _stopSendingStats(session: Invitation | Inviter): void { + _stopSendingStats(session: WazoSession): void { const sessionId = this.getSipSessionId(session); logger.trace('Check for stopping stats', { sessionId, @@ -2507,25 +2454,23 @@ export default class WebRTCClient extends Emitter { } async _disconnectTransport(force = false) { - if (force && this.userAgent && this.userAgent.transport) { + const transport = this.userAgent?.transport as WazoTransport; + + if (force && transport) { // Bypass sip.js state machine that prevent to close WS with the state `Connecting` - // @ts-ignore - this.userAgent.transport.disconnectResolve = () => {}; + transport.disconnectResolve = () => {}; - // @ts-ignore - if (this.userAgent.transport._ws) { - // @ts-ignore - this.userAgent.transport._ws.close(1000); + if (transport._ws) { + transport._ws.close(1000); } return; } // Check if `disconnectPromise` is not present to avoid `Disconnect promise must not be defined` errors. - // @ts-ignore - if (this.userAgent && this.userAgent.transport && !this.userAgent.transport.disconnectPromise) { + if (transport && !transport.disconnectPromise) { try { - await this.userAgent.transport.disconnect(); + await transport.disconnect(); } catch (e: any) { logger.error('WebRTC transport disconnect, error', e); } @@ -2657,7 +2602,7 @@ export default class WebRTCClient extends Emitter { throw new Error(error); } - // @ts-ignore + // @ts-ignore: private if (!session.incomingInviteRequest.acceptable) { const error = 'Trying to reject a non `acceptable` session'; logger.warn(error, { state: session.state, sessionId: session.id }); @@ -2677,7 +2622,7 @@ export default class WebRTCClient extends Emitter { return; } - // @ts-ignore + // @ts-ignore: private if (!session.incomingInviteRequest.rejectable) { logger.warn('Trying to reject a non `rejectable` session', { state: session.state, sessionId: session.id }); return; @@ -2703,7 +2648,7 @@ export default class WebRTCClient extends Emitter { } } - async _bye(session: Invitation | Inviter, options: SessionByeOptions = {}) { + async _bye(session: WazoSession, options: SessionByeOptions = {}) { if (session.state !== SessionState.Established) { logger.warn('Trying to end a session in a wrong state', { state: session.state, sessionId: session.id }); return null; diff --git a/src/websocket-client.ts b/src/websocket-client.ts index 12d4698c..7044a73e 100644 --- a/src/websocket-client.ts +++ b/src/websocket-client.ts @@ -1,5 +1,5 @@ /* eslint-disable no-underscore-dangle */ -import ReconnectingWebSocket from 'reconnecting-websocket'; +import ReconnectingWebSocket, { ErrorEvent } from 'reconnecting-websocket'; import Session from './domain/Session'; import Emitter from './utils/Emitter'; @@ -252,10 +252,9 @@ class WebSocketClient extends Emitter { } }; - this.socket.onerror = event => { + this.socket.onerror = (event: ErrorEvent & { code?: any }) => { logger.info('Wazo WS error', { message: event.message, - // @ts-ignore code: event.code, readyState: event.target.readyState, });