@webex/contact-center 3.12.0-next.9 → 3.12.0-task-refactor.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +438 -0
- package/ai-docs/README.md +131 -0
- package/ai-docs/RULES.md +455 -0
- package/ai-docs/patterns/event-driven-patterns.md +485 -0
- package/ai-docs/patterns/testing-patterns.md +480 -0
- package/ai-docs/patterns/typescript-patterns.md +365 -0
- package/ai-docs/templates/README.md +102 -0
- package/ai-docs/templates/documentation/create-agents-md.md +240 -0
- package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
- package/ai-docs/templates/existing-service/bug-fix.md +254 -0
- package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
- package/ai-docs/templates/new-method/00-master.md +80 -0
- package/ai-docs/templates/new-method/01-requirements.md +232 -0
- package/ai-docs/templates/new-method/02-implementation.md +295 -0
- package/ai-docs/templates/new-method/03-tests.md +201 -0
- package/ai-docs/templates/new-method/04-validation.md +141 -0
- package/ai-docs/templates/new-service/00-master.md +109 -0
- package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
- package/ai-docs/templates/new-service/02-code-generation.md +346 -0
- package/ai-docs/templates/new-service/03-integration.md +178 -0
- package/ai-docs/templates/new-service/04-test-generation.md +205 -0
- package/ai-docs/templates/new-service/05-validation.md +145 -0
- package/dist/cc.js +65 -123
- package/dist/cc.js.map +1 -1
- package/dist/constants.js +13 -2
- package/dist/constants.js.map +1 -1
- package/dist/index.js +13 -5
- package/dist/index.js.map +1 -1
- package/dist/metrics/behavioral-events.js +26 -13
- package/dist/metrics/behavioral-events.js.map +1 -1
- package/dist/metrics/constants.js +7 -6
- package/dist/metrics/constants.js.map +1 -1
- package/dist/services/ApiAiAssistant.js +0 -3
- package/dist/services/ApiAiAssistant.js.map +1 -1
- package/dist/services/config/Util.js +2 -3
- package/dist/services/config/Util.js.map +1 -1
- package/dist/services/config/types.js +16 -14
- package/dist/services/config/types.js.map +1 -1
- package/dist/services/constants.js +0 -1
- package/dist/services/constants.js.map +1 -1
- package/dist/services/core/Err.js.map +1 -1
- package/dist/services/core/Utils.js +79 -55
- package/dist/services/core/Utils.js.map +1 -1
- package/dist/services/core/aqm-reqs.js +17 -92
- package/dist/services/core/aqm-reqs.js.map +1 -1
- package/dist/services/core/websocket/WebSocketManager.js +5 -25
- package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
- package/dist/services/core/websocket/types.js.map +1 -1
- package/dist/services/index.js +1 -2
- package/dist/services/index.js.map +1 -1
- package/dist/services/task/Task.js +644 -0
- package/dist/services/task/Task.js.map +1 -0
- package/dist/services/task/TaskFactory.js +45 -0
- package/dist/services/task/TaskFactory.js.map +1 -0
- package/dist/services/task/TaskManager.js +556 -532
- package/dist/services/task/TaskManager.js.map +1 -1
- package/dist/services/task/TaskUtils.js +132 -28
- package/dist/services/task/TaskUtils.js.map +1 -1
- package/dist/services/task/constants.js +7 -6
- package/dist/services/task/constants.js.map +1 -1
- package/dist/services/task/dialer.js +0 -51
- package/dist/services/task/dialer.js.map +1 -1
- package/dist/services/task/digital/Digital.js +77 -0
- package/dist/services/task/digital/Digital.js.map +1 -0
- package/dist/services/task/state-machine/TaskStateMachine.js +634 -0
- package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
- package/dist/services/task/state-machine/actions.js +366 -0
- package/dist/services/task/state-machine/actions.js.map +1 -0
- package/dist/services/task/state-machine/constants.js +139 -0
- package/dist/services/task/state-machine/constants.js.map +1 -0
- package/dist/services/task/state-machine/guards.js +256 -0
- package/dist/services/task/state-machine/guards.js.map +1 -0
- package/dist/services/task/state-machine/index.js +53 -0
- package/dist/services/task/state-machine/index.js.map +1 -0
- package/dist/services/task/state-machine/types.js +54 -0
- package/dist/services/task/state-machine/types.js.map +1 -0
- package/dist/services/task/state-machine/uiControlsComputer.js +369 -0
- package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
- package/dist/services/task/taskDataNormalizer.js +99 -0
- package/dist/services/task/taskDataNormalizer.js.map +1 -0
- package/dist/services/task/types.js +157 -18
- package/dist/services/task/types.js.map +1 -1
- package/dist/services/task/voice/Voice.js +1031 -0
- package/dist/services/task/voice/Voice.js.map +1 -0
- package/dist/services/task/voice/WebRTC.js +149 -0
- package/dist/services/task/voice/WebRTC.js.map +1 -0
- package/dist/types/cc.d.ts +4 -33
- package/dist/types/constants.d.ts +13 -2
- package/dist/types/index.d.ts +11 -5
- package/dist/types/metrics/constants.d.ts +5 -3
- package/dist/types/services/ApiAiAssistant.d.ts +1 -1
- package/dist/types/services/config/types.d.ts +97 -25
- package/dist/types/services/core/Err.d.ts +0 -2
- package/dist/types/services/core/Utils.d.ts +25 -23
- package/dist/types/services/core/aqm-reqs.d.ts +0 -49
- package/dist/types/services/core/websocket/WebSocketManager.d.ts +1 -1
- package/dist/types/services/core/websocket/connection-service.d.ts +0 -1
- package/dist/types/services/core/websocket/types.d.ts +1 -1
- package/dist/types/services/index.d.ts +1 -1
- package/dist/types/services/task/Task.d.ts +146 -0
- package/dist/types/services/task/TaskFactory.d.ts +12 -0
- package/dist/types/services/task/TaskUtils.d.ts +39 -8
- package/dist/types/services/task/constants.d.ts +5 -4
- package/dist/types/services/task/dialer.d.ts +0 -15
- package/dist/types/services/task/digital/Digital.d.ts +22 -0
- package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +906 -0
- package/dist/types/services/task/state-machine/actions.d.ts +8 -0
- package/dist/types/services/task/state-machine/constants.d.ts +91 -0
- package/dist/types/services/task/state-machine/guards.d.ts +78 -0
- package/dist/types/services/task/state-machine/index.d.ts +13 -0
- package/dist/types/services/task/state-machine/types.d.ts +256 -0
- package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
- package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
- package/dist/types/services/task/types.d.ts +539 -88
- package/dist/types/services/task/voice/Voice.d.ts +183 -0
- package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
- package/dist/types/types.d.ts +68 -0
- package/dist/types/webex.d.ts +1 -0
- package/dist/types.js +70 -0
- package/dist/types.js.map +1 -1
- package/dist/webex.js +14 -2
- package/dist/webex.js.map +1 -1
- package/package.json +14 -11
- package/src/cc.ts +91 -177
- package/src/constants.ts +13 -2
- package/src/index.ts +14 -5
- package/src/metrics/ai-docs/AGENTS.md +348 -0
- package/src/metrics/ai-docs/ARCHITECTURE.md +336 -0
- package/src/metrics/behavioral-events.ts +28 -14
- package/src/metrics/constants.ts +7 -8
- package/src/services/ApiAiAssistant.ts +2 -4
- package/src/services/agent/ai-docs/AGENTS.md +238 -0
- package/src/services/agent/ai-docs/ARCHITECTURE.md +302 -0
- package/src/services/ai-docs/AGENTS.md +384 -0
- package/src/services/config/Util.ts +2 -3
- package/src/services/config/ai-docs/AGENTS.md +253 -0
- package/src/services/config/ai-docs/ARCHITECTURE.md +424 -0
- package/src/services/config/types.ts +108 -20
- package/src/services/constants.ts +0 -1
- package/src/services/core/Err.ts +0 -1
- package/src/services/core/Utils.ts +90 -67
- package/src/services/core/ai-docs/AGENTS.md +379 -0
- package/src/services/core/ai-docs/ARCHITECTURE.md +696 -0
- package/src/services/core/aqm-reqs.ts +22 -100
- package/src/services/core/websocket/WebSocketManager.ts +4 -23
- package/src/services/core/websocket/types.ts +1 -1
- package/src/services/index.ts +1 -2
- package/src/services/task/Task.ts +785 -0
- package/src/services/task/TaskFactory.ts +55 -0
- package/src/services/task/TaskManager.ts +567 -633
- package/src/services/task/TaskUtils.ts +175 -31
- package/src/services/task/ai-docs/AGENTS.md +448 -0
- package/src/services/task/ai-docs/ARCHITECTURE.md +573 -0
- package/src/services/task/constants.ts +5 -4
- package/src/services/task/dialer.ts +1 -56
- package/src/services/task/digital/Digital.ts +95 -0
- package/src/services/task/state-machine/TaskStateMachine.ts +793 -0
- package/src/services/task/state-machine/actions.ts +409 -0
- package/src/services/task/state-machine/ai-docs/AGENTS.md +495 -0
- package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1135 -0
- package/src/services/task/state-machine/constants.ts +150 -0
- package/src/services/task/state-machine/guards.ts +295 -0
- package/src/services/task/state-machine/index.ts +28 -0
- package/src/services/task/state-machine/types.ts +228 -0
- package/src/services/task/state-machine/uiControlsComputer.ts +529 -0
- package/src/services/task/taskDataNormalizer.ts +137 -0
- package/src/services/task/types.ts +641 -95
- package/src/services/task/voice/Voice.ts +1255 -0
- package/src/services/task/voice/WebRTC.ts +187 -0
- package/src/types.ts +88 -5
- package/src/utils/AGENTS.md +276 -0
- package/src/webex.js +2 -0
- package/test/unit/spec/cc.ts +59 -142
- package/test/unit/spec/logger-proxy.ts +70 -0
- package/test/unit/spec/services/ApiAiAssistant.ts +17 -0
- package/test/unit/spec/services/config/index.ts +26 -55
- package/test/unit/spec/services/core/Utils.ts +103 -52
- package/test/unit/spec/services/core/websocket/WebSocketManager.ts +48 -112
- package/test/unit/spec/services/core/websocket/connection-service.ts +5 -4
- package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
- package/test/unit/spec/services/task/Task.ts +416 -0
- package/test/unit/spec/services/task/TaskFactory.ts +62 -0
- package/test/unit/spec/services/task/TaskManager.ts +781 -1735
- package/test/unit/spec/services/task/TaskUtils.ts +125 -0
- package/test/unit/spec/services/task/dialer.ts +112 -198
- package/test/unit/spec/services/task/digital/Digital.ts +105 -0
- package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +473 -0
- package/test/unit/spec/services/task/state-machine/guards.ts +288 -0
- package/test/unit/spec/services/task/state-machine/types.ts +18 -0
- package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +147 -0
- package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
- package/test/unit/spec/services/task/voice/Voice.ts +587 -0
- package/test/unit/spec/services/task/voice/WebRTC.ts +242 -0
- package/umd/contact-center.min.js +2 -2
- package/umd/contact-center.min.js.map +1 -1
- package/dist/services/task/index.js +0 -1525
- package/dist/services/task/index.js.map +0 -1
- package/dist/types/services/task/index.d.ts +0 -650
- package/src/services/task/index.ts +0 -1801
- package/test/unit/spec/services/task/index.ts +0 -2184
|
@@ -0,0 +1,696 @@
|
|
|
1
|
+
# Core Service - Architecture
|
|
2
|
+
|
|
3
|
+
> **Purpose**: Technical documentation for core infrastructure components.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## WebSocketManager
|
|
8
|
+
|
|
9
|
+
### Connection Sequence
|
|
10
|
+
|
|
11
|
+
```mermaid
|
|
12
|
+
sequenceDiagram
|
|
13
|
+
participant cc
|
|
14
|
+
participant WSM as WebSocketManager
|
|
15
|
+
participant WS as WebSocket
|
|
16
|
+
participant BE as ccBackend
|
|
17
|
+
|
|
18
|
+
cc->>WSM: initWebSocket(config)
|
|
19
|
+
Note right of cc: config is SubscribeRequest passed as {body: config}
|
|
20
|
+
|
|
21
|
+
WSM->>BE: POST /subscribe (get WS URL)
|
|
22
|
+
BE-->>WSM: {webSocketUrl, subscriptionId}
|
|
23
|
+
WSM->>WS: new WebSocket(url)
|
|
24
|
+
WS->>BE: Connect
|
|
25
|
+
BE-->>WS: Welcome event
|
|
26
|
+
WS-->>WSM: onmessage(Welcome)
|
|
27
|
+
WSM-->>cc: Resolve with WelcomeEvent
|
|
28
|
+
|
|
29
|
+
loop Message handling
|
|
30
|
+
BE-->>WS: Events
|
|
31
|
+
WS-->>WSM: onmessage
|
|
32
|
+
WSM->>WSM: emit('message', event)
|
|
33
|
+
end
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Config reference:
|
|
37
|
+
|
|
38
|
+
- `initWebSocket(options: {body: SubscribeRequest})`: [src/services/core/websocket/WebSocketManager.ts](../websocket/WebSocketManager.ts)
|
|
39
|
+
- `SubscribeRequest` type: [src/types.ts](../../../types.ts)
|
|
40
|
+
|
|
41
|
+
### End-to-End Core Flow (Complete Picture)
|
|
42
|
+
|
|
43
|
+
This diagram shows the complete lifecycle from component instantiation through normal operation, including when and how each layer is created, engaged, and their method invocation sequences.
|
|
44
|
+
|
|
45
|
+
```mermaid
|
|
46
|
+
sequenceDiagram
|
|
47
|
+
autonumber
|
|
48
|
+
participant CC as CC Plugin
|
|
49
|
+
participant Svc as Services
|
|
50
|
+
participant AQM as AqmReqs
|
|
51
|
+
participant WSM as WebSocketManager
|
|
52
|
+
participant WS as WebSocket
|
|
53
|
+
participant CS as ConnectionService
|
|
54
|
+
participant KW as Keepalive Worker
|
|
55
|
+
participant WR as WebexRequest
|
|
56
|
+
participant BE as CC Backend
|
|
57
|
+
|
|
58
|
+
Note over CC,BE: INSTANTIATION: Services constructor (src/services/index.ts:39-51)
|
|
59
|
+
CC->>Svc: new Services({webex, connectionConfig})
|
|
60
|
+
Svc->>WSM: new WebSocketManager({webex})
|
|
61
|
+
Note right of WSM: Creates keepalive worker via Blob + URL.createObjectURL<br/>(Worker created but NOT started yet)
|
|
62
|
+
Svc->>AQM: new AqmReqs(webSocketManager)
|
|
63
|
+
Svc->>CS: new ConnectionService({webSocketManager, subscribeRequest})
|
|
64
|
+
CS->>CS: setupEventListeners()
|
|
65
|
+
CS->>WSM: webSocketManager.on('message', onPing)
|
|
66
|
+
CS->>WSM: webSocketManager.on('socketClose', onSocketClose)
|
|
67
|
+
CC->>WSM: webSocketManager.on('message', handleWebSocketMessage)
|
|
68
|
+
|
|
69
|
+
Note over CC,BE: REGISTRATION: cc.register() (src/cc.ts:457-486)
|
|
70
|
+
CC->>CC: setupEventListeners()
|
|
71
|
+
CC->>CS: connectionService.on('connectionLost', handleConnectionLost)
|
|
72
|
+
CC->>CC: connectWebsocket()
|
|
73
|
+
CC->>WSM: initWebSocket({body: subscribeRequest})
|
|
74
|
+
|
|
75
|
+
Note over WSM,BE: WebSocketManager.initWebSocket() (WebSocketManager.ts:47-61)
|
|
76
|
+
WSM->>BE: POST /v1/notification/subscribe
|
|
77
|
+
BE-->>WSM: {webSocketUrl, subscriptionId}
|
|
78
|
+
WSM->>WSM: connect()
|
|
79
|
+
WSM->>WS: new WebSocket(url)
|
|
80
|
+
|
|
81
|
+
Note over WSM,KW: websocket.onopen handler (WebSocketManager.ts:107-133)
|
|
82
|
+
WSM->>WS: send({keepalive: 'true'}) - initial ping
|
|
83
|
+
WSM->>WSM: Setup keepaliveWorker.onmessage handler
|
|
84
|
+
WSM->>KW: postMessage({type: 'start', intervalDuration: 4000, closeSocketTimeout: 5000})
|
|
85
|
+
Note right of KW: ⚡ Worker starts periodic interval<br/>⚡ Begins monitoring navigator.onLine
|
|
86
|
+
WSM->>WS: Setup handlers such as websocket.onMessage, websocket.onClose
|
|
87
|
+
|
|
88
|
+
BE-->>WS: WELCOME event
|
|
89
|
+
WS-->>WSM: WELCOME event
|
|
90
|
+
WSM->>CS: emit('message', welcomeEvent)
|
|
91
|
+
CS->>CS: onPing(welcomeEvent)
|
|
92
|
+
Note right of CS: setTimeout(handleConnectionLost, 8000)<br/>setTimeout(handleRestoreFailed, 50000)
|
|
93
|
+
WSM-->>CC: Resolve with WelcomeResponse
|
|
94
|
+
|
|
95
|
+
Note over CC,BE: NORMAL OPERATION: API calls with websocket bind pattern
|
|
96
|
+
CC->>AQM: req({url, method, body, bind})
|
|
97
|
+
AQM->>WR: request({service, resource, method, body})
|
|
98
|
+
WR->>BE: HTTP request
|
|
99
|
+
BE-->>WR: HTTP response (ack/tracking)
|
|
100
|
+
WR-->>AQM: IHttpResponse
|
|
101
|
+
AQM->>AQM: wait for matching websocket bind event
|
|
102
|
+
BE-->>WSM: async notification event
|
|
103
|
+
WSM->>CS: emit('message', event)
|
|
104
|
+
CS->>CS: onPing() - clearTimeout & reset timers
|
|
105
|
+
WSM->>AQM: message event matches bind
|
|
106
|
+
AQM-->>CC: resolve promise with result
|
|
107
|
+
|
|
108
|
+
Note over KW,BE: KEEPALIVE: Periodic pings every 4s (keepalive.worker.js)
|
|
109
|
+
loop Every 4 seconds
|
|
110
|
+
KW->>KW: checkNetworkStatus()
|
|
111
|
+
KW-->>WSM: postMessage({type: 'keepalive', onlineStatus})
|
|
112
|
+
WSM->>BE: send({keepalive: 'true'})
|
|
113
|
+
BE-->>WSM: {keepalive: 'true'}
|
|
114
|
+
WSM->>CS: emit('message', {keepalive: 'true'})
|
|
115
|
+
CS->>CS: onPing() - reset timers
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
Note over KW,CS: OFFLINE DETECTION: Network goes offline
|
|
119
|
+
alt Browser goes offline (navigator.onLine = false)
|
|
120
|
+
KW->>KW: Start closeSocketTimeout timer (5s)
|
|
121
|
+
alt Socket doesn't close within 5s
|
|
122
|
+
KW-->>WSM: postMessage({type: 'closeSocket'})
|
|
123
|
+
WSM->>WSM: websocket.close()
|
|
124
|
+
WSM->>KW: postMessage({type: 'terminate'})
|
|
125
|
+
WSM->>CS: emit('socketClose')
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
Note over CS: onSocketClose() (connection-service.ts:135-141)
|
|
129
|
+
CS->>CS: clearTimerOnRestoreFailed()
|
|
130
|
+
CS->>CS: setInterval(handleSocketClose, 5000)
|
|
131
|
+
|
|
132
|
+
loop Every 5s (CONNECTIVITY_CHECK_INTERVAL)
|
|
133
|
+
CS->>CS: handleSocketClose() - check navigator.onLine
|
|
134
|
+
alt Browser back online
|
|
135
|
+
CS->>WSM: initWebSocket({body: subscribeRequest})
|
|
136
|
+
WSM->>BE: POST /subscribe + WebSocket reconnect
|
|
137
|
+
WSM->>KW: postMessage({type: 'start', ...})
|
|
138
|
+
BE-->>WSM: WELCOME event
|
|
139
|
+
WSM->>CS: emit('message', welcomeEvent)
|
|
140
|
+
CS->>CS: onPing() detects isSocketReconnected=true
|
|
141
|
+
CS->>CS: dispatchConnectionEvent(socketReconnected=true)
|
|
142
|
+
CS->>CC: emit('connectionLost', {isSocketReconnected: true})
|
|
143
|
+
CS->>CS: clearInterval(reconnectInterval)
|
|
144
|
+
else Still offline
|
|
145
|
+
CS->>CS: Wait for next interval
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
Note over CS,CC: CONNECTION LOST: No messages received within 8s
|
|
151
|
+
alt reconnectingTimer fires (WS_DISCONNECT_ALLOWED = 8s)
|
|
152
|
+
CS->>CS: handleConnectionLost()
|
|
153
|
+
CS->>CS: isConnectionLost = true
|
|
154
|
+
CS->>CC: emit('connectionLost', {isConnectionLost: true})
|
|
155
|
+
|
|
156
|
+
alt restoreTimer fires (LOST_CONNECTION_RECOVERY_TIMEOUT = 50s)
|
|
157
|
+
CS->>CS: handleRestoreFailed()
|
|
158
|
+
CS->>CS: isRestoreFailed = true
|
|
159
|
+
CS->>WSM: shouldReconnect = false
|
|
160
|
+
CS->>CS: clearInterval(reconnectInterval)
|
|
161
|
+
CS->>CC: emit('connectionLost', {isRestoreFailed: true})
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Component Instantiation Order
|
|
167
|
+
|
|
168
|
+
1. **WebSocketManager** ([src/services/core/websocket/WebSocketManager.ts:32-45](../websocket/WebSocketManager.ts#L32-L45)) - Creates keepalive worker (not started)
|
|
169
|
+
2. **AqmReqs** ([src/services/core/aqm-reqs.ts](../aqm-reqs.ts)) - Initialized with WebSocketManager reference
|
|
170
|
+
3. **Service layers** (config, agent, contact, dialer) - Created with AqmReqs reference
|
|
171
|
+
4. **ConnectionService** ([src/services/core/websocket/connection-service.ts:30-41](../websocket/connection-service.ts#L30-L41)) - Wires event listeners immediately
|
|
172
|
+
|
|
173
|
+
#### Key Method Invocations
|
|
174
|
+
|
|
175
|
+
**WebSocketManager.initWebSocket** (WebSocketManager.ts:47-61):
|
|
176
|
+
|
|
177
|
+
- `register()` → POST /subscribe → get WebSocket URL
|
|
178
|
+
- `connect()` → Create WebSocket → Setup handlers (onopen, onmessage, onclose, onerror)
|
|
179
|
+
|
|
180
|
+
**websocket.onopen** (WebSocketManager.ts:107-133):
|
|
181
|
+
|
|
182
|
+
- Send initial keepalive ping
|
|
183
|
+
- Wire `keepaliveWorker.onmessage` handler
|
|
184
|
+
- **Start keepalive worker** via `postMessage({type: 'start', intervalDuration: 4000, closeSocketTimeout: 5000})`
|
|
185
|
+
|
|
186
|
+
**ConnectionService.onPing** (connection-service.ts:92-118) - Called on every message:
|
|
187
|
+
|
|
188
|
+
- Clear existing timers (reconnectingTimer, restoreTimer)
|
|
189
|
+
- Handle connection recovery state transitions
|
|
190
|
+
- Schedule new timers: `setTimeout(handleConnectionLost, 8000)`, `setTimeout(handleRestoreFailed, 50000)`
|
|
191
|
+
|
|
192
|
+
**ConnectionService.onSocketClose** (connection-service.ts:135-141):
|
|
193
|
+
|
|
194
|
+
- Clear reconnect interval
|
|
195
|
+
- Start periodic reconnection attempts: `setInterval(handleSocketClose, 5000)`
|
|
196
|
+
|
|
197
|
+
**ConnectionService.handleSocketClose** (connection-service.ts:120-133):
|
|
198
|
+
|
|
199
|
+
- Check `navigator.onLine`
|
|
200
|
+
- If online: reinitialize WebSocket and set `isSocketReconnected = true`
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## ConnectionService
|
|
205
|
+
|
|
206
|
+
Monitors WebSocket health via keepalive messages, detects disconnections, triggers reconnection attempts, and emits connection state events to the application layer. Extends `EventEmitter`.
|
|
207
|
+
|
|
208
|
+
### Constructor
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
constructor(options: ConnectionServiceOptions)
|
|
212
|
+
|
|
213
|
+
type ConnectionServiceOptions = {
|
|
214
|
+
webSocketManager: WebSocketManager;
|
|
215
|
+
subscribeRequest: SubscribeRequest;
|
|
216
|
+
};
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Type References
|
|
220
|
+
|
|
221
|
+
- `SubscribeRequest`: [src/types.ts](../../../types.ts)
|
|
222
|
+
- `ConnectionProp`: [src/services/core/websocket/types.ts](../websocket/types.ts)
|
|
223
|
+
- `ConnectionServiceOptions`: [src/services/core/websocket/types.ts](../websocket/types.ts)
|
|
224
|
+
- `ConnectionLostDetails`: [src/services/core/websocket/types.ts](../websocket/types.ts)
|
|
225
|
+
|
|
226
|
+
The constructor wires up two listeners on `WebSocketManager`:
|
|
227
|
+
|
|
228
|
+
- `'message'` → `onPing` (resets disconnect/restore timers on every incoming message)
|
|
229
|
+
- `'socketClose'` → `onSocketClose` (starts the reconnection interval)
|
|
230
|
+
|
|
231
|
+
Code reference (`src/services/core/websocket/connection-service.ts`):
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
private setupEventListeners() {
|
|
235
|
+
this.webSocketManager.on('message', this.onPing.bind(this));
|
|
236
|
+
this.webSocketManager.on('socketClose', this.onSocketClose.bind(this));
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Key Constants
|
|
241
|
+
|
|
242
|
+
| Constant | Value | Purpose |
|
|
243
|
+
| ---------------------------------- | --------- | -------------------------------------------- |
|
|
244
|
+
| `LOST_CONNECTION_RECOVERY_TIMEOUT` | 50 000 ms | Max wait before declaring restore failed |
|
|
245
|
+
| `WS_DISCONNECT_ALLOWED` | 8 000 ms | Grace period before flagging connection lost |
|
|
246
|
+
| `CONNECTIVITY_CHECK_INTERVAL` | 5 000 ms | Interval between reconnection attempts |
|
|
247
|
+
|
|
248
|
+
### Properties
|
|
249
|
+
|
|
250
|
+
| Property | Type | Description |
|
|
251
|
+
| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------ |
|
|
252
|
+
| `connectionProp` | `ConnectionProp` | Runtime configuration object (for example `lostConnectionRecoveryTimeout`) used by reconnect/restore timers. |
|
|
253
|
+
| `wsDisconnectAllowed` | `number` | Timeout window before `handleConnectionLost` is triggered when no ping/message is received. |
|
|
254
|
+
| `reconnectingTimer` | `ReturnType<typeof setTimeout>` | Per-message timeout that schedules lost-connection detection. |
|
|
255
|
+
| `restoreTimer` | `ReturnType<typeof setTimeout>` | Timeout that marks restore failure if recovery does not complete in time. |
|
|
256
|
+
| `reconnectInterval` | `ReturnType<typeof setInterval>` | Periodic retry loop started after socket close to attempt reconnection. |
|
|
257
|
+
| `isConnectionLost` | `boolean` | Indicates that the connection has been marked as lost. |
|
|
258
|
+
| `isRestoreFailed` | `boolean` | Indicates that recovery has exceeded the configured restore timeout. |
|
|
259
|
+
| `isSocketReconnected` | `boolean` | Indicates that a reconnect attempt succeeded and socket is back. |
|
|
260
|
+
| `isKeepAlive` | `boolean` | Tracks whether the latest incoming message is a keepalive signal. |
|
|
261
|
+
| `webSocketManager` | `WebSocketManager` | Core WebSocket dependency used for event subscription and re-initialization. |
|
|
262
|
+
| `subscribeRequest` | `SubscribeRequest` | Cached subscribe payload reused during reconnect (`initWebSocket`). |
|
|
263
|
+
|
|
264
|
+
### Methods
|
|
265
|
+
|
|
266
|
+
The only public method is `setConnectionProp`. All other methods are private implementation details and are documented for architectural understanding.
|
|
267
|
+
|
|
268
|
+
1. `setConnectionProp(prop: ConnectionProp): void` (public)
|
|
269
|
+
|
|
270
|
+
- **Purpose**: Updates connection-level runtime settings used by timers (mainly recovery timeout behavior).
|
|
271
|
+
- **Params**: `prop` - new connection config object.
|
|
272
|
+
- **Returns**: `void`.
|
|
273
|
+
- **Usage**: Called by higher layers when timeout behavior must be tuned after initialization.
|
|
274
|
+
|
|
275
|
+
2. `setupEventListeners(): void` (private)
|
|
276
|
+
|
|
277
|
+
- **Purpose**: Wires `WebSocketManager` events to internal handlers (`'message' -> onPing`, `'socketClose' -> onSocketClose`).
|
|
278
|
+
- **Params**: none.
|
|
279
|
+
- **Returns**: `void`.
|
|
280
|
+
- **Usage**: Invoked from the constructor once, during `ConnectionService` setup.
|
|
281
|
+
|
|
282
|
+
3. `onPing(event: any): void` (private)
|
|
283
|
+
|
|
284
|
+
- **Purpose**: Handles every incoming socket message, resets timers, updates keepalive/recovery flags, and emits recovery events when state changes.
|
|
285
|
+
- **Params**: `event` - raw message payload (JSON string) received from the socket event stream.
|
|
286
|
+
- **Returns**: `void`.
|
|
287
|
+
- **Usage**: Triggered automatically by the `'message'` listener.
|
|
288
|
+
|
|
289
|
+
4. `onSocketClose(): void` (private)
|
|
290
|
+
|
|
291
|
+
- **Purpose**: Starts the reconnect interval when socket close is detected.
|
|
292
|
+
- **Params**: none.
|
|
293
|
+
- **Returns**: `void`.
|
|
294
|
+
- **Usage**: Triggered automatically by the `'socketClose'` listener.
|
|
295
|
+
|
|
296
|
+
5. `handleSocketClose(): Promise<void>` (private)
|
|
297
|
+
|
|
298
|
+
- **Purpose**: Performs one reconnect attempt; if browser is online, reinitializes WebSocket and marks socket as reconnected.
|
|
299
|
+
- **Params**: none.
|
|
300
|
+
- **Returns**: `Promise<void>` (rejects when browser is offline).
|
|
301
|
+
- **Usage**: Called repeatedly from `onSocketClose` interval loop.
|
|
302
|
+
|
|
303
|
+
6. `handleConnectionLost(): void` (private)
|
|
304
|
+
|
|
305
|
+
- **Purpose**: Marks the connection as lost and dispatches a connection status event.
|
|
306
|
+
- **Params**: none.
|
|
307
|
+
- **Returns**: `void`.
|
|
308
|
+
- **Usage**: Scheduled by `onPing` via `reconnectingTimer` after inactivity.
|
|
309
|
+
|
|
310
|
+
7. `handleRestoreFailed(): Promise<void>` (private)
|
|
311
|
+
|
|
312
|
+
- **Purpose**: Marks restore as failed, disables reconnect, emits failure state, and clears reconnect interval.
|
|
313
|
+
- **Params**: none.
|
|
314
|
+
- **Returns**: `Promise<void>`.
|
|
315
|
+
- **Usage**: Scheduled by `onPing` via `restoreTimer`.
|
|
316
|
+
|
|
317
|
+
8. `clearTimerOnRestoreFailed(): Promise<void>` (private)
|
|
318
|
+
|
|
319
|
+
- **Purpose**: Stops active reconnect interval to avoid duplicate retries.
|
|
320
|
+
- **Params**: none.
|
|
321
|
+
- **Returns**: `Promise<void>`.
|
|
322
|
+
- **Usage**: Called from reconnect/failure paths whenever interval cleanup is needed.
|
|
323
|
+
|
|
324
|
+
9. `updateConnectionData(): void` (private)
|
|
325
|
+
|
|
326
|
+
- **Purpose**: Resets transient connection flags (`isConnectionLost`, `isRestoreFailed`, `isSocketReconnected`) after recovery.
|
|
327
|
+
- **Params**: none.
|
|
328
|
+
- **Returns**: `void`.
|
|
329
|
+
- **Usage**: Called inside `onPing` before dispatching recovered state.
|
|
330
|
+
|
|
331
|
+
10. `dispatchConnectionEvent(socketReconnected = false): void` (private)
|
|
332
|
+
- **Purpose**: Builds `ConnectionLostDetails`, forwards it to `WebSocketManager.handleConnectionLost`, and emits `'connectionLost'`.
|
|
333
|
+
- **Params**: `socketReconnected` - optional override used when reconnect is explicitly detected.
|
|
334
|
+
- **Returns**: `void`.
|
|
335
|
+
- **Usage**: Used by lost/recovered/restore-failed paths to publish uniform connection state.
|
|
336
|
+
|
|
337
|
+
### Reconnection Flow
|
|
338
|
+
|
|
339
|
+
```mermaid
|
|
340
|
+
sequenceDiagram
|
|
341
|
+
participant App as cc
|
|
342
|
+
participant CS as ConnectionService
|
|
343
|
+
participant WSM as WebSocketManager
|
|
344
|
+
|
|
345
|
+
Note over WSM: WebSocket closes
|
|
346
|
+
WSM->>CS: emit('socketClose')
|
|
347
|
+
CS->>CS: onSocketClose()
|
|
348
|
+
CS->>CS: clearTimerOnRestoreFailed()
|
|
349
|
+
CS->>CS: Start reconnectInterval every 5 s
|
|
350
|
+
|
|
351
|
+
loop Every CONNECTIVITY_CHECK_INTERVAL
|
|
352
|
+
CS->>CS: handleSocketClose()
|
|
353
|
+
alt Browser online
|
|
354
|
+
CS->>WSM: initWebSocket({body: subscribeRequest})
|
|
355
|
+
CS->>CS: clearTimerOnRestoreFailed()
|
|
356
|
+
CS->>CS: isSocketReconnected = true
|
|
357
|
+
else Browser offline
|
|
358
|
+
CS->>CS: Throw error, retry next interval
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
Note over CS: Next keepalive ping arrives
|
|
363
|
+
CS->>CS: dispatchConnectionEvent(socketReconnected=true)
|
|
364
|
+
CS->>App: emit('connectionLost', details)
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
### Events
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
type ConnectionLostDetails = {
|
|
371
|
+
isConnectionLost: boolean;
|
|
372
|
+
isRestoreFailed: boolean;
|
|
373
|
+
isSocketReconnected: boolean;
|
|
374
|
+
isKeepAlive: boolean;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
connectionService.on('connectionLost', (details: ConnectionLostDetails) => {
|
|
378
|
+
if (details.isConnectionLost) {
|
|
379
|
+
// Connection lost — waiting for recovery
|
|
380
|
+
} else if (details.isRestoreFailed) {
|
|
381
|
+
// Recovery timeout (50 s) exceeded
|
|
382
|
+
} else if (details.isSocketReconnected) {
|
|
383
|
+
// Socket successfully reconnected
|
|
384
|
+
}
|
|
385
|
+
});
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
---
|
|
389
|
+
|
|
390
|
+
## AqmReqs Pattern
|
|
391
|
+
|
|
392
|
+
`AqmReqs` coordinates the Contact Center request lifecycle by sending an HTTP request and then waiting for the matching WebSocket response bind. This gives service methods a single promise-based API that resolves only when the backend confirms completion.
|
|
393
|
+
|
|
394
|
+
```typescript
|
|
395
|
+
import AqmReqs from '../aqm-reqs';
|
|
396
|
+
|
|
397
|
+
const aqmReqs = new AqmReqs();
|
|
398
|
+
|
|
399
|
+
const response = await aqmReqs.req({
|
|
400
|
+
url: '/v1/agent/state',
|
|
401
|
+
method: 'POST',
|
|
402
|
+
body: {agentId: 'agent-123', state: 'AVAILABLE'},
|
|
403
|
+
bind: {
|
|
404
|
+
eventType: 'agent-state-change',
|
|
405
|
+
matcher: (event) => event.agentId === 'agent-123',
|
|
406
|
+
},
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// `response` resolves after matching bind event arrives
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Request/Response Flow
|
|
413
|
+
|
|
414
|
+
```mermaid
|
|
415
|
+
flowchart TD
|
|
416
|
+
A[Service method called] --> B[AqmReqs.req]
|
|
417
|
+
B --> C[Build request config]
|
|
418
|
+
C --> D[Send HTTP request via WebexRequest.request]
|
|
419
|
+
D --> E[Wait for matching WebSocket notification]
|
|
420
|
+
E --> F{Success bind matched?}
|
|
421
|
+
F -->|Yes| G[Resolve promise]
|
|
422
|
+
F -->|No| H[Reject with error]
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## WebexRequest
|
|
426
|
+
|
|
427
|
+
### Singleton Pattern
|
|
428
|
+
|
|
429
|
+
```typescript
|
|
430
|
+
class WebexRequest {
|
|
431
|
+
private static instance: WebexRequest;
|
|
432
|
+
private webex: WebexSDK;
|
|
433
|
+
|
|
434
|
+
private constructor(options: {webex: WebexSDK}) {}
|
|
435
|
+
|
|
436
|
+
public static getInstance(options?: {webex: WebexSDK}): WebexRequest {
|
|
437
|
+
if (!WebexRequest.instance && options && options.webex) {
|
|
438
|
+
WebexRequest.instance = new WebexRequest(options);
|
|
439
|
+
}
|
|
440
|
+
return WebexRequest.instance;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
public async request(options: {
|
|
444
|
+
service: string; // Service key used by `webex.request` to resolve the target host
|
|
445
|
+
resource: string; // API path within the service (for example: v1/notification/subscribe)
|
|
446
|
+
method: HTTP_METHODS;
|
|
447
|
+
body?: RequestBody;
|
|
448
|
+
}): Promise<IHttpResponse>;
|
|
449
|
+
|
|
450
|
+
public async uploadLogs(metaData: LogsMetaData = {}): Promise<UploadLogsResponse>;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
export default WebexRequest;
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
Type references:
|
|
457
|
+
|
|
458
|
+
- `WebexSDK`: [src/types.ts](../../../types.ts)
|
|
459
|
+
- `HTTP_METHODS`: [src/types.ts](../../../types.ts)
|
|
460
|
+
- `RequestBody`: [src/types.ts](../../../types.ts)
|
|
461
|
+
- `IHttpResponse`: [src/types.ts](../../../types.ts)
|
|
462
|
+
- `LogsMetaData`: [src/types.ts](../../../types.ts)
|
|
463
|
+
- `UploadLogsResponse`: [src/types.ts](../../../types.ts)
|
|
464
|
+
|
|
465
|
+
### Request Flow
|
|
466
|
+
|
|
467
|
+
```mermaid
|
|
468
|
+
sequenceDiagram
|
|
469
|
+
participant Svc as Service
|
|
470
|
+
participant WR as WebexRequest
|
|
471
|
+
participant WX as webex.request
|
|
472
|
+
participant API as Backend
|
|
473
|
+
|
|
474
|
+
Svc->>WR: request(config)
|
|
475
|
+
WR->>WR: Build request options
|
|
476
|
+
WR->>WX: webex.request(options)
|
|
477
|
+
WX->>API: HTTP request
|
|
478
|
+
API-->>WX: Response
|
|
479
|
+
WX-->>WR: {statusCode, body, headers}
|
|
480
|
+
WR-->>Svc: Response
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
---
|
|
484
|
+
|
|
485
|
+
## Keepalive Worker
|
|
486
|
+
|
|
487
|
+
### Purpose
|
|
488
|
+
|
|
489
|
+
Maintains WebSocket connection with periodic pings and monitors network status. Has a dual role:
|
|
490
|
+
|
|
491
|
+
1. **Keepalive**: Sends periodic messages to detect connection issues
|
|
492
|
+
2. **Network Monitoring**: Tracks online/offline transitions and forces socket closure if offline too long
|
|
493
|
+
|
|
494
|
+
### Implementation
|
|
495
|
+
|
|
496
|
+
> **Source**: [`keepalive.worker.js`](../websocket/keepalive.worker.js) — a Web Worker script embedded as a string and loaded via `Blob` + `URL.createObjectURL` in `WebSocketManager`.
|
|
497
|
+
|
|
498
|
+
#### Worker Message Contract
|
|
499
|
+
|
|
500
|
+
**Inbound (main thread → worker):**
|
|
501
|
+
|
|
502
|
+
| Message Type | Fields | Effect |
|
|
503
|
+
| ------------ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------- |
|
|
504
|
+
| `start` | `intervalDuration` (default 4000ms), `isSocketClosed`, `closeSocketTimeout` (default 5000ms) | Starts periodic keepalive interval and resets offline handler |
|
|
505
|
+
| `terminate` | — | Clears the keepalive interval and resets offline handler |
|
|
506
|
+
|
|
507
|
+
**Outbound (worker → main thread):**
|
|
508
|
+
|
|
509
|
+
| Message Type | Fields | Trigger |
|
|
510
|
+
| ------------- | ----------------------- | ------------------------------------------------------------------------------------ |
|
|
511
|
+
| `keepalive` | `onlineStatus: boolean` | Every `intervalDuration` ms, and on browser online/offline events |
|
|
512
|
+
| `closeSocket` | — | When offline for longer than `closeSocketTimeout` and socket hasn't closed naturally |
|
|
513
|
+
|
|
514
|
+
#### Key Behavior
|
|
515
|
+
|
|
516
|
+
1. **Periodic ping**: Every `intervalDuration` ms, calls `checkNetworkStatus()` which posts a `keepalive` message with the current `navigator.onLine` status
|
|
517
|
+
2. **Offline detection**: When network goes offline, starts a `closeSocketTimeout` timer. If the socket hasn't closed naturally by then, posts `closeSocket` to force closure
|
|
518
|
+
3. **Online/offline listeners**: The worker also listens to browser `online`/`offline` events for immediate network change detection
|
|
519
|
+
|
|
520
|
+
---
|
|
521
|
+
|
|
522
|
+
## Error Handling
|
|
523
|
+
|
|
524
|
+
This section documents shared error helpers in `Utils.ts` that normalize errors, enrich them with context, and ensure consistent logging/upload behavior across services.
|
|
525
|
+
|
|
526
|
+
### Error Types
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
// Msg - Generic message interface (GlobalTypes.ts:7-16)
|
|
530
|
+
export type Msg<T = any> = {
|
|
531
|
+
type: string;
|
|
532
|
+
orgId: string;
|
|
533
|
+
trackingId: string;
|
|
534
|
+
data: T;
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// Failure - Backend error structure (GlobalTypes.ts:23-34)
|
|
538
|
+
// Built on Msg<T> with specific error data fields
|
|
539
|
+
export type Failure = Msg<{
|
|
540
|
+
agentId: string;
|
|
541
|
+
trackingId: string;
|
|
542
|
+
reasonCode: number;
|
|
543
|
+
orgId: string;
|
|
544
|
+
reason: string;
|
|
545
|
+
}>;
|
|
546
|
+
|
|
547
|
+
// AugmentedError - Extended Error with flexible data field (GlobalTypes.ts:59-61)
|
|
548
|
+
export interface AugmentedError extends Error {
|
|
549
|
+
data?: Record<string, any>;
|
|
550
|
+
}
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
### getErrorDetails Flow
|
|
554
|
+
|
|
555
|
+
```mermaid
|
|
556
|
+
flowchart TD
|
|
557
|
+
A[Error caught] --> B[Cast error.details to Failure]
|
|
558
|
+
B --> C[Extract reason from failure.data.reason]
|
|
559
|
+
C --> D{Is silentRelogin + AGENT_NOT_FOUND?}
|
|
560
|
+
D -->|Yes| E[Skip logging/upload]
|
|
561
|
+
D -->|No| F[Log error with LoggerProxy]
|
|
562
|
+
F --> G[Upload logs via WebexRequest]
|
|
563
|
+
G --> H[Check if stationLogin]
|
|
564
|
+
H -->|Yes| I[Get field-specific error data]
|
|
565
|
+
H -->|No| J[Use generic error]
|
|
566
|
+
I --> K[Create Error with data property]
|
|
567
|
+
J --> K
|
|
568
|
+
K --> L["Return {error, reason}"]
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### getErrorDetails
|
|
572
|
+
|
|
573
|
+
Use this helper for agent/config-style flows where backend failure payloads are transformed into a user-facing `Error` plus `reason`, with optional station-login field metadata.
|
|
574
|
+
|
|
575
|
+
```typescript
|
|
576
|
+
export const getErrorDetails = (error: any, methodName: string, moduleName: string) => {
|
|
577
|
+
let errData = {message: '', fieldName: ''};
|
|
578
|
+
|
|
579
|
+
const failure = error.details as Failure;
|
|
580
|
+
const reason = failure?.data?.reason ?? `Error while performing ${methodName}`;
|
|
581
|
+
|
|
582
|
+
// Log error (unless AGENT_NOT_FOUND in silentRelogin)
|
|
583
|
+
if (!(reason === 'AGENT_NOT_FOUND' && methodName === 'silentRelogin')) {
|
|
584
|
+
LoggerProxy.error(`${methodName} failed with reason: ${reason}`, {
|
|
585
|
+
module: moduleName,
|
|
586
|
+
method: methodName,
|
|
587
|
+
trackingId: failure?.trackingId,
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
// Upload logs
|
|
591
|
+
WebexRequest.getInstance().uploadLogs({
|
|
592
|
+
correlationId: failure?.trackingId,
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// For stationLogin, extract field-specific error data (message + fieldName)
|
|
597
|
+
if (methodName === 'stationLogin') {
|
|
598
|
+
errData = getStationLoginErrorData(failure, error.loginOption);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
const err = new Error(reason);
|
|
602
|
+
// @ts-ignore - custom property for backward compatibility
|
|
603
|
+
err.data = errData;
|
|
604
|
+
|
|
605
|
+
return {error: err, reason};
|
|
606
|
+
};
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
### generateTaskErrorObject
|
|
610
|
+
|
|
611
|
+
Use this helper for task/interaction flows where richer task error metadata (`errorType`, `errorData`, `reasonCode`, `trackingId`) is required on the returned `AugmentedError`.
|
|
612
|
+
|
|
613
|
+
```typescript
|
|
614
|
+
export const generateTaskErrorObject = (
|
|
615
|
+
error: any,
|
|
616
|
+
methodName: string,
|
|
617
|
+
moduleName: string
|
|
618
|
+
): AugmentedError => {
|
|
619
|
+
const trackingId = error?.details?.trackingId || error?.trackingId || '';
|
|
620
|
+
const errorMsg = error?.details?.msg;
|
|
621
|
+
|
|
622
|
+
const errorMessage = errorMsg?.errorMessage || error.message || 'Error';
|
|
623
|
+
const errorType = errorMsg?.errorType || error.name || 'Unknown Error';
|
|
624
|
+
const errorData = errorMsg?.errorData || '';
|
|
625
|
+
const reasonCode = errorMsg?.reasonCode || 0;
|
|
626
|
+
|
|
627
|
+
LoggerProxy.error(`${methodName} failed: ${errorMessage} (${errorType})`, {
|
|
628
|
+
module: moduleName,
|
|
629
|
+
method: methodName,
|
|
630
|
+
trackingId,
|
|
631
|
+
});
|
|
632
|
+
WebexRequest.getInstance().uploadLogs({correlationId: trackingId});
|
|
633
|
+
|
|
634
|
+
const reason = `${errorType}: ${errorMessage}${errorData ? ` (${errorData})` : ''}`;
|
|
635
|
+
const err: AugmentedError = new Error(reason);
|
|
636
|
+
err.data = {
|
|
637
|
+
message: errorMessage,
|
|
638
|
+
errorType,
|
|
639
|
+
errorData,
|
|
640
|
+
reasonCode,
|
|
641
|
+
trackingId,
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
return err;
|
|
645
|
+
};
|
|
646
|
+
```
|
|
647
|
+
|
|
648
|
+
### Usage Guidance
|
|
649
|
+
|
|
650
|
+
```typescript
|
|
651
|
+
// Use getErrorDetails for:
|
|
652
|
+
// - Agent service operations
|
|
653
|
+
// - Station login/logout flows
|
|
654
|
+
//
|
|
655
|
+
// Use generateTaskErrorObject for:
|
|
656
|
+
// - Task service operations
|
|
657
|
+
// - Interaction-related errors
|
|
658
|
+
```
|
|
659
|
+
|
|
660
|
+
---
|
|
661
|
+
|
|
662
|
+
## Troubleshooting
|
|
663
|
+
|
|
664
|
+
### Issue: WebSocket not connecting
|
|
665
|
+
|
|
666
|
+
**Cause**: Subscribe API failed or invalid URL
|
|
667
|
+
|
|
668
|
+
**Solution**: Check subscribe response and WebSocket URL
|
|
669
|
+
|
|
670
|
+
### Issue: Messages not received
|
|
671
|
+
|
|
672
|
+
**Cause**: Event listener not registered
|
|
673
|
+
|
|
674
|
+
**Solution**: Ensure `on('message', handler)` called before connect
|
|
675
|
+
|
|
676
|
+
### Issue: Connection drops frequently
|
|
677
|
+
|
|
678
|
+
**Cause**: Keepalive not enabled or network issues
|
|
679
|
+
|
|
680
|
+
**Solution**: Verify worker-driven keepalive is running after socket `onopen`, and check network/offline transitions
|
|
681
|
+
|
|
682
|
+
---
|
|
683
|
+
|
|
684
|
+
## Related Files
|
|
685
|
+
|
|
686
|
+
- [Root Orchestrator AGENTS.md](../../../../AGENTS.md) — Task routing, critical rules, cross-service patterns
|
|
687
|
+
- [Core AGENTS.md](./AGENTS.md) — Core service usage guide and modification patterns
|
|
688
|
+
- [WebSocketManager.ts](../websocket/WebSocketManager.ts) — WebSocket lifecycle, keepalive worker integration
|
|
689
|
+
- [ConnectionService.ts](../websocket/connection-service.ts) — Reconnection logic, connection state events
|
|
690
|
+
- [keepalive.worker.js](../websocket/keepalive.worker.js) — Web Worker for periodic keepalive and offline detection
|
|
691
|
+
- [WebexRequest.ts](../WebexRequest.ts) — Singleton HTTP request handler
|
|
692
|
+
- [Utils.ts](../Utils.ts) — `getErrorDetails` (line 88), `generateTaskErrorObject` (line 143), consult utilities
|
|
693
|
+
- [Err.ts](../Err.ts) — `Err.Message` and `Err.Details` error classes
|
|
694
|
+
- [aqm-reqs.ts](../aqm-reqs.ts) — AQM request/response pattern, WebSocket notification binding
|
|
695
|
+
- [GlobalTypes.ts](../GlobalTypes.ts) — `Msg`, `Failure`, `AugmentedError` type definitions
|
|
696
|
+
- [types.ts](../types.ts) — `Pending`, `Req`, `Conf`, `Res` types for AqmReqs
|