{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import InboundCallSession from \"./call-session/inbound.js\";\nimport type CallSession from \"./call-session/index.js\";\nimport OutboundCallSession from \"./call-session/outbound.js\";\nimport { DefaultDeviceManager } from \"./device-manager.js\";\nimport EventEmitter from \"./event-emitter.js\";\nimport { DefaultSipClient } from \"./sip-client.js\";\nimport type InboundMessage from \"./sip-message/inbound.js\";\nimport ResponseMessage from \"./sip-message/outbound/response.js\";\nimport type {\n  DeviceManager,\n  SipClient,\n  SipInfo,\n  WebPhoneOptions,\n} from \"./types.js\";\n\nclass WebPhone extends EventEmitter {\n  public sipInfo: SipInfo;\n  public sipClient: SipClient;\n  public deviceManager: DeviceManager;\n  public callSessions: CallSession[] = [];\n  public autoAnswer = true;\n  public options: WebPhoneOptions;\n\n  public disposed = false;\n\n  public constructor(options: WebPhoneOptions) {\n    super();\n    this.options = options;\n    this.sipInfo = options.sipInfo;\n    this.sipClient = options.sipClient ?? new DefaultSipClient(options);\n    this.deviceManager = options.deviceManager ?? new DefaultDeviceManager();\n    this.autoAnswer = options.autoAnswer ?? true;\n\n    this.sipClient.on(\n      \"inboundMessage\",\n      async (inboundMessage: InboundMessage) => {\n        // either inbound BYE/CANCEL or server reply to outbound BYE/CANCEL\n        if (\n          inboundMessage.headers.CSeq.endsWith(\" BYE\") ||\n          inboundMessage.headers.CSeq.endsWith(\" CANCEL\")\n        ) {\n          const index = this.callSessions.findIndex(\n            (callSession) =>\n              callSession.callId === inboundMessage.headers[\"Call-Id\"],\n          );\n          if (index !== -1) {\n            const callSession = this.callSessions[index];\n            this.callSessions.splice(index, 1);\n            callSession.dispose();\n          }\n        }\n\n        // listen for incoming calls\n        if (!inboundMessage.subject.startsWith(\"INVITE sip:\")) {\n          return;\n        }\n\n        // re-INVITE\n        const callSession = this.callSessions.find((callSession) => {\n          return (\n            callSession.callId === inboundMessage.headers[\"Call-Id\"] &&\n            callSession.localPeer === inboundMessage.headers.To &&\n            callSession.remotePeer === inboundMessage.headers.From\n          );\n        });\n        if (callSession) {\n          callSession.handleReInvite(inboundMessage);\n          return;\n        }\n\n        this.callSessions.push(new InboundCallSession(this, inboundMessage));\n        // write it this way so that it will be compatible with manate, inboundCallSession will be managed\n        const inboundCallSession = this.callSessions[\n          this.callSessions.length - 1\n        ] as InboundCallSession;\n        this.emit(\"inboundCall\", inboundCallSession);\n\n        // tell SIP server that we are ringing\n        let tempMesage = new ResponseMessage(inboundMessage, {\n          responseCode: 100,\n        });\n        await this.sipClient.reply(tempMesage);\n        tempMesage = new ResponseMessage(inboundMessage, { responseCode: 180 });\n        await this.sipClient.reply(tempMesage);\n\n        // if we don't send this, toVoicemail() will not work\n        await inboundCallSession.confirmReceive();\n\n        // auto answer\n        if (!this.autoAnswer) {\n          return;\n        }\n        if (\n          inboundCallSession.sipMessage.headers[\"Alert-Info\"] !== \"Auto Answer\"\n        ) {\n          return;\n        }\n        let delay = 0;\n        const callInfoHeader =\n          inboundCallSession.sipMessage.headers[\"Call-Info\"];\n        if (callInfoHeader) {\n          const match = callInfoHeader.match(/Answer-After=(\\d+)/);\n          if (match) {\n            delay = parseInt(match[1], 10); // Convert the captured value to an integer\n          }\n        }\n        setTimeout(() => {\n          inboundCallSession.answer();\n        }, delay);\n      },\n    );\n  }\n\n  public async start() {\n    await this.sipClient.start();\n  }\n\n  public async dispose() {\n    this.disposed = true;\n    // properly dispose all call sessions\n    for (const callSession of this.callSessions) {\n      if (callSession.state === \"answered\") {\n        await callSession.hangup();\n      } else if (callSession.direction === \"inbound\") {\n        await (callSession as InboundCallSession).decline();\n      } else {\n        await (callSession as OutboundCallSession).cancel();\n      }\n      // callSession.dispose() will be auto triggered by the above methods\n    }\n    this.removeAllListeners();\n    await this.sipClient.dispose();\n  }\n\n  // make an outbound call\n  public async call(\n    callee: string,\n    callerId?: string,\n    options?: { headers?: Record<string, string> },\n  ) {\n    this.callSessions.push(new OutboundCallSession(this, callee));\n    // write it this way so that it will be compatible with manate, outboundCallSession will be managed\n    const outboundCallSession = this.callSessions[\n      this.callSessions.length - 1\n    ] as OutboundCallSession;\n    this.emit(\"outboundCall\", outboundCallSession);\n    await outboundCallSession.init();\n    await outboundCallSession.call(callerId, options);\n    return outboundCallSession;\n  }\n}\n\nexport default WebPhone;\n"],"mappings":";;;;;;;AAeA,IAAM,WAAN,cAAuB,aAAa;CAClC;CACA;CACA;CACA,eAAqC,EAAE;CACvC,aAAoB;CACpB;CAEA,WAAkB;CAElB,YAAmB,SAA0B;AAC3C,SAAO;AACP,OAAK,UAAU;AACf,OAAK,UAAU,QAAQ;AACvB,OAAK,YAAY,QAAQ,aAAa,IAAI,iBAAiB,QAAQ;AACnE,OAAK,gBAAgB,QAAQ,iBAAiB,IAAI,sBAAsB;AACxE,OAAK,aAAa,QAAQ,cAAc;AAExC,OAAK,UAAU,GACb,kBACA,OAAO,mBAAmC;AAExC,OACE,eAAe,QAAQ,KAAK,SAAS,OAAO,IAC5C,eAAe,QAAQ,KAAK,SAAS,UAAU,EAC/C;IACA,MAAM,QAAQ,KAAK,aAAa,WAC7B,gBACC,YAAY,WAAW,eAAe,QAAQ,WACjD;AACD,QAAI,UAAU,IAAI;KAChB,MAAM,cAAc,KAAK,aAAa;AACtC,UAAK,aAAa,OAAO,OAAO,EAAE;AAClC,iBAAY,SAAS;;;AAKzB,OAAI,CAAC,eAAe,QAAQ,WAAW,cAAc,CACnD;GAIF,MAAM,cAAc,KAAK,aAAa,MAAM,gBAAgB;AAC1D,WACE,YAAY,WAAW,eAAe,QAAQ,cAC9C,YAAY,cAAc,eAAe,QAAQ,MACjD,YAAY,eAAe,eAAe,QAAQ;KAEpD;AACF,OAAI,aAAa;AACf,gBAAY,eAAe,eAAe;AAC1C;;AAGF,QAAK,aAAa,KAAK,IAAI,mBAAmB,MAAM,eAAe,CAAC;GAEpE,MAAM,qBAAqB,KAAK,aAC9B,KAAK,aAAa,SAAS;AAE7B,QAAK,KAAK,eAAe,mBAAmB;GAG5C,IAAI,aAAa,IAAI,gBAAgB,gBAAgB,EACnD,cAAc,KACf,CAAC;AACF,SAAM,KAAK,UAAU,MAAM,WAAW;AACtC,gBAAa,IAAI,gBAAgB,gBAAgB,EAAE,cAAc,KAAK,CAAC;AACvE,SAAM,KAAK,UAAU,MAAM,WAAW;AAGtC,SAAM,mBAAmB,gBAAgB;AAGzC,OAAI,CAAC,KAAK,WACR;AAEF,OACE,mBAAmB,WAAW,QAAQ,kBAAkB,cAExD;GAEF,IAAI,QAAQ;GACZ,MAAM,iBACJ,mBAAmB,WAAW,QAAQ;AACxC,OAAI,gBAAgB;IAClB,MAAM,QAAQ,eAAe,MAAM,qBAAqB;AACxD,QAAI,MACF,SAAQ,SAAS,MAAM,IAAI,GAAG;;AAGlC,oBAAiB;AACf,uBAAmB,QAAQ;MAC1B,MAAM;IAEZ;;CAGH,MAAa,QAAQ;AACnB,QAAM,KAAK,UAAU,OAAO;;CAG9B,MAAa,UAAU;AACrB,OAAK,WAAW;AAEhB,OAAK,MAAM,eAAe,KAAK,aAC7B,KAAI,YAAY,UAAU,WACxB,OAAM,YAAY,QAAQ;WACjB,YAAY,cAAc,UACnC,OAAO,YAAmC,SAAS;MAEnD,OAAO,YAAoC,QAAQ;AAIvD,OAAK,oBAAoB;AACzB,QAAM,KAAK,UAAU,SAAS;;CAIhC,MAAa,KACX,QACA,UACA,SACA;AACA,OAAK,aAAa,KAAK,IAAI,oBAAoB,MAAM,OAAO,CAAC;EAE7D,MAAM,sBAAsB,KAAK,aAC/B,KAAK,aAAa,SAAS;AAE7B,OAAK,KAAK,gBAAgB,oBAAoB;AAC9C,QAAM,oBAAoB,MAAM;AAChC,QAAM,oBAAoB,KAAK,UAAU,QAAQ;AACjD,SAAO"}