@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.
- package/LICENSE +21 -0
- package/README.md +358 -0
- package/dist/browser/auth/credentialHandler.d.ts +43 -0
- package/dist/browser/auth/credentialHandler.js +147 -0
- package/dist/browser/auth/credentialHandler.js.map +1 -0
- package/dist/browser/errors/connectionErrors.d.ts +68 -0
- package/dist/browser/errors/connectionErrors.js +136 -0
- package/dist/browser/errors/connectionErrors.js.map +1 -0
- package/dist/browser/errors/index.d.ts +2 -0
- package/dist/browser/errors/index.js +4 -0
- package/dist/browser/errors/index.js.map +1 -0
- package/dist/browser/handlers/sessionHandlers.d.ts +250 -0
- package/dist/browser/handlers/sessionHandlers.js +4 -0
- package/dist/browser/handlers/sessionHandlers.js.map +1 -0
- package/dist/browser/handlers/subscriptionManager.d.ts +54 -0
- package/dist/browser/handlers/subscriptionManager.js +250 -0
- package/dist/browser/handlers/subscriptionManager.js.map +1 -0
- package/dist/browser/index.d.ts +7 -0
- package/dist/browser/index.js +12 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/logger.d.ts +2 -0
- package/dist/browser/logger.js +5 -0
- package/dist/browser/logger.js.map +1 -0
- package/dist/browser/models/index.d.ts +2 -0
- package/dist/browser/models/index.js +4 -0
- package/dist/browser/models/index.js.map +1 -0
- package/dist/browser/models/models.d.ts +2154 -0
- package/dist/browser/models/models.js +2251 -0
- package/dist/browser/models/models.js.map +1 -0
- package/dist/browser/package.json +3 -0
- package/dist/browser/protocol/messageParser.d.ts +42 -0
- package/dist/browser/protocol/messageParser.js +150 -0
- package/dist/browser/protocol/messageParser.js.map +1 -0
- package/dist/browser/voiceLiveClient.d.ts +65 -0
- package/dist/browser/voiceLiveClient.js +81 -0
- package/dist/browser/voiceLiveClient.js.map +1 -0
- package/dist/browser/voiceLiveSession.d.ts +138 -0
- package/dist/browser/voiceLiveSession.js +429 -0
- package/dist/browser/voiceLiveSession.js.map +1 -0
- package/dist/browser/websocket/connectionManager.d.ts +88 -0
- package/dist/browser/websocket/connectionManager.js +183 -0
- package/dist/browser/websocket/connectionManager.js.map +1 -0
- package/dist/browser/websocket/websocketBrowser.d.ts +26 -0
- package/dist/browser/websocket/websocketBrowser.js +175 -0
- package/dist/browser/websocket/websocketBrowser.js.map +1 -0
- package/dist/browser/websocket/websocketFactory.d.ts +23 -0
- package/dist/browser/websocket/websocketFactory.js +80 -0
- package/dist/browser/websocket/websocketFactory.js.map +1 -0
- package/dist/browser/websocket/websocketLike.d.ts +78 -0
- package/dist/browser/websocket/websocketLike.js +13 -0
- package/dist/browser/websocket/websocketLike.js.map +1 -0
- package/dist/browser/websocket/websocketNode.d.ts +26 -0
- package/dist/browser/websocket/websocketNode.js +180 -0
- package/dist/browser/websocket/websocketNode.js.map +1 -0
- package/dist/commonjs/auth/credentialHandler.d.ts +43 -0
- package/dist/commonjs/auth/credentialHandler.js +151 -0
- package/dist/commonjs/auth/credentialHandler.js.map +1 -0
- package/dist/commonjs/errors/connectionErrors.d.ts +68 -0
- package/dist/commonjs/errors/connectionErrors.js +146 -0
- package/dist/commonjs/errors/connectionErrors.js.map +1 -0
- package/dist/commonjs/errors/index.d.ts +2 -0
- package/dist/commonjs/errors/index.js +7 -0
- package/dist/commonjs/errors/index.js.map +1 -0
- package/dist/commonjs/handlers/sessionHandlers.d.ts +250 -0
- package/dist/commonjs/handlers/sessionHandlers.js +5 -0
- package/dist/commonjs/handlers/sessionHandlers.js.map +1 -0
- package/dist/commonjs/handlers/subscriptionManager.d.ts +54 -0
- package/dist/commonjs/handlers/subscriptionManager.js +255 -0
- package/dist/commonjs/handlers/subscriptionManager.js.map +1 -0
- package/dist/commonjs/index.d.ts +7 -0
- package/dist/commonjs/index.js +45 -0
- package/dist/commonjs/index.js.map +1 -0
- package/dist/commonjs/logger.d.ts +2 -0
- package/dist/commonjs/logger.js +8 -0
- package/dist/commonjs/logger.js.map +1 -0
- package/dist/commonjs/models/index.d.ts +2 -0
- package/dist/commonjs/models/index.js +27 -0
- package/dist/commonjs/models/index.js.map +1 -0
- package/dist/commonjs/models/models.d.ts +2154 -0
- package/dist/commonjs/models/models.js +2463 -0
- package/dist/commonjs/models/models.js.map +1 -0
- package/dist/commonjs/package.json +3 -0
- package/dist/commonjs/protocol/messageParser.d.ts +42 -0
- package/dist/commonjs/protocol/messageParser.js +154 -0
- package/dist/commonjs/protocol/messageParser.js.map +1 -0
- package/dist/commonjs/tsdoc-metadata.json +11 -0
- package/dist/commonjs/voiceLiveClient.d.ts +65 -0
- package/dist/commonjs/voiceLiveClient.js +85 -0
- package/dist/commonjs/voiceLiveClient.js.map +1 -0
- package/dist/commonjs/voiceLiveSession.d.ts +138 -0
- package/dist/commonjs/voiceLiveSession.js +433 -0
- package/dist/commonjs/voiceLiveSession.js.map +1 -0
- package/dist/commonjs/websocket/connectionManager.d.ts +88 -0
- package/dist/commonjs/websocket/connectionManager.js +187 -0
- package/dist/commonjs/websocket/connectionManager.js.map +1 -0
- package/dist/commonjs/websocket/websocketBrowser.d.ts +26 -0
- package/dist/commonjs/websocket/websocketBrowser.js +179 -0
- package/dist/commonjs/websocket/websocketBrowser.js.map +1 -0
- package/dist/commonjs/websocket/websocketFactory.d.ts +23 -0
- package/dist/commonjs/websocket/websocketFactory.js +86 -0
- package/dist/commonjs/websocket/websocketFactory.js.map +1 -0
- package/dist/commonjs/websocket/websocketLike.d.ts +78 -0
- package/dist/commonjs/websocket/websocketLike.js +16 -0
- package/dist/commonjs/websocket/websocketLike.js.map +1 -0
- package/dist/commonjs/websocket/websocketNode.d.ts +26 -0
- package/dist/commonjs/websocket/websocketNode.js +185 -0
- package/dist/commonjs/websocket/websocketNode.js.map +1 -0
- package/dist/esm/auth/credentialHandler.d.ts +43 -0
- package/dist/esm/auth/credentialHandler.js +147 -0
- package/dist/esm/auth/credentialHandler.js.map +1 -0
- package/dist/esm/errors/connectionErrors.d.ts +68 -0
- package/dist/esm/errors/connectionErrors.js +136 -0
- package/dist/esm/errors/connectionErrors.js.map +1 -0
- package/dist/esm/errors/index.d.ts +2 -0
- package/dist/esm/errors/index.js +4 -0
- package/dist/esm/errors/index.js.map +1 -0
- package/dist/esm/handlers/sessionHandlers.d.ts +250 -0
- package/dist/esm/handlers/sessionHandlers.js +4 -0
- package/dist/esm/handlers/sessionHandlers.js.map +1 -0
- package/dist/esm/handlers/subscriptionManager.d.ts +54 -0
- package/dist/esm/handlers/subscriptionManager.js +250 -0
- package/dist/esm/handlers/subscriptionManager.js.map +1 -0
- package/dist/esm/index.d.ts +7 -0
- package/dist/esm/index.js +12 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/logger.d.ts +2 -0
- package/dist/esm/logger.js +5 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/models/index.d.ts +2 -0
- package/dist/esm/models/index.js +4 -0
- package/dist/esm/models/index.js.map +1 -0
- package/dist/esm/models/models.d.ts +2154 -0
- package/dist/esm/models/models.js +2251 -0
- package/dist/esm/models/models.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/protocol/messageParser.d.ts +42 -0
- package/dist/esm/protocol/messageParser.js +150 -0
- package/dist/esm/protocol/messageParser.js.map +1 -0
- package/dist/esm/voiceLiveClient.d.ts +65 -0
- package/dist/esm/voiceLiveClient.js +81 -0
- package/dist/esm/voiceLiveClient.js.map +1 -0
- package/dist/esm/voiceLiveSession.d.ts +138 -0
- package/dist/esm/voiceLiveSession.js +429 -0
- package/dist/esm/voiceLiveSession.js.map +1 -0
- package/dist/esm/websocket/connectionManager.d.ts +88 -0
- package/dist/esm/websocket/connectionManager.js +183 -0
- package/dist/esm/websocket/connectionManager.js.map +1 -0
- package/dist/esm/websocket/websocketBrowser.d.ts +26 -0
- package/dist/esm/websocket/websocketBrowser.js +175 -0
- package/dist/esm/websocket/websocketBrowser.js.map +1 -0
- package/dist/esm/websocket/websocketFactory.d.ts +23 -0
- package/dist/esm/websocket/websocketFactory.js +80 -0
- package/dist/esm/websocket/websocketFactory.js.map +1 -0
- package/dist/esm/websocket/websocketLike.d.ts +78 -0
- package/dist/esm/websocket/websocketLike.js +13 -0
- package/dist/esm/websocket/websocketLike.js.map +1 -0
- package/dist/esm/websocket/websocketNode.d.ts +26 -0
- package/dist/esm/websocket/websocketNode.js +180 -0
- package/dist/esm/websocket/websocketNode.js.map +1 -0
- package/dist/react-native/auth/credentialHandler.d.ts +43 -0
- package/dist/react-native/auth/credentialHandler.js +147 -0
- package/dist/react-native/auth/credentialHandler.js.map +1 -0
- package/dist/react-native/errors/connectionErrors.d.ts +68 -0
- package/dist/react-native/errors/connectionErrors.js +136 -0
- package/dist/react-native/errors/connectionErrors.js.map +1 -0
- package/dist/react-native/errors/index.d.ts +2 -0
- package/dist/react-native/errors/index.js +4 -0
- package/dist/react-native/errors/index.js.map +1 -0
- package/dist/react-native/handlers/sessionHandlers.d.ts +250 -0
- package/dist/react-native/handlers/sessionHandlers.js +4 -0
- package/dist/react-native/handlers/sessionHandlers.js.map +1 -0
- package/dist/react-native/handlers/subscriptionManager.d.ts +54 -0
- package/dist/react-native/handlers/subscriptionManager.js +250 -0
- package/dist/react-native/handlers/subscriptionManager.js.map +1 -0
- package/dist/react-native/index.d.ts +7 -0
- package/dist/react-native/index.js +12 -0
- package/dist/react-native/index.js.map +1 -0
- package/dist/react-native/logger.d.ts +2 -0
- package/dist/react-native/logger.js +5 -0
- package/dist/react-native/logger.js.map +1 -0
- package/dist/react-native/models/index.d.ts +2 -0
- package/dist/react-native/models/index.js +4 -0
- package/dist/react-native/models/index.js.map +1 -0
- package/dist/react-native/models/models.d.ts +2154 -0
- package/dist/react-native/models/models.js +2251 -0
- package/dist/react-native/models/models.js.map +1 -0
- package/dist/react-native/package.json +3 -0
- package/dist/react-native/protocol/messageParser.d.ts +42 -0
- package/dist/react-native/protocol/messageParser.js +150 -0
- package/dist/react-native/protocol/messageParser.js.map +1 -0
- package/dist/react-native/voiceLiveClient.d.ts +65 -0
- package/dist/react-native/voiceLiveClient.js +81 -0
- package/dist/react-native/voiceLiveClient.js.map +1 -0
- package/dist/react-native/voiceLiveSession.d.ts +138 -0
- package/dist/react-native/voiceLiveSession.js +429 -0
- package/dist/react-native/voiceLiveSession.js.map +1 -0
- package/dist/react-native/websocket/connectionManager.d.ts +88 -0
- package/dist/react-native/websocket/connectionManager.js +183 -0
- package/dist/react-native/websocket/connectionManager.js.map +1 -0
- package/dist/react-native/websocket/websocketBrowser.d.ts +26 -0
- package/dist/react-native/websocket/websocketBrowser.js +175 -0
- package/dist/react-native/websocket/websocketBrowser.js.map +1 -0
- package/dist/react-native/websocket/websocketFactory.d.ts +23 -0
- package/dist/react-native/websocket/websocketFactory.js +80 -0
- package/dist/react-native/websocket/websocketFactory.js.map +1 -0
- package/dist/react-native/websocket/websocketLike.d.ts +78 -0
- package/dist/react-native/websocket/websocketLike.js +13 -0
- package/dist/react-native/websocket/websocketLike.js.map +1 -0
- package/dist/react-native/websocket/websocketNode.d.ts +26 -0
- package/dist/react-native/websocket/websocketNode.js +180 -0
- package/dist/react-native/websocket/websocketNode.js.map +1 -0
- package/package.json +150 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.ConnectionManager = exports.ConnectionState = void 0;
|
|
6
|
+
const index_js_1 = require("../errors/index.js");
|
|
7
|
+
const logger_js_1 = require("../logger.js");
|
|
8
|
+
/**
|
|
9
|
+
* Connection state enumeration for lifecycle management
|
|
10
|
+
*/
|
|
11
|
+
var ConnectionState;
|
|
12
|
+
(function (ConnectionState) {
|
|
13
|
+
ConnectionState["Disconnected"] = "disconnected";
|
|
14
|
+
ConnectionState["Connecting"] = "connecting";
|
|
15
|
+
ConnectionState["Connected"] = "connected";
|
|
16
|
+
ConnectionState["Disconnecting"] = "disconnecting";
|
|
17
|
+
// Removed: Reconnecting - no reconnection in fail-fast model
|
|
18
|
+
})(ConnectionState || (exports.ConnectionState = ConnectionState = {}));
|
|
19
|
+
/**
|
|
20
|
+
* Manages WebSocket connection lifecycle with fail-fast semantics
|
|
21
|
+
*/
|
|
22
|
+
class ConnectionManager {
|
|
23
|
+
_websocketFactory;
|
|
24
|
+
_options;
|
|
25
|
+
_eventHandlers;
|
|
26
|
+
_state = ConnectionState.Disconnected;
|
|
27
|
+
_previousState = ConnectionState.Disconnected;
|
|
28
|
+
_websocket;
|
|
29
|
+
_abortController;
|
|
30
|
+
constructor(_websocketFactory, _options, _eventHandlers = {}) {
|
|
31
|
+
this._websocketFactory = _websocketFactory;
|
|
32
|
+
this._options = _options;
|
|
33
|
+
this._eventHandlers = _eventHandlers;
|
|
34
|
+
// No reconnection setup needed - fail fast model
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Initiates a WebSocket connection
|
|
38
|
+
*/
|
|
39
|
+
async connect(abortSignal) {
|
|
40
|
+
if (this._state === ConnectionState.Connected) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (this._state === ConnectionState.Connecting) {
|
|
44
|
+
throw new index_js_1.VoiceLiveConnectionError("Connection attempt already in progress", index_js_1.VoiceLiveErrorCodes.InvalidState);
|
|
45
|
+
}
|
|
46
|
+
this._setState(ConnectionState.Connecting);
|
|
47
|
+
// Create new abort controller for this connection attempt
|
|
48
|
+
this._abortController = new AbortController();
|
|
49
|
+
// Chain with provided abort signal
|
|
50
|
+
if (abortSignal) {
|
|
51
|
+
if (abortSignal.aborted) {
|
|
52
|
+
this._abortController.abort();
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
abortSignal.addEventListener("abort", () => {
|
|
56
|
+
this._abortController?.abort();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
this._websocket = this._websocketFactory();
|
|
62
|
+
this._setupWebSocketHandlers();
|
|
63
|
+
await this._websocket.connect(this._options.endpoint, this._options.protocols, this._abortController.signal);
|
|
64
|
+
this._setState(ConnectionState.Connected);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
this._setState(ConnectionState.Disconnected);
|
|
68
|
+
if (error instanceof index_js_1.VoiceLiveConnectionError) {
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
throw new index_js_1.VoiceLiveConnectionError(`Failed to connect: ${error instanceof Error ? error.message : "Unknown error"}`, index_js_1.VoiceLiveErrorCodes.ConnectionFailed, "connection_attempt", true, error instanceof Error ? error : new Error(String(error)));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Disconnects the WebSocket connection
|
|
78
|
+
*/
|
|
79
|
+
async disconnect(_abortSignal) {
|
|
80
|
+
if (this._state === ConnectionState.Disconnected) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Abort any ongoing connection attempt
|
|
84
|
+
this._abortController?.abort();
|
|
85
|
+
this._setState(ConnectionState.Disconnecting);
|
|
86
|
+
try {
|
|
87
|
+
if (this._websocket) {
|
|
88
|
+
await this._websocket.disconnect(1000, "Client disconnect");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
this._setState(ConnectionState.Disconnected);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Sends data through the WebSocket connection
|
|
97
|
+
*/
|
|
98
|
+
async send(data, abortSignal) {
|
|
99
|
+
if (!this._websocket || this._state !== ConnectionState.Connected) {
|
|
100
|
+
throw new index_js_1.VoiceLiveConnectionError("Cannot send message: WebSocket not connected", index_js_1.VoiceLiveErrorCodes.NotConnected);
|
|
101
|
+
}
|
|
102
|
+
return this._websocket.send(data, abortSignal);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Sets up event handlers for the WebSocket instance
|
|
106
|
+
*/
|
|
107
|
+
_setupWebSocketHandlers() {
|
|
108
|
+
if (!this._websocket)
|
|
109
|
+
return;
|
|
110
|
+
logger_js_1.logger.info("Setting up WebSocket event handlers");
|
|
111
|
+
this._websocket.onOpen(() => {
|
|
112
|
+
logger_js_1.logger.info("WebSocket connection opened");
|
|
113
|
+
// Connection opened - handled in connect() method
|
|
114
|
+
});
|
|
115
|
+
this._websocket.onClose((code, reason) => {
|
|
116
|
+
this._handleConnectionLost(code, reason);
|
|
117
|
+
});
|
|
118
|
+
this._websocket.onError((error) => {
|
|
119
|
+
this._handleConnectionError(error);
|
|
120
|
+
});
|
|
121
|
+
this._websocket.onMessage((data) => {
|
|
122
|
+
this._eventHandlers.onMessage?.(data);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Handles any connection loss - always fatal in fail-fast model
|
|
127
|
+
*/
|
|
128
|
+
_handleConnectionLost(code, reason) {
|
|
129
|
+
// Check if this was an expected disconnection before changing state
|
|
130
|
+
if (this._state === ConnectionState.Disconnecting) {
|
|
131
|
+
// This was an expected disconnection, don't treat as error
|
|
132
|
+
this._setState(ConnectionState.Disconnected);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
this._setState(ConnectionState.Disconnected);
|
|
136
|
+
// In fail-fast model, ANY unexpected connection loss is fatal
|
|
137
|
+
let error;
|
|
138
|
+
if (code === 1000) {
|
|
139
|
+
// Normal close, but unexpected
|
|
140
|
+
error = new index_js_1.VoiceLiveConnectionError("WebSocket connection closed unexpectedly", index_js_1.VoiceLiveErrorCodes.ConnectionLost, "connection_lost");
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
// Use classifier for other codes
|
|
144
|
+
error = (0, index_js_1.classifyWebSocketClose)(code, reason);
|
|
145
|
+
}
|
|
146
|
+
// Always notify of fatal error for unexpected disconnections
|
|
147
|
+
this._eventHandlers.onError?.(error);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Handles WebSocket errors
|
|
151
|
+
*/
|
|
152
|
+
_handleConnectionError(error) {
|
|
153
|
+
const connectionError = new index_js_1.VoiceLiveConnectionError(`WebSocket error: ${error.message}`, index_js_1.VoiceLiveErrorCodes.WebSocketError, "websocket_error", true, error);
|
|
154
|
+
this._eventHandlers.onError?.(connectionError);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Updates the connection state and notifies handlers
|
|
158
|
+
*/
|
|
159
|
+
_setState(state) {
|
|
160
|
+
if (this._state !== state) {
|
|
161
|
+
this._previousState = this._state;
|
|
162
|
+
this._state = state;
|
|
163
|
+
this._eventHandlers.onStateChange?.(state, this._previousState);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Public properties and methods
|
|
167
|
+
/**
|
|
168
|
+
* Gets the current connection state
|
|
169
|
+
*/
|
|
170
|
+
get state() {
|
|
171
|
+
return this._state;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Checks if the connection is currently established
|
|
175
|
+
*/
|
|
176
|
+
get isConnected() {
|
|
177
|
+
return this._state === ConnectionState.Connected;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Updates the event handlers
|
|
181
|
+
*/
|
|
182
|
+
updateEventHandlers(handlers) {
|
|
183
|
+
Object.assign(this._eventHandlers, handlers);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
exports.ConnectionManager = ConnectionManager;
|
|
187
|
+
//# sourceMappingURL=connectionManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectionManager.js","sourceRoot":"","sources":["../../../src/websocket/connectionManager.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;;;AAIlC,iDAI4B;AAC5B,4CAAsC;AAEtC;;GAEG;AACH,IAAY,eAMX;AAND,WAAY,eAAe;IACzB,gDAA6B,CAAA;IAC7B,4CAAyB,CAAA;IACzB,0CAAuB,CAAA;IACvB,kDAA+B,CAAA;IAC/B,6DAA6D;AAC/D,CAAC,EANW,eAAe,+BAAf,eAAe,QAM1B;AA4BD;;GAEG;AACH,MAAa,iBAAiB;IAOlB;IACA;IACA;IARF,MAAM,GAAoB,eAAe,CAAC,YAAY,CAAC;IACvD,cAAc,GAAoB,eAAe,CAAC,YAAY,CAAC;IAC/D,UAAU,CAA0B;IACpC,gBAAgB,CAAmB;IAE3C,YACU,iBAA+C,EAC/C,QAA2B,EAC3B,iBAA0C,EAAE;QAF5C,sBAAiB,GAAjB,iBAAiB,CAA8B;QAC/C,aAAQ,GAAR,QAAQ,CAAmB;QAC3B,mBAAc,GAAd,cAAc,CAA8B;QAEpD,iDAAiD;IACnD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAC,WAA6B;QACzC,IAAI,IAAI,CAAC,MAAM,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;YAC9C,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,KAAK,eAAe,CAAC,UAAU,EAAE,CAAC;YAC/C,MAAM,IAAI,mCAAwB,CAChC,wCAAwC,EACxC,8BAAmB,CAAC,YAAY,CACjC,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;QAE3C,0DAA0D;QAC1D,IAAI,CAAC,gBAAgB,GAAG,IAAI,eAAe,EAAE,CAAC;QAE9C,mCAAmC;QACnC,IAAI,WAAW,EAAE,CAAC;YAChB,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;gBACxB,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;YAChC,CAAC;iBAAM,CAAC;gBACN,WAAW,CAAC,gBAAgB,CAAC,OAAO,EAAE,GAAG,EAAE;oBACzC,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,CAAC;gBACjC,CAAC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC3C,IAAI,CAAC,uBAAuB,EAAE,CAAC;YAE/B,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAC3B,IAAI,CAAC,QAAQ,CAAC,QAAQ,EACtB,IAAI,CAAC,QAAQ,CAAC,SAAS,EACvB,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAC7B,CAAC;YAEF,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,SAAS,CAAC,CAAC;QAC5C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;YAE7C,IAAI,KAAK,YAAY,mCAAwB,EAAE,CAAC;gBAC9C,MAAM,KAAK,CAAC;YACd,CAAC;iBAAM,CAAC;gBACN,MAAM,IAAI,mCAAwB,CAChC,sBAAsB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,EAAE,EAChF,8BAAmB,CAAC,gBAAgB,EACpC,oBAAoB,EACpB,IAAI,EACJ,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAC1D,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,YAA8B;QAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,eAAe,CAAC,YAAY,EAAE,CAAC;YACjD,OAAO;QACT,CAAC;QAED,uCAAuC;QACvC,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,CAAC;QAE/B,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC;QAE9C,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;gBACpB,MAAM,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CAAC,IAA0B,EAAE,WAA6B;QAClE,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,KAAK,eAAe,CAAC,SAAS,EAAE,CAAC;YAClE,MAAM,IAAI,mCAAwB,CAChC,8CAA8C,EAC9C,8BAAmB,CAAC,YAAY,CACjC,CAAC;QACJ,CAAC;QAED,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACjD,CAAC;IAED;;OAEG;IACK,uBAAuB;QAC7B,IAAI,CAAC,IAAI,CAAC,UAAU;YAAE,OAAO;QAC7B,kBAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;QACnD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,EAAE;YAC1B,kBAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;YAC3C,kDAAkD;QACpD,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACvC,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;YAChC,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,EAAE;YACjC,IAAI,CAAC,cAAc,CAAC,SAAS,EAAE,CAAC,IAAI,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,qBAAqB,CAAC,IAAY,EAAE,MAAc;QACxD,oEAAoE;QACpE,IAAI,IAAI,CAAC,MAAM,KAAK,eAAe,CAAC,aAAa,EAAE,CAAC;YAClD,2DAA2D;YAC3D,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;YAC7C,OAAO;QACT,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC;QAE7C,8DAA8D;QAC9D,IAAI,KAA+B,CAAC;QAEpC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YAClB,+BAA+B;YAC/B,KAAK,GAAG,IAAI,mCAAwB,CAClC,0CAA0C,EAC1C,8BAAmB,CAAC,cAAc,EAClC,iBAAiB,CAClB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,iCAAiC;YACjC,KAAK,GAAG,IAAA,iCAAsB,EAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC/C,CAAC;QAED,6DAA6D;QAC7D,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;IACvC,CAAC;IAED;;OAEG;IACK,sBAAsB,CAAC,KAAY;QACzC,MAAM,eAAe,GAAG,IAAI,mCAAwB,CAClD,oBAAoB,KAAK,CAAC,OAAO,EAAE,EACnC,8BAAmB,CAAC,cAAc,EAClC,iBAAiB,EACjB,IAAI,EACJ,KAAK,CACN,CAAC;QAEF,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,eAAe,CAAC,CAAC;IACjD,CAAC;IAED;;OAEG;IACK,SAAS,CAAC,KAAsB;QACtC,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YAC1B,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,MAAM,CAAC;YAClC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpB,IAAI,CAAC,cAAc,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAED,gCAAgC;IAEhC;;OAEG;IACH,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,MAAM,KAAK,eAAe,CAAC,SAAS,CAAC;IACnD,CAAC;IAED;;OAEG;IACH,mBAAmB,CAAC,QAA0C;QAC5D,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;IAC/C,CAAC;CACF;AArND,8CAqNC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AbortSignalLike } from \"@azure/abort-controller\";\nimport type { VoiceLiveWebSocketLike } from \"./websocketLike.js\";\nimport {\n VoiceLiveConnectionError,\n VoiceLiveErrorCodes,\n classifyWebSocketClose,\n} from \"../errors/index.js\";\nimport { logger } from \"../logger.js\";\n\n/**\n * Connection state enumeration for lifecycle management\n */\nexport enum ConnectionState {\n Disconnected = \"disconnected\",\n Connecting = \"connecting\",\n Connected = \"connected\",\n Disconnecting = \"disconnecting\",\n // Removed: Reconnecting - no reconnection in fail-fast model\n}\n\n/**\n * Configuration options for connection management\n */\nexport interface ConnectionOptions {\n /** WebSocket endpoint URL */\n endpoint: string;\n /** WebSocket protocols to use */\n protocols?: string[];\n /** Connection timeout in milliseconds */\n connectionTimeout?: number;\n // Removed all reconnection options - fail fast model\n}\n\n/**\n * Event handlers for connection lifecycle events\n */\nexport interface ConnectionEventHandlers {\n /** Called when connection state changes */\n onStateChange?: (state: ConnectionState, previousState: ConnectionState) => void;\n /** Called when a message is received */\n onMessage?: (data: string | ArrayBuffer) => void;\n /** Called when an error occurs or connection is lost */\n onError?: (error: VoiceLiveConnectionError) => void;\n // Removed: onReconnectAttempt - no reconnection in fail-fast model\n}\n\n/**\n * Manages WebSocket connection lifecycle with fail-fast semantics\n */\nexport class ConnectionManager {\n private _state: ConnectionState = ConnectionState.Disconnected;\n private _previousState: ConnectionState = ConnectionState.Disconnected;\n private _websocket?: VoiceLiveWebSocketLike;\n private _abortController?: AbortController;\n\n constructor(\n private _websocketFactory: () => VoiceLiveWebSocketLike,\n private _options: ConnectionOptions,\n private _eventHandlers: ConnectionEventHandlers = {},\n ) {\n // No reconnection setup needed - fail fast model\n }\n\n /**\n * Initiates a WebSocket connection\n */\n async connect(abortSignal?: AbortSignalLike): Promise<void> {\n if (this._state === ConnectionState.Connected) {\n return;\n }\n\n if (this._state === ConnectionState.Connecting) {\n throw new VoiceLiveConnectionError(\n \"Connection attempt already in progress\",\n VoiceLiveErrorCodes.InvalidState,\n );\n }\n\n this._setState(ConnectionState.Connecting);\n\n // Create new abort controller for this connection attempt\n this._abortController = new AbortController();\n\n // Chain with provided abort signal\n if (abortSignal) {\n if (abortSignal.aborted) {\n this._abortController.abort();\n } else {\n abortSignal.addEventListener(\"abort\", () => {\n this._abortController?.abort();\n });\n }\n }\n\n try {\n this._websocket = this._websocketFactory();\n this._setupWebSocketHandlers();\n\n await this._websocket.connect(\n this._options.endpoint,\n this._options.protocols,\n this._abortController.signal,\n );\n\n this._setState(ConnectionState.Connected);\n } catch (error) {\n this._setState(ConnectionState.Disconnected);\n\n if (error instanceof VoiceLiveConnectionError) {\n throw error;\n } else {\n throw new VoiceLiveConnectionError(\n `Failed to connect: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n VoiceLiveErrorCodes.ConnectionFailed,\n \"connection_attempt\",\n true,\n error instanceof Error ? error : new Error(String(error)),\n );\n }\n }\n }\n\n /**\n * Disconnects the WebSocket connection\n */\n async disconnect(_abortSignal?: AbortSignalLike): Promise<void> {\n if (this._state === ConnectionState.Disconnected) {\n return;\n }\n\n // Abort any ongoing connection attempt\n this._abortController?.abort();\n\n this._setState(ConnectionState.Disconnecting);\n\n try {\n if (this._websocket) {\n await this._websocket.disconnect(1000, \"Client disconnect\");\n }\n } finally {\n this._setState(ConnectionState.Disconnected);\n }\n }\n\n /**\n * Sends data through the WebSocket connection\n */\n async send(data: string | ArrayBuffer, abortSignal?: AbortSignalLike): Promise<void> {\n if (!this._websocket || this._state !== ConnectionState.Connected) {\n throw new VoiceLiveConnectionError(\n \"Cannot send message: WebSocket not connected\",\n VoiceLiveErrorCodes.NotConnected,\n );\n }\n\n return this._websocket.send(data, abortSignal);\n }\n\n /**\n * Sets up event handlers for the WebSocket instance\n */\n private _setupWebSocketHandlers(): void {\n if (!this._websocket) return;\n logger.info(\"Setting up WebSocket event handlers\");\n this._websocket.onOpen(() => {\n logger.info(\"WebSocket connection opened\");\n // Connection opened - handled in connect() method\n });\n\n this._websocket.onClose((code, reason) => {\n this._handleConnectionLost(code, reason);\n });\n\n this._websocket.onError((error) => {\n this._handleConnectionError(error);\n });\n\n this._websocket.onMessage((data) => {\n this._eventHandlers.onMessage?.(data);\n });\n }\n\n /**\n * Handles any connection loss - always fatal in fail-fast model\n */\n private _handleConnectionLost(code: number, reason: string): void {\n // Check if this was an expected disconnection before changing state\n if (this._state === ConnectionState.Disconnecting) {\n // This was an expected disconnection, don't treat as error\n this._setState(ConnectionState.Disconnected);\n return;\n }\n\n this._setState(ConnectionState.Disconnected);\n\n // In fail-fast model, ANY unexpected connection loss is fatal\n let error: VoiceLiveConnectionError;\n\n if (code === 1000) {\n // Normal close, but unexpected\n error = new VoiceLiveConnectionError(\n \"WebSocket connection closed unexpectedly\",\n VoiceLiveErrorCodes.ConnectionLost,\n \"connection_lost\",\n );\n } else {\n // Use classifier for other codes\n error = classifyWebSocketClose(code, reason);\n }\n\n // Always notify of fatal error for unexpected disconnections\n this._eventHandlers.onError?.(error);\n }\n\n /**\n * Handles WebSocket errors\n */\n private _handleConnectionError(error: Error): void {\n const connectionError = new VoiceLiveConnectionError(\n `WebSocket error: ${error.message}`,\n VoiceLiveErrorCodes.WebSocketError,\n \"websocket_error\",\n true,\n error,\n );\n\n this._eventHandlers.onError?.(connectionError);\n }\n\n /**\n * Updates the connection state and notifies handlers\n */\n private _setState(state: ConnectionState): void {\n if (this._state !== state) {\n this._previousState = this._state;\n this._state = state;\n this._eventHandlers.onStateChange?.(state, this._previousState);\n }\n }\n\n // Public properties and methods\n\n /**\n * Gets the current connection state\n */\n get state(): ConnectionState {\n return this._state;\n }\n\n /**\n * Checks if the connection is currently established\n */\n get isConnected(): boolean {\n return this._state === ConnectionState.Connected;\n }\n\n /**\n * Updates the event handlers\n */\n updateEventHandlers(handlers: Partial<ConnectionEventHandlers>): void {\n Object.assign(this._eventHandlers, handlers);\n }\n}\n"]}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { AbortSignalLike } from "@azure/abort-controller";
|
|
2
|
+
import { WebSocketState, type VoiceLiveWebSocketLike, type WebSocketFactoryOptions } from "./websocketLike.js";
|
|
3
|
+
/**
|
|
4
|
+
* Browser WebSocket implementation using native WebSocket API
|
|
5
|
+
*/
|
|
6
|
+
export declare class VoiceLiveWebSocketBrowser implements VoiceLiveWebSocketLike {
|
|
7
|
+
private _ws?;
|
|
8
|
+
private _url?;
|
|
9
|
+
private _options;
|
|
10
|
+
private _onOpen?;
|
|
11
|
+
private _onClose?;
|
|
12
|
+
private _onMessage?;
|
|
13
|
+
private _onError?;
|
|
14
|
+
constructor(options?: WebSocketFactoryOptions);
|
|
15
|
+
connect(url: string, protocols?: string[], abortSignal?: AbortSignalLike): Promise<void>;
|
|
16
|
+
disconnect(code?: number, reason?: string): Promise<void>;
|
|
17
|
+
send(data: string | ArrayBuffer, abortSignal?: AbortSignalLike): Promise<void>;
|
|
18
|
+
onOpen(handler: () => void): void;
|
|
19
|
+
onClose(handler: (code: number, reason: string) => void): void;
|
|
20
|
+
onMessage(handler: (data: string | ArrayBuffer) => void): void;
|
|
21
|
+
onError(handler: (error: Error) => void): void;
|
|
22
|
+
get isConnected(): boolean;
|
|
23
|
+
get readyState(): WebSocketState;
|
|
24
|
+
get url(): string | undefined;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=websocketBrowser.d.ts.map
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.VoiceLiveWebSocketBrowser = void 0;
|
|
6
|
+
const websocketLike_js_1 = require("./websocketLike.js");
|
|
7
|
+
const connectionErrors_js_1 = require("../errors/connectionErrors.js");
|
|
8
|
+
/**
|
|
9
|
+
* Browser WebSocket implementation using native WebSocket API
|
|
10
|
+
*/
|
|
11
|
+
class VoiceLiveWebSocketBrowser {
|
|
12
|
+
_ws;
|
|
13
|
+
_url;
|
|
14
|
+
_options;
|
|
15
|
+
_onOpen;
|
|
16
|
+
_onClose;
|
|
17
|
+
_onMessage;
|
|
18
|
+
_onError;
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this._options = {
|
|
21
|
+
connectionTimeoutInMs: 30000,
|
|
22
|
+
maxMessageSize: 1024 * 1024, // 1MB
|
|
23
|
+
...options,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
async connect(url, protocols, abortSignal) {
|
|
27
|
+
if (this._ws && this._ws.readyState !== WebSocket.CLOSED) {
|
|
28
|
+
throw new connectionErrors_js_1.VoiceLiveConnectionError("WebSocket is already connected or connecting", connectionErrors_js_1.VoiceLiveErrorCodes.AlreadyConnected);
|
|
29
|
+
}
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const timeoutId = setTimeout(() => {
|
|
32
|
+
reject(new connectionErrors_js_1.VoiceLiveConnectionError(`WebSocket connection timeout after ${this._options.connectionTimeoutInMs}ms`, connectionErrors_js_1.VoiceLiveErrorCodes.ConnectionTimeout));
|
|
33
|
+
}, this._options.connectionTimeoutInMs);
|
|
34
|
+
// Handle abort signal
|
|
35
|
+
const abortHandler = () => {
|
|
36
|
+
clearTimeout(timeoutId);
|
|
37
|
+
if (this._ws) {
|
|
38
|
+
this._ws.close();
|
|
39
|
+
}
|
|
40
|
+
reject(new connectionErrors_js_1.VoiceLiveConnectionError("WebSocket connection aborted", connectionErrors_js_1.VoiceLiveErrorCodes.OperationCancelled));
|
|
41
|
+
};
|
|
42
|
+
if (abortSignal) {
|
|
43
|
+
if (abortSignal.aborted) {
|
|
44
|
+
abortHandler();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
abortSignal.addEventListener("abort", abortHandler);
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
this._ws = new WebSocket(url, protocols);
|
|
51
|
+
this._ws.binaryType = "arraybuffer";
|
|
52
|
+
this._url = url;
|
|
53
|
+
this._ws.addEventListener("open", () => {
|
|
54
|
+
clearTimeout(timeoutId);
|
|
55
|
+
if (abortSignal) {
|
|
56
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
57
|
+
}
|
|
58
|
+
this._onOpen?.();
|
|
59
|
+
resolve();
|
|
60
|
+
});
|
|
61
|
+
this._ws.addEventListener("close", (event) => {
|
|
62
|
+
clearTimeout(timeoutId);
|
|
63
|
+
if (abortSignal) {
|
|
64
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
65
|
+
}
|
|
66
|
+
this._onClose?.(event.code, event.reason);
|
|
67
|
+
});
|
|
68
|
+
this._ws.addEventListener("message", (event) => {
|
|
69
|
+
if (typeof event.data === "string") {
|
|
70
|
+
this._onMessage?.(event.data);
|
|
71
|
+
}
|
|
72
|
+
else if (event.data instanceof ArrayBuffer) {
|
|
73
|
+
this._onMessage?.(event.data);
|
|
74
|
+
}
|
|
75
|
+
else if (event.data instanceof Blob) {
|
|
76
|
+
// Convert Blob to ArrayBuffer
|
|
77
|
+
const reader = new FileReader();
|
|
78
|
+
reader.onload = () => {
|
|
79
|
+
if (reader.result instanceof ArrayBuffer) {
|
|
80
|
+
this._onMessage?.(reader.result);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
reader.onerror = () => {
|
|
84
|
+
this._onError?.(new connectionErrors_js_1.VoiceLiveConnectionError("Failed to read blob data", connectionErrors_js_1.VoiceLiveErrorCodes.WebSocketError, "blob_read"));
|
|
85
|
+
};
|
|
86
|
+
reader.readAsArrayBuffer(event.data);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
this._ws.addEventListener("error", () => {
|
|
90
|
+
clearTimeout(timeoutId);
|
|
91
|
+
if (abortSignal) {
|
|
92
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
93
|
+
}
|
|
94
|
+
const error = new connectionErrors_js_1.VoiceLiveConnectionError("WebSocket connection failed", connectionErrors_js_1.VoiceLiveErrorCodes.WebSocketError, "websocket_connection", true);
|
|
95
|
+
this._onError?.(error);
|
|
96
|
+
reject(error);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
clearTimeout(timeoutId);
|
|
101
|
+
if (abortSignal) {
|
|
102
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
103
|
+
}
|
|
104
|
+
reject(new connectionErrors_js_1.VoiceLiveConnectionError(`Failed to create WebSocket: ${error instanceof Error ? error.message : "Unknown error"}`, connectionErrors_js_1.VoiceLiveErrorCodes.ConnectionFailed, "websocket_creation", false, error instanceof Error ? error : new Error(String(error))));
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
async disconnect(code = 1000, reason) {
|
|
109
|
+
if (!this._ws || this._ws.readyState === WebSocket.CLOSED) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
return new Promise((resolve) => {
|
|
113
|
+
const closeHandler = () => {
|
|
114
|
+
resolve();
|
|
115
|
+
};
|
|
116
|
+
if (this._ws.readyState === WebSocket.CLOSING) {
|
|
117
|
+
this._ws.addEventListener("close", closeHandler, { once: true });
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this._ws.addEventListener("close", closeHandler, { once: true });
|
|
121
|
+
this._ws.close(code, reason);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
async send(data, abortSignal) {
|
|
126
|
+
if (!this._ws || this._ws.readyState !== WebSocket.OPEN) {
|
|
127
|
+
throw new connectionErrors_js_1.VoiceLiveConnectionError("WebSocket is not connected", connectionErrors_js_1.VoiceLiveErrorCodes.NotConnected);
|
|
128
|
+
}
|
|
129
|
+
if (abortSignal?.aborted) {
|
|
130
|
+
throw new connectionErrors_js_1.VoiceLiveConnectionError("Send operation aborted", connectionErrors_js_1.VoiceLiveErrorCodes.OperationCancelled);
|
|
131
|
+
}
|
|
132
|
+
return new Promise((resolve, reject) => {
|
|
133
|
+
const abortHandler = () => {
|
|
134
|
+
reject(new connectionErrors_js_1.VoiceLiveConnectionError("Send operation aborted", connectionErrors_js_1.VoiceLiveErrorCodes.OperationCancelled));
|
|
135
|
+
};
|
|
136
|
+
if (abortSignal) {
|
|
137
|
+
abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
this._ws.send(data);
|
|
141
|
+
if (abortSignal) {
|
|
142
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
143
|
+
}
|
|
144
|
+
resolve();
|
|
145
|
+
}
|
|
146
|
+
catch (error) {
|
|
147
|
+
if (abortSignal) {
|
|
148
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
149
|
+
}
|
|
150
|
+
reject(new connectionErrors_js_1.VoiceLiveConnectionError(`Failed to send WebSocket message: ${error instanceof Error ? error.message : "Unknown error"}`, connectionErrors_js_1.VoiceLiveErrorCodes.WebSocketError, "message_send", true, error instanceof Error ? error : new Error(String(error))));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
onOpen(handler) {
|
|
155
|
+
this._onOpen = handler;
|
|
156
|
+
}
|
|
157
|
+
onClose(handler) {
|
|
158
|
+
this._onClose = handler;
|
|
159
|
+
}
|
|
160
|
+
onMessage(handler) {
|
|
161
|
+
this._onMessage = handler;
|
|
162
|
+
}
|
|
163
|
+
onError(handler) {
|
|
164
|
+
this._onError = handler;
|
|
165
|
+
}
|
|
166
|
+
get isConnected() {
|
|
167
|
+
return this._ws?.readyState === WebSocket.OPEN;
|
|
168
|
+
}
|
|
169
|
+
get readyState() {
|
|
170
|
+
if (!this._ws)
|
|
171
|
+
return websocketLike_js_1.WebSocketState.Closed;
|
|
172
|
+
return this._ws.readyState;
|
|
173
|
+
}
|
|
174
|
+
get url() {
|
|
175
|
+
return this._url;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
exports.VoiceLiveWebSocketBrowser = VoiceLiveWebSocketBrowser;
|
|
179
|
+
//# sourceMappingURL=websocketBrowser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocketBrowser.js","sourceRoot":"","sources":["../../../src/websocket/websocketBrowser.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AbortSignalLike } from \"@azure/abort-controller\";\nimport {\n WebSocketState,\n type VoiceLiveWebSocketLike,\n type WebSocketFactoryOptions,\n} from \"./websocketLike.js\";\nimport { VoiceLiveConnectionError, VoiceLiveErrorCodes } from \"../errors/connectionErrors.js\";\n\n/**\n * Browser WebSocket implementation using native WebSocket API\n */\nexport class VoiceLiveWebSocketBrowser implements VoiceLiveWebSocketLike {\n private _ws?: WebSocket;\n private _url?: string;\n private _options: WebSocketFactoryOptions;\n\n private _onOpen?: () => void;\n private _onClose?: (code: number, reason: string) => void;\n private _onMessage?: (data: string | ArrayBuffer) => void;\n private _onError?: (error: Error) => void;\n\n constructor(options: WebSocketFactoryOptions = {}) {\n this._options = {\n connectionTimeoutInMs: 30000,\n maxMessageSize: 1024 * 1024, // 1MB\n ...options,\n };\n }\n\n async connect(url: string, protocols?: string[], abortSignal?: AbortSignalLike): Promise<void> {\n if (this._ws && this._ws.readyState !== WebSocket.CLOSED) {\n throw new VoiceLiveConnectionError(\n \"WebSocket is already connected or connecting\",\n VoiceLiveErrorCodes.AlreadyConnected,\n );\n }\n\n return new Promise((resolve, reject) => {\n const timeoutId = setTimeout(() => {\n reject(\n new VoiceLiveConnectionError(\n `WebSocket connection timeout after ${this._options.connectionTimeoutInMs}ms`,\n VoiceLiveErrorCodes.ConnectionTimeout,\n ),\n );\n }, this._options.connectionTimeoutInMs);\n // Handle abort signal\n const abortHandler = (): void => {\n clearTimeout(timeoutId);\n if (this._ws) {\n this._ws.close();\n }\n reject(\n new VoiceLiveConnectionError(\n \"WebSocket connection aborted\",\n VoiceLiveErrorCodes.OperationCancelled,\n ),\n );\n };\n\n if (abortSignal) {\n if (abortSignal.aborted) {\n abortHandler();\n return;\n }\n abortSignal.addEventListener(\"abort\", abortHandler);\n }\n\n try {\n this._ws = new WebSocket(url, protocols);\n this._ws.binaryType = \"arraybuffer\";\n this._url = url;\n\n this._ws.addEventListener(\"open\", () => {\n clearTimeout(timeoutId);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n this._onOpen?.();\n resolve();\n });\n\n this._ws.addEventListener(\"close\", (event: CloseEvent) => {\n clearTimeout(timeoutId);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n this._onClose?.(event.code, event.reason);\n });\n\n this._ws.addEventListener(\"message\", (event: MessageEvent) => {\n if (typeof event.data === \"string\") {\n this._onMessage?.(event.data);\n } else if (event.data instanceof ArrayBuffer) {\n this._onMessage?.(event.data);\n } else if (event.data instanceof Blob) {\n // Convert Blob to ArrayBuffer\n const reader = new FileReader();\n reader.onload = () => {\n if (reader.result instanceof ArrayBuffer) {\n this._onMessage?.(reader.result);\n }\n };\n reader.onerror = () => {\n this._onError?.(\n new VoiceLiveConnectionError(\n \"Failed to read blob data\",\n VoiceLiveErrorCodes.WebSocketError,\n \"blob_read\",\n ),\n );\n };\n reader.readAsArrayBuffer(event.data);\n }\n });\n\n this._ws.addEventListener(\"error\", () => {\n clearTimeout(timeoutId);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n const error = new VoiceLiveConnectionError(\n \"WebSocket connection failed\",\n VoiceLiveErrorCodes.WebSocketError,\n \"websocket_connection\",\n true,\n );\n this._onError?.(error);\n reject(error);\n });\n } catch (error) {\n clearTimeout(timeoutId);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n reject(\n new VoiceLiveConnectionError(\n `Failed to create WebSocket: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n VoiceLiveErrorCodes.ConnectionFailed,\n \"websocket_creation\",\n false,\n error instanceof Error ? error : new Error(String(error)),\n ),\n );\n }\n });\n }\n\n async disconnect(code: number = 1000, reason?: string): Promise<void> {\n if (!this._ws || this._ws.readyState === WebSocket.CLOSED) {\n return;\n }\n\n return new Promise((resolve) => {\n const closeHandler = (): void => {\n resolve();\n };\n\n if (this._ws!.readyState === WebSocket.CLOSING) {\n this._ws!.addEventListener(\"close\", closeHandler, { once: true });\n } else {\n this._ws!.addEventListener(\"close\", closeHandler, { once: true });\n this._ws!.close(code, reason);\n }\n });\n }\n\n async send(data: string | ArrayBuffer, abortSignal?: AbortSignalLike): Promise<void> {\n if (!this._ws || this._ws.readyState !== WebSocket.OPEN) {\n throw new VoiceLiveConnectionError(\n \"WebSocket is not connected\",\n VoiceLiveErrorCodes.NotConnected,\n );\n }\n\n if (abortSignal?.aborted) {\n throw new VoiceLiveConnectionError(\n \"Send operation aborted\",\n VoiceLiveErrorCodes.OperationCancelled,\n );\n }\n\n return new Promise((resolve, reject) => {\n const abortHandler = (): void => {\n reject(\n new VoiceLiveConnectionError(\n \"Send operation aborted\",\n VoiceLiveErrorCodes.OperationCancelled,\n ),\n );\n };\n\n if (abortSignal) {\n abortSignal.addEventListener(\"abort\", abortHandler, { once: true });\n }\n\n try {\n this._ws!.send(data);\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n resolve();\n } catch (error) {\n if (abortSignal) {\n abortSignal.removeEventListener(\"abort\", abortHandler);\n }\n reject(\n new VoiceLiveConnectionError(\n `Failed to send WebSocket message: ${error instanceof Error ? error.message : \"Unknown error\"}`,\n VoiceLiveErrorCodes.WebSocketError,\n \"message_send\",\n true,\n error instanceof Error ? error : new Error(String(error)),\n ),\n );\n }\n });\n }\n\n onOpen(handler: () => void): void {\n this._onOpen = handler;\n }\n\n onClose(handler: (code: number, reason: string) => void): void {\n this._onClose = handler;\n }\n\n onMessage(handler: (data: string | ArrayBuffer) => void): void {\n this._onMessage = handler;\n }\n\n onError(handler: (error: Error) => void): void {\n this._onError = handler;\n }\n\n get isConnected(): boolean {\n return this._ws?.readyState === WebSocket.OPEN;\n }\n\n get readyState(): WebSocketState {\n if (!this._ws) return WebSocketState.Closed;\n return this._ws.readyState as WebSocketState;\n }\n\n get url(): string | undefined {\n return this._url;\n }\n}\n"]}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { VoiceLiveWebSocketLike, VoiceLiveWebSocketFactoryLike, WebSocketFactoryOptions } from "./websocketLike.js";
|
|
2
|
+
/**
|
|
3
|
+
* Factory for creating platform-appropriate WebSocket instances
|
|
4
|
+
*/
|
|
5
|
+
export declare class VoiceLiveWebSocketFactory implements VoiceLiveWebSocketFactoryLike {
|
|
6
|
+
create(options?: WebSocketFactoryOptions): VoiceLiveWebSocketLike;
|
|
7
|
+
private _createBrowserWebSocket;
|
|
8
|
+
private _createNodeWebSocket;
|
|
9
|
+
private _detectPlatform;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Platform detection utility
|
|
13
|
+
*/
|
|
14
|
+
export declare function detectPlatform(): "browser" | "node" | "unknown";
|
|
15
|
+
/**
|
|
16
|
+
* Check if platform supports WebSocket
|
|
17
|
+
*/
|
|
18
|
+
export declare function isWebSocketSupported(): boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Default factory instance
|
|
21
|
+
*/
|
|
22
|
+
export declare const defaultWebSocketFactory: VoiceLiveWebSocketFactory;
|
|
23
|
+
//# sourceMappingURL=websocketFactory.d.ts.map
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) Microsoft Corporation.
|
|
3
|
+
// Licensed under the MIT License.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.defaultWebSocketFactory = exports.VoiceLiveWebSocketFactory = void 0;
|
|
6
|
+
exports.detectPlatform = detectPlatform;
|
|
7
|
+
exports.isWebSocketSupported = isWebSocketSupported;
|
|
8
|
+
/**
|
|
9
|
+
* Factory for creating platform-appropriate WebSocket instances
|
|
10
|
+
*/
|
|
11
|
+
class VoiceLiveWebSocketFactory {
|
|
12
|
+
create(options) {
|
|
13
|
+
const platform = this._detectPlatform();
|
|
14
|
+
switch (platform) {
|
|
15
|
+
case "browser":
|
|
16
|
+
return this._createBrowserWebSocket(options);
|
|
17
|
+
case "node":
|
|
18
|
+
return this._createNodeWebSocket(options);
|
|
19
|
+
default:
|
|
20
|
+
throw new Error(`Unsupported environment for WebSocket: ${platform}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
_createBrowserWebSocket(options) {
|
|
24
|
+
// Use dynamic require to avoid bundling Node.js specific code in browser builds
|
|
25
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
26
|
+
const { VoiceLiveWebSocketBrowser } = require("./websocketBrowser.js");
|
|
27
|
+
return new VoiceLiveWebSocketBrowser(options);
|
|
28
|
+
}
|
|
29
|
+
_createNodeWebSocket(options) {
|
|
30
|
+
// Use dynamic require to avoid bundling browser specific code in Node.js builds
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
32
|
+
const { VoiceLiveWebSocketNode } = require("./websocketNode.js");
|
|
33
|
+
return new VoiceLiveWebSocketNode(options);
|
|
34
|
+
}
|
|
35
|
+
_detectPlatform() {
|
|
36
|
+
// Check for browser environment
|
|
37
|
+
if (typeof self !== "undefined" && typeof self.WebSocket !== "undefined") {
|
|
38
|
+
return "browser";
|
|
39
|
+
}
|
|
40
|
+
// Check for Node.js environment
|
|
41
|
+
if (typeof process !== "undefined" &&
|
|
42
|
+
process.versions != null &&
|
|
43
|
+
process.versions.node != null) {
|
|
44
|
+
return "node";
|
|
45
|
+
}
|
|
46
|
+
// Check for global object (Node.js) vs window (browser)
|
|
47
|
+
if (typeof global !== "undefined" && typeof require !== "undefined") {
|
|
48
|
+
return "node";
|
|
49
|
+
}
|
|
50
|
+
return "unknown";
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.VoiceLiveWebSocketFactory = VoiceLiveWebSocketFactory;
|
|
54
|
+
/**
|
|
55
|
+
* Platform detection utility
|
|
56
|
+
*/
|
|
57
|
+
function detectPlatform() {
|
|
58
|
+
const factory = new VoiceLiveWebSocketFactory();
|
|
59
|
+
return factory._detectPlatform();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if platform supports WebSocket
|
|
63
|
+
*/
|
|
64
|
+
function isWebSocketSupported() {
|
|
65
|
+
const platform = detectPlatform();
|
|
66
|
+
switch (platform) {
|
|
67
|
+
case "browser":
|
|
68
|
+
return typeof WebSocket !== "undefined";
|
|
69
|
+
case "node":
|
|
70
|
+
try {
|
|
71
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
72
|
+
require("ws");
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
default:
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Default factory instance
|
|
84
|
+
*/
|
|
85
|
+
exports.defaultWebSocketFactory = new VoiceLiveWebSocketFactory();
|
|
86
|
+
//# sourceMappingURL=websocketFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"websocketFactory.js","sourceRoot":"","sources":["../../../src/websocket/websocketFactory.ts"],"names":[],"mappings":";AAAA,uCAAuC;AACvC,kCAAkC;;;AAoElC,wCAGC;AAKD,oDAmBC;AAvFD;;GAEG;AACH,MAAa,yBAAyB;IACpC,MAAM,CAAC,OAAiC;QACtC,MAAM,QAAQ,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QAExC,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,SAAS;gBACZ,OAAO,IAAI,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;YAE/C,KAAK,MAAM;gBACT,OAAO,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;YAE5C;gBACE,MAAM,IAAI,KAAK,CAAC,0CAA0C,QAAQ,EAAE,CAAC,CAAC;QAC1E,CAAC;IACH,CAAC;IAEO,uBAAuB,CAAC,OAAiC;QAC/D,gFAAgF;QAChF,iEAAiE;QACjE,MAAM,EAAE,yBAAyB,EAAE,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;QACvE,OAAO,IAAI,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAChD,CAAC;IAEO,oBAAoB,CAAC,OAAiC;QAC5D,gFAAgF;QAChF,iEAAiE;QACjE,MAAM,EAAE,sBAAsB,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;QACjE,OAAO,IAAI,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAC7C,CAAC;IAEO,eAAe;QACrB,gCAAgC;QAChC,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,OAAQ,IAAY,CAAC,SAAS,KAAK,WAAW,EAAE,CAAC;YAClF,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,gCAAgC;QAChC,IACE,OAAO,OAAO,KAAK,WAAW;YAC9B,OAAO,CAAC,QAAQ,IAAI,IAAI;YACxB,OAAO,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,EAC7B,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,wDAAwD;QACxD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,OAAO,OAAO,KAAK,WAAW,EAAE,CAAC;YACpE,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AApDD,8DAoDC;AAED;;GAEG;AACH,SAAgB,cAAc;IAC5B,MAAM,OAAO,GAAG,IAAI,yBAAyB,EAAE,CAAC;IAChD,OAAQ,OAAe,CAAC,eAAe,EAAE,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,SAAgB,oBAAoB;IAClC,MAAM,QAAQ,GAAG,cAAc,EAAE,CAAC;IAElC,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,SAAS;YACZ,OAAO,OAAO,SAAS,KAAK,WAAW,CAAC;QAE1C,KAAK,MAAM;YACT,IAAI,CAAC;gBACH,iEAAiE;gBACjE,OAAO,CAAC,IAAI,CAAC,CAAC;gBACd,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,KAAK,CAAC;YACf,CAAC;QAEH;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED;;GAEG;AACU,QAAA,uBAAuB,GAAG,IAAI,yBAAyB,EAAE,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type {\n VoiceLiveWebSocketLike,\n VoiceLiveWebSocketFactoryLike,\n WebSocketFactoryOptions,\n} from \"./websocketLike.js\";\n\n/**\n * Factory for creating platform-appropriate WebSocket instances\n */\nexport class VoiceLiveWebSocketFactory implements VoiceLiveWebSocketFactoryLike {\n create(options?: WebSocketFactoryOptions): VoiceLiveWebSocketLike {\n const platform = this._detectPlatform();\n\n switch (platform) {\n case \"browser\":\n return this._createBrowserWebSocket(options);\n\n case \"node\":\n return this._createNodeWebSocket(options);\n\n default:\n throw new Error(`Unsupported environment for WebSocket: ${platform}`);\n }\n }\n\n private _createBrowserWebSocket(options?: WebSocketFactoryOptions): VoiceLiveWebSocketLike {\n // Use dynamic require to avoid bundling Node.js specific code in browser builds\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { VoiceLiveWebSocketBrowser } = require(\"./websocketBrowser.js\");\n return new VoiceLiveWebSocketBrowser(options);\n }\n\n private _createNodeWebSocket(options?: WebSocketFactoryOptions): VoiceLiveWebSocketLike {\n // Use dynamic require to avoid bundling browser specific code in Node.js builds\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { VoiceLiveWebSocketNode } = require(\"./websocketNode.js\");\n return new VoiceLiveWebSocketNode(options);\n }\n\n private _detectPlatform(): \"browser\" | \"node\" | \"unknown\" {\n // Check for browser environment\n if (typeof self !== \"undefined\" && typeof (self as any).WebSocket !== \"undefined\") {\n return \"browser\";\n }\n\n // Check for Node.js environment\n if (\n typeof process !== \"undefined\" &&\n process.versions != null &&\n process.versions.node != null\n ) {\n return \"node\";\n }\n\n // Check for global object (Node.js) vs window (browser)\n if (typeof global !== \"undefined\" && typeof require !== \"undefined\") {\n return \"node\";\n }\n\n return \"unknown\";\n }\n}\n\n/**\n * Platform detection utility\n */\nexport function detectPlatform(): \"browser\" | \"node\" | \"unknown\" {\n const factory = new VoiceLiveWebSocketFactory();\n return (factory as any)._detectPlatform();\n}\n\n/**\n * Check if platform supports WebSocket\n */\nexport function isWebSocketSupported(): boolean {\n const platform = detectPlatform();\n\n switch (platform) {\n case \"browser\":\n return typeof WebSocket !== \"undefined\";\n\n case \"node\":\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n require(\"ws\");\n return true;\n } catch {\n return false;\n }\n\n default:\n return false;\n }\n}\n\n/**\n * Default factory instance\n */\nexport const defaultWebSocketFactory = new VoiceLiveWebSocketFactory();\n"]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { AbortSignalLike } from "@azure/abort-controller";
|
|
2
|
+
/**
|
|
3
|
+
* WebSocket ready state enumeration matching standard WebSocket values
|
|
4
|
+
*/
|
|
5
|
+
export declare enum WebSocketState {
|
|
6
|
+
Connecting = 0,
|
|
7
|
+
Open = 1,
|
|
8
|
+
Closing = 2,
|
|
9
|
+
Closed = 3
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Options for WebSocket creation and connection
|
|
13
|
+
*/
|
|
14
|
+
export interface WebSocketFactoryOptions {
|
|
15
|
+
/** Connection timeout in milliseconds */
|
|
16
|
+
connectionTimeoutInMs?: number;
|
|
17
|
+
/** Maximum message size in bytes */
|
|
18
|
+
maxMessageSize?: number;
|
|
19
|
+
/** Enable compression if supported */
|
|
20
|
+
compression?: boolean;
|
|
21
|
+
/** Custom headers for connection (Node.js only) */
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Platform-agnostic WebSocket interface for Voice Live connections
|
|
26
|
+
*/
|
|
27
|
+
export interface VoiceLiveWebSocketLike {
|
|
28
|
+
/**
|
|
29
|
+
* Establishes WebSocket connection to the specified URL
|
|
30
|
+
*/
|
|
31
|
+
connect(url: string, protocols?: string[], abortSignal?: AbortSignalLike): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Closes the WebSocket connection
|
|
34
|
+
*/
|
|
35
|
+
disconnect(code?: number, reason?: string): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Sends data through the WebSocket connection
|
|
38
|
+
*/
|
|
39
|
+
send(data: string | ArrayBuffer, abortSignal?: AbortSignalLike): Promise<void>;
|
|
40
|
+
/**
|
|
41
|
+
* Registers handler for connection open event
|
|
42
|
+
*/
|
|
43
|
+
onOpen(handler: () => void): void;
|
|
44
|
+
/**
|
|
45
|
+
* Registers handler for connection close event
|
|
46
|
+
*/
|
|
47
|
+
onClose(handler: (code: number, reason: string) => void): void;
|
|
48
|
+
/**
|
|
49
|
+
* Registers handler for message reception
|
|
50
|
+
*/
|
|
51
|
+
onMessage(handler: (data: string | ArrayBuffer) => void): void;
|
|
52
|
+
/**
|
|
53
|
+
* Registers handler for connection errors
|
|
54
|
+
*/
|
|
55
|
+
onError(handler: (error: Error) => void): void;
|
|
56
|
+
/**
|
|
57
|
+
* Gets current connection state
|
|
58
|
+
*/
|
|
59
|
+
readonly isConnected: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Gets current connection state enum
|
|
62
|
+
*/
|
|
63
|
+
readonly readyState: WebSocketState;
|
|
64
|
+
/**
|
|
65
|
+
* Gets the connected URL if applicable
|
|
66
|
+
*/
|
|
67
|
+
readonly url?: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Factory interface for creating WebSocket instances
|
|
71
|
+
*/
|
|
72
|
+
export interface VoiceLiveWebSocketFactoryLike {
|
|
73
|
+
/**
|
|
74
|
+
* Creates a new WebSocket instance for the target platform
|
|
75
|
+
*/
|
|
76
|
+
create(options?: WebSocketFactoryOptions): VoiceLiveWebSocketLike;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=websocketLike.d.ts.map
|