@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,183 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import { VoiceLiveConnectionError, VoiceLiveErrorCodes, classifyWebSocketClose, } from "../errors/index.js";
|
|
4
|
+
import { logger } from "../logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* Connection state enumeration for lifecycle management
|
|
7
|
+
*/
|
|
8
|
+
export var ConnectionState;
|
|
9
|
+
(function (ConnectionState) {
|
|
10
|
+
ConnectionState["Disconnected"] = "disconnected";
|
|
11
|
+
ConnectionState["Connecting"] = "connecting";
|
|
12
|
+
ConnectionState["Connected"] = "connected";
|
|
13
|
+
ConnectionState["Disconnecting"] = "disconnecting";
|
|
14
|
+
// Removed: Reconnecting - no reconnection in fail-fast model
|
|
15
|
+
})(ConnectionState || (ConnectionState = {}));
|
|
16
|
+
/**
|
|
17
|
+
* Manages WebSocket connection lifecycle with fail-fast semantics
|
|
18
|
+
*/
|
|
19
|
+
export class ConnectionManager {
|
|
20
|
+
_websocketFactory;
|
|
21
|
+
_options;
|
|
22
|
+
_eventHandlers;
|
|
23
|
+
_state = ConnectionState.Disconnected;
|
|
24
|
+
_previousState = ConnectionState.Disconnected;
|
|
25
|
+
_websocket;
|
|
26
|
+
_abortController;
|
|
27
|
+
constructor(_websocketFactory, _options, _eventHandlers = {}) {
|
|
28
|
+
this._websocketFactory = _websocketFactory;
|
|
29
|
+
this._options = _options;
|
|
30
|
+
this._eventHandlers = _eventHandlers;
|
|
31
|
+
// No reconnection setup needed - fail fast model
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Initiates a WebSocket connection
|
|
35
|
+
*/
|
|
36
|
+
async connect(abortSignal) {
|
|
37
|
+
if (this._state === ConnectionState.Connected) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (this._state === ConnectionState.Connecting) {
|
|
41
|
+
throw new VoiceLiveConnectionError("Connection attempt already in progress", VoiceLiveErrorCodes.InvalidState);
|
|
42
|
+
}
|
|
43
|
+
this._setState(ConnectionState.Connecting);
|
|
44
|
+
// Create new abort controller for this connection attempt
|
|
45
|
+
this._abortController = new AbortController();
|
|
46
|
+
// Chain with provided abort signal
|
|
47
|
+
if (abortSignal) {
|
|
48
|
+
if (abortSignal.aborted) {
|
|
49
|
+
this._abortController.abort();
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
abortSignal.addEventListener("abort", () => {
|
|
53
|
+
this._abortController?.abort();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
this._websocket = this._websocketFactory();
|
|
59
|
+
this._setupWebSocketHandlers();
|
|
60
|
+
await this._websocket.connect(this._options.endpoint, this._options.protocols, this._abortController.signal);
|
|
61
|
+
this._setState(ConnectionState.Connected);
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
this._setState(ConnectionState.Disconnected);
|
|
65
|
+
if (error instanceof VoiceLiveConnectionError) {
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
throw new VoiceLiveConnectionError(`Failed to connect: ${error instanceof Error ? error.message : "Unknown error"}`, VoiceLiveErrorCodes.ConnectionFailed, "connection_attempt", true, error instanceof Error ? error : new Error(String(error)));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Disconnects the WebSocket connection
|
|
75
|
+
*/
|
|
76
|
+
async disconnect(_abortSignal) {
|
|
77
|
+
if (this._state === ConnectionState.Disconnected) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Abort any ongoing connection attempt
|
|
81
|
+
this._abortController?.abort();
|
|
82
|
+
this._setState(ConnectionState.Disconnecting);
|
|
83
|
+
try {
|
|
84
|
+
if (this._websocket) {
|
|
85
|
+
await this._websocket.disconnect(1000, "Client disconnect");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
this._setState(ConnectionState.Disconnected);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Sends data through the WebSocket connection
|
|
94
|
+
*/
|
|
95
|
+
async send(data, abortSignal) {
|
|
96
|
+
if (!this._websocket || this._state !== ConnectionState.Connected) {
|
|
97
|
+
throw new VoiceLiveConnectionError("Cannot send message: WebSocket not connected", VoiceLiveErrorCodes.NotConnected);
|
|
98
|
+
}
|
|
99
|
+
return this._websocket.send(data, abortSignal);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Sets up event handlers for the WebSocket instance
|
|
103
|
+
*/
|
|
104
|
+
_setupWebSocketHandlers() {
|
|
105
|
+
if (!this._websocket)
|
|
106
|
+
return;
|
|
107
|
+
logger.info("Setting up WebSocket event handlers");
|
|
108
|
+
this._websocket.onOpen(() => {
|
|
109
|
+
logger.info("WebSocket connection opened");
|
|
110
|
+
// Connection opened - handled in connect() method
|
|
111
|
+
});
|
|
112
|
+
this._websocket.onClose((code, reason) => {
|
|
113
|
+
this._handleConnectionLost(code, reason);
|
|
114
|
+
});
|
|
115
|
+
this._websocket.onError((error) => {
|
|
116
|
+
this._handleConnectionError(error);
|
|
117
|
+
});
|
|
118
|
+
this._websocket.onMessage((data) => {
|
|
119
|
+
this._eventHandlers.onMessage?.(data);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Handles any connection loss - always fatal in fail-fast model
|
|
124
|
+
*/
|
|
125
|
+
_handleConnectionLost(code, reason) {
|
|
126
|
+
// Check if this was an expected disconnection before changing state
|
|
127
|
+
if (this._state === ConnectionState.Disconnecting) {
|
|
128
|
+
// This was an expected disconnection, don't treat as error
|
|
129
|
+
this._setState(ConnectionState.Disconnected);
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
this._setState(ConnectionState.Disconnected);
|
|
133
|
+
// In fail-fast model, ANY unexpected connection loss is fatal
|
|
134
|
+
let error;
|
|
135
|
+
if (code === 1000) {
|
|
136
|
+
// Normal close, but unexpected
|
|
137
|
+
error = new VoiceLiveConnectionError("WebSocket connection closed unexpectedly", VoiceLiveErrorCodes.ConnectionLost, "connection_lost");
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// Use classifier for other codes
|
|
141
|
+
error = classifyWebSocketClose(code, reason);
|
|
142
|
+
}
|
|
143
|
+
// Always notify of fatal error for unexpected disconnections
|
|
144
|
+
this._eventHandlers.onError?.(error);
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Handles WebSocket errors
|
|
148
|
+
*/
|
|
149
|
+
_handleConnectionError(error) {
|
|
150
|
+
const connectionError = new VoiceLiveConnectionError(`WebSocket error: ${error.message}`, VoiceLiveErrorCodes.WebSocketError, "websocket_error", true, error);
|
|
151
|
+
this._eventHandlers.onError?.(connectionError);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Updates the connection state and notifies handlers
|
|
155
|
+
*/
|
|
156
|
+
_setState(state) {
|
|
157
|
+
if (this._state !== state) {
|
|
158
|
+
this._previousState = this._state;
|
|
159
|
+
this._state = state;
|
|
160
|
+
this._eventHandlers.onStateChange?.(state, this._previousState);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Public properties and methods
|
|
164
|
+
/**
|
|
165
|
+
* Gets the current connection state
|
|
166
|
+
*/
|
|
167
|
+
get state() {
|
|
168
|
+
return this._state;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Checks if the connection is currently established
|
|
172
|
+
*/
|
|
173
|
+
get isConnected() {
|
|
174
|
+
return this._state === ConnectionState.Connected;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Updates the event handlers
|
|
178
|
+
*/
|
|
179
|
+
updateEventHandlers(handlers) {
|
|
180
|
+
Object.assign(this._eventHandlers, handlers);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=connectionManager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connectionManager.js","sourceRoot":"","sources":["../../../src/websocket/connectionManager.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 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,175 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
import { WebSocketState, } from "./websocketLike.js";
|
|
4
|
+
import { VoiceLiveConnectionError, VoiceLiveErrorCodes } from "../errors/connectionErrors.js";
|
|
5
|
+
/**
|
|
6
|
+
* Browser WebSocket implementation using native WebSocket API
|
|
7
|
+
*/
|
|
8
|
+
export class VoiceLiveWebSocketBrowser {
|
|
9
|
+
_ws;
|
|
10
|
+
_url;
|
|
11
|
+
_options;
|
|
12
|
+
_onOpen;
|
|
13
|
+
_onClose;
|
|
14
|
+
_onMessage;
|
|
15
|
+
_onError;
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this._options = {
|
|
18
|
+
connectionTimeoutInMs: 30000,
|
|
19
|
+
maxMessageSize: 1024 * 1024, // 1MB
|
|
20
|
+
...options,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async connect(url, protocols, abortSignal) {
|
|
24
|
+
if (this._ws && this._ws.readyState !== WebSocket.CLOSED) {
|
|
25
|
+
throw new VoiceLiveConnectionError("WebSocket is already connected or connecting", VoiceLiveErrorCodes.AlreadyConnected);
|
|
26
|
+
}
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
const timeoutId = setTimeout(() => {
|
|
29
|
+
reject(new VoiceLiveConnectionError(`WebSocket connection timeout after ${this._options.connectionTimeoutInMs}ms`, VoiceLiveErrorCodes.ConnectionTimeout));
|
|
30
|
+
}, this._options.connectionTimeoutInMs);
|
|
31
|
+
// Handle abort signal
|
|
32
|
+
const abortHandler = () => {
|
|
33
|
+
clearTimeout(timeoutId);
|
|
34
|
+
if (this._ws) {
|
|
35
|
+
this._ws.close();
|
|
36
|
+
}
|
|
37
|
+
reject(new VoiceLiveConnectionError("WebSocket connection aborted", VoiceLiveErrorCodes.OperationCancelled));
|
|
38
|
+
};
|
|
39
|
+
if (abortSignal) {
|
|
40
|
+
if (abortSignal.aborted) {
|
|
41
|
+
abortHandler();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
abortSignal.addEventListener("abort", abortHandler);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
this._ws = new WebSocket(url, protocols);
|
|
48
|
+
this._ws.binaryType = "arraybuffer";
|
|
49
|
+
this._url = url;
|
|
50
|
+
this._ws.addEventListener("open", () => {
|
|
51
|
+
clearTimeout(timeoutId);
|
|
52
|
+
if (abortSignal) {
|
|
53
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
54
|
+
}
|
|
55
|
+
this._onOpen?.();
|
|
56
|
+
resolve();
|
|
57
|
+
});
|
|
58
|
+
this._ws.addEventListener("close", (event) => {
|
|
59
|
+
clearTimeout(timeoutId);
|
|
60
|
+
if (abortSignal) {
|
|
61
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
62
|
+
}
|
|
63
|
+
this._onClose?.(event.code, event.reason);
|
|
64
|
+
});
|
|
65
|
+
this._ws.addEventListener("message", (event) => {
|
|
66
|
+
if (typeof event.data === "string") {
|
|
67
|
+
this._onMessage?.(event.data);
|
|
68
|
+
}
|
|
69
|
+
else if (event.data instanceof ArrayBuffer) {
|
|
70
|
+
this._onMessage?.(event.data);
|
|
71
|
+
}
|
|
72
|
+
else if (event.data instanceof Blob) {
|
|
73
|
+
// Convert Blob to ArrayBuffer
|
|
74
|
+
const reader = new FileReader();
|
|
75
|
+
reader.onload = () => {
|
|
76
|
+
if (reader.result instanceof ArrayBuffer) {
|
|
77
|
+
this._onMessage?.(reader.result);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
reader.onerror = () => {
|
|
81
|
+
this._onError?.(new VoiceLiveConnectionError("Failed to read blob data", VoiceLiveErrorCodes.WebSocketError, "blob_read"));
|
|
82
|
+
};
|
|
83
|
+
reader.readAsArrayBuffer(event.data);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
this._ws.addEventListener("error", () => {
|
|
87
|
+
clearTimeout(timeoutId);
|
|
88
|
+
if (abortSignal) {
|
|
89
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
90
|
+
}
|
|
91
|
+
const error = new VoiceLiveConnectionError("WebSocket connection failed", VoiceLiveErrorCodes.WebSocketError, "websocket_connection", true);
|
|
92
|
+
this._onError?.(error);
|
|
93
|
+
reject(error);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
clearTimeout(timeoutId);
|
|
98
|
+
if (abortSignal) {
|
|
99
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
100
|
+
}
|
|
101
|
+
reject(new VoiceLiveConnectionError(`Failed to create WebSocket: ${error instanceof Error ? error.message : "Unknown error"}`, VoiceLiveErrorCodes.ConnectionFailed, "websocket_creation", false, error instanceof Error ? error : new Error(String(error))));
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
async disconnect(code = 1000, reason) {
|
|
106
|
+
if (!this._ws || this._ws.readyState === WebSocket.CLOSED) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
const closeHandler = () => {
|
|
111
|
+
resolve();
|
|
112
|
+
};
|
|
113
|
+
if (this._ws.readyState === WebSocket.CLOSING) {
|
|
114
|
+
this._ws.addEventListener("close", closeHandler, { once: true });
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
this._ws.addEventListener("close", closeHandler, { once: true });
|
|
118
|
+
this._ws.close(code, reason);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async send(data, abortSignal) {
|
|
123
|
+
if (!this._ws || this._ws.readyState !== WebSocket.OPEN) {
|
|
124
|
+
throw new VoiceLiveConnectionError("WebSocket is not connected", VoiceLiveErrorCodes.NotConnected);
|
|
125
|
+
}
|
|
126
|
+
if (abortSignal?.aborted) {
|
|
127
|
+
throw new VoiceLiveConnectionError("Send operation aborted", VoiceLiveErrorCodes.OperationCancelled);
|
|
128
|
+
}
|
|
129
|
+
return new Promise((resolve, reject) => {
|
|
130
|
+
const abortHandler = () => {
|
|
131
|
+
reject(new VoiceLiveConnectionError("Send operation aborted", VoiceLiveErrorCodes.OperationCancelled));
|
|
132
|
+
};
|
|
133
|
+
if (abortSignal) {
|
|
134
|
+
abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
135
|
+
}
|
|
136
|
+
try {
|
|
137
|
+
this._ws.send(data);
|
|
138
|
+
if (abortSignal) {
|
|
139
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
140
|
+
}
|
|
141
|
+
resolve();
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
if (abortSignal) {
|
|
145
|
+
abortSignal.removeEventListener("abort", abortHandler);
|
|
146
|
+
}
|
|
147
|
+
reject(new VoiceLiveConnectionError(`Failed to send WebSocket message: ${error instanceof Error ? error.message : "Unknown error"}`, VoiceLiveErrorCodes.WebSocketError, "message_send", true, error instanceof Error ? error : new Error(String(error))));
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
onOpen(handler) {
|
|
152
|
+
this._onOpen = handler;
|
|
153
|
+
}
|
|
154
|
+
onClose(handler) {
|
|
155
|
+
this._onClose = handler;
|
|
156
|
+
}
|
|
157
|
+
onMessage(handler) {
|
|
158
|
+
this._onMessage = handler;
|
|
159
|
+
}
|
|
160
|
+
onError(handler) {
|
|
161
|
+
this._onError = handler;
|
|
162
|
+
}
|
|
163
|
+
get isConnected() {
|
|
164
|
+
return this._ws?.readyState === WebSocket.OPEN;
|
|
165
|
+
}
|
|
166
|
+
get readyState() {
|
|
167
|
+
if (!this._ws)
|
|
168
|
+
return WebSocketState.Closed;
|
|
169
|
+
return this._ws.readyState;
|
|
170
|
+
}
|
|
171
|
+
get url() {
|
|
172
|
+
return this._url;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# 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,80 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
/**
|
|
4
|
+
* Factory for creating platform-appropriate WebSocket instances
|
|
5
|
+
*/
|
|
6
|
+
export class VoiceLiveWebSocketFactory {
|
|
7
|
+
create(options) {
|
|
8
|
+
const platform = this._detectPlatform();
|
|
9
|
+
switch (platform) {
|
|
10
|
+
case "browser":
|
|
11
|
+
return this._createBrowserWebSocket(options);
|
|
12
|
+
case "node":
|
|
13
|
+
return this._createNodeWebSocket(options);
|
|
14
|
+
default:
|
|
15
|
+
throw new Error(`Unsupported environment for WebSocket: ${platform}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
_createBrowserWebSocket(options) {
|
|
19
|
+
// Use dynamic require to avoid bundling Node.js specific code in browser builds
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
21
|
+
const { VoiceLiveWebSocketBrowser } = require("./websocketBrowser.js");
|
|
22
|
+
return new VoiceLiveWebSocketBrowser(options);
|
|
23
|
+
}
|
|
24
|
+
_createNodeWebSocket(options) {
|
|
25
|
+
// Use dynamic require to avoid bundling browser specific code in Node.js builds
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
27
|
+
const { VoiceLiveWebSocketNode } = require("./websocketNode.js");
|
|
28
|
+
return new VoiceLiveWebSocketNode(options);
|
|
29
|
+
}
|
|
30
|
+
_detectPlatform() {
|
|
31
|
+
// Check for browser environment
|
|
32
|
+
if (typeof self !== "undefined" && typeof self.WebSocket !== "undefined") {
|
|
33
|
+
return "browser";
|
|
34
|
+
}
|
|
35
|
+
// Check for Node.js environment
|
|
36
|
+
if (typeof process !== "undefined" &&
|
|
37
|
+
process.versions != null &&
|
|
38
|
+
process.versions.node != null) {
|
|
39
|
+
return "node";
|
|
40
|
+
}
|
|
41
|
+
// Check for global object (Node.js) vs window (browser)
|
|
42
|
+
if (typeof global !== "undefined" && typeof require !== "undefined") {
|
|
43
|
+
return "node";
|
|
44
|
+
}
|
|
45
|
+
return "unknown";
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Platform detection utility
|
|
50
|
+
*/
|
|
51
|
+
export function detectPlatform() {
|
|
52
|
+
const factory = new VoiceLiveWebSocketFactory();
|
|
53
|
+
return factory._detectPlatform();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if platform supports WebSocket
|
|
57
|
+
*/
|
|
58
|
+
export function isWebSocketSupported() {
|
|
59
|
+
const platform = detectPlatform();
|
|
60
|
+
switch (platform) {
|
|
61
|
+
case "browser":
|
|
62
|
+
return typeof WebSocket !== "undefined";
|
|
63
|
+
case "node":
|
|
64
|
+
try {
|
|
65
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
66
|
+
require("ws");
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
default:
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Default factory instance
|
|
78
|
+
*/
|
|
79
|
+
export const defaultWebSocketFactory = new VoiceLiveWebSocketFactory();
|
|
80
|
+
//# 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;AAQlC;;GAEG;AACH,MAAM,OAAO,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;AAED;;GAEG;AACH,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,IAAI,yBAAyB,EAAE,CAAC;IAChD,OAAQ,OAAe,CAAC,eAAe,EAAE,CAAC;AAC5C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,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;AACH,MAAM,CAAC,MAAM,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
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Copyright (c) Microsoft Corporation.
|
|
2
|
+
// Licensed under the MIT License.
|
|
3
|
+
/**
|
|
4
|
+
* WebSocket ready state enumeration matching standard WebSocket values
|
|
5
|
+
*/
|
|
6
|
+
export var WebSocketState;
|
|
7
|
+
(function (WebSocketState) {
|
|
8
|
+
WebSocketState[WebSocketState["Connecting"] = 0] = "Connecting";
|
|
9
|
+
WebSocketState[WebSocketState["Open"] = 1] = "Open";
|
|
10
|
+
WebSocketState[WebSocketState["Closing"] = 2] = "Closing";
|
|
11
|
+
WebSocketState[WebSocketState["Closed"] = 3] = "Closed";
|
|
12
|
+
})(WebSocketState || (WebSocketState = {}));
|
|
13
|
+
//# sourceMappingURL=websocketLike.js.map
|