diff --git a/demos/demo-minimal-js/index.js b/demos/demo-minimal-js/index.js index 63255a65..b499595e 100644 --- a/demos/demo-minimal-js/index.js +++ b/demos/demo-minimal-js/index.js @@ -1,10 +1,9 @@ -/* eslint-disable import/no-relative-packages */ -import CallingExtensions from "../../src/CallingExtensions"; -import { messageType, callEndStatus } from "../../src/Constants"; -// import CallingExtensions, { Constants } from "@hubspot/calling-extensions-sdk"; -// const { messageType, callEndStatus } = Constants; +import CallingExtensions, { Constants } from "@hubspot/calling-extensions-sdk"; +import { v4 as uuidv4 } from "uuid"; +const { messageType, callEndStatus } = Constants; export const state = { + externalCallId: "", engagementId: 0, fromNumber: "+123456", incomingContactName: "", @@ -55,6 +54,7 @@ function enableButtons(ids) { const cti = new CallingExtensions({ debugMode: true, eventHandlers: { + // eslint-disable-next-line object-curly-newline onReady: ({ engagementId, portalId, userId, ownerId } = {}) => { cti.initialized({ engagementId, @@ -220,11 +220,13 @@ export function userUnavailable() { } export function incomingCall() { + state.externalCallId = uuidv4(); window.setTimeout(() => { cti.incomingCall({ createEngagement: true, fromNumber: state.fromNumber, toNumber: state.toNumber, + externalCallId: state.externalCallId, }); }, 500); disableButtons([OUTGOING_CALL, INCOMING_CALL, USER_UNAVAILABLE]); @@ -232,11 +234,13 @@ export function incomingCall() { } export function outgoingCall() { + state.externalCallId = uuidv4(); window.setTimeout(() => { cti.outgoingCall({ createEngagement: true, toNumber: state.toNumber, fromNumber: state.fromNumber, + externalCallId: state.externalCallId, }); }, 500); disableButtons([OUTGOING_CALL, INCOMING_CALL, USER_UNAVAILABLE]); @@ -244,13 +248,14 @@ export function outgoingCall() { } export function answerCall() { - cti.callAnswered(); + cti.callAnswered({ externalCallId: state.externalCallId }); disableButtons([ANSWER_CALL]); } export function endCall() { cti.callEnded({ callEndStatus: callEndStatus.INTERNAL_COMPLETED, + externalCallId: state.externalCallId, }); disableButtons([ANSWER_CALL, END_CALL]); enableButtons([COMPLETE_CALL]); @@ -259,12 +264,14 @@ export function endCall() { export function completeCall() { cti.callCompleted({ engagementId: state.engagementId, + externalCallId: state.externalCallId, hideWidget: false, engagementProperties: { hs_call_title: "Demo call", hs_call_body: "Resolved issue", }, }); + state.externalCallId = ""; disableButtons([COMPLETE_CALL]); enableButtons([OUTGOING_CALL, INCOMING_CALL, USER_UNAVAILABLE]); } diff --git a/demos/demo-minimal-js/package-lock.json b/demos/demo-minimal-js/package-lock.json index 5f891afa..b00eb7c3 100644 --- a/demos/demo-minimal-js/package-lock.json +++ b/demos/demo-minimal-js/package-lock.json @@ -9,7 +9,8 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@hubspot/calling-extensions-sdk": "^0.6.3" + "@hubspot/calling-extensions-sdk": "^0.7.0-beta.0", + "uuid": "^10.0.0" }, "devDependencies": { "cross-env": "^7.0.3", @@ -32,9 +33,9 @@ } }, "node_modules/@hubspot/calling-extensions-sdk": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@hubspot/calling-extensions-sdk/-/calling-extensions-sdk-0.6.3.tgz", - "integrity": "sha512-3HaIVj/ATJ8RjN5H+VfY/DU7kNm0DCaNU80AEGcdn3/hseIucH5fOlU42EHy/TaW2rFZJHr/OUiBNMf4E6AbwA==", + "version": "0.7.0-beta.0", + "resolved": "https://registry.npmjs.org/@hubspot/calling-extensions-sdk/-/calling-extensions-sdk-0.7.0-beta.0.tgz", + "integrity": "sha512-afw8c/bnquyIy1OfdKMLh5Ic/fYX2uMIz2tsunCuSFRUVRpStYkAdIXjWi9vblMmcz/+7k50yeNNl8QclRxQXw==", "engines": { "node": ">=14" } @@ -3191,6 +3192,15 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3541,10 +3551,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } diff --git a/demos/demo-minimal-js/package.json b/demos/demo-minimal-js/package.json index 1fbf8332..d6fde470 100644 --- a/demos/demo-minimal-js/package.json +++ b/demos/demo-minimal-js/package.json @@ -21,6 +21,7 @@ "webpack-dev-server": "^4.11.1" }, "dependencies": { - "@hubspot/calling-extensions-sdk": "^0.6.3" + "@hubspot/calling-extensions-sdk": "^0.7.0-beta.0", + "uuid": "^10.0.0" } } diff --git a/demos/demo-react-ts/package-lock.json b/demos/demo-react-ts/package-lock.json index ba2f015d..909e96e6 100644 --- a/demos/demo-react-ts/package-lock.json +++ b/demos/demo-react-ts/package-lock.json @@ -9,11 +9,12 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@hubspot/calling-extensions-sdk": "^0.6.3", + "@hubspot/calling-extensions-sdk": "^0.7.0-beta.0", "react": "^18.2.0", "react-aria": "^3.22.0", "react-dom": "^18.2.0", - "styled-components": "^5.3.6" + "styled-components": "^5.3.6", + "uuid": "^10.0.0" }, "devDependencies": { "@babel/core": "^7.20.12", @@ -1859,9 +1860,9 @@ } }, "node_modules/@hubspot/calling-extensions-sdk": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@hubspot/calling-extensions-sdk/-/calling-extensions-sdk-0.6.3.tgz", - "integrity": "sha512-3HaIVj/ATJ8RjN5H+VfY/DU7kNm0DCaNU80AEGcdn3/hseIucH5fOlU42EHy/TaW2rFZJHr/OUiBNMf4E6AbwA==", + "version": "0.7.0-beta.0", + "resolved": "https://registry.npmjs.org/@hubspot/calling-extensions-sdk/-/calling-extensions-sdk-0.7.0-beta.0.tgz", + "integrity": "sha512-afw8c/bnquyIy1OfdKMLh5Ic/fYX2uMIz2tsunCuSFRUVRpStYkAdIXjWi9vblMmcz/+7k50yeNNl8QclRxQXw==", "engines": { "node": ">=14" } @@ -3838,9 +3839,10 @@ } }, "node_modules/@types/ws": { - "version": "8.5.4", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, - "license": "MIT", "dependencies": { "@types/node": "*" } @@ -6414,9 +6416,10 @@ } }, "node_modules/jasmine-browser-runner": { - "version": "1.3.0", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jasmine-browser-runner/-/jasmine-browser-runner-1.4.0.tgz", + "integrity": "sha512-UOwxL7/tL5K3Ww9LUw4EFWyZCQyTflzTgRFr80Bm/22EEUqlOiDhdqhYblu3LEZ+ZZ8qfwrlBShtcc+MAwNetg==", "dev": true, - "license": "MIT", "dependencies": { "ejs": "^3.1.6", "express": "^4.16.4", @@ -6527,6 +6530,16 @@ "node": ">=0.10.0" } }, + "node_modules/launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "dev": true, + "dependencies": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, "node_modules/lie": { "version": "3.3.0", "dev": true, @@ -6660,11 +6673,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -7617,9 +7631,10 @@ } }, "node_modules/semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -7802,6 +7817,15 @@ "node": ">=8" } }, + "node_modules/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/side-channel": { "version": "1.0.4", "dev": true, @@ -7830,6 +7854,15 @@ "websocket-driver": "^0.7.4" } }, + "node_modules/sockjs/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/source-map": { "version": "0.6.1", "dev": true, @@ -8305,9 +8338,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "dev": true, - "license": "MIT", + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -8463,9 +8500,10 @@ } }, "node_modules/webpack-dev-server": { - "version": "4.11.1", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dev": true, - "license": "MIT", "dependencies": { "@types/bonjour": "^3.5.9", "@types/connect-history-api-fallback": "^1.3.5", @@ -8473,7 +8511,7 @@ "@types/serve-index": "^1.9.1", "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.0.11", "chokidar": "^3.5.3", @@ -8486,6 +8524,7 @@ "html-entities": "^2.3.2", "http-proxy-middleware": "^2.0.3", "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", "open": "^8.0.9", "p-retry": "^4.5.0", "rimraf": "^3.0.2", @@ -8494,8 +8533,8 @@ "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" }, "bin": { "webpack-dev-server": "bin/webpack-dev-server.js" @@ -8511,6 +8550,9 @@ "webpack": "^4.37.0 || ^5.0.0" }, "peerDependenciesMeta": { + "webpack": { + "optional": true + }, "webpack-cli": { "optional": true } @@ -9837,9 +9879,9 @@ } }, "@hubspot/calling-extensions-sdk": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@hubspot/calling-extensions-sdk/-/calling-extensions-sdk-0.6.3.tgz", - "integrity": "sha512-3HaIVj/ATJ8RjN5H+VfY/DU7kNm0DCaNU80AEGcdn3/hseIucH5fOlU42EHy/TaW2rFZJHr/OUiBNMf4E6AbwA==" + "version": "0.7.0-beta.0", + "resolved": "https://registry.npmjs.org/@hubspot/calling-extensions-sdk/-/calling-extensions-sdk-0.7.0-beta.0.tgz", + "integrity": "sha512-afw8c/bnquyIy1OfdKMLh5Ic/fYX2uMIz2tsunCuSFRUVRpStYkAdIXjWi9vblMmcz/+7k50yeNNl8QclRxQXw==" }, "@internationalized/date": { "version": "3.1.0", @@ -11206,7 +11248,9 @@ } }, "@types/ws": { - "version": "8.5.4", + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.12.tgz", + "integrity": "sha512-3tPRkv1EtkDpzlgyKyI8pGsGZAGPEaXeu0DOj5DI25Ja91bdAYddYHbADRYVrZMRbfW+1l5YwXVDKohDJNQxkQ==", "dev": true, "requires": { "@types/node": "*" @@ -12900,7 +12944,9 @@ } }, "jasmine-browser-runner": { - "version": "1.3.0", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jasmine-browser-runner/-/jasmine-browser-runner-1.4.0.tgz", + "integrity": "sha512-UOwxL7/tL5K3Ww9LUw4EFWyZCQyTflzTgRFr80Bm/22EEUqlOiDhdqhYblu3LEZ+ZZ8qfwrlBShtcc+MAwNetg==", "dev": true, "requires": { "ejs": "^3.1.6", @@ -12973,6 +13019,16 @@ "version": "6.0.3", "dev": true }, + "launch-editor": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.9.1.tgz", + "integrity": "sha512-Gcnl4Bd+hRO9P9icCP/RVVT2o8SFlPXofuCxvA2SaZuH45whSvf5p8x5oih5ftLiVhEI4sp5xDY+R+b3zJBh5w==", + "dev": true, + "requires": { + "picocolors": "^1.0.0", + "shell-quote": "^1.8.1" + } + }, "lie": { "version": "3.3.0", "dev": true, @@ -13063,10 +13119,12 @@ "dev": true }, "micromatch": { - "version": "4.0.5", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, @@ -13688,7 +13746,9 @@ } }, "semver": { - "version": "6.3.0", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, "send": { @@ -13829,6 +13889,12 @@ "version": "3.0.0", "dev": true }, + "shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true + }, "side-channel": { "version": "1.0.4", "dev": true, @@ -13849,6 +13915,14 @@ "faye-websocket": "^0.11.3", "uuid": "^8.3.2", "websocket-driver": "^0.7.4" + }, + "dependencies": { + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "dev": true + } } }, "source-map": { @@ -14153,8 +14227,9 @@ "dev": true }, "uuid": { - "version": "8.3.2", - "dev": true + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==" }, "vary": { "version": "1.1.2", @@ -14285,7 +14360,9 @@ } }, "webpack-dev-server": { - "version": "4.11.1", + "version": "4.15.2", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz", + "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==", "dev": true, "requires": { "@types/bonjour": "^3.5.9", @@ -14294,7 +14371,7 @@ "@types/serve-index": "^1.9.1", "@types/serve-static": "^1.13.10", "@types/sockjs": "^0.3.33", - "@types/ws": "^8.5.1", + "@types/ws": "^8.5.5", "ansi-html-community": "^0.0.8", "bonjour-service": "^1.0.11", "chokidar": "^3.5.3", @@ -14307,6 +14384,7 @@ "html-entities": "^2.3.2", "http-proxy-middleware": "^2.0.3", "ipaddr.js": "^2.0.1", + "launch-editor": "^2.6.0", "open": "^8.0.9", "p-retry": "^4.5.0", "rimraf": "^3.0.2", @@ -14315,8 +14393,8 @@ "serve-index": "^1.9.1", "sockjs": "^0.3.24", "spdy": "^4.0.2", - "webpack-dev-middleware": "^5.3.1", - "ws": "^8.4.2" + "webpack-dev-middleware": "^5.3.4", + "ws": "^8.13.0" }, "dependencies": { "ipaddr.js": { diff --git a/demos/demo-react-ts/package.json b/demos/demo-react-ts/package.json index 10b6d57c..b39ec981 100644 --- a/demos/demo-react-ts/package.json +++ b/demos/demo-react-ts/package.json @@ -18,11 +18,12 @@ "node": ">=14" }, "dependencies": { - "@hubspot/calling-extensions-sdk": "^0.6.3", + "@hubspot/calling-extensions-sdk": "^0.7.0-beta.0", "react": "^18.2.0", "react-aria": "^3.22.0", "react-dom": "^18.2.0", - "styled-components": "^5.3.6" + "styled-components": "^5.3.6", + "uuid": "^10.0.0" }, "devDependencies": { "@babel/core": "^7.20.12", diff --git a/demos/demo-react-ts/src/hooks/useCti.ts b/demos/demo-react-ts/src/hooks/useCti.ts index 5bf6b22c..e1b17fb0 100644 --- a/demos/demo-react-ts/src/hooks/useCti.ts +++ b/demos/demo-react-ts/src/hooks/useCti.ts @@ -1,31 +1,22 @@ /* eslint-disable import/no-relative-packages */ import { useMemo, useState } from "react"; -// import CallingExtensions from "@hubspot/calling-extensions-sdk/src/CallingExtensions"; -// import { callStatus } from "@hubspot/calling-extensions-sdk/src/Constants"; - -// @ts-expect-error module not typed -import CallingExtensions from "../../../../src/CallingExtensions"; - -// @TODO Move it to CallingExtensions and export it once migrated to typescript -type ObjectCoordinates = { - portalId: number; - objectTypeId: string; - objectId: number; -}; - -export type ContactIdMatch = { - callerIdType: "CONTACT"; - objectCoordinates: ObjectCoordinates; - firstName: string; - lastName: string; - email: string; -}; - -export type CompanyIdMatch = { - callerIdType: "COMPANY"; - objectCoordinates: ObjectCoordinates; - name: string; -}; +import { v4 as uuidv4 } from "uuid"; +import CallingExtensions, { + CompanyIdMatch, + ContactIdMatch, + OnCallAnswered, + OnCallCompleted, + OnCallEnded, + OnError, + OnIncomingCall, + OnInitialized, + OnMessage, + OnNavigateToRecord, + OnOutgoingCall, + OnPublishToChannel, + OnResize, + Options, +} from "@hubspot/calling-extensions-sdk"; const INCOMING_NUMBER_KEY = "LocalSettings:Calling:DemoReact:incomingNumber"; const INCOMING_CONTACT_NAME_KEY = @@ -33,27 +24,22 @@ const INCOMING_CONTACT_NAME_KEY = // @TODO Move it to CallingExtensions and export it once migrated to typescript interface CallingExtensionsContract { - initialized: (userData: unknown) => void; + initialized: (userData: OnInitialized) => void; userAvailable: () => void; userUnavailable: () => void; userLoggedIn: () => void; userLoggedOut: () => void; - incomingCall: (callDetails: { fromNumber: string }) => void; - outgoingCall: (callDetails: unknown) => void; - callAnswered: () => void; + incomingCall: (callDetails: OnIncomingCall) => void; + outgoingCall: (callDetails: OnOutgoingCall) => void; + callAnswered: (payload: OnCallAnswered) => void; callData: (data: unknown) => void; - callEnded: (engagementData: unknown) => void; - callCompleted: (callCompletedData: unknown) => void; - sendError: (errorData: unknown) => void; - resizeWidget: (sizeInfo: unknown) => void; - sendMessage: (message: unknown) => void; + callEnded: (engagementData: OnCallEnded) => void; + callCompleted: (callCompletedData: OnCallCompleted) => void; + sendError: (errorData: OnError) => void; + resizeWidget: (sizeInfo: OnResize) => void; + sendMessage: (message: OnMessage) => void; logDebugMessage: (messageData: unknown) => void; -} - -// @TODO Move it to CallingExtensions and export it once migrated to typescript -interface CallingExtensionsOptions { - debugMode: boolean; - eventHandlers: unknown; + publishToChannel: (data: OnPublishToChannel) => void; } // @TODO Move it to CallingExtensions and export it once migrated to typescript @@ -62,10 +48,20 @@ class CallingExtensionsWrapper implements CallingExtensionsContract { private _incomingNumber = ""; - constructor(options: CallingExtensionsOptions) { + private _externalCallId = ""; + + constructor(options: Options) { this._cti = new CallingExtensions(options); } + get externalCallId() { + return this._externalCallId; + } + + set externalCallId(id: string) { + this._externalCallId = id; + } + get incomingNumber() { return this._incomingNumber; } @@ -74,7 +70,7 @@ class CallingExtensionsWrapper implements CallingExtensionsContract { this._incomingNumber = number; } - initialized(userData: unknown) { + initialized(userData: OnInitialized) { return this._cti.initialized(userData); } @@ -94,48 +90,70 @@ class CallingExtensionsWrapper implements CallingExtensionsContract { return this._cti.userLoggedOut(); } - incomingCall(callDetails: { fromNumber: string }) { - this._incomingNumber = callDetails.fromNumber; - return this._cti.incomingCall(callDetails); + incomingCall(callDetails: OnIncomingCall) { + this.incomingNumber = callDetails.fromNumber; + this.externalCallId = uuidv4(); + return this._cti.incomingCall({ + ...callDetails, + externalCallId: this.externalCallId, + }); } - outgoingCall(callDetails: unknown) { - return this._cti.outgoingCall(callDetails); + outgoingCall(callDetails: OnOutgoingCall) { + this.externalCallId = uuidv4(); + return this._cti.outgoingCall({ + ...callDetails, + externalCallId: this.externalCallId, + }); } - navigateToRecord(data: { objectCoordinates: ObjectCoordinates }) { + navigateToRecord(data: OnNavigateToRecord) { return this._cti.navigateToRecord(data); } - callAnswered() { - return this._cti.callAnswered(); + callAnswered(data: OnCallAnswered) { + return this._cti.callAnswered({ + ...data, + externalCallId: this.externalCallId, + }); } callData(data: unknown) { return this._cti.callData(data); } - callEnded(engagementData: unknown) { - return this._cti.callEnded(engagementData); + callEnded(engagementData: OnCallEnded) { + return this._cti.callEnded({ + ...engagementData, + externalCallId: this.externalCallId, + }); + } + + callCompleted(callCompletedData: OnCallCompleted) { + this._cti.callCompleted({ + ...callCompletedData, + externalCallId: this.externalCallId, + }); + this.externalCallId = ""; } - callCompleted(callCompletedData: unknown) { - return this._cti.callCompleted(callCompletedData); + publishToChannel(data: OnPublishToChannel) { + return this._cti.publishToChannel(data); } - sendError(errorData: unknown) { + sendError(errorData: OnError) { return this._cti.sendError(errorData); } - resizeWidget(sizeInfo: unknown) { + resizeWidget(sizeInfo: OnResize) { return this._cti.resizeWidget(sizeInfo); } - sendMessage(message: unknown) { + sendMessage(message: OnMessage) { return this._cti.sendMessage(message); } - logDebugMessage(messageData: unknown) { + logDebugMessage(messageData: any) { return this._cti.logDebugMessage(messageData); } } @@ -147,7 +165,6 @@ export const useCti = ( const [engagementId, setEngagementId] = useState(null); const [incomingContactName, setIncomingContactName] = useState(""); const cti = useMemo(() => { - const defaultSize = { width: 400, height: 600 }; return new CallingExtensionsWrapper({ debugMode: true, eventHandlers: { @@ -161,9 +178,12 @@ export const useCti = ( cti.initialized({ isLoggedIn: true, - sizeInfo: defaultSize, engagementId, - }); + sizeInfo: { + width: 400, + height: 650, + }, + } as OnInitialized); const incomingNumber = window.localStorage.getItem(INCOMING_NUMBER_KEY); const incomingContactName = window.localStorage.getItem( @@ -252,6 +272,18 @@ export const useCti = ( onPublishToChannelSucceeded: (data: any, _rawEvent: any) => { /** HubSpot successfully published the call to the connected channel. */ }, + onSetCallState: (data: any, _rawEvent: any) => { + /** HubSpot successfully published the call to the connected channel. */ + }, + onEndCall: (data: any, _rawEvent: any) => { + /** HubSpot successfully ended the call. */ + }, + onInitiateCallIdSucceeded: (data: any, _rawEvent: any) => { + /** HubSpot successfully initiated the call. */ + }, + onInitiateCallIdFailed: (data: any, _rawEvent: any) => { + /** HubSpot was unable to initiate call. */ + }, }, }); }, []); diff --git a/index.js b/index.js index b9f70806..463874c5 100644 --- a/index.js +++ b/index.js @@ -4,3 +4,4 @@ import IFrameManager from "./src/IFrameManager"; export default CallingExtensions; export { Constants, IFrameManager }; +export * from "./src/typedefs"; diff --git a/package-lock.json b/package-lock.json index 6f45f007..a7e9c7bf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@hubspot/calling-extensions-sdk", - "version": "0.6.3", + "version": "0.7.0-beta.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@hubspot/calling-extensions-sdk", - "version": "0.6.3", + "version": "0.7.0-beta.0", "license": "MIT", "devDependencies": { "@babel/cli": "^7.21.0", @@ -25,7 +25,8 @@ "jasmine-browser-runner": "^1.3.0", "jasmine-core": "^4.6.0", "prettier": "^2.8.8", - "typescript": "^5.0.4", + "ts-loader": "^9.5.1", + "typescript": "^5.5.4", "webpack": "^5.77.0", "webpack-cli": "^5.0.1" }, @@ -128,9 +129,9 @@ } }, "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -210,9 +211,9 @@ } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -274,9 +275,9 @@ } }, "node_modules/@babel/helper-define-polyfill-provider/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -1615,9 +1616,9 @@ } }, "node_modules/@babel/preset-env/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -1999,26 +2000,11 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2026,12 +2012,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/parser": { "version": "5.59.7", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.7.tgz", @@ -2143,26 +2123,11 @@ } } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2170,12 +2135,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/utils": { "version": "5.59.7", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", @@ -2202,26 +2161,11 @@ "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@typescript-eslint/utils/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz", - "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -2229,12 +2173,6 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/utils/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "5.59.7", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", @@ -2745,9 +2683,9 @@ } }, "node_modules/babel-loader/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -2768,9 +2706,9 @@ } }, "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -3609,9 +3547,9 @@ } }, "node_modules/eslint-config-airbnb-base/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -3714,9 +3652,9 @@ } }, "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -3753,9 +3691,9 @@ } }, "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -3841,9 +3779,9 @@ } }, "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -4393,9 +4331,9 @@ } }, "node_modules/find-cache-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" @@ -5635,12 +5573,12 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -6505,9 +6443,9 @@ } }, "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, "bin": { "semver": "bin/semver" @@ -6959,6 +6897,117 @@ "node": ">=0.6" } }, + "node_modules/ts-loader": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", + "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4", + "source-map": "^0.7.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/ts-loader/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-loader/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ts-loader/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/ts-loader/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-loader/node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-loader/node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ts-loader/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tsconfig-paths": { "version": "3.14.2", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", @@ -7056,16 +7105,16 @@ } }, "node_modules/typescript": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.0.4.tgz", - "integrity": "sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=12.20" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { diff --git a/package.json b/package.json index d31b17ee..75990e57 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hubspot/calling-extensions-sdk", - "version": "0.6.3", + "version": "0.7.0-beta.0", "description": "A JavaScript SDK for integrating calling apps into HubSpot.", "publishConfig": { "access": "public" @@ -30,10 +30,12 @@ }, "main": "./dist/main.js", "files": [ - "dist/", + "dist/**/*", "src/", - "index.js" + "index.js", + "src/typedefs.js" ], + "types": "./dist/types/index.d.ts", "module": "./index.js", "author": "", "license": "MIT", @@ -57,7 +59,8 @@ "jasmine-browser-runner": "^1.3.0", "jasmine-core": "^4.6.0", "prettier": "^2.8.8", - "typescript": "^5.0.4", + "ts-loader": "^9.5.1", + "typescript": "^5.5.4", "webpack": "^5.77.0", "webpack-cli": "^5.0.1" } diff --git a/src/CallingExtensions.js b/src/CallingExtensions.js index 5a11aafe..80f94ba4 100644 --- a/src/CallingExtensions.js +++ b/src/CallingExtensions.js @@ -1,3 +1,5 @@ +// @ts-check + "use es6"; import IFrameManager from "./IFrameManager"; @@ -11,37 +13,12 @@ import { const prefix = `[calling-extensions-sdk@${VERSION}]`; -/** - * @typedef {Object} EventHandlers - * @property {function} onReady - Called when HubSpot is ready to receive messages. - * @property {function} onDialNumber - Called when the HubSpot sends a dial number from the contact. - * @property {function} onEngagementCreated - Called when HubSpot creates an engagement - * for the call. - * @property {function} onVisibilityChanged - Called when the call widget's visibility changes. - */ - -/** - * @typedef {Object} IframeOptions - * @property {string} src - iframe URL - * @property {string} height - Height of iframe - * @property {string} width - Width of iframe - * @property {string} hostElementSelector - Selector for host element where iframe will be bound - */ - -/** - * @typedef {Object} Options - * @property {IframeOptions} iFrameOptions - iFrame configuration options - * @property {boolean} debugMode - Whether to log various inbound/outbound debug messages - * to the console. - * @property {EventHandlers} eventHandlers - Event handlers handle inbound messages. - */ - /* * CallingExtensions allows call providers to communicate with HubSpot. */ class CallingExtensions { /** - * @param {Options} options + * @param {import('./typedefs').Options} options */ constructor(options) { if (!options || !options.eventHandlers) { @@ -53,118 +30,184 @@ class CallingExtensions { this.iFrameManager = new IFrameManager({ iFrameOptions: options.iFrameOptions, debugMode: options.debugMode, - onMessageHandler: event => this.onMessageHandler(event), + onMessageHandler: (/** @type {any} */ event) => + // eslint-disable-next-line implicit-arrow-linebreak + this.onMessageHandler(event), }); } - initialized(userData) { + /** + * Send a message indicating that the soft phone is ready for interaction. + * + * @param {import('./typedefs').OnInitialized} payload + */ + initialized(payload) { this.sendMessage({ type: messageType.INITIALIZED, - data: userData, + data: payload, }); } + /** + * Event when user's availability is changed to available + */ userAvailable() { this.sendMessage({ type: messageType.USER_AVAILABLE, }); } + /** + * Event when user's availability is changed to unavailable + */ userUnavailable() { this.sendMessage({ type: messageType.USER_UNAVAILABLE, }); } + /** + * Sends a message indicating that the user has logged in. + */ userLoggedIn() { this.sendMessage({ type: messageType.LOGGED_IN, }); } + /** + * Sends a message indicating that the user has logged out. + */ userLoggedOut() { this.sendMessage({ type: messageType.LOGGED_OUT, }); } - incomingCall(callDetails) { + /** + * Event when incoming call is received. + * + * @param {import('./typedefs').OnIncomingCall} callInfo + */ + incomingCall(callInfo) { this.sendMessage({ type: messageType.INCOMING_CALL, - data: callDetails, + data: callInfo, }); } - outgoingCall(callDetails) { + /** + * Sends a message to notify HubSpot that an outgoing call has started. + * + * @param {import('./typedefs').OnOutgoingCall} callInfo + */ + outgoingCall(callInfo) { this.sendMessage({ type: messageType.OUTGOING_CALL_STARTED, - data: callDetails, + data: callInfo, }); } /** - * Event when an inbound call is answered. + * Sends a message to notify HubSpot that a call is being answered. * - * @param {Object} data - The data object to be published. - * @param {number} data.externalCallId - Call ID maintained by integrator + * @param {import('./typedefs').OnCallAnswered} payload */ - callAnswered(data) { + callAnswered(payload) { this.sendMessage({ type: messageType.CALL_ANSWERED, - data, + data: payload, }); } - navigateToRecord(data) { + /** + * Event to navigate to record page. + * + * @param {import('./typedefs').OnNavigateToRecord} payload + */ + navigateToRecord(payload) { this.sendMessage({ type: messageType.NAVIGATE_TO_RECORD, - data, + data: payload, }); } - callData(data) { + /** + * @param {any} payload + */ + callData(payload) { this.sendMessage({ type: messageType.CALL_DATA, - data, + data: payload, }); } - callEnded(engagementData) { + /** + * Sends a message to notify HubSpot that the call has ended. + * + * @param {import('./typedefs').OnCallEnded} data + */ + callEnded(data) { this.sendMessage({ type: messageType.CALL_ENDED, - data: engagementData, + data, }); } - callCompleted(callCompletedData) { + /** + * Sends a message to notify HubSpot that the call has completed. + * + * @param {import('./typedefs').OnCallCompleted} data + */ + callCompleted(data) { this.sendMessage({ type: messageType.CALL_COMPLETED, - data: callCompletedData, + data, }); } - sendError(errorData) { + /** + * Sends a message to notify HubSpot that the calling app has encountered an error. + * @param {import('./typedefs').OnError} data + */ + sendError(data) { this.sendMessage({ type: messageType.ERROR, - data: errorData, + data, }); } - resizeWidget(sizeInfo) { + /** + * Sends a message to notify HubSpot that the calling app needs to be resized. + * + * @param {import('./typedefs').OnResize} data + */ + resizeWidget(data) { this.sendMessage({ type: messageType.RESIZE_WIDGET, - data: sizeInfo, + data, }); } + /** + * + * @param {import('./typedefs').OnMessage} message + */ sendMessage(message) { this.iFrameManager.sendMessage(message); } + /** + * + * @param {{message: string, type: string}} param0 + */ logDebugMessage({ message, type = debugMessageType.GENERIC_MESSAGE }) { this.iFrameManager.logDebugMessage(prefix, type, message); } + /** + * @param {{ type: keyof import('./typedefs').EventHandlers; data: any; }} event + */ onMessageHandler(event) { const { type, data } = event; const { eventHandlers } = this.options; @@ -172,7 +215,9 @@ class CallingExtensions { let handler; if (type in messageHandlerNames) { const name = messageHandlerNames[type]; - handler = eventHandlers[name]; + if (name in eventHandlers) { + handler = eventHandlers[name]; + } } else { // Send back a message indicating an unknown event is received this.sendMessage({ @@ -199,8 +244,7 @@ class CallingExtensions { /** * Publishes the call to a connected channel. * - * @param {Object} data - The data object to be published. - * @param {number} data.engagementId - The HubSpot engagementId created by the calling app. + * @param {import('./typedefs').OnPublishToChannel} data - The data object to be published. */ publishToChannel(data) { this.sendMessage({ diff --git a/src/Constants.js b/src/Constants.js index 47565630..d1074d3a 100644 --- a/src/Constants.js +++ b/src/Constants.js @@ -59,6 +59,9 @@ export const messageType = { UNLOADING: "UNLOADING", }; +/** + * @type {{ [key: string]: keyof import('./typedefs').EventHandlers }} + */ export const messageHandlerNames = { [messageType.CALLER_ID_MATCH_FAILED]: "onCallerIdMatchFailed", [messageType.CALLER_ID_MATCH_SUCCEEDED]: "onCallerIdMatchSucceeded", diff --git a/src/IFrameManager.js b/src/IFrameManager.js index fc63be76..95fc4255 100644 --- a/src/IFrameManager.js +++ b/src/IFrameManager.js @@ -1,3 +1,5 @@ +// @ts-check + "use es6"; import { messageType, debugMessageType, VERSION } from "./Constants"; @@ -9,7 +11,12 @@ const prefix = `[calling-extensions-sdk@${VERSION}]`; * the options. */ class IFrameManager { + /** + * + * @param {import('./typedefs').IframeManagerOptions} options + */ constructor(options) { + /** @type {import('./typedefs').IframeManagerOptions | null} */ this.options = options; const { iFrameOptions, onMessageHandler, debugMode } = options; @@ -21,39 +28,50 @@ class IFrameManager { this.debugMode = debugMode; // Keeps track of all the callbacks + /** @type {{ [key: string]: Function }} */ this.callbacks = {}; this.instanceId = Date.now(); this.instanceRegexp = new RegExp(`^${this.instanceId}`); this.isReady = false; - this.messageHandler = event => this.onMessage(event); + this.messageHandler = (/** @type {any} */ event) => this.onMessage(event); window.addEventListener("message", this.messageHandler); if (iFrameOptions) { - this.iFrame = IFrameManager.createIFrame(iFrameOptions, () => { - this.firstSyncSent = Date.now(); - this.isReady = false; - this.sendSync(); - }); + /** @type {HTMLIFrameElement | null} */ + this.iFrame = IFrameManager.createIFrame( + iFrameOptions, + () => { + this.firstSyncSent = Date.now(); + this.isReady = false; + this.sendSync(); + }, + this.handleLoadError, + ); + } else { + /** @type {HTMLIFrameElement | null} */ + this.iFrame = null; } - this.destinationWindow = iFrameOptions - ? this.iFrame.contentWindow - : window.parent; + this.destinationWindow = + iFrameOptions && this.iFrame ? this.iFrame.contentWindow : window.parent; this.destinationHost = IFrameManager.getDestinationHost(iFrameOptions); } - /* + /** * Creates a new message id + * @param {string|number} instanceId + * @returns {string} */ static createMessageId(instanceId) { return `${instanceId}_${Date.now()}`; } - /* + /** * Gets the html element that hosts the iFrame + * @param {string} hostElementSelector */ static getHostElement(hostElementSelector) { const hostElement = document.querySelector(hostElementSelector); @@ -65,18 +83,38 @@ class IFrameManager { return hostElement; } + /** + * @param {string} url + */ static extractHostFromUrl(url) { const a = document.createElement("a"); a.href = url; return `${a.protocol}//${a.host}`; } + /** + * + * @param {import('./typedefs').IframeOptions} [iFrameOptions] + */ static getDestinationHost(iFrameOptions) { const url = iFrameOptions ? iFrameOptions.src : document.referrer; return IFrameManager.extractHostFromUrl(url); } - static createIFrame(iFrameOptions, onLoadCallback) { + handleLoadError() { + this.onMessageHandler({ + type: messageType.SYNC_ACK_FAILED, + }); + } + + /** + * @param {import('./typedefs').IframeOptions} iFrameOptions + * @param { (this: GlobalEventHandlers, ev: Event) => any } onLoadCallback + * @param { OnErrorEventHandler } onLoadErrorCallback + * @returns {HTMLIFrameElement | null} + */ + static createIFrame(iFrameOptions, onLoadCallback, onLoadErrorCallback) { + // eslint-disable-next-line object-curly-newline const { src, width, height, hostElementSelector } = iFrameOptions; if (!src || !width || !height || !hostElementSelector) { @@ -85,9 +123,10 @@ class IFrameManager { ); } - const iFrame = document.createElement("iFrame"); + /** @type {HTMLIFrameElement} */ + const iFrame = document.createElement("iframe"); iFrame.onload = onLoadCallback; - iFrame.onerror = this.handleLoadError; + iFrame.onerror = onLoadErrorCallback; iFrame.src = src; iFrame.width = width; iFrame.height = height; @@ -98,26 +137,33 @@ class IFrameManager { element.innerHTML = ""; element.appendChild(iFrame); - return element.querySelector("iFrame"); - } - - handleLoadError() { - this.onMessageHandler({ - type: messageType.SYNC_ACK_FAILED, - }); + return element.querySelector("iframe"); } + /** + * @param {import('./typedefs').SizeInfo} sizeInfo + */ updateIFrameSize(sizeInfo) { const { width, height } = sizeInfo; - const formatSize = size => (typeof size === "number" ? `${size}px` : size); - if (width) { - this.iFrame.setAttribute("width", formatSize(width)); - } - if (height) { - this.iFrame.setAttribute("height", formatSize(height)); + if (this.iFrame) { + if (width) { + this.iFrame.setAttribute("width", IFrameManager.formatSize(width)); + } + if (height) { + this.iFrame.setAttribute("height", IFrameManager.formatSize(height)); + } } } + /** + * + * @param {number | string} size + * @returns {string} + */ + static formatSize(size) { + return typeof size === "number" ? `${size}px` : size; + } + onReady(data = {}) { this.isReady = true; this.onMessageHandler({ @@ -132,7 +178,7 @@ class IFrameManager { remove() { window.removeEventListener("message", this.messageHandler); - if (this.iFrame) { + if (this.iFrame && this.options && this.options.iFrameOptions) { const element = IFrameManager.getHostElement( this.options.iFrameOptions.hostElementSelector, ); @@ -144,8 +190,10 @@ class IFrameManager { } } - /* + /** * Send a message to the destination window. + * @param {{type: string, messageId?: string|number, hostUrl?: string}} message + * @param {function} [callback] */ sendMessage(message, callback) { const { type } = message; @@ -174,11 +222,17 @@ class IFrameManager { }); this.logDebugMessage(prefix, debugMessageType.TO_HUBSPOT, type, message); - this.destinationWindow.postMessage(newMessage, this.destinationHost); + if (this.destinationWindow) { + this.destinationWindow.postMessage(newMessage, this.destinationHost); + } } + /** + * @param {{ data: any; origin?: any; }} event + */ onMessage(event) { const { data, origin } = event; + // eslint-disable-next-line object-curly-newline const { type, engagementId, portalId, userId, ownerId } = event.data; if (type === messageType.SYNC) { // The iFrame host can send this message multiple times, don't respond more than once @@ -252,7 +306,7 @@ class IFrameManager { type: messageType.SYNC, hostUrl: IFrameManager.extractHostFromUrl(window.location.href), }, - eventData => { + (/** @type {{ debugMode?: any; iFrameUrl?: any; }} */ eventData) => { const { iFrameUrl } = eventData; this.destinationHost = iFrameUrl || this.destinationHost; this.onReady(); @@ -269,6 +323,9 @@ class IFrameManager { }, 100); } + /** + * @param {any[]} args + */ logDebugMessage(...args) { if (this.debugMode) { console.log.call(null, args); diff --git a/src/typedefs.js b/src/typedefs.js new file mode 100644 index 00000000..1852ab31 --- /dev/null +++ b/src/typedefs.js @@ -0,0 +1,177 @@ +/* eslint-disable max-len */ +// typedefs.js +/** + * @namespace typedefs + */ + +/** + * @typedef {Object.} EventHandlers + * @property {function} onReady - Called when HubSpot is ready to receive messages. + * @property {function} onDialNumber - Called when the HubSpot sends a dial number from the contact. + * @property {function} onEngagementCreated - Called when HubSpot creates an engagement for the call. + * @property {function} onVisibilityChanged - Called when the call widget's visibility changes. + * @property {function} onCallerIdMatchFailed - Called when the caller ID match fails. + * @property {function} onCallerIdMatchSucceeded - Called when the caller ID match succeeds. + * @property {function} onCreateEngagementFailed - Called when creating an engagement fails. + * @property {function} onCreateEngagementSucceeded - Called when creating an engagement succeeds. + * @property {function} onNavigateToRecordFailed - Called when navigating to a record fails. + * @property {function} onPublishToChannelFailed - Called when publishing to a channel fails. + * @property {function} onPublishToChannelSucceeded - Called when publishing to a channel succeeds. + * @property {function} onSetCallState - Called when the call state changes. + * @property {function} onUpdateEngagementFailed - Called when updating an engagement fails. + * @property {function} onUpdateEngagementSucceeded - Called when updating an engagement succeeds. + * @property {function} onEndCall - Called when the call ends. + * @property {function} onInitiateCallIdSucceeded - Called when initiating a call ID succeeds. + * @property {function} onInitiateCallIdFailed - Called when initiating a call ID fails. + * @property {function} [defaultEventHandler] - Default event handler to handle unhandled events. + */ + +/** + * @typedef {Object} IframeOptions + * @property {string} src - iframe URL + * @property {string} height - Height of iframe + * @property {string} width - Width of iframe + * @property {string} hostElementSelector - Selector for host element where iframe will be bound + */ + +/** + * @typedef {Object} Options + * @property {IframeOptions} [iFrameOptions] - iFrame configuration options + * @property {boolean} debugMode - Whether to log various inbound/outbound debug messages + * to the console. + * @property {EventHandlers} eventHandlers - Event handlers handle inbound messages. + */ + +/** + * @typedef {Object} IframeManagerOptions + * @property {IframeOptions} [iFrameOptions] - iFrame configuration options + * @property {boolean} debugMode - Whether to log various inbound/outbound debug messages + * to the console. + * @property {function} onMessageHandler - Callback function to handle inbound messages. + */ + +/** + * @typedef {Object} OnResize + * @property {number} width + * @property {number} height + */ + +/** + * @typedef {OnResize} SizeInfo + */ + +/** + * @typedef {Object} OnInitialized + * @property {boolean} [isLoggedIn] + * @property {number} [engagementId] + */ + +/** + * @typedef {Object} OnIncomingCall + * @property {string} externalCallId + * @property {number} [callStartTime] + * @property {boolean} [createEngagement] + * @property {string} fromNumber + * @property {string} toNumber + */ + +/** + * @typedef {Object} OnOutgoingCall + * @property {string} externalCallId + * @property {number} [callStartTime] + * @property {boolean} [createEngagement] + * @property {string} [fromNumber] + * @property {string} [toNumber] + */ + +/** + * @typedef {Object} OnCallAnswered + * @property {string} externalCallId + */ + +/** + * @typedef {Object} OnPublishToChannel + * @property {string} externalCallId + * @property {number} engagementId + */ + +/** + * @typedef {'COMPLETED'|'FAILED'|'CANCELED'|'BUSY'|'NO_ANSWER'|'REJECTED'|'MISSED'} EndStatus + */ + +/** + * @typedef {Object} OnCallEnded + * @property {string} externalCallId + * @property {number} engagementId + * @property {EndStatus} [callEndStatus] + */ + +/** + * @typedef {Object} RawEngagementProperties + * @property {number|string} hs_timestamp - This field marks the call's time of creation and determines where the call sits on the record timeline. + * @property {string} [hs_call_body] - The description of the call, including any notes that you want to add. + * @property {string} [hs_call_callee_object_id] - The ID of the HubSpot record associated with the call. This will be the recipient of the call for OUTBOUND calls, or the dialer of the call for INBOUND calls. + * @property {string} [hs_call_callee_object_type] - The type of the object to which the call's associated record belongs (e.g., specifies if the record is a contact or company). This will be the object of the recipient for OUTBOUNDcalls, or the object of the dialer for INBOUND calls. + * @property {string} [hs_call_direction] - The direction of the call from the perspective of the HubSpot user. If the user is the call recipient, the direction should be set to INBOUND. If the user initiated the call, the direction should be set to OUTBOUND. + * @property {string} [hs_call_disposition] + * @property {string} [hs_call_duration] - The duration of the call in milliseconds. + * @property {string} [hs_call_from_number] - The phone number that the call was made from. + * @property {string} [hs_call_recording_url] - The URL that stores the call recording. URLS to .mp3 or .wav files can be played back on CRM records. Only HTTPS, secure URLs will be accepted. + * @property {string} [hs_call_status] - The status of the call. The statuses are BUSY, CALLING_CRM_USER, CANCELED, COMPLETED, CONNECTING, FAILED, IN_PROGRESS, NO_ANSWER, QUEUED, and RINGING. + * @property {string} [hs_call_title] - The title of the call. + * @property {string} [hs_call_source] - The source of the call. This is not required, but it is required to leverage the recording and transcriptions pipeline. If the property is set, it must be set to INTEGRATIONS_PLATFORM. + * @property {string} [hs_call_to_number] - The phone number that received the call. + * @property {string} [hubspot_owner_id] - The ID of the owner associated with the call. This field determines the user listed as the call creator on the record timeline. + * @property {string} [hs_activity_type] - The type of call. The options are based on the call types set in your HubSpot account. + * @property {string} [hs_attachment_ids] - The IDs of the call's attachments. Multiple attachment IDs are separated by a semi-colon. + */ + +/** + * @typedef {Object} OnCallCompleted + * @property {string} externalCallId + * @property {number|string} [engagementId] + * @property {boolean} [hideWidget] + * @property {RawEngagementProperties} [engagementProperties] + */ + +/** + * @typedef {Object} ObjectCoordinates + * @property {number} portalId + * @property {number} objectId + * @property {"0-1"|"0-2"} objectTypeId + */ + +/** + * @typedef {Object} OnNavigateToRecord + * @property {number} [engagementId] + * @property {ObjectCoordinates} objectCoordinates + */ + +/** + * @typedef {Object} OnError + * @property {string} message + */ + +/** + * @typedef {Object} OnMessage + * @property {string} type + * @property {Object} [data] + */ + +/** + * @typedef {Object} ContactIdMatch + * @property {"CONTACT"} callerIdType + * @property {ObjectCoordinates} objectCoordinates + * @property {string} firstName + * @property {string} lastName + * @property {string} email + */ + +/** + * @typedef {Object} CompanyIdMatch + * @property {"COMPANY"} callerIdType + * @property {ObjectCoordinates} objectCoordinates + * @property {string} name + */ + +export {}; diff --git a/test/spec/IFrameManager-test.js b/test/spec/IFrameManager-test.js index 0fb1615a..3aa0ce04 100644 --- a/test/spec/IFrameManager-test.js +++ b/test/spec/IFrameManager-test.js @@ -68,8 +68,10 @@ describe("iFrameManager", () => { }, }; - it("should get origin", done => { - spyOn(IFrameManager, "createIFrame").and.returnValue({ contentWindow: null }); + it("should get origin", done => { + spyOn(IFrameManager, "createIFrame").and.returnValue({ + contentWindow: null, + }); const instance = createInstance(options); expect(instance).toBeDefined(); @@ -78,16 +80,23 @@ describe("iFrameManager", () => { }); it("should create an iFrame with options", () => { - const createIFrameSpy = spyOn(IFrameManager, "createIFrame").and.returnValue({ contentWindow: null }); + const createIFrameSpy = spyOn( + IFrameManager, + "createIFrame", + ).and.returnValue({ contentWindow: null }); const instance = createInstance(options); expect(instance).toBeDefined(); - expect(createIFrameSpy).toHaveBeenCalledWith(jasmine.objectContaining({ - hostElementSelector: "body", - src: "https://a.b.c.d.e.f.c", - width: "100px", - height: "100px", - }), jasmine.any(Function)); + expect(createIFrameSpy).toHaveBeenCalledWith( + jasmine.objectContaining({ + hostElementSelector: "body", + src: "https://a.b.c.d.e.f.c", + width: "100px", + height: "100px", + }), + jasmine.any(Function), + jasmine.any(Function), + ); }); }); }); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..801ad1a8 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "declaration": true, + "declarationDir": "./dist/types", + "outDir": "./dist", + "strict": true, + "allowJs": true, + "checkJs": true, + "resolveJsonModule": true, + "esModuleInterop": true + }, + "include": ["src/**/*", "src/typedefs.js"], + "exclude": ["node_modules"] +} diff --git a/webpack.config.js b/webpack.config.js index a5c5abdb..ea27618e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,6 +11,17 @@ const babelLoader = { }, }; +const tsLoader = { + test: /\.js$/, + exclude: /(node_modules)/, + use: { + loader: "ts-loader", + options: { + allowTsInNodeModules: true, + }, + }, +}; + module.exports = { entry: "./index.js", mode: "production", @@ -20,7 +31,10 @@ module.exports = { path: path.resolve(__dirname, "dist"), clean: true, }, + resolve: { + extensions: [".ts", ".js"], + }, module: { - rules: [babelLoader], + rules: [babelLoader, tsLoader], }, };