@azure/ai-voicelive 1.0.0-alpha.20251117.2

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 (212) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +358 -0
  3. package/dist/browser/auth/credentialHandler.d.ts +43 -0
  4. package/dist/browser/auth/credentialHandler.js +147 -0
  5. package/dist/browser/auth/credentialHandler.js.map +1 -0
  6. package/dist/browser/errors/connectionErrors.d.ts +68 -0
  7. package/dist/browser/errors/connectionErrors.js +136 -0
  8. package/dist/browser/errors/connectionErrors.js.map +1 -0
  9. package/dist/browser/errors/index.d.ts +2 -0
  10. package/dist/browser/errors/index.js +4 -0
  11. package/dist/browser/errors/index.js.map +1 -0
  12. package/dist/browser/handlers/sessionHandlers.d.ts +250 -0
  13. package/dist/browser/handlers/sessionHandlers.js +4 -0
  14. package/dist/browser/handlers/sessionHandlers.js.map +1 -0
  15. package/dist/browser/handlers/subscriptionManager.d.ts +54 -0
  16. package/dist/browser/handlers/subscriptionManager.js +250 -0
  17. package/dist/browser/handlers/subscriptionManager.js.map +1 -0
  18. package/dist/browser/index.d.ts +7 -0
  19. package/dist/browser/index.js +12 -0
  20. package/dist/browser/index.js.map +1 -0
  21. package/dist/browser/logger.d.ts +2 -0
  22. package/dist/browser/logger.js +5 -0
  23. package/dist/browser/logger.js.map +1 -0
  24. package/dist/browser/models/index.d.ts +2 -0
  25. package/dist/browser/models/index.js +4 -0
  26. package/dist/browser/models/index.js.map +1 -0
  27. package/dist/browser/models/models.d.ts +2154 -0
  28. package/dist/browser/models/models.js +2251 -0
  29. package/dist/browser/models/models.js.map +1 -0
  30. package/dist/browser/package.json +3 -0
  31. package/dist/browser/protocol/messageParser.d.ts +42 -0
  32. package/dist/browser/protocol/messageParser.js +150 -0
  33. package/dist/browser/protocol/messageParser.js.map +1 -0
  34. package/dist/browser/voiceLiveClient.d.ts +65 -0
  35. package/dist/browser/voiceLiveClient.js +81 -0
  36. package/dist/browser/voiceLiveClient.js.map +1 -0
  37. package/dist/browser/voiceLiveSession.d.ts +138 -0
  38. package/dist/browser/voiceLiveSession.js +429 -0
  39. package/dist/browser/voiceLiveSession.js.map +1 -0
  40. package/dist/browser/websocket/connectionManager.d.ts +88 -0
  41. package/dist/browser/websocket/connectionManager.js +183 -0
  42. package/dist/browser/websocket/connectionManager.js.map +1 -0
  43. package/dist/browser/websocket/websocketBrowser.d.ts +26 -0
  44. package/dist/browser/websocket/websocketBrowser.js +175 -0
  45. package/dist/browser/websocket/websocketBrowser.js.map +1 -0
  46. package/dist/browser/websocket/websocketFactory.d.ts +23 -0
  47. package/dist/browser/websocket/websocketFactory.js +80 -0
  48. package/dist/browser/websocket/websocketFactory.js.map +1 -0
  49. package/dist/browser/websocket/websocketLike.d.ts +78 -0
  50. package/dist/browser/websocket/websocketLike.js +13 -0
  51. package/dist/browser/websocket/websocketLike.js.map +1 -0
  52. package/dist/browser/websocket/websocketNode.d.ts +26 -0
  53. package/dist/browser/websocket/websocketNode.js +180 -0
  54. package/dist/browser/websocket/websocketNode.js.map +1 -0
  55. package/dist/commonjs/auth/credentialHandler.d.ts +43 -0
  56. package/dist/commonjs/auth/credentialHandler.js +151 -0
  57. package/dist/commonjs/auth/credentialHandler.js.map +1 -0
  58. package/dist/commonjs/errors/connectionErrors.d.ts +68 -0
  59. package/dist/commonjs/errors/connectionErrors.js +146 -0
  60. package/dist/commonjs/errors/connectionErrors.js.map +1 -0
  61. package/dist/commonjs/errors/index.d.ts +2 -0
  62. package/dist/commonjs/errors/index.js +7 -0
  63. package/dist/commonjs/errors/index.js.map +1 -0
  64. package/dist/commonjs/handlers/sessionHandlers.d.ts +250 -0
  65. package/dist/commonjs/handlers/sessionHandlers.js +5 -0
  66. package/dist/commonjs/handlers/sessionHandlers.js.map +1 -0
  67. package/dist/commonjs/handlers/subscriptionManager.d.ts +54 -0
  68. package/dist/commonjs/handlers/subscriptionManager.js +255 -0
  69. package/dist/commonjs/handlers/subscriptionManager.js.map +1 -0
  70. package/dist/commonjs/index.d.ts +7 -0
  71. package/dist/commonjs/index.js +45 -0
  72. package/dist/commonjs/index.js.map +1 -0
  73. package/dist/commonjs/logger.d.ts +2 -0
  74. package/dist/commonjs/logger.js +8 -0
  75. package/dist/commonjs/logger.js.map +1 -0
  76. package/dist/commonjs/models/index.d.ts +2 -0
  77. package/dist/commonjs/models/index.js +27 -0
  78. package/dist/commonjs/models/index.js.map +1 -0
  79. package/dist/commonjs/models/models.d.ts +2154 -0
  80. package/dist/commonjs/models/models.js +2463 -0
  81. package/dist/commonjs/models/models.js.map +1 -0
  82. package/dist/commonjs/package.json +3 -0
  83. package/dist/commonjs/protocol/messageParser.d.ts +42 -0
  84. package/dist/commonjs/protocol/messageParser.js +154 -0
  85. package/dist/commonjs/protocol/messageParser.js.map +1 -0
  86. package/dist/commonjs/tsdoc-metadata.json +11 -0
  87. package/dist/commonjs/voiceLiveClient.d.ts +65 -0
  88. package/dist/commonjs/voiceLiveClient.js +85 -0
  89. package/dist/commonjs/voiceLiveClient.js.map +1 -0
  90. package/dist/commonjs/voiceLiveSession.d.ts +138 -0
  91. package/dist/commonjs/voiceLiveSession.js +433 -0
  92. package/dist/commonjs/voiceLiveSession.js.map +1 -0
  93. package/dist/commonjs/websocket/connectionManager.d.ts +88 -0
  94. package/dist/commonjs/websocket/connectionManager.js +187 -0
  95. package/dist/commonjs/websocket/connectionManager.js.map +1 -0
  96. package/dist/commonjs/websocket/websocketBrowser.d.ts +26 -0
  97. package/dist/commonjs/websocket/websocketBrowser.js +179 -0
  98. package/dist/commonjs/websocket/websocketBrowser.js.map +1 -0
  99. package/dist/commonjs/websocket/websocketFactory.d.ts +23 -0
  100. package/dist/commonjs/websocket/websocketFactory.js +86 -0
  101. package/dist/commonjs/websocket/websocketFactory.js.map +1 -0
  102. package/dist/commonjs/websocket/websocketLike.d.ts +78 -0
  103. package/dist/commonjs/websocket/websocketLike.js +16 -0
  104. package/dist/commonjs/websocket/websocketLike.js.map +1 -0
  105. package/dist/commonjs/websocket/websocketNode.d.ts +26 -0
  106. package/dist/commonjs/websocket/websocketNode.js +185 -0
  107. package/dist/commonjs/websocket/websocketNode.js.map +1 -0
  108. package/dist/esm/auth/credentialHandler.d.ts +43 -0
  109. package/dist/esm/auth/credentialHandler.js +147 -0
  110. package/dist/esm/auth/credentialHandler.js.map +1 -0
  111. package/dist/esm/errors/connectionErrors.d.ts +68 -0
  112. package/dist/esm/errors/connectionErrors.js +136 -0
  113. package/dist/esm/errors/connectionErrors.js.map +1 -0
  114. package/dist/esm/errors/index.d.ts +2 -0
  115. package/dist/esm/errors/index.js +4 -0
  116. package/dist/esm/errors/index.js.map +1 -0
  117. package/dist/esm/handlers/sessionHandlers.d.ts +250 -0
  118. package/dist/esm/handlers/sessionHandlers.js +4 -0
  119. package/dist/esm/handlers/sessionHandlers.js.map +1 -0
  120. package/dist/esm/handlers/subscriptionManager.d.ts +54 -0
  121. package/dist/esm/handlers/subscriptionManager.js +250 -0
  122. package/dist/esm/handlers/subscriptionManager.js.map +1 -0
  123. package/dist/esm/index.d.ts +7 -0
  124. package/dist/esm/index.js +12 -0
  125. package/dist/esm/index.js.map +1 -0
  126. package/dist/esm/logger.d.ts +2 -0
  127. package/dist/esm/logger.js +5 -0
  128. package/dist/esm/logger.js.map +1 -0
  129. package/dist/esm/models/index.d.ts +2 -0
  130. package/dist/esm/models/index.js +4 -0
  131. package/dist/esm/models/index.js.map +1 -0
  132. package/dist/esm/models/models.d.ts +2154 -0
  133. package/dist/esm/models/models.js +2251 -0
  134. package/dist/esm/models/models.js.map +1 -0
  135. package/dist/esm/package.json +3 -0
  136. package/dist/esm/protocol/messageParser.d.ts +42 -0
  137. package/dist/esm/protocol/messageParser.js +150 -0
  138. package/dist/esm/protocol/messageParser.js.map +1 -0
  139. package/dist/esm/voiceLiveClient.d.ts +65 -0
  140. package/dist/esm/voiceLiveClient.js +81 -0
  141. package/dist/esm/voiceLiveClient.js.map +1 -0
  142. package/dist/esm/voiceLiveSession.d.ts +138 -0
  143. package/dist/esm/voiceLiveSession.js +429 -0
  144. package/dist/esm/voiceLiveSession.js.map +1 -0
  145. package/dist/esm/websocket/connectionManager.d.ts +88 -0
  146. package/dist/esm/websocket/connectionManager.js +183 -0
  147. package/dist/esm/websocket/connectionManager.js.map +1 -0
  148. package/dist/esm/websocket/websocketBrowser.d.ts +26 -0
  149. package/dist/esm/websocket/websocketBrowser.js +175 -0
  150. package/dist/esm/websocket/websocketBrowser.js.map +1 -0
  151. package/dist/esm/websocket/websocketFactory.d.ts +23 -0
  152. package/dist/esm/websocket/websocketFactory.js +80 -0
  153. package/dist/esm/websocket/websocketFactory.js.map +1 -0
  154. package/dist/esm/websocket/websocketLike.d.ts +78 -0
  155. package/dist/esm/websocket/websocketLike.js +13 -0
  156. package/dist/esm/websocket/websocketLike.js.map +1 -0
  157. package/dist/esm/websocket/websocketNode.d.ts +26 -0
  158. package/dist/esm/websocket/websocketNode.js +180 -0
  159. package/dist/esm/websocket/websocketNode.js.map +1 -0
  160. package/dist/react-native/auth/credentialHandler.d.ts +43 -0
  161. package/dist/react-native/auth/credentialHandler.js +147 -0
  162. package/dist/react-native/auth/credentialHandler.js.map +1 -0
  163. package/dist/react-native/errors/connectionErrors.d.ts +68 -0
  164. package/dist/react-native/errors/connectionErrors.js +136 -0
  165. package/dist/react-native/errors/connectionErrors.js.map +1 -0
  166. package/dist/react-native/errors/index.d.ts +2 -0
  167. package/dist/react-native/errors/index.js +4 -0
  168. package/dist/react-native/errors/index.js.map +1 -0
  169. package/dist/react-native/handlers/sessionHandlers.d.ts +250 -0
  170. package/dist/react-native/handlers/sessionHandlers.js +4 -0
  171. package/dist/react-native/handlers/sessionHandlers.js.map +1 -0
  172. package/dist/react-native/handlers/subscriptionManager.d.ts +54 -0
  173. package/dist/react-native/handlers/subscriptionManager.js +250 -0
  174. package/dist/react-native/handlers/subscriptionManager.js.map +1 -0
  175. package/dist/react-native/index.d.ts +7 -0
  176. package/dist/react-native/index.js +12 -0
  177. package/dist/react-native/index.js.map +1 -0
  178. package/dist/react-native/logger.d.ts +2 -0
  179. package/dist/react-native/logger.js +5 -0
  180. package/dist/react-native/logger.js.map +1 -0
  181. package/dist/react-native/models/index.d.ts +2 -0
  182. package/dist/react-native/models/index.js +4 -0
  183. package/dist/react-native/models/index.js.map +1 -0
  184. package/dist/react-native/models/models.d.ts +2154 -0
  185. package/dist/react-native/models/models.js +2251 -0
  186. package/dist/react-native/models/models.js.map +1 -0
  187. package/dist/react-native/package.json +3 -0
  188. package/dist/react-native/protocol/messageParser.d.ts +42 -0
  189. package/dist/react-native/protocol/messageParser.js +150 -0
  190. package/dist/react-native/protocol/messageParser.js.map +1 -0
  191. package/dist/react-native/voiceLiveClient.d.ts +65 -0
  192. package/dist/react-native/voiceLiveClient.js +81 -0
  193. package/dist/react-native/voiceLiveClient.js.map +1 -0
  194. package/dist/react-native/voiceLiveSession.d.ts +138 -0
  195. package/dist/react-native/voiceLiveSession.js +429 -0
  196. package/dist/react-native/voiceLiveSession.js.map +1 -0
  197. package/dist/react-native/websocket/connectionManager.d.ts +88 -0
  198. package/dist/react-native/websocket/connectionManager.js +183 -0
  199. package/dist/react-native/websocket/connectionManager.js.map +1 -0
  200. package/dist/react-native/websocket/websocketBrowser.d.ts +26 -0
  201. package/dist/react-native/websocket/websocketBrowser.js +175 -0
  202. package/dist/react-native/websocket/websocketBrowser.js.map +1 -0
  203. package/dist/react-native/websocket/websocketFactory.d.ts +23 -0
  204. package/dist/react-native/websocket/websocketFactory.js +80 -0
  205. package/dist/react-native/websocket/websocketFactory.js.map +1 -0
  206. package/dist/react-native/websocket/websocketLike.d.ts +78 -0
  207. package/dist/react-native/websocket/websocketLike.js +13 -0
  208. package/dist/react-native/websocket/websocketLike.js.map +1 -0
  209. package/dist/react-native/websocket/websocketNode.d.ts +26 -0
  210. package/dist/react-native/websocket/websocketNode.js +180 -0
  211. package/dist/react-native/websocket/websocketNode.js.map +1 -0
  212. package/package.json +150 -0
@@ -0,0 +1,433 @@
1
+ "use strict";
2
+ // Copyright (c) Microsoft Corporation.
3
+ // Licensed under the MIT License.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.VoiceLiveSession = void 0;
6
+ const connectionManager_js_1 = require("./websocket/connectionManager.js");
7
+ const websocketFactory_js_1 = require("./websocket/websocketFactory.js");
8
+ const messageParser_js_1 = require("./protocol/messageParser.js");
9
+ const credentialHandler_js_1 = require("./auth/credentialHandler.js");
10
+ const index_js_1 = require("./errors/index.js");
11
+ const logger_js_1 = require("./logger.js");
12
+ const subscriptionManager_js_1 = require("./handlers/subscriptionManager.js");
13
+ /**
14
+ * Represents a WebSocket-based session for real-time voice communication with the Azure VoiceLive service.
15
+ *
16
+ * This class manages the connection, handles real-time communication, and provides access to all
17
+ * interactive features including audio streaming, conversation management, and avatar control.
18
+ */
19
+ class VoiceLiveSession {
20
+ _endpoint;
21
+ _credentialHandler;
22
+ _options;
23
+ _messageParser;
24
+ _model;
25
+ _connectionManager;
26
+ _sessionId;
27
+ _activeTurnId;
28
+ _disposed = false;
29
+ // Handler-based subscription management
30
+ _subscriptionManager;
31
+ /**
32
+ * Creates an instance of VoiceLiveSession.
33
+ *
34
+ * @param endpoint - The Voice Live service endpoint URL
35
+ * @param credential - Azure TokenCredential or KeyCredential for authentication
36
+ * @param apiVersion - API version to use for the Voice Live service
37
+ * @param model - The model name to use for the session
38
+ * @param options - Optional configuration for the session
39
+ */
40
+ constructor(endpoint, credential, apiVersion, model, options = {}) {
41
+ this._endpoint = this._normalizeEndpoint(endpoint);
42
+ this._credentialHandler = new credentialHandler_js_1.CredentialHandler(credential);
43
+ this._options = this._buildDefaultOptions(options);
44
+ this._messageParser = new messageParser_js_1.VoiceLiveMessageParser();
45
+ this._model = model;
46
+ // Initialize handler-based subscription management
47
+ this._subscriptionManager = new subscriptionManager_js_1.SubscriptionManager();
48
+ logger_js_1.logger.info("VoiceLiveSession created", {
49
+ endpoint: this._endpoint,
50
+ model: this._model,
51
+ apiVersion: apiVersion,
52
+ enableDebugLogging: this._options.enableDebugLogging,
53
+ });
54
+ }
55
+ /**
56
+ * Establishes connection to the Voice Live service with authentication.
57
+ */
58
+ async connect(options = {}) {
59
+ this._ensureNotDisposed();
60
+ if (this.isConnected) {
61
+ logger_js_1.logger.info("VoiceLiveSession already connected");
62
+ return;
63
+ }
64
+ try {
65
+ logger_js_1.logger.info("Connecting to Voice Live service", {
66
+ endpoint: this._endpoint,
67
+ model: this._model,
68
+ });
69
+ // Get WebSocket URL with authentication and model
70
+ const wsUrl = await this._credentialHandler.getWebSocketUrl(this._endpoint, "2025-10-01", // TODO: make this configurable through constructor
71
+ this._model);
72
+ const authHeaders = await this._credentialHandler.getAuthHeaders();
73
+ // Create connection manager
74
+ const websocketFactory = new websocketFactory_js_1.VoiceLiveWebSocketFactory();
75
+ this._connectionManager = new connectionManager_js_1.ConnectionManager(() => websocketFactory.create({
76
+ headers: { ...authHeaders },
77
+ connectionTimeoutInMs: this._options.connectionTimeoutInMs,
78
+ compression: true,
79
+ }), {
80
+ endpoint: wsUrl,
81
+ connectionTimeout: options.timeoutInMs || this._options.connectionTimeoutInMs,
82
+ });
83
+ // Setup connection event handlers
84
+ this._setupConnectionEventHandlers();
85
+ // Connect with proper error handling
86
+ await this._connectionManager.connect(options.abortSignal);
87
+ logger_js_1.logger.info("Successfully connected to Voice Live service");
88
+ }
89
+ catch (error) {
90
+ logger_js_1.logger.error("Failed to connect to Voice Live service", { error });
91
+ // Use error classification
92
+ if (error instanceof index_js_1.VoiceLiveConnectionError) {
93
+ throw error;
94
+ }
95
+ else {
96
+ throw (0, index_js_1.classifyConnectionError)(error);
97
+ }
98
+ }
99
+ }
100
+ /**
101
+ * Disconnects from the Voice Live service and cleans up resources.
102
+ */
103
+ async disconnect() {
104
+ if (!this._connectionManager) {
105
+ return;
106
+ }
107
+ logger_js_1.logger.info("Disconnecting from Voice Live service");
108
+ try {
109
+ await this._connectionManager.disconnect();
110
+ }
111
+ catch (error) {
112
+ logger_js_1.logger.error("Error during disconnect", { error });
113
+ }
114
+ finally {
115
+ this._connectionManager = undefined;
116
+ this._sessionId = undefined;
117
+ this._activeTurnId = undefined;
118
+ logger_js_1.logger.info("Disconnected from Voice Live service");
119
+ }
120
+ }
121
+ /**
122
+ * Subscribe to VoiceLive session events using strongly-typed handlers.
123
+ * This follows the Azure SDK pattern used by EventHub, Service Bus, etc.
124
+ *
125
+ * @param handlers - Handler functions for different types of events
126
+ * @returns A subscription object that can be used to stop receiving events
127
+ */
128
+ subscribe(handlers) {
129
+ this._ensureNotDisposed();
130
+ logger_js_1.logger.info("Creating VoiceLive session subscription");
131
+ return this._subscriptionManager.createSubscription(handlers);
132
+ }
133
+ /**
134
+ * Sends a custom client event to the service.
135
+ */
136
+ async sendEvent(event, options = {}) {
137
+ this._ensureConnected();
138
+ await this._sendEvent(event, options);
139
+ }
140
+ /**
141
+ * Updates the session configuration with the service.
142
+ */
143
+ async updateSession(session, options = {}) {
144
+ this._ensureConnected();
145
+ const updateEvent = {
146
+ type: "session.update",
147
+ session: session,
148
+ eventId: this._generateEventId(),
149
+ };
150
+ await this._sendEvent(updateEvent, options);
151
+ }
152
+ /**
153
+ * Sends audio data to the service using turn-based or buffer-based approach.
154
+ */
155
+ async sendAudio(audioData, options = {}) {
156
+ this._ensureConnected();
157
+ const audioBase64 = this._arrayBufferToBase64(audioData);
158
+ if (options.turnId) {
159
+ // Turn-based audio
160
+ const appendEvent = {
161
+ type: "input_audio.turn.append",
162
+ audio: audioBase64,
163
+ turnId: options.turnId,
164
+ eventId: this._generateEventId(),
165
+ };
166
+ await this._sendEvent(appendEvent, options);
167
+ }
168
+ else {
169
+ // Buffer-based audio (VAD mode)
170
+ const bufferEvent = {
171
+ type: "input_audio_buffer.append",
172
+ audio: audioBase64,
173
+ eventId: this._generateEventId(),
174
+ };
175
+ await this._sendEvent(bufferEvent, options);
176
+ }
177
+ }
178
+ /**
179
+ * Starts a new audio turn for turn-based audio input.
180
+ */
181
+ async startAudioTurn(options = {}) {
182
+ this._ensureConnected();
183
+ const turnId = options.turnId || this._generateTurnId();
184
+ this._activeTurnId = turnId;
185
+ const startEvent = {
186
+ type: "input_audio.turn.start",
187
+ turnId: turnId,
188
+ eventId: this._generateEventId(),
189
+ };
190
+ await this._sendEvent(startEvent, options);
191
+ return turnId;
192
+ }
193
+ /**
194
+ * Ends the current audio turn.
195
+ */
196
+ async endAudioTurn(turnId, options = {}) {
197
+ this._ensureConnected();
198
+ const targetTurnId = turnId || this._activeTurnId;
199
+ if (!targetTurnId) {
200
+ throw new index_js_1.VoiceLiveConnectionError("No active audio turn to end", "INVALID_STATE");
201
+ }
202
+ const endEvent = {
203
+ type: "input_audio.turn.end",
204
+ turnId: targetTurnId,
205
+ eventId: this._generateEventId(),
206
+ };
207
+ await this._sendEvent(endEvent, options);
208
+ if (targetTurnId === this._activeTurnId) {
209
+ this._activeTurnId = undefined;
210
+ }
211
+ }
212
+ /**
213
+ * Adds a conversation item (message) to the conversation.
214
+ */
215
+ async addConversationItem(item, options = {}) {
216
+ this._ensureConnected();
217
+ const createEvent = {
218
+ type: "conversation.item.create",
219
+ item: item,
220
+ eventId: this._generateEventId(),
221
+ };
222
+ await this._sendEvent(createEvent, options);
223
+ }
224
+ /**
225
+ * Indicates whether the session is currently connected to the Voice Live service.
226
+ */
227
+ get isConnected() {
228
+ return this._connectionManager?.isConnected || false;
229
+ }
230
+ /**
231
+ * Gets the current connection state of the session.
232
+ */
233
+ get connectionState() {
234
+ return this._connectionManager?.state || connectionManager_js_1.ConnectionState.Disconnected;
235
+ }
236
+ /**
237
+ * Gets the current session ID.
238
+ */
239
+ get sessionId() {
240
+ return this._sessionId;
241
+ }
242
+ /**
243
+ * Gets the current active audio turn ID.
244
+ */
245
+ get activeTurnId() {
246
+ return this._activeTurnId;
247
+ }
248
+ /**
249
+ * Disposes the session and cleans up resources.
250
+ */
251
+ async dispose() {
252
+ if (this._disposed) {
253
+ return;
254
+ }
255
+ logger_js_1.logger.info("Disposing VoiceLiveSession");
256
+ try {
257
+ // Close all subscriptions first
258
+ await this._subscriptionManager.closeAll();
259
+ // Then disconnect
260
+ await this.disconnect();
261
+ }
262
+ catch (error) {
263
+ logger_js_1.logger.error("Error during session disposal", { error });
264
+ }
265
+ finally {
266
+ this._disposed = true;
267
+ logger_js_1.logger.info("VoiceLiveSession disposed");
268
+ }
269
+ }
270
+ // Private methods
271
+ _buildDefaultOptions(options) {
272
+ return {
273
+ connectionTimeoutInMs: options.connectionTimeoutInMs || 30000,
274
+ enableDebugLogging: options.enableDebugLogging ?? false,
275
+ };
276
+ }
277
+ _normalizeEndpoint(endpoint) {
278
+ // Ensure endpoint has proper protocol
279
+ if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) {
280
+ endpoint = `https://${endpoint}`;
281
+ }
282
+ // Remove trailing slash
283
+ return endpoint.replace(/\/$/, "");
284
+ }
285
+ _setupConnectionEventHandlers() {
286
+ if (!this._connectionManager)
287
+ return;
288
+ this._connectionManager.updateEventHandlers({
289
+ onStateChange: (state, previousState) => {
290
+ logger_js_1.logger.info("Connection state changed", { state, previousState });
291
+ // Handle connection state changes for handler-based subscriptions
292
+ if (state === connectionManager_js_1.ConnectionState.Connected && previousState === connectionManager_js_1.ConnectionState.Connecting) {
293
+ this._notifyConnectionEvent("connected", {
294
+ connectionId: `conn_${Date.now()}`,
295
+ timestamp: new Date(),
296
+ });
297
+ }
298
+ else if (state === connectionManager_js_1.ConnectionState.Disconnected &&
299
+ previousState === connectionManager_js_1.ConnectionState.Connected) {
300
+ this._notifyConnectionEvent("disconnected", {
301
+ code: 1006, // Abnormal closure
302
+ reason: "Connection lost during session",
303
+ wasClean: false,
304
+ timestamp: new Date(),
305
+ });
306
+ this._markSessionAsDead("Connection lost during session - session is permanently unusable");
307
+ }
308
+ },
309
+ onMessage: (data) => {
310
+ this._handleIncomingMessage(data);
311
+ },
312
+ onError: (error) => {
313
+ logger_js_1.logger.error("Connection error - marking session as dead", { error });
314
+ this._notifyConnectionEvent("error", {
315
+ error: error,
316
+ context: "WebSocket connection error",
317
+ recoverable: false,
318
+ timestamp: new Date(),
319
+ });
320
+ this._markSessionAsDead(`Connection error: ${error.message}`);
321
+ },
322
+ });
323
+ }
324
+ _markSessionAsDead(reason) {
325
+ if (this._disposed)
326
+ return;
327
+ logger_js_1.logger.error("Session marked as permanently dead", { reason });
328
+ // Mark as disposed to prevent further use
329
+ this._disposed = true;
330
+ // Clean up connection manager
331
+ this._connectionManager = undefined;
332
+ this._sessionId = undefined;
333
+ this._activeTurnId = undefined;
334
+ }
335
+ _handleIncomingMessage(data) {
336
+ try {
337
+ logger_js_1.logger.info("Message received", {
338
+ type: typeof data,
339
+ size: typeof data === "string" ? data.length : data.byteLength,
340
+ });
341
+ // Parse and process the message
342
+ const parsed = this._messageParser.parseIncomingMessage(data);
343
+ if (parsed && parsed.type === "server") {
344
+ // Handle server events
345
+ this._handleServerEvent(parsed.event);
346
+ }
347
+ }
348
+ catch (error) {
349
+ logger_js_1.logger.error("Error handling incoming message", { error });
350
+ }
351
+ }
352
+ _handleServerEvent(event) {
353
+ // Extract session information from events
354
+ if (event.type === "session.created" && event.session?.id) {
355
+ this._sessionId = event.session.id;
356
+ logger_js_1.logger.info("Session created", { sessionId: this._sessionId });
357
+ }
358
+ // Notify handler-based subscriptions
359
+ this._notifyServerEvent(event);
360
+ }
361
+ _notifyConnectionEvent(eventType, args) {
362
+ const context = {
363
+ endpoint: this._endpoint,
364
+ sessionId: this._sessionId,
365
+ timestamp: new Date(),
366
+ model: this._model,
367
+ };
368
+ // Fire and forget - don't await to avoid blocking
369
+ this._subscriptionManager.processConnectionEvent(eventType, args, context).catch((error) => {
370
+ logger_js_1.logger.error("Error processing connection event in handlers", { eventType, error });
371
+ });
372
+ }
373
+ _notifyServerEvent(event) {
374
+ if (!this._sessionId) {
375
+ // Can't notify server events without a session ID
376
+ return;
377
+ }
378
+ const context = {
379
+ endpoint: this._endpoint,
380
+ sessionId: this._sessionId,
381
+ timestamp: new Date(),
382
+ model: this._model,
383
+ conversationId: undefined, // Could extract from event if available
384
+ };
385
+ // Fire and forget - don't await to avoid blocking
386
+ this._subscriptionManager.processServerEvent(event, context).catch((error) => {
387
+ logger_js_1.logger.error("Error processing server event in handlers", { eventType: event.type, error });
388
+ });
389
+ }
390
+ async _sendEvent(event, options) {
391
+ if (!this._connectionManager?.isConnected) {
392
+ throw new index_js_1.VoiceLiveConnectionError("Not connected to Voice Live service", "NOT_CONNECTED");
393
+ }
394
+ try {
395
+ const serialized = this._messageParser.serializeOutgoingMessage(event);
396
+ await this._connectionManager.send(serialized, options.abortSignal);
397
+ logger_js_1.logger.info("Sent event", { type: event.type, eventId: event.eventId });
398
+ }
399
+ catch (error) {
400
+ if (error instanceof index_js_1.VoiceLiveConnectionError) {
401
+ throw error;
402
+ }
403
+ throw (0, index_js_1.classifyConnectionError)(error);
404
+ }
405
+ }
406
+ _ensureConnected() {
407
+ this._ensureNotDisposed();
408
+ if (!this.isConnected) {
409
+ throw new index_js_1.VoiceLiveConnectionError("Must be connected to Voice Live service", "NOT_CONNECTED");
410
+ }
411
+ }
412
+ _ensureNotDisposed() {
413
+ if (this._disposed) {
414
+ throw new index_js_1.VoiceLiveConnectionError("Session is permanently dead and cannot be used. Create a new session to continue.", "SESSION_DEAD");
415
+ }
416
+ }
417
+ _generateEventId() {
418
+ return `evt_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
419
+ }
420
+ _generateTurnId() {
421
+ return `turn_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
422
+ }
423
+ _arrayBufferToBase64(buffer) {
424
+ const bytes = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;
425
+ let binary = "";
426
+ for (let i = 0; i < bytes.byteLength; i++) {
427
+ binary += String.fromCharCode(bytes[i]);
428
+ }
429
+ return btoa(binary);
430
+ }
431
+ }
432
+ exports.VoiceLiveSession = VoiceLiveSession;
433
+ //# sourceMappingURL=voiceLiveSession.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voiceLiveSession.js","sourceRoot":"","sources":["../../src/voiceLiveSession.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { KeyCredential, TokenCredential } from \"@azure/core-auth\";\nimport type { AbortSignalLike } from \"@azure/abort-controller\";\nimport type {\n RequestSession,\n ClientEventSessionUpdate,\n ClientEventUnion,\n ClientEventInputAudioBufferAppend,\n ClientEventInputAudioTurnStart,\n ClientEventInputAudioTurnAppend,\n ClientEventInputAudioTurnEnd,\n ConversationRequestItem,\n ClientEventConversationItemCreate,\n ServerEventUnion,\n} from \"./models/index.js\";\nimport { ConnectionManager, ConnectionState } from \"./websocket/connectionManager.js\";\nimport { VoiceLiveWebSocketFactory } from \"./websocket/websocketFactory.js\";\nimport { VoiceLiveMessageParser } from \"./protocol/messageParser.js\";\nimport { CredentialHandler } from \"./auth/credentialHandler.js\";\nimport { VoiceLiveConnectionError, classifyConnectionError } from \"./errors/index.js\";\nimport { logger } from \"./logger.js\";\nimport type {\n VoiceLiveSessionHandlers,\n VoiceLiveSubscription,\n ConnectionContext,\n SessionContext,\n ConnectedEventArgs,\n DisconnectedEventArgs,\n ErrorEventArgs,\n} from \"./handlers/sessionHandlers.js\";\nimport { SubscriptionManager } from \"./handlers/subscriptionManager.js\";\n\nexport interface VoiceLiveSessionOptions {\n /** Connection timeout in milliseconds */\n connectionTimeoutInMs?: number;\n /** Enable debug logging for development */\n enableDebugLogging?: boolean;\n}\n\nexport interface CreateSessionOptions extends VoiceLiveSessionOptions {}\n\nexport interface StartSessionOptions extends VoiceLiveSessionOptions {}\nexport interface ConnectOptions {\n /** Abort signal to cancel connection attempt */\n abortSignal?: AbortSignalLike;\n /** Override connection timeout for this operation */\n timeoutInMs?: number;\n}\n\nexport interface SendEventOptions {\n /** Abort signal to cancel send operation */\n abortSignal?: AbortSignalLike;\n /** Timeout for send operation */\n timeoutInMs?: number;\n}\n\nexport interface AudioStreamOptions extends SendEventOptions {\n /** Turn ID for turn-based audio (if not provided, uses buffer mode) */\n turnId?: string;\n}\n\nexport interface TurnOptions extends SendEventOptions {\n /** Custom turn ID (if not provided, one will be generated) */\n turnId?: string;\n}\n\n/**\n * Represents a WebSocket-based session for real-time voice communication with the Azure VoiceLive service.\n *\n * This class manages the connection, handles real-time communication, and provides access to all\n * interactive features including audio streaming, conversation management, and avatar control.\n */\nexport class VoiceLiveSession {\n private readonly _endpoint: string;\n private readonly _credentialHandler: CredentialHandler;\n private readonly _options: Required<VoiceLiveSessionOptions>;\n private readonly _messageParser: VoiceLiveMessageParser;\n private readonly _model: string;\n private _connectionManager?: ConnectionManager;\n private _sessionId?: string;\n private _activeTurnId?: string;\n private _disposed = false;\n\n // Handler-based subscription management\n private readonly _subscriptionManager: SubscriptionManager;\n\n /**\n * Creates an instance of VoiceLiveSession.\n *\n * @param endpoint - The Voice Live service endpoint URL\n * @param credential - Azure TokenCredential or KeyCredential for authentication\n * @param apiVersion - API version to use for the Voice Live service\n * @param model - The model name to use for the session\n * @param options - Optional configuration for the session\n */\n constructor(\n endpoint: string,\n credential: TokenCredential | KeyCredential,\n apiVersion: string,\n model: string,\n options: VoiceLiveSessionOptions = {},\n ) {\n this._endpoint = this._normalizeEndpoint(endpoint);\n this._credentialHandler = new CredentialHandler(credential);\n this._options = this._buildDefaultOptions(options);\n this._messageParser = new VoiceLiveMessageParser();\n this._model = model;\n\n // Initialize handler-based subscription management\n this._subscriptionManager = new SubscriptionManager();\n\n logger.info(\"VoiceLiveSession created\", {\n endpoint: this._endpoint,\n model: this._model,\n apiVersion: apiVersion,\n enableDebugLogging: this._options.enableDebugLogging,\n });\n }\n\n /**\n * Establishes connection to the Voice Live service with authentication.\n */\n async connect(options: ConnectOptions = {}): Promise<void> {\n this._ensureNotDisposed();\n\n if (this.isConnected) {\n logger.info(\"VoiceLiveSession already connected\");\n return;\n }\n\n try {\n logger.info(\"Connecting to Voice Live service\", {\n endpoint: this._endpoint,\n model: this._model,\n });\n\n // Get WebSocket URL with authentication and model\n const wsUrl = await this._credentialHandler.getWebSocketUrl(\n this._endpoint,\n \"2025-10-01\", // TODO: make this configurable through constructor\n this._model,\n );\n const authHeaders = await this._credentialHandler.getAuthHeaders();\n\n // Create connection manager\n const websocketFactory = new VoiceLiveWebSocketFactory();\n this._connectionManager = new ConnectionManager(\n () =>\n websocketFactory.create({\n headers: { ...authHeaders },\n connectionTimeoutInMs: this._options.connectionTimeoutInMs,\n compression: true,\n }),\n {\n endpoint: wsUrl,\n connectionTimeout: options.timeoutInMs || this._options.connectionTimeoutInMs,\n },\n );\n\n // Setup connection event handlers\n this._setupConnectionEventHandlers();\n\n // Connect with proper error handling\n await this._connectionManager.connect(options.abortSignal);\n\n logger.info(\"Successfully connected to Voice Live service\");\n } catch (error) {\n logger.error(\"Failed to connect to Voice Live service\", { error });\n\n // Use error classification\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n } else {\n throw classifyConnectionError(error);\n }\n }\n }\n\n /**\n * Disconnects from the Voice Live service and cleans up resources.\n */\n async disconnect(): Promise<void> {\n if (!this._connectionManager) {\n return;\n }\n\n logger.info(\"Disconnecting from Voice Live service\");\n\n try {\n await this._connectionManager.disconnect();\n } catch (error) {\n logger.error(\"Error during disconnect\", { error });\n } finally {\n this._connectionManager = undefined;\n this._sessionId = undefined;\n this._activeTurnId = undefined;\n logger.info(\"Disconnected from Voice Live service\");\n }\n }\n\n /**\n * Subscribe to VoiceLive session events using strongly-typed handlers.\n * This follows the Azure SDK pattern used by EventHub, Service Bus, etc.\n *\n * @param handlers - Handler functions for different types of events\n * @returns A subscription object that can be used to stop receiving events\n */\n subscribe(handlers: VoiceLiveSessionHandlers): VoiceLiveSubscription {\n this._ensureNotDisposed();\n\n logger.info(\"Creating VoiceLive session subscription\");\n return this._subscriptionManager.createSubscription(handlers);\n }\n\n /**\n * Sends a custom client event to the service.\n */\n async sendEvent(event: ClientEventUnion, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n await this._sendEvent(event, options);\n }\n\n /**\n * Updates the session configuration with the service.\n */\n async updateSession(session: RequestSession, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n\n const updateEvent: ClientEventSessionUpdate = {\n type: \"session.update\",\n session: session,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(updateEvent, options);\n }\n\n /**\n * Sends audio data to the service using turn-based or buffer-based approach.\n */\n async sendAudio(\n audioData: ArrayBuffer | Uint8Array,\n options: AudioStreamOptions = {},\n ): Promise<void> {\n this._ensureConnected();\n\n const audioBase64 = this._arrayBufferToBase64(audioData);\n\n if (options.turnId) {\n // Turn-based audio\n const appendEvent: ClientEventInputAudioTurnAppend = {\n type: \"input_audio.turn.append\",\n audio: audioBase64,\n turnId: options.turnId,\n eventId: this._generateEventId(),\n };\n await this._sendEvent(appendEvent, options);\n } else {\n // Buffer-based audio (VAD mode)\n const bufferEvent: ClientEventInputAudioBufferAppend = {\n type: \"input_audio_buffer.append\",\n audio: audioBase64,\n eventId: this._generateEventId(),\n };\n await this._sendEvent(bufferEvent, options);\n }\n }\n\n /**\n * Starts a new audio turn for turn-based audio input.\n */\n async startAudioTurn(options: TurnOptions = {}): Promise<string> {\n this._ensureConnected();\n\n const turnId = options.turnId || this._generateTurnId();\n this._activeTurnId = turnId;\n\n const startEvent: ClientEventInputAudioTurnStart = {\n type: \"input_audio.turn.start\",\n turnId: turnId,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(startEvent, options);\n return turnId;\n }\n\n /**\n * Ends the current audio turn.\n */\n async endAudioTurn(turnId?: string, options: SendEventOptions = {}): Promise<void> {\n this._ensureConnected();\n\n const targetTurnId = turnId || this._activeTurnId;\n if (!targetTurnId) {\n throw new VoiceLiveConnectionError(\"No active audio turn to end\", \"INVALID_STATE\");\n }\n\n const endEvent: ClientEventInputAudioTurnEnd = {\n type: \"input_audio.turn.end\",\n turnId: targetTurnId,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(endEvent, options);\n\n if (targetTurnId === this._activeTurnId) {\n this._activeTurnId = undefined;\n }\n }\n\n /**\n * Adds a conversation item (message) to the conversation.\n */\n async addConversationItem(\n item: ConversationRequestItem,\n options: SendEventOptions = {},\n ): Promise<void> {\n this._ensureConnected();\n\n const createEvent: ClientEventConversationItemCreate = {\n type: \"conversation.item.create\",\n item: item,\n eventId: this._generateEventId(),\n };\n\n await this._sendEvent(createEvent, options);\n }\n\n /**\n * Indicates whether the session is currently connected to the Voice Live service.\n */\n get isConnected(): boolean {\n return this._connectionManager?.isConnected || false;\n }\n\n /**\n * Gets the current connection state of the session.\n */\n get connectionState(): ConnectionState {\n return this._connectionManager?.state || ConnectionState.Disconnected;\n }\n\n /**\n * Gets the current session ID.\n */\n get sessionId(): string | undefined {\n return this._sessionId;\n }\n\n /**\n * Gets the current active audio turn ID.\n */\n get activeTurnId(): string | undefined {\n return this._activeTurnId;\n }\n\n /**\n * Disposes the session and cleans up resources.\n */\n async dispose(): Promise<void> {\n if (this._disposed) {\n return;\n }\n\n logger.info(\"Disposing VoiceLiveSession\");\n\n try {\n // Close all subscriptions first\n await this._subscriptionManager.closeAll();\n\n // Then disconnect\n await this.disconnect();\n } catch (error) {\n logger.error(\"Error during session disposal\", { error });\n } finally {\n this._disposed = true;\n logger.info(\"VoiceLiveSession disposed\");\n }\n }\n\n // Private methods\n\n private _buildDefaultOptions(\n options: VoiceLiveSessionOptions,\n ): Required<VoiceLiveSessionOptions> {\n return {\n connectionTimeoutInMs: options.connectionTimeoutInMs || 30000,\n enableDebugLogging: options.enableDebugLogging ?? false,\n };\n }\n\n private _normalizeEndpoint(endpoint: string): string {\n // Ensure endpoint has proper protocol\n if (!endpoint.startsWith(\"http://\") && !endpoint.startsWith(\"https://\")) {\n endpoint = `https://${endpoint}`;\n }\n\n // Remove trailing slash\n return endpoint.replace(/\\/$/, \"\");\n }\n\n private _setupConnectionEventHandlers(): void {\n if (!this._connectionManager) return;\n\n this._connectionManager.updateEventHandlers({\n onStateChange: (state, previousState) => {\n logger.info(\"Connection state changed\", { state, previousState });\n\n // Handle connection state changes for handler-based subscriptions\n if (state === ConnectionState.Connected && previousState === ConnectionState.Connecting) {\n this._notifyConnectionEvent(\"connected\", {\n connectionId: `conn_${Date.now()}`,\n timestamp: new Date(),\n });\n } else if (\n state === ConnectionState.Disconnected &&\n previousState === ConnectionState.Connected\n ) {\n this._notifyConnectionEvent(\"disconnected\", {\n code: 1006, // Abnormal closure\n reason: \"Connection lost during session\",\n wasClean: false,\n timestamp: new Date(),\n });\n this._markSessionAsDead(\n \"Connection lost during session - session is permanently unusable\",\n );\n }\n },\n onMessage: (data) => {\n this._handleIncomingMessage(data);\n },\n onError: (error) => {\n logger.error(\"Connection error - marking session as dead\", { error });\n this._notifyConnectionEvent(\"error\", {\n error: error,\n context: \"WebSocket connection error\",\n recoverable: false,\n timestamp: new Date(),\n });\n this._markSessionAsDead(`Connection error: ${error.message}`);\n },\n });\n }\n\n private _markSessionAsDead(reason: string): void {\n if (this._disposed) return;\n\n logger.error(\"Session marked as permanently dead\", { reason });\n\n // Mark as disposed to prevent further use\n this._disposed = true;\n\n // Clean up connection manager\n this._connectionManager = undefined;\n this._sessionId = undefined;\n this._activeTurnId = undefined;\n }\n\n private _handleIncomingMessage(data: string | ArrayBuffer): void {\n try {\n logger.info(\"Message received\", {\n type: typeof data,\n size: typeof data === \"string\" ? data.length : data.byteLength,\n });\n\n // Parse and process the message\n const parsed = this._messageParser.parseIncomingMessage(data);\n if (parsed && parsed.type === \"server\") {\n // Handle server events\n this._handleServerEvent(parsed.event);\n }\n } catch (error) {\n logger.error(\"Error handling incoming message\", { error });\n }\n }\n\n private _handleServerEvent(event: any): void {\n // Extract session information from events\n if (event.type === \"session.created\" && event.session?.id) {\n this._sessionId = event.session.id;\n logger.info(\"Session created\", { sessionId: this._sessionId });\n }\n\n // Notify handler-based subscriptions\n this._notifyServerEvent(event);\n }\n\n private _notifyConnectionEvent(\n eventType: \"connected\" | \"disconnected\" | \"error\",\n args: ConnectedEventArgs | DisconnectedEventArgs | ErrorEventArgs,\n ): void {\n const context: ConnectionContext = {\n endpoint: this._endpoint,\n sessionId: this._sessionId,\n timestamp: new Date(),\n model: this._model,\n };\n\n // Fire and forget - don't await to avoid blocking\n this._subscriptionManager.processConnectionEvent(eventType, args, context).catch((error) => {\n logger.error(\"Error processing connection event in handlers\", { eventType, error });\n });\n }\n\n private _notifyServerEvent(event: ServerEventUnion): void {\n if (!this._sessionId) {\n // Can't notify server events without a session ID\n return;\n }\n\n const context: SessionContext = {\n endpoint: this._endpoint,\n sessionId: this._sessionId,\n timestamp: new Date(),\n model: this._model,\n conversationId: undefined, // Could extract from event if available\n };\n\n // Fire and forget - don't await to avoid blocking\n this._subscriptionManager.processServerEvent(event, context).catch((error) => {\n logger.error(\"Error processing server event in handlers\", { eventType: event.type, error });\n });\n }\n\n private async _sendEvent(event: ClientEventUnion, options: SendEventOptions): Promise<void> {\n if (!this._connectionManager?.isConnected) {\n throw new VoiceLiveConnectionError(\"Not connected to Voice Live service\", \"NOT_CONNECTED\");\n }\n\n try {\n const serialized = this._messageParser.serializeOutgoingMessage(event);\n await this._connectionManager.send(serialized, options.abortSignal);\n\n logger.info(\"Sent event\", { type: event.type, eventId: (event as any).eventId });\n } catch (error) {\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n }\n\n throw classifyConnectionError(error);\n }\n }\n\n private _ensureConnected(): void {\n this._ensureNotDisposed();\n if (!this.isConnected) {\n throw new VoiceLiveConnectionError(\n \"Must be connected to Voice Live service\",\n \"NOT_CONNECTED\",\n );\n }\n }\n\n private _ensureNotDisposed(): void {\n if (this._disposed) {\n throw new VoiceLiveConnectionError(\n \"Session is permanently dead and cannot be used. Create a new session to continue.\",\n \"SESSION_DEAD\",\n );\n }\n }\n\n private _generateEventId(): string {\n return `evt_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private _generateTurnId(): string {\n return `turn_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;\n }\n\n private _arrayBufferToBase64(buffer: ArrayBuffer | Uint8Array): string {\n const bytes = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;\n let binary = \"\";\n for (let i = 0; i < bytes.byteLength; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n }\n}\n"]}
@@ -0,0 +1,88 @@
1
+ import type { AbortSignalLike } from "@azure/abort-controller";
2
+ import type { VoiceLiveWebSocketLike } from "./websocketLike.js";
3
+ import { VoiceLiveConnectionError } from "../errors/index.js";
4
+ /**
5
+ * Connection state enumeration for lifecycle management
6
+ */
7
+ export declare enum ConnectionState {
8
+ Disconnected = "disconnected",
9
+ Connecting = "connecting",
10
+ Connected = "connected",
11
+ Disconnecting = "disconnecting"
12
+ }
13
+ /**
14
+ * Configuration options for connection management
15
+ */
16
+ export interface ConnectionOptions {
17
+ /** WebSocket endpoint URL */
18
+ endpoint: string;
19
+ /** WebSocket protocols to use */
20
+ protocols?: string[];
21
+ /** Connection timeout in milliseconds */
22
+ connectionTimeout?: number;
23
+ }
24
+ /**
25
+ * Event handlers for connection lifecycle events
26
+ */
27
+ export interface ConnectionEventHandlers {
28
+ /** Called when connection state changes */
29
+ onStateChange?: (state: ConnectionState, previousState: ConnectionState) => void;
30
+ /** Called when a message is received */
31
+ onMessage?: (data: string | ArrayBuffer) => void;
32
+ /** Called when an error occurs or connection is lost */
33
+ onError?: (error: VoiceLiveConnectionError) => void;
34
+ }
35
+ /**
36
+ * Manages WebSocket connection lifecycle with fail-fast semantics
37
+ */
38
+ export declare class ConnectionManager {
39
+ private _websocketFactory;
40
+ private _options;
41
+ private _eventHandlers;
42
+ private _state;
43
+ private _previousState;
44
+ private _websocket?;
45
+ private _abortController?;
46
+ constructor(_websocketFactory: () => VoiceLiveWebSocketLike, _options: ConnectionOptions, _eventHandlers?: ConnectionEventHandlers);
47
+ /**
48
+ * Initiates a WebSocket connection
49
+ */
50
+ connect(abortSignal?: AbortSignalLike): Promise<void>;
51
+ /**
52
+ * Disconnects the WebSocket connection
53
+ */
54
+ disconnect(_abortSignal?: AbortSignalLike): Promise<void>;
55
+ /**
56
+ * Sends data through the WebSocket connection
57
+ */
58
+ send(data: string | ArrayBuffer, abortSignal?: AbortSignalLike): Promise<void>;
59
+ /**
60
+ * Sets up event handlers for the WebSocket instance
61
+ */
62
+ private _setupWebSocketHandlers;
63
+ /**
64
+ * Handles any connection loss - always fatal in fail-fast model
65
+ */
66
+ private _handleConnectionLost;
67
+ /**
68
+ * Handles WebSocket errors
69
+ */
70
+ private _handleConnectionError;
71
+ /**
72
+ * Updates the connection state and notifies handlers
73
+ */
74
+ private _setState;
75
+ /**
76
+ * Gets the current connection state
77
+ */
78
+ get state(): ConnectionState;
79
+ /**
80
+ * Checks if the connection is currently established
81
+ */
82
+ get isConnected(): boolean;
83
+ /**
84
+ * Updates the event handlers
85
+ */
86
+ updateEventHandlers(handlers: Partial<ConnectionEventHandlers>): void;
87
+ }
88
+ //# sourceMappingURL=connectionManager.d.ts.map