@webex/contact-center 3.9.0-next.1 → 3.9.0-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/cc.js +104 -62
  2. package/dist/cc.js.map +1 -1
  3. package/dist/index.js +9 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/logger-proxy.js +24 -1
  6. package/dist/logger-proxy.js.map +1 -1
  7. package/dist/metrics/MetricsManager.js +1 -1
  8. package/dist/metrics/MetricsManager.js.map +1 -1
  9. package/dist/metrics/behavioral-events.js +39 -0
  10. package/dist/metrics/behavioral-events.js.map +1 -1
  11. package/dist/metrics/constants.js +12 -1
  12. package/dist/metrics/constants.js.map +1 -1
  13. package/dist/services/AddressBook.js +271 -0
  14. package/dist/services/AddressBook.js.map +1 -0
  15. package/dist/services/EntryPoint.js +227 -0
  16. package/dist/services/EntryPoint.js.map +1 -0
  17. package/dist/services/Queue.js +261 -0
  18. package/dist/services/Queue.js.map +1 -0
  19. package/dist/services/config/constants.js +24 -2
  20. package/dist/services/config/constants.js.map +1 -1
  21. package/dist/services/config/index.js +1 -43
  22. package/dist/services/config/index.js.map +1 -1
  23. package/dist/services/config/types.js +0 -5
  24. package/dist/services/config/types.js.map +1 -1
  25. package/dist/services/core/GlobalTypes.js.map +1 -1
  26. package/dist/services/core/Utils.js +121 -2
  27. package/dist/services/core/Utils.js.map +1 -1
  28. package/dist/services/core/aqm-reqs.js +0 -4
  29. package/dist/services/core/aqm-reqs.js.map +1 -1
  30. package/dist/services/core/websocket/WebSocketManager.js +0 -4
  31. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  32. package/dist/services/task/TaskManager.js +1 -0
  33. package/dist/services/task/TaskManager.js.map +1 -1
  34. package/dist/services/task/index.js +145 -71
  35. package/dist/services/task/index.js.map +1 -1
  36. package/dist/types/cc.d.ts +77 -43
  37. package/dist/types/index.d.ts +3 -2
  38. package/dist/types/metrics/constants.d.ts +7 -0
  39. package/dist/types/services/AddressBook.d.ts +74 -0
  40. package/dist/types/services/EntryPoint.d.ts +67 -0
  41. package/dist/types/services/Queue.d.ts +76 -0
  42. package/dist/types/services/config/constants.d.ts +23 -1
  43. package/dist/types/services/config/index.d.ts +1 -14
  44. package/dist/types/services/config/types.d.ts +0 -64
  45. package/dist/types/services/core/GlobalTypes.d.ts +25 -0
  46. package/dist/types/services/core/Utils.d.ts +27 -1
  47. package/dist/types/services/task/index.d.ts +1 -1
  48. package/dist/types/types.d.ts +162 -0
  49. package/dist/types/utils/PageCache.d.ts +173 -0
  50. package/dist/types.js +17 -0
  51. package/dist/types.js.map +1 -1
  52. package/dist/utils/PageCache.js +192 -0
  53. package/dist/utils/PageCache.js.map +1 -0
  54. package/dist/webex.js +1 -1
  55. package/package.json +8 -8
  56. package/src/cc.ts +120 -80
  57. package/src/index.ts +16 -2
  58. package/src/logger-proxy.ts +24 -1
  59. package/src/metrics/MetricsManager.ts +1 -1
  60. package/src/metrics/behavioral-events.ts +42 -0
  61. package/src/metrics/constants.ts +15 -0
  62. package/src/services/AddressBook.ts +291 -0
  63. package/src/services/EntryPoint.ts +241 -0
  64. package/src/services/Queue.ts +277 -0
  65. package/src/services/config/constants.ts +26 -2
  66. package/src/services/config/index.ts +1 -55
  67. package/src/services/config/types.ts +0 -65
  68. package/src/services/core/GlobalTypes.ts +27 -0
  69. package/src/services/core/Utils.ts +155 -1
  70. package/src/services/core/aqm-reqs.ts +0 -5
  71. package/src/services/core/websocket/WebSocketManager.ts +0 -4
  72. package/src/services/task/TaskManager.ts +1 -0
  73. package/src/services/task/index.ts +172 -56
  74. package/src/types.ts +180 -0
  75. package/src/utils/PageCache.ts +252 -0
  76. package/test/unit/spec/cc.ts +30 -82
  77. package/test/unit/spec/metrics/MetricsManager.ts +0 -1
  78. package/test/unit/spec/services/AddressBook.ts +332 -0
  79. package/test/unit/spec/services/EntryPoint.ts +259 -0
  80. package/test/unit/spec/services/Queue.ts +323 -0
  81. package/test/unit/spec/services/config/index.ts +0 -71
  82. package/test/unit/spec/services/core/Utils.ts +50 -0
  83. package/test/unit/spec/services/core/aqm-reqs.ts +1 -3
  84. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +0 -4
  85. package/test/unit/spec/services/task/TaskManager.ts +8 -1
  86. package/test/unit/spec/services/task/index.ts +226 -122
  87. package/umd/contact-center.min.js +2 -2
  88. package/umd/contact-center.min.js.map +1 -1
@@ -32,3 +32,30 @@ export type Failure = Msg<{
32
32
  /** Human-readable description of the failure reason */
33
33
  reason: string;
34
34
  }>;
35
+
36
+ /**
37
+ * Represents task API error details in a structured format
38
+ * @public
39
+ */
40
+ export interface TaskError {
41
+ /** Original error object for throwing */
42
+ error: Error;
43
+ /** Unique tracking identifier for correlation */
44
+ trackingId: string;
45
+ /** Detailed error message from the API */
46
+ errorMessage: string;
47
+ /** Type/category of the error (e.g., "Bad Request") */
48
+ errorType: string;
49
+ /** Additional error context data */
50
+ errorData: string;
51
+ /** Numeric reason code */
52
+ reasonCode: number;
53
+ }
54
+
55
+ /**
56
+ * An Error object augmented with a flexible data field for additional context.
57
+ * Use this to attach structured data to thrown errors without ts-ignore.
58
+ */
59
+ export interface AugmentedError extends Error {
60
+ data?: Record<string, any>;
61
+ }
@@ -1,8 +1,14 @@
1
1
  import * as Err from './Err';
2
2
  import {LoginOption, WebexRequestPayload} from '../../types';
3
- import {Failure} from './GlobalTypes';
3
+ import {Failure, AugmentedError} from './GlobalTypes';
4
4
  import LoggerProxy from '../../logger-proxy';
5
5
  import WebexRequest from './WebexRequest';
6
+ import {
7
+ TaskData,
8
+ ConsultTransferPayLoad,
9
+ CONSULT_TRANSFER_DESTINATION_TYPE,
10
+ Interaction,
11
+ } from '../task/types';
6
12
 
7
13
  /**
8
14
  * Extracts common error details from a Webex request payload.
@@ -19,6 +25,28 @@ const getCommonErrorDetails = (errObj: WebexRequestPayload) => {
19
25
  };
20
26
  };
21
27
 
28
+ /**
29
+ * Checks if the destination type represents an entry point variant (EPDN or ENTRYPOINT).
30
+ */
31
+ const isEntryPointOrEpdn = (destAgentType?: string): boolean => {
32
+ return destAgentType === 'EPDN' || destAgentType === 'ENTRYPOINT';
33
+ };
34
+
35
+ /**
36
+ * Determines if the task involves dialing a number based on the destination type.
37
+ * Returns 'DIAL_NUMBER' for dial-related destinations, empty string otherwise.
38
+ */
39
+ const getAgentActionTypeFromTask = (taskData?: TaskData): 'DIAL_NUMBER' | '' => {
40
+ const destAgentType = taskData?.destinationType;
41
+
42
+ // Check if destination requires dialing: direct dial number or entry point variants
43
+ const isDialNumber = destAgentType === 'DN';
44
+ const isEntryPointVariant = isEntryPointOrEpdn(destAgentType);
45
+
46
+ // If the destination type is a dial number or an entry point variant, return 'DIAL_NUMBER'
47
+ return isDialNumber || isEntryPointVariant ? 'DIAL_NUMBER' : '';
48
+ };
49
+
22
50
  export const isValidDialNumber = (input: string): boolean => {
23
51
  // This regex checks for a valid dial number format for only few countries such as US, Canada.
24
52
  const regexForDn = /1[0-9]{3}[2-9][0-9]{6}([,]{1,10}[0-9]+){0,1}/;
@@ -115,6 +143,62 @@ export const getErrorDetails = (error: any, methodName: string, moduleName: stri
115
143
  };
116
144
  };
117
145
 
146
+ /**
147
+ * Extracts error details from task API errors and logs them. Also uploads logs for the error.
148
+ * This handles the specific error format returned by task API calls.
149
+ *
150
+ * @param error - The error object from task API calls with structure: {id: string, details: {trackingId: string, msg: {...}}}
151
+ * @param methodName - The name of the method where the error occurred.
152
+ * @param moduleName - The name of the module where the error occurred.
153
+ * @returns AugmentedError containing structured error details on err.data for metrics and logging
154
+ * @public
155
+ * @example
156
+ * const taskError = generateTaskErrorObject(error, 'transfer', 'TaskModule');
157
+ * throw taskError.error;
158
+ * @ignore
159
+ */
160
+ export const generateTaskErrorObject = (
161
+ error: any,
162
+ methodName: string,
163
+ moduleName: string
164
+ ): AugmentedError => {
165
+ const trackingId = error?.details?.trackingId || error?.trackingId || '';
166
+ const errorMsg = error?.details?.msg;
167
+
168
+ const fallbackMessage =
169
+ (error && typeof error.message === 'string' && error.message) ||
170
+ `Error while performing ${methodName}`;
171
+ const errorMessage = errorMsg?.errorMessage || fallbackMessage;
172
+ const errorType =
173
+ errorMsg?.errorType ||
174
+ (error && typeof error.name === 'string' && error.name) ||
175
+ 'Unknown Error';
176
+ const errorData = errorMsg?.errorData || '';
177
+ const reasonCode = errorMsg?.reasonCode || 0;
178
+
179
+ // Log and upload for Task API formatted errors
180
+ LoggerProxy.error(`${methodName} failed: ${errorMessage} (${errorType})`, {
181
+ module: moduleName,
182
+ method: methodName,
183
+ trackingId,
184
+ });
185
+ WebexRequest.getInstance().uploadLogs({
186
+ correlationId: trackingId,
187
+ });
188
+
189
+ const reason = `${errorType}: ${errorMessage}${errorData ? ` (${errorData})` : ''}`;
190
+ const err: AugmentedError = new Error(reason);
191
+ err.data = {
192
+ message: errorMessage,
193
+ errorType,
194
+ errorData,
195
+ reasonCode,
196
+ trackingId,
197
+ };
198
+
199
+ return err;
200
+ };
201
+
118
202
  /**
119
203
  * Creates an error details object suitable for use with the Err.Details class.
120
204
  *
@@ -130,3 +214,73 @@ export const createErrDetailsObject = (errObj: WebexRequestPayload) => {
130
214
 
131
215
  return new Err.Details('Service.reqs.generic.failure', details);
132
216
  };
217
+
218
+ /**
219
+ * Derives the consult transfer destination type based on the provided task data.
220
+ *
221
+ * Logic parity with desktop behavior:
222
+ * - If agent action is dialing a number (DN/EPDN/ENTRYPOINT):
223
+ * - ENTRYPOINT/EPDN map to ENTRYPOINT
224
+ * - DN maps to DIALNUMBER
225
+ * - Otherwise defaults to AGENT
226
+ *
227
+ * @param taskData - The task data used to infer the agent action and destination type
228
+ * @returns The normalized destination type to be used for consult transfer
229
+ */
230
+ /**
231
+ * Checks if a participant type represents a non-customer participant.
232
+ * Non-customer participants include agents, dial numbers, entry point dial numbers,
233
+ * and entry points.
234
+ */
235
+ const isNonCustomerParticipant = (participantType: string): boolean => {
236
+ return (
237
+ participantType === 'Agent' ||
238
+ participantType === 'DN' ||
239
+ participantType === 'EpDn' ||
240
+ participantType === 'entryPoint'
241
+ );
242
+ };
243
+
244
+ /**
245
+ * Gets the destination agent ID from participants data by finding the first
246
+ * non-customer participant that is not the current agent and is not in wrap-up state.
247
+ *
248
+ * @param participants - The participants data from the interaction
249
+ * @param agentId - The current agent's ID to exclude from the search
250
+ * @returns The destination agent ID, or empty string if none found
251
+ */
252
+ export const getDestinationAgentId = (
253
+ participants: Interaction['participants'],
254
+ agentId: string
255
+ ): string => {
256
+ let id = '';
257
+
258
+ if (participants) {
259
+ Object.keys(participants).forEach((participant) => {
260
+ const participantData = participants[participant];
261
+ if (
262
+ isNonCustomerParticipant(participantData.type) &&
263
+ participantData.id !== agentId &&
264
+ !participantData.isWrapUp
265
+ ) {
266
+ id = participantData.id;
267
+ }
268
+ });
269
+ }
270
+
271
+ return id;
272
+ };
273
+
274
+ export const deriveConsultTransferDestinationType = (
275
+ taskData?: TaskData
276
+ ): ConsultTransferPayLoad['destinationType'] => {
277
+ const agentActionType = getAgentActionTypeFromTask(taskData);
278
+
279
+ if (agentActionType === 'DIAL_NUMBER') {
280
+ return isEntryPointOrEpdn(taskData?.destinationType)
281
+ ? CONSULT_TRANSFER_DESTINATION_TYPE.ENTRYPOINT
282
+ : CONSULT_TRANSFER_DESTINATION_TYPE.DIALNUMBER;
283
+ }
284
+
285
+ return CONSULT_TRANSFER_DESTINATION_TYPE.AGENT;
286
+ };
@@ -224,11 +224,6 @@ export default class AqmReqs {
224
224
  }
225
225
 
226
226
  if (event.keepalive === 'true') {
227
- LoggerProxy.info(`Keepalive from web socket`, {
228
- module: AQM_REQS_FILE,
229
- method: METHODS.ON_MESSAGE,
230
- });
231
-
232
227
  return;
233
228
  }
234
229
 
@@ -178,10 +178,6 @@ export class WebSocketManager extends EventEmitter {
178
178
  issueReason = 'WebSocket auto close timed out. Forcefully closed websocket.';
179
179
  } else {
180
180
  const onlineStatus = navigator.onLine;
181
- LoggerProxy.info(`[WebSocketStatus] | desktop online status is ${onlineStatus}`, {
182
- module: WEB_SOCKET_MANAGER_FILE,
183
- method: METHODS.WEB_SOCKET_ON_CLOSE_HANDLER,
184
- });
185
181
  issueReason = !onlineStatus
186
182
  ? 'network issue'
187
183
  : 'missing keepalive from either desktop or notif service';
@@ -270,6 +270,7 @@ export default class TaskManager extends EventEmitter {
270
270
  break;
271
271
  case CC_EVENTS.AGENT_CONSULTING:
272
272
  // Received when agent is in an active consult state
273
+ // TODO: Check if we can use backend consult state instead of isConsulted
273
274
  task = this.updateTaskData(task, payload.data);
274
275
  if (task.data.isConsulted) {
275
276
  // Fire only if you are the agent who received the consult request