kugelaudio 0.1.11 → 0.1.12
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/dist/index.js +37 -15
- package/dist/index.mjs +44 -15
- package/package.json +5 -1
- package/src/client.ts +45 -22
- package/src/websocket.ts +33 -0
package/dist/index.js
CHANGED
|
@@ -135,8 +135,26 @@ function createWavBlob(audio, sampleRate) {
|
|
|
135
135
|
return new Blob([wavBuffer], { type: "audio/wav" });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// src/websocket.ts
|
|
139
|
+
var isBrowser = typeof window !== "undefined" && typeof window.WebSocket !== "undefined";
|
|
140
|
+
function getWebSocket() {
|
|
141
|
+
if (isBrowser) {
|
|
142
|
+
return window.WebSocket;
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
const ws = require("ws");
|
|
146
|
+
return ws;
|
|
147
|
+
} catch {
|
|
148
|
+
throw new Error(
|
|
149
|
+
'WebSocket not available. In Node.js, install the "ws" package: npm install ws'
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
var WebSocketCompat = getWebSocket();
|
|
154
|
+
|
|
138
155
|
// src/client.ts
|
|
139
156
|
var DEFAULT_API_URL = "https://api.kugelaudio.com";
|
|
157
|
+
var WebSocketImpl = getWebSocket();
|
|
140
158
|
var ModelsResource = class {
|
|
141
159
|
constructor(client) {
|
|
142
160
|
this.client = client;
|
|
@@ -212,6 +230,7 @@ var VoicesResource = class {
|
|
|
212
230
|
var TTSResource = class {
|
|
213
231
|
constructor(client) {
|
|
214
232
|
this.client = client;
|
|
233
|
+
// Using any for WebSocket to support both browser WebSocket and ws package
|
|
215
234
|
this.wsConnection = null;
|
|
216
235
|
this.wsUrl = null;
|
|
217
236
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
@@ -241,7 +260,7 @@ var TTSResource = class {
|
|
|
241
260
|
* Check if WebSocket connection is established and open.
|
|
242
261
|
*/
|
|
243
262
|
isConnected() {
|
|
244
|
-
return this.wsConnection !== null && this.wsConnection.readyState ===
|
|
263
|
+
return this.wsConnection !== null && this.wsConnection.readyState === WebSocketImpl.OPEN;
|
|
245
264
|
}
|
|
246
265
|
/**
|
|
247
266
|
* Generate audio from text with streaming via WebSocket.
|
|
@@ -299,7 +318,7 @@ var TTSResource = class {
|
|
|
299
318
|
*/
|
|
300
319
|
async getConnection() {
|
|
301
320
|
const url = this.buildWsUrl();
|
|
302
|
-
if (this.wsConnection && this.wsUrl === url && this.wsConnection.readyState ===
|
|
321
|
+
if (this.wsConnection && this.wsUrl === url && this.wsConnection.readyState === WebSocketImpl.OPEN) {
|
|
303
322
|
return this.wsConnection;
|
|
304
323
|
}
|
|
305
324
|
if (this.wsConnection) {
|
|
@@ -310,7 +329,7 @@ var TTSResource = class {
|
|
|
310
329
|
this.wsConnection = null;
|
|
311
330
|
}
|
|
312
331
|
return new Promise((resolve, reject) => {
|
|
313
|
-
const ws = new
|
|
332
|
+
const ws = new WebSocketImpl(url);
|
|
314
333
|
ws.onopen = () => {
|
|
315
334
|
this.wsConnection = ws;
|
|
316
335
|
this.wsUrl = url;
|
|
@@ -328,7 +347,8 @@ var TTSResource = class {
|
|
|
328
347
|
setupMessageHandler(ws) {
|
|
329
348
|
ws.onmessage = (event) => {
|
|
330
349
|
try {
|
|
331
|
-
const
|
|
350
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
351
|
+
const data = JSON.parse(messageData);
|
|
332
352
|
const [requestId, pending] = [...this.pendingRequests.entries()][0] || [];
|
|
333
353
|
if (!pending) return;
|
|
334
354
|
if (data.error) {
|
|
@@ -431,7 +451,7 @@ var TTSResource = class {
|
|
|
431
451
|
streamWithoutPooling(options, callbacks) {
|
|
432
452
|
return new Promise((resolve, reject) => {
|
|
433
453
|
const url = this.buildWsUrl();
|
|
434
|
-
const ws = new
|
|
454
|
+
const ws = new WebSocketImpl(url);
|
|
435
455
|
ws.onopen = () => {
|
|
436
456
|
callbacks.onOpen?.();
|
|
437
457
|
ws.send(JSON.stringify({
|
|
@@ -447,7 +467,8 @@ var TTSResource = class {
|
|
|
447
467
|
};
|
|
448
468
|
ws.onmessage = (event) => {
|
|
449
469
|
try {
|
|
450
|
-
const
|
|
470
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
471
|
+
const data = JSON.parse(messageData);
|
|
451
472
|
if (data.error) {
|
|
452
473
|
const error = this.parseError(data.error);
|
|
453
474
|
callbacks.onError?.(error);
|
|
@@ -593,12 +614,13 @@ var MultiContextSession = class {
|
|
|
593
614
|
authParam = "api_key";
|
|
594
615
|
}
|
|
595
616
|
const url = `${wsUrl}/ws/tts/multi?${authParam}=${this.client.apiKey}`;
|
|
596
|
-
this.ws = new
|
|
617
|
+
this.ws = new WebSocketImpl(url);
|
|
597
618
|
this.ws.onopen = () => {
|
|
598
619
|
};
|
|
599
620
|
this.ws.onmessage = (event) => {
|
|
600
621
|
try {
|
|
601
|
-
const
|
|
622
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
623
|
+
const data = JSON.parse(messageData);
|
|
602
624
|
if (data.error) {
|
|
603
625
|
this.callbacks.onError?.(
|
|
604
626
|
new KugelAudioError(data.error),
|
|
@@ -662,7 +684,7 @@ var MultiContextSession = class {
|
|
|
662
684
|
* Create a new context with optional voice settings.
|
|
663
685
|
*/
|
|
664
686
|
createContext(contextId, options) {
|
|
665
|
-
if (!this.ws || this.ws.readyState !==
|
|
687
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) {
|
|
666
688
|
throw new KugelAudioError("WebSocket not connected");
|
|
667
689
|
}
|
|
668
690
|
const msg = {
|
|
@@ -693,7 +715,7 @@ var MultiContextSession = class {
|
|
|
693
715
|
* Send text to a specific context.
|
|
694
716
|
*/
|
|
695
717
|
send(contextId, text, flush = false) {
|
|
696
|
-
if (!this.ws || this.ws.readyState !==
|
|
718
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) {
|
|
697
719
|
throw new KugelAudioError("WebSocket not connected");
|
|
698
720
|
}
|
|
699
721
|
if (!this.contexts.has(contextId) && !this.isStarted) {
|
|
@@ -709,7 +731,7 @@ var MultiContextSession = class {
|
|
|
709
731
|
* Flush a context's buffer.
|
|
710
732
|
*/
|
|
711
733
|
flush(contextId) {
|
|
712
|
-
if (!this.ws || this.ws.readyState !==
|
|
734
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
713
735
|
this.ws.send(JSON.stringify({
|
|
714
736
|
flush: true,
|
|
715
737
|
context_id: contextId
|
|
@@ -719,7 +741,7 @@ var MultiContextSession = class {
|
|
|
719
741
|
* Close a specific context.
|
|
720
742
|
*/
|
|
721
743
|
closeContext(contextId) {
|
|
722
|
-
if (!this.ws || this.ws.readyState !==
|
|
744
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
723
745
|
this.ws.send(JSON.stringify({
|
|
724
746
|
close_context: true,
|
|
725
747
|
context_id: contextId
|
|
@@ -729,7 +751,7 @@ var MultiContextSession = class {
|
|
|
729
751
|
* Send keep-alive to reset a context's inactivity timeout.
|
|
730
752
|
*/
|
|
731
753
|
keepAlive(contextId) {
|
|
732
|
-
if (!this.ws || this.ws.readyState !==
|
|
754
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
733
755
|
this.ws.send(JSON.stringify({
|
|
734
756
|
text: "",
|
|
735
757
|
context_id: contextId
|
|
@@ -739,7 +761,7 @@ var MultiContextSession = class {
|
|
|
739
761
|
* Close the session and all contexts.
|
|
740
762
|
*/
|
|
741
763
|
close() {
|
|
742
|
-
if (this.ws && this.ws.readyState ===
|
|
764
|
+
if (this.ws && this.ws.readyState === WebSocketImpl.OPEN) {
|
|
743
765
|
this.ws.send(JSON.stringify({ close_socket: true }));
|
|
744
766
|
this.ws.close();
|
|
745
767
|
}
|
|
@@ -757,7 +779,7 @@ var MultiContextSession = class {
|
|
|
757
779
|
* Check if connected.
|
|
758
780
|
*/
|
|
759
781
|
get isConnected() {
|
|
760
|
-
return this.ws !== null && this.ws.readyState ===
|
|
782
|
+
return this.ws !== null && this.ws.readyState === WebSocketImpl.OPEN;
|
|
761
783
|
}
|
|
762
784
|
};
|
|
763
785
|
var KugelAudio = class _KugelAudio {
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
1
8
|
// src/errors.ts
|
|
2
9
|
var KugelAudioError = class _KugelAudioError extends Error {
|
|
3
10
|
constructor(message, statusCode) {
|
|
@@ -99,8 +106,26 @@ function createWavBlob(audio, sampleRate) {
|
|
|
99
106
|
return new Blob([wavBuffer], { type: "audio/wav" });
|
|
100
107
|
}
|
|
101
108
|
|
|
109
|
+
// src/websocket.ts
|
|
110
|
+
var isBrowser = typeof window !== "undefined" && typeof window.WebSocket !== "undefined";
|
|
111
|
+
function getWebSocket() {
|
|
112
|
+
if (isBrowser) {
|
|
113
|
+
return window.WebSocket;
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
const ws = __require("ws");
|
|
117
|
+
return ws;
|
|
118
|
+
} catch {
|
|
119
|
+
throw new Error(
|
|
120
|
+
'WebSocket not available. In Node.js, install the "ws" package: npm install ws'
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
var WebSocketCompat = getWebSocket();
|
|
125
|
+
|
|
102
126
|
// src/client.ts
|
|
103
127
|
var DEFAULT_API_URL = "https://api.kugelaudio.com";
|
|
128
|
+
var WebSocketImpl = getWebSocket();
|
|
104
129
|
var ModelsResource = class {
|
|
105
130
|
constructor(client) {
|
|
106
131
|
this.client = client;
|
|
@@ -176,6 +201,7 @@ var VoicesResource = class {
|
|
|
176
201
|
var TTSResource = class {
|
|
177
202
|
constructor(client) {
|
|
178
203
|
this.client = client;
|
|
204
|
+
// Using any for WebSocket to support both browser WebSocket and ws package
|
|
179
205
|
this.wsConnection = null;
|
|
180
206
|
this.wsUrl = null;
|
|
181
207
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
@@ -205,7 +231,7 @@ var TTSResource = class {
|
|
|
205
231
|
* Check if WebSocket connection is established and open.
|
|
206
232
|
*/
|
|
207
233
|
isConnected() {
|
|
208
|
-
return this.wsConnection !== null && this.wsConnection.readyState ===
|
|
234
|
+
return this.wsConnection !== null && this.wsConnection.readyState === WebSocketImpl.OPEN;
|
|
209
235
|
}
|
|
210
236
|
/**
|
|
211
237
|
* Generate audio from text with streaming via WebSocket.
|
|
@@ -263,7 +289,7 @@ var TTSResource = class {
|
|
|
263
289
|
*/
|
|
264
290
|
async getConnection() {
|
|
265
291
|
const url = this.buildWsUrl();
|
|
266
|
-
if (this.wsConnection && this.wsUrl === url && this.wsConnection.readyState ===
|
|
292
|
+
if (this.wsConnection && this.wsUrl === url && this.wsConnection.readyState === WebSocketImpl.OPEN) {
|
|
267
293
|
return this.wsConnection;
|
|
268
294
|
}
|
|
269
295
|
if (this.wsConnection) {
|
|
@@ -274,7 +300,7 @@ var TTSResource = class {
|
|
|
274
300
|
this.wsConnection = null;
|
|
275
301
|
}
|
|
276
302
|
return new Promise((resolve, reject) => {
|
|
277
|
-
const ws = new
|
|
303
|
+
const ws = new WebSocketImpl(url);
|
|
278
304
|
ws.onopen = () => {
|
|
279
305
|
this.wsConnection = ws;
|
|
280
306
|
this.wsUrl = url;
|
|
@@ -292,7 +318,8 @@ var TTSResource = class {
|
|
|
292
318
|
setupMessageHandler(ws) {
|
|
293
319
|
ws.onmessage = (event) => {
|
|
294
320
|
try {
|
|
295
|
-
const
|
|
321
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
322
|
+
const data = JSON.parse(messageData);
|
|
296
323
|
const [requestId, pending] = [...this.pendingRequests.entries()][0] || [];
|
|
297
324
|
if (!pending) return;
|
|
298
325
|
if (data.error) {
|
|
@@ -395,7 +422,7 @@ var TTSResource = class {
|
|
|
395
422
|
streamWithoutPooling(options, callbacks) {
|
|
396
423
|
return new Promise((resolve, reject) => {
|
|
397
424
|
const url = this.buildWsUrl();
|
|
398
|
-
const ws = new
|
|
425
|
+
const ws = new WebSocketImpl(url);
|
|
399
426
|
ws.onopen = () => {
|
|
400
427
|
callbacks.onOpen?.();
|
|
401
428
|
ws.send(JSON.stringify({
|
|
@@ -411,7 +438,8 @@ var TTSResource = class {
|
|
|
411
438
|
};
|
|
412
439
|
ws.onmessage = (event) => {
|
|
413
440
|
try {
|
|
414
|
-
const
|
|
441
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
442
|
+
const data = JSON.parse(messageData);
|
|
415
443
|
if (data.error) {
|
|
416
444
|
const error = this.parseError(data.error);
|
|
417
445
|
callbacks.onError?.(error);
|
|
@@ -557,12 +585,13 @@ var MultiContextSession = class {
|
|
|
557
585
|
authParam = "api_key";
|
|
558
586
|
}
|
|
559
587
|
const url = `${wsUrl}/ws/tts/multi?${authParam}=${this.client.apiKey}`;
|
|
560
|
-
this.ws = new
|
|
588
|
+
this.ws = new WebSocketImpl(url);
|
|
561
589
|
this.ws.onopen = () => {
|
|
562
590
|
};
|
|
563
591
|
this.ws.onmessage = (event) => {
|
|
564
592
|
try {
|
|
565
|
-
const
|
|
593
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
594
|
+
const data = JSON.parse(messageData);
|
|
566
595
|
if (data.error) {
|
|
567
596
|
this.callbacks.onError?.(
|
|
568
597
|
new KugelAudioError(data.error),
|
|
@@ -626,7 +655,7 @@ var MultiContextSession = class {
|
|
|
626
655
|
* Create a new context with optional voice settings.
|
|
627
656
|
*/
|
|
628
657
|
createContext(contextId, options) {
|
|
629
|
-
if (!this.ws || this.ws.readyState !==
|
|
658
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) {
|
|
630
659
|
throw new KugelAudioError("WebSocket not connected");
|
|
631
660
|
}
|
|
632
661
|
const msg = {
|
|
@@ -657,7 +686,7 @@ var MultiContextSession = class {
|
|
|
657
686
|
* Send text to a specific context.
|
|
658
687
|
*/
|
|
659
688
|
send(contextId, text, flush = false) {
|
|
660
|
-
if (!this.ws || this.ws.readyState !==
|
|
689
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) {
|
|
661
690
|
throw new KugelAudioError("WebSocket not connected");
|
|
662
691
|
}
|
|
663
692
|
if (!this.contexts.has(contextId) && !this.isStarted) {
|
|
@@ -673,7 +702,7 @@ var MultiContextSession = class {
|
|
|
673
702
|
* Flush a context's buffer.
|
|
674
703
|
*/
|
|
675
704
|
flush(contextId) {
|
|
676
|
-
if (!this.ws || this.ws.readyState !==
|
|
705
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
677
706
|
this.ws.send(JSON.stringify({
|
|
678
707
|
flush: true,
|
|
679
708
|
context_id: contextId
|
|
@@ -683,7 +712,7 @@ var MultiContextSession = class {
|
|
|
683
712
|
* Close a specific context.
|
|
684
713
|
*/
|
|
685
714
|
closeContext(contextId) {
|
|
686
|
-
if (!this.ws || this.ws.readyState !==
|
|
715
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
687
716
|
this.ws.send(JSON.stringify({
|
|
688
717
|
close_context: true,
|
|
689
718
|
context_id: contextId
|
|
@@ -693,7 +722,7 @@ var MultiContextSession = class {
|
|
|
693
722
|
* Send keep-alive to reset a context's inactivity timeout.
|
|
694
723
|
*/
|
|
695
724
|
keepAlive(contextId) {
|
|
696
|
-
if (!this.ws || this.ws.readyState !==
|
|
725
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
697
726
|
this.ws.send(JSON.stringify({
|
|
698
727
|
text: "",
|
|
699
728
|
context_id: contextId
|
|
@@ -703,7 +732,7 @@ var MultiContextSession = class {
|
|
|
703
732
|
* Close the session and all contexts.
|
|
704
733
|
*/
|
|
705
734
|
close() {
|
|
706
|
-
if (this.ws && this.ws.readyState ===
|
|
735
|
+
if (this.ws && this.ws.readyState === WebSocketImpl.OPEN) {
|
|
707
736
|
this.ws.send(JSON.stringify({ close_socket: true }));
|
|
708
737
|
this.ws.close();
|
|
709
738
|
}
|
|
@@ -721,7 +750,7 @@ var MultiContextSession = class {
|
|
|
721
750
|
* Check if connected.
|
|
722
751
|
*/
|
|
723
752
|
get isConnected() {
|
|
724
|
-
return this.ws !== null && this.ws.readyState ===
|
|
753
|
+
return this.ws !== null && this.ws.readyState === WebSocketImpl.OPEN;
|
|
725
754
|
}
|
|
726
755
|
};
|
|
727
756
|
var KugelAudio = class _KugelAudio {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kugelaudio",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"description": "Official JavaScript/TypeScript SDK for KugelAudio TTS API",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -52,5 +52,9 @@
|
|
|
52
52
|
},
|
|
53
53
|
"engines": {
|
|
54
54
|
"node": ">=18.0.0"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"tsx": "^4.21.0",
|
|
58
|
+
"ws": "^8.18.0"
|
|
55
59
|
}
|
|
56
60
|
}
|
package/src/client.ts
CHANGED
|
@@ -19,9 +19,13 @@ import type {
|
|
|
19
19
|
Voice
|
|
20
20
|
} from './types';
|
|
21
21
|
import { base64ToArrayBuffer } from './utils';
|
|
22
|
+
import { getWebSocket } from './websocket';
|
|
22
23
|
|
|
23
24
|
const DEFAULT_API_URL = 'https://api.kugelaudio.com';
|
|
24
25
|
|
|
26
|
+
// Get WebSocket constructor for current environment
|
|
27
|
+
const WebSocketImpl = getWebSocket();
|
|
28
|
+
|
|
25
29
|
/**
|
|
26
30
|
* Models resource for listing TTS models.
|
|
27
31
|
*/
|
|
@@ -111,7 +115,8 @@ class VoicesResource {
|
|
|
111
115
|
* TTS resource for text-to-speech generation.
|
|
112
116
|
*/
|
|
113
117
|
class TTSResource {
|
|
114
|
-
|
|
118
|
+
// Using any for WebSocket to support both browser WebSocket and ws package
|
|
119
|
+
private wsConnection: InstanceType<typeof WebSocketImpl> | null = null;
|
|
115
120
|
private wsUrl: string | null = null;
|
|
116
121
|
private pendingRequests: Map<number, {
|
|
117
122
|
callbacks: StreamCallbacks;
|
|
@@ -147,7 +152,7 @@ class TTSResource {
|
|
|
147
152
|
* Check if WebSocket connection is established and open.
|
|
148
153
|
*/
|
|
149
154
|
isConnected(): boolean {
|
|
150
|
-
return this.wsConnection !== null && this.wsConnection.readyState ===
|
|
155
|
+
return this.wsConnection !== null && this.wsConnection.readyState === WebSocketImpl.OPEN;
|
|
151
156
|
}
|
|
152
157
|
|
|
153
158
|
/**
|
|
@@ -214,14 +219,14 @@ class TTSResource {
|
|
|
214
219
|
* Get or create a WebSocket connection for connection pooling.
|
|
215
220
|
* This avoids the ~220ms connect overhead on each request.
|
|
216
221
|
*/
|
|
217
|
-
private async getConnection(): Promise<
|
|
222
|
+
private async getConnection(): Promise<InstanceType<typeof WebSocketImpl>> {
|
|
218
223
|
const url = this.buildWsUrl();
|
|
219
224
|
|
|
220
225
|
// Return existing connection if valid
|
|
221
226
|
if (
|
|
222
227
|
this.wsConnection &&
|
|
223
228
|
this.wsUrl === url &&
|
|
224
|
-
this.wsConnection.readyState ===
|
|
229
|
+
this.wsConnection.readyState === WebSocketImpl.OPEN
|
|
225
230
|
) {
|
|
226
231
|
return this.wsConnection;
|
|
227
232
|
}
|
|
@@ -238,7 +243,7 @@ class TTSResource {
|
|
|
238
243
|
|
|
239
244
|
// Create new connection
|
|
240
245
|
return new Promise((resolve, reject) => {
|
|
241
|
-
const ws = new
|
|
246
|
+
const ws = new WebSocketImpl(url);
|
|
242
247
|
|
|
243
248
|
ws.onopen = () => {
|
|
244
249
|
this.wsConnection = ws;
|
|
@@ -256,10 +261,16 @@ class TTSResource {
|
|
|
256
261
|
/**
|
|
257
262
|
* Setup message handler for pooled connection.
|
|
258
263
|
*/
|
|
259
|
-
private setupMessageHandler(ws:
|
|
260
|
-
ws.onmessage = (event) => {
|
|
264
|
+
private setupMessageHandler(ws: InstanceType<typeof WebSocketImpl>): void {
|
|
265
|
+
ws.onmessage = (event: { data: unknown }) => {
|
|
261
266
|
try {
|
|
262
|
-
|
|
267
|
+
// Handle both browser (string) and Node.js (Buffer) message formats
|
|
268
|
+
const messageData = typeof event.data === 'string'
|
|
269
|
+
? event.data
|
|
270
|
+
: event.data instanceof Buffer
|
|
271
|
+
? event.data.toString()
|
|
272
|
+
: String(event.data);
|
|
273
|
+
const data = JSON.parse(messageData);
|
|
263
274
|
|
|
264
275
|
// Get the current pending request (we process one at a time)
|
|
265
276
|
const [requestId, pending] = [...this.pendingRequests.entries()][0] || [];
|
|
@@ -389,7 +400,7 @@ class TTSResource {
|
|
|
389
400
|
): Promise<void> {
|
|
390
401
|
return new Promise((resolve, reject) => {
|
|
391
402
|
const url = this.buildWsUrl();
|
|
392
|
-
const ws = new
|
|
403
|
+
const ws = new WebSocketImpl(url);
|
|
393
404
|
|
|
394
405
|
ws.onopen = () => {
|
|
395
406
|
callbacks.onOpen?.();
|
|
@@ -406,9 +417,15 @@ class TTSResource {
|
|
|
406
417
|
}));
|
|
407
418
|
};
|
|
408
419
|
|
|
409
|
-
ws.onmessage = (event) => {
|
|
420
|
+
ws.onmessage = (event: { data: unknown }) => {
|
|
410
421
|
try {
|
|
411
|
-
|
|
422
|
+
// Handle both browser (string) and Node.js (Buffer) message formats
|
|
423
|
+
const messageData = typeof event.data === 'string'
|
|
424
|
+
? event.data
|
|
425
|
+
: event.data instanceof Buffer
|
|
426
|
+
? event.data.toString()
|
|
427
|
+
: String(event.data);
|
|
428
|
+
const data = JSON.parse(messageData);
|
|
412
429
|
|
|
413
430
|
if (data.error) {
|
|
414
431
|
const error = this.parseError(data.error);
|
|
@@ -539,7 +556,7 @@ class TTSResource {
|
|
|
539
556
|
* Multi-context WebSocket session for concurrent TTS streams.
|
|
540
557
|
*/
|
|
541
558
|
class MultiContextSession {
|
|
542
|
-
private ws:
|
|
559
|
+
private ws: InstanceType<typeof WebSocketImpl> | null = null;
|
|
543
560
|
private config: import('./types').MultiContextConfig;
|
|
544
561
|
private callbacks: import('./types').MultiContextCallbacks = {};
|
|
545
562
|
private contexts: Set<string> = new Set();
|
|
@@ -580,15 +597,21 @@ class MultiContextSession {
|
|
|
580
597
|
}
|
|
581
598
|
|
|
582
599
|
const url = `${wsUrl}/ws/tts/multi?${authParam}=${this.client.apiKey}`;
|
|
583
|
-
this.ws = new
|
|
600
|
+
this.ws = new WebSocketImpl(url);
|
|
584
601
|
|
|
585
602
|
this.ws.onopen = () => {
|
|
586
603
|
// Connection established, ready to create contexts
|
|
587
604
|
};
|
|
588
605
|
|
|
589
|
-
this.ws.onmessage = (event) => {
|
|
606
|
+
this.ws.onmessage = (event: { data: unknown }) => {
|
|
590
607
|
try {
|
|
591
|
-
|
|
608
|
+
// Handle both browser (string) and Node.js (Buffer) message formats
|
|
609
|
+
const messageData = typeof event.data === 'string'
|
|
610
|
+
? event.data
|
|
611
|
+
: event.data instanceof Buffer
|
|
612
|
+
? event.data.toString()
|
|
613
|
+
: String(event.data);
|
|
614
|
+
const data = JSON.parse(messageData);
|
|
592
615
|
|
|
593
616
|
if (data.error) {
|
|
594
617
|
this.callbacks.onError?.(
|
|
@@ -669,7 +692,7 @@ class MultiContextSession {
|
|
|
669
692
|
voiceSettings?: import('./types').ContextVoiceSettings;
|
|
670
693
|
}
|
|
671
694
|
): void {
|
|
672
|
-
if (!this.ws || this.ws.readyState !==
|
|
695
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) {
|
|
673
696
|
throw new KugelAudioError('WebSocket not connected');
|
|
674
697
|
}
|
|
675
698
|
|
|
@@ -708,7 +731,7 @@ class MultiContextSession {
|
|
|
708
731
|
* Send text to a specific context.
|
|
709
732
|
*/
|
|
710
733
|
send(contextId: string, text: string, flush = false): void {
|
|
711
|
-
if (!this.ws || this.ws.readyState !==
|
|
734
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) {
|
|
712
735
|
throw new KugelAudioError('WebSocket not connected');
|
|
713
736
|
}
|
|
714
737
|
|
|
@@ -728,7 +751,7 @@ class MultiContextSession {
|
|
|
728
751
|
* Flush a context's buffer.
|
|
729
752
|
*/
|
|
730
753
|
flush(contextId: string): void {
|
|
731
|
-
if (!this.ws || this.ws.readyState !==
|
|
754
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
732
755
|
|
|
733
756
|
this.ws.send(JSON.stringify({
|
|
734
757
|
flush: true,
|
|
@@ -740,7 +763,7 @@ class MultiContextSession {
|
|
|
740
763
|
* Close a specific context.
|
|
741
764
|
*/
|
|
742
765
|
closeContext(contextId: string): void {
|
|
743
|
-
if (!this.ws || this.ws.readyState !==
|
|
766
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
744
767
|
|
|
745
768
|
this.ws.send(JSON.stringify({
|
|
746
769
|
close_context: true,
|
|
@@ -752,7 +775,7 @@ class MultiContextSession {
|
|
|
752
775
|
* Send keep-alive to reset a context's inactivity timeout.
|
|
753
776
|
*/
|
|
754
777
|
keepAlive(contextId: string): void {
|
|
755
|
-
if (!this.ws || this.ws.readyState !==
|
|
778
|
+
if (!this.ws || this.ws.readyState !== WebSocketImpl.OPEN) return;
|
|
756
779
|
|
|
757
780
|
this.ws.send(JSON.stringify({
|
|
758
781
|
text: '',
|
|
@@ -764,7 +787,7 @@ class MultiContextSession {
|
|
|
764
787
|
* Close the session and all contexts.
|
|
765
788
|
*/
|
|
766
789
|
close(): void {
|
|
767
|
-
if (this.ws && this.ws.readyState ===
|
|
790
|
+
if (this.ws && this.ws.readyState === WebSocketImpl.OPEN) {
|
|
768
791
|
this.ws.send(JSON.stringify({ close_socket: true }));
|
|
769
792
|
this.ws.close();
|
|
770
793
|
}
|
|
@@ -784,7 +807,7 @@ class MultiContextSession {
|
|
|
784
807
|
* Check if connected.
|
|
785
808
|
*/
|
|
786
809
|
get isConnected(): boolean {
|
|
787
|
-
return this.ws !== null && this.ws.readyState ===
|
|
810
|
+
return this.ws !== null && this.ws.readyState === WebSocketImpl.OPEN;
|
|
788
811
|
}
|
|
789
812
|
}
|
|
790
813
|
|
package/src/websocket.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket compatibility layer for browser and Node.js environments.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Detect environment
|
|
6
|
+
const isBrowser = typeof window !== 'undefined' && typeof window.WebSocket !== 'undefined';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get the WebSocket constructor for the current environment.
|
|
10
|
+
* Uses native WebSocket in browsers, ws package in Node.js.
|
|
11
|
+
*/
|
|
12
|
+
export function getWebSocket(): typeof WebSocket {
|
|
13
|
+
if (isBrowser) {
|
|
14
|
+
return window.WebSocket;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Node.js environment - use ws package
|
|
18
|
+
try {
|
|
19
|
+
// Dynamic require to avoid bundler issues
|
|
20
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
21
|
+
const ws = require('ws');
|
|
22
|
+
return ws;
|
|
23
|
+
} catch {
|
|
24
|
+
throw new Error(
|
|
25
|
+
'WebSocket not available. In Node.js, install the "ws" package: npm install ws'
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* WebSocket constructor that works in both environments.
|
|
32
|
+
*/
|
|
33
|
+
export const WebSocketCompat = getWebSocket();
|