kugelaudio 0.1.11 → 0.1.13
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 +46 -15
- package/dist/index.mjs +53 -15
- package/package.json +5 -1
- package/src/client.ts +51 -18
- package/src/websocket.ts +44 -0
package/dist/index.js
CHANGED
|
@@ -135,8 +135,35 @@ function createWavBlob(audio, sampleRate) {
|
|
|
135
135
|
return new Blob([wavBuffer], { type: "audio/wav" });
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
+
// src/websocket.ts
|
|
139
|
+
var _cachedWs = null;
|
|
140
|
+
function getWebSocket() {
|
|
141
|
+
if (_cachedWs) return _cachedWs;
|
|
142
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.WebSocket !== "undefined") {
|
|
143
|
+
_cachedWs = globalThis.WebSocket;
|
|
144
|
+
return _cachedWs;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const _require = typeof require !== "undefined" ? require : Function('return typeof require !== "undefined" ? require : undefined')();
|
|
148
|
+
if (_require) {
|
|
149
|
+
const ws = _require("ws");
|
|
150
|
+
_cachedWs = ws.default || ws;
|
|
151
|
+
return _cachedWs;
|
|
152
|
+
}
|
|
153
|
+
} catch {
|
|
154
|
+
}
|
|
155
|
+
throw new Error(
|
|
156
|
+
'WebSocket not available. In Node.js, install the "ws" package: npm install ws'
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
138
160
|
// src/client.ts
|
|
139
161
|
var DEFAULT_API_URL = "https://api.kugelaudio.com";
|
|
162
|
+
function createWs(url) {
|
|
163
|
+
const WS = getWebSocket();
|
|
164
|
+
return new WS(url);
|
|
165
|
+
}
|
|
166
|
+
var WS_OPEN = 1;
|
|
140
167
|
var ModelsResource = class {
|
|
141
168
|
constructor(client) {
|
|
142
169
|
this.client = client;
|
|
@@ -212,6 +239,7 @@ var VoicesResource = class {
|
|
|
212
239
|
var TTSResource = class {
|
|
213
240
|
constructor(client) {
|
|
214
241
|
this.client = client;
|
|
242
|
+
// Using any for WebSocket to support both browser WebSocket and ws package
|
|
215
243
|
this.wsConnection = null;
|
|
216
244
|
this.wsUrl = null;
|
|
217
245
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
@@ -241,7 +269,7 @@ var TTSResource = class {
|
|
|
241
269
|
* Check if WebSocket connection is established and open.
|
|
242
270
|
*/
|
|
243
271
|
isConnected() {
|
|
244
|
-
return this.wsConnection !== null && this.wsConnection.readyState ===
|
|
272
|
+
return this.wsConnection !== null && this.wsConnection.readyState === WS_OPEN;
|
|
245
273
|
}
|
|
246
274
|
/**
|
|
247
275
|
* Generate audio from text with streaming via WebSocket.
|
|
@@ -299,7 +327,7 @@ var TTSResource = class {
|
|
|
299
327
|
*/
|
|
300
328
|
async getConnection() {
|
|
301
329
|
const url = this.buildWsUrl();
|
|
302
|
-
if (this.wsConnection && this.wsUrl === url && this.wsConnection.readyState ===
|
|
330
|
+
if (this.wsConnection && this.wsUrl === url && this.wsConnection.readyState === WS_OPEN) {
|
|
303
331
|
return this.wsConnection;
|
|
304
332
|
}
|
|
305
333
|
if (this.wsConnection) {
|
|
@@ -310,7 +338,7 @@ var TTSResource = class {
|
|
|
310
338
|
this.wsConnection = null;
|
|
311
339
|
}
|
|
312
340
|
return new Promise((resolve, reject) => {
|
|
313
|
-
const ws =
|
|
341
|
+
const ws = createWs(url);
|
|
314
342
|
ws.onopen = () => {
|
|
315
343
|
this.wsConnection = ws;
|
|
316
344
|
this.wsUrl = url;
|
|
@@ -328,7 +356,8 @@ var TTSResource = class {
|
|
|
328
356
|
setupMessageHandler(ws) {
|
|
329
357
|
ws.onmessage = (event) => {
|
|
330
358
|
try {
|
|
331
|
-
const
|
|
359
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
360
|
+
const data = JSON.parse(messageData);
|
|
332
361
|
const [requestId, pending] = [...this.pendingRequests.entries()][0] || [];
|
|
333
362
|
if (!pending) return;
|
|
334
363
|
if (data.error) {
|
|
@@ -431,7 +460,7 @@ var TTSResource = class {
|
|
|
431
460
|
streamWithoutPooling(options, callbacks) {
|
|
432
461
|
return new Promise((resolve, reject) => {
|
|
433
462
|
const url = this.buildWsUrl();
|
|
434
|
-
const ws =
|
|
463
|
+
const ws = createWs(url);
|
|
435
464
|
ws.onopen = () => {
|
|
436
465
|
callbacks.onOpen?.();
|
|
437
466
|
ws.send(JSON.stringify({
|
|
@@ -447,7 +476,8 @@ var TTSResource = class {
|
|
|
447
476
|
};
|
|
448
477
|
ws.onmessage = (event) => {
|
|
449
478
|
try {
|
|
450
|
-
const
|
|
479
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
480
|
+
const data = JSON.parse(messageData);
|
|
451
481
|
if (data.error) {
|
|
452
482
|
const error = this.parseError(data.error);
|
|
453
483
|
callbacks.onError?.(error);
|
|
@@ -593,12 +623,13 @@ var MultiContextSession = class {
|
|
|
593
623
|
authParam = "api_key";
|
|
594
624
|
}
|
|
595
625
|
const url = `${wsUrl}/ws/tts/multi?${authParam}=${this.client.apiKey}`;
|
|
596
|
-
this.ws =
|
|
626
|
+
this.ws = createWs(url);
|
|
597
627
|
this.ws.onopen = () => {
|
|
598
628
|
};
|
|
599
629
|
this.ws.onmessage = (event) => {
|
|
600
630
|
try {
|
|
601
|
-
const
|
|
631
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
632
|
+
const data = JSON.parse(messageData);
|
|
602
633
|
if (data.error) {
|
|
603
634
|
this.callbacks.onError?.(
|
|
604
635
|
new KugelAudioError(data.error),
|
|
@@ -662,7 +693,7 @@ var MultiContextSession = class {
|
|
|
662
693
|
* Create a new context with optional voice settings.
|
|
663
694
|
*/
|
|
664
695
|
createContext(contextId, options) {
|
|
665
|
-
if (!this.ws || this.ws.readyState !==
|
|
696
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) {
|
|
666
697
|
throw new KugelAudioError("WebSocket not connected");
|
|
667
698
|
}
|
|
668
699
|
const msg = {
|
|
@@ -693,7 +724,7 @@ var MultiContextSession = class {
|
|
|
693
724
|
* Send text to a specific context.
|
|
694
725
|
*/
|
|
695
726
|
send(contextId, text, flush = false) {
|
|
696
|
-
if (!this.ws || this.ws.readyState !==
|
|
727
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) {
|
|
697
728
|
throw new KugelAudioError("WebSocket not connected");
|
|
698
729
|
}
|
|
699
730
|
if (!this.contexts.has(contextId) && !this.isStarted) {
|
|
@@ -709,7 +740,7 @@ var MultiContextSession = class {
|
|
|
709
740
|
* Flush a context's buffer.
|
|
710
741
|
*/
|
|
711
742
|
flush(contextId) {
|
|
712
|
-
if (!this.ws || this.ws.readyState !==
|
|
743
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
713
744
|
this.ws.send(JSON.stringify({
|
|
714
745
|
flush: true,
|
|
715
746
|
context_id: contextId
|
|
@@ -719,7 +750,7 @@ var MultiContextSession = class {
|
|
|
719
750
|
* Close a specific context.
|
|
720
751
|
*/
|
|
721
752
|
closeContext(contextId) {
|
|
722
|
-
if (!this.ws || this.ws.readyState !==
|
|
753
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
723
754
|
this.ws.send(JSON.stringify({
|
|
724
755
|
close_context: true,
|
|
725
756
|
context_id: contextId
|
|
@@ -729,7 +760,7 @@ var MultiContextSession = class {
|
|
|
729
760
|
* Send keep-alive to reset a context's inactivity timeout.
|
|
730
761
|
*/
|
|
731
762
|
keepAlive(contextId) {
|
|
732
|
-
if (!this.ws || this.ws.readyState !==
|
|
763
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
733
764
|
this.ws.send(JSON.stringify({
|
|
734
765
|
text: "",
|
|
735
766
|
context_id: contextId
|
|
@@ -739,7 +770,7 @@ var MultiContextSession = class {
|
|
|
739
770
|
* Close the session and all contexts.
|
|
740
771
|
*/
|
|
741
772
|
close() {
|
|
742
|
-
if (this.ws && this.ws.readyState ===
|
|
773
|
+
if (this.ws && this.ws.readyState === WS_OPEN) {
|
|
743
774
|
this.ws.send(JSON.stringify({ close_socket: true }));
|
|
744
775
|
this.ws.close();
|
|
745
776
|
}
|
|
@@ -757,7 +788,7 @@ var MultiContextSession = class {
|
|
|
757
788
|
* Check if connected.
|
|
758
789
|
*/
|
|
759
790
|
get isConnected() {
|
|
760
|
-
return this.ws !== null && this.ws.readyState ===
|
|
791
|
+
return this.ws !== null && this.ws.readyState === WS_OPEN;
|
|
761
792
|
}
|
|
762
793
|
};
|
|
763
794
|
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,35 @@ function createWavBlob(audio, sampleRate) {
|
|
|
99
106
|
return new Blob([wavBuffer], { type: "audio/wav" });
|
|
100
107
|
}
|
|
101
108
|
|
|
109
|
+
// src/websocket.ts
|
|
110
|
+
var _cachedWs = null;
|
|
111
|
+
function getWebSocket() {
|
|
112
|
+
if (_cachedWs) return _cachedWs;
|
|
113
|
+
if (typeof globalThis !== "undefined" && typeof globalThis.WebSocket !== "undefined") {
|
|
114
|
+
_cachedWs = globalThis.WebSocket;
|
|
115
|
+
return _cachedWs;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const _require = typeof __require !== "undefined" ? __require : Function('return typeof require !== "undefined" ? require : undefined')();
|
|
119
|
+
if (_require) {
|
|
120
|
+
const ws = _require("ws");
|
|
121
|
+
_cachedWs = ws.default || ws;
|
|
122
|
+
return _cachedWs;
|
|
123
|
+
}
|
|
124
|
+
} catch {
|
|
125
|
+
}
|
|
126
|
+
throw new Error(
|
|
127
|
+
'WebSocket not available. In Node.js, install the "ws" package: npm install ws'
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
102
131
|
// src/client.ts
|
|
103
132
|
var DEFAULT_API_URL = "https://api.kugelaudio.com";
|
|
133
|
+
function createWs(url) {
|
|
134
|
+
const WS = getWebSocket();
|
|
135
|
+
return new WS(url);
|
|
136
|
+
}
|
|
137
|
+
var WS_OPEN = 1;
|
|
104
138
|
var ModelsResource = class {
|
|
105
139
|
constructor(client) {
|
|
106
140
|
this.client = client;
|
|
@@ -176,6 +210,7 @@ var VoicesResource = class {
|
|
|
176
210
|
var TTSResource = class {
|
|
177
211
|
constructor(client) {
|
|
178
212
|
this.client = client;
|
|
213
|
+
// Using any for WebSocket to support both browser WebSocket and ws package
|
|
179
214
|
this.wsConnection = null;
|
|
180
215
|
this.wsUrl = null;
|
|
181
216
|
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
@@ -205,7 +240,7 @@ var TTSResource = class {
|
|
|
205
240
|
* Check if WebSocket connection is established and open.
|
|
206
241
|
*/
|
|
207
242
|
isConnected() {
|
|
208
|
-
return this.wsConnection !== null && this.wsConnection.readyState ===
|
|
243
|
+
return this.wsConnection !== null && this.wsConnection.readyState === WS_OPEN;
|
|
209
244
|
}
|
|
210
245
|
/**
|
|
211
246
|
* Generate audio from text with streaming via WebSocket.
|
|
@@ -263,7 +298,7 @@ var TTSResource = class {
|
|
|
263
298
|
*/
|
|
264
299
|
async getConnection() {
|
|
265
300
|
const url = this.buildWsUrl();
|
|
266
|
-
if (this.wsConnection && this.wsUrl === url && this.wsConnection.readyState ===
|
|
301
|
+
if (this.wsConnection && this.wsUrl === url && this.wsConnection.readyState === WS_OPEN) {
|
|
267
302
|
return this.wsConnection;
|
|
268
303
|
}
|
|
269
304
|
if (this.wsConnection) {
|
|
@@ -274,7 +309,7 @@ var TTSResource = class {
|
|
|
274
309
|
this.wsConnection = null;
|
|
275
310
|
}
|
|
276
311
|
return new Promise((resolve, reject) => {
|
|
277
|
-
const ws =
|
|
312
|
+
const ws = createWs(url);
|
|
278
313
|
ws.onopen = () => {
|
|
279
314
|
this.wsConnection = ws;
|
|
280
315
|
this.wsUrl = url;
|
|
@@ -292,7 +327,8 @@ var TTSResource = class {
|
|
|
292
327
|
setupMessageHandler(ws) {
|
|
293
328
|
ws.onmessage = (event) => {
|
|
294
329
|
try {
|
|
295
|
-
const
|
|
330
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
331
|
+
const data = JSON.parse(messageData);
|
|
296
332
|
const [requestId, pending] = [...this.pendingRequests.entries()][0] || [];
|
|
297
333
|
if (!pending) return;
|
|
298
334
|
if (data.error) {
|
|
@@ -395,7 +431,7 @@ var TTSResource = class {
|
|
|
395
431
|
streamWithoutPooling(options, callbacks) {
|
|
396
432
|
return new Promise((resolve, reject) => {
|
|
397
433
|
const url = this.buildWsUrl();
|
|
398
|
-
const ws =
|
|
434
|
+
const ws = createWs(url);
|
|
399
435
|
ws.onopen = () => {
|
|
400
436
|
callbacks.onOpen?.();
|
|
401
437
|
ws.send(JSON.stringify({
|
|
@@ -411,7 +447,8 @@ var TTSResource = class {
|
|
|
411
447
|
};
|
|
412
448
|
ws.onmessage = (event) => {
|
|
413
449
|
try {
|
|
414
|
-
const
|
|
450
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
451
|
+
const data = JSON.parse(messageData);
|
|
415
452
|
if (data.error) {
|
|
416
453
|
const error = this.parseError(data.error);
|
|
417
454
|
callbacks.onError?.(error);
|
|
@@ -557,12 +594,13 @@ var MultiContextSession = class {
|
|
|
557
594
|
authParam = "api_key";
|
|
558
595
|
}
|
|
559
596
|
const url = `${wsUrl}/ws/tts/multi?${authParam}=${this.client.apiKey}`;
|
|
560
|
-
this.ws =
|
|
597
|
+
this.ws = createWs(url);
|
|
561
598
|
this.ws.onopen = () => {
|
|
562
599
|
};
|
|
563
600
|
this.ws.onmessage = (event) => {
|
|
564
601
|
try {
|
|
565
|
-
const
|
|
602
|
+
const messageData = typeof event.data === "string" ? event.data : event.data instanceof Buffer ? event.data.toString() : String(event.data);
|
|
603
|
+
const data = JSON.parse(messageData);
|
|
566
604
|
if (data.error) {
|
|
567
605
|
this.callbacks.onError?.(
|
|
568
606
|
new KugelAudioError(data.error),
|
|
@@ -626,7 +664,7 @@ var MultiContextSession = class {
|
|
|
626
664
|
* Create a new context with optional voice settings.
|
|
627
665
|
*/
|
|
628
666
|
createContext(contextId, options) {
|
|
629
|
-
if (!this.ws || this.ws.readyState !==
|
|
667
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) {
|
|
630
668
|
throw new KugelAudioError("WebSocket not connected");
|
|
631
669
|
}
|
|
632
670
|
const msg = {
|
|
@@ -657,7 +695,7 @@ var MultiContextSession = class {
|
|
|
657
695
|
* Send text to a specific context.
|
|
658
696
|
*/
|
|
659
697
|
send(contextId, text, flush = false) {
|
|
660
|
-
if (!this.ws || this.ws.readyState !==
|
|
698
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) {
|
|
661
699
|
throw new KugelAudioError("WebSocket not connected");
|
|
662
700
|
}
|
|
663
701
|
if (!this.contexts.has(contextId) && !this.isStarted) {
|
|
@@ -673,7 +711,7 @@ var MultiContextSession = class {
|
|
|
673
711
|
* Flush a context's buffer.
|
|
674
712
|
*/
|
|
675
713
|
flush(contextId) {
|
|
676
|
-
if (!this.ws || this.ws.readyState !==
|
|
714
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
677
715
|
this.ws.send(JSON.stringify({
|
|
678
716
|
flush: true,
|
|
679
717
|
context_id: contextId
|
|
@@ -683,7 +721,7 @@ var MultiContextSession = class {
|
|
|
683
721
|
* Close a specific context.
|
|
684
722
|
*/
|
|
685
723
|
closeContext(contextId) {
|
|
686
|
-
if (!this.ws || this.ws.readyState !==
|
|
724
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
687
725
|
this.ws.send(JSON.stringify({
|
|
688
726
|
close_context: true,
|
|
689
727
|
context_id: contextId
|
|
@@ -693,7 +731,7 @@ var MultiContextSession = class {
|
|
|
693
731
|
* Send keep-alive to reset a context's inactivity timeout.
|
|
694
732
|
*/
|
|
695
733
|
keepAlive(contextId) {
|
|
696
|
-
if (!this.ws || this.ws.readyState !==
|
|
734
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
697
735
|
this.ws.send(JSON.stringify({
|
|
698
736
|
text: "",
|
|
699
737
|
context_id: contextId
|
|
@@ -703,7 +741,7 @@ var MultiContextSession = class {
|
|
|
703
741
|
* Close the session and all contexts.
|
|
704
742
|
*/
|
|
705
743
|
close() {
|
|
706
|
-
if (this.ws && this.ws.readyState ===
|
|
744
|
+
if (this.ws && this.ws.readyState === WS_OPEN) {
|
|
707
745
|
this.ws.send(JSON.stringify({ close_socket: true }));
|
|
708
746
|
this.ws.close();
|
|
709
747
|
}
|
|
@@ -721,7 +759,7 @@ var MultiContextSession = class {
|
|
|
721
759
|
* Check if connected.
|
|
722
760
|
*/
|
|
723
761
|
get isConnected() {
|
|
724
|
-
return this.ws !== null && this.ws.readyState ===
|
|
762
|
+
return this.ws !== null && this.ws.readyState === WS_OPEN;
|
|
725
763
|
}
|
|
726
764
|
};
|
|
727
765
|
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.13",
|
|
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,23 @@ 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
|
+
/**
|
|
27
|
+
* Create a new WebSocket instance.
|
|
28
|
+
* Lazily resolves the constructor to avoid top-level side-effects
|
|
29
|
+
* that break server-side bundlers (Turbopack/Webpack).
|
|
30
|
+
*/
|
|
31
|
+
function createWs(url: string): WebSocket {
|
|
32
|
+
const WS = getWebSocket();
|
|
33
|
+
return new WS(url);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** WebSocket OPEN readyState constant. */
|
|
37
|
+
const WS_OPEN = 1;
|
|
38
|
+
|
|
25
39
|
/**
|
|
26
40
|
* Models resource for listing TTS models.
|
|
27
41
|
*/
|
|
@@ -111,6 +125,7 @@ class VoicesResource {
|
|
|
111
125
|
* TTS resource for text-to-speech generation.
|
|
112
126
|
*/
|
|
113
127
|
class TTSResource {
|
|
128
|
+
// Using any for WebSocket to support both browser WebSocket and ws package
|
|
114
129
|
private wsConnection: WebSocket | null = null;
|
|
115
130
|
private wsUrl: string | null = null;
|
|
116
131
|
private pendingRequests: Map<number, {
|
|
@@ -147,7 +162,7 @@ class TTSResource {
|
|
|
147
162
|
* Check if WebSocket connection is established and open.
|
|
148
163
|
*/
|
|
149
164
|
isConnected(): boolean {
|
|
150
|
-
return this.wsConnection !== null && this.wsConnection.readyState ===
|
|
165
|
+
return this.wsConnection !== null && this.wsConnection.readyState === WS_OPEN;
|
|
151
166
|
}
|
|
152
167
|
|
|
153
168
|
/**
|
|
@@ -221,7 +236,7 @@ class TTSResource {
|
|
|
221
236
|
if (
|
|
222
237
|
this.wsConnection &&
|
|
223
238
|
this.wsUrl === url &&
|
|
224
|
-
this.wsConnection.readyState ===
|
|
239
|
+
this.wsConnection.readyState === WS_OPEN
|
|
225
240
|
) {
|
|
226
241
|
return this.wsConnection;
|
|
227
242
|
}
|
|
@@ -238,7 +253,7 @@ class TTSResource {
|
|
|
238
253
|
|
|
239
254
|
// Create new connection
|
|
240
255
|
return new Promise((resolve, reject) => {
|
|
241
|
-
const ws =
|
|
256
|
+
const ws = createWs(url);
|
|
242
257
|
|
|
243
258
|
ws.onopen = () => {
|
|
244
259
|
this.wsConnection = ws;
|
|
@@ -257,9 +272,15 @@ class TTSResource {
|
|
|
257
272
|
* Setup message handler for pooled connection.
|
|
258
273
|
*/
|
|
259
274
|
private setupMessageHandler(ws: WebSocket): void {
|
|
260
|
-
ws.onmessage = (event) => {
|
|
275
|
+
ws.onmessage = (event: { data: unknown }) => {
|
|
261
276
|
try {
|
|
262
|
-
|
|
277
|
+
// Handle both browser (string) and Node.js (Buffer) message formats
|
|
278
|
+
const messageData = typeof event.data === 'string'
|
|
279
|
+
? event.data
|
|
280
|
+
: event.data instanceof Buffer
|
|
281
|
+
? event.data.toString()
|
|
282
|
+
: String(event.data);
|
|
283
|
+
const data = JSON.parse(messageData);
|
|
263
284
|
|
|
264
285
|
// Get the current pending request (we process one at a time)
|
|
265
286
|
const [requestId, pending] = [...this.pendingRequests.entries()][0] || [];
|
|
@@ -389,7 +410,7 @@ class TTSResource {
|
|
|
389
410
|
): Promise<void> {
|
|
390
411
|
return new Promise((resolve, reject) => {
|
|
391
412
|
const url = this.buildWsUrl();
|
|
392
|
-
const ws =
|
|
413
|
+
const ws = createWs(url);
|
|
393
414
|
|
|
394
415
|
ws.onopen = () => {
|
|
395
416
|
callbacks.onOpen?.();
|
|
@@ -406,9 +427,15 @@ class TTSResource {
|
|
|
406
427
|
}));
|
|
407
428
|
};
|
|
408
429
|
|
|
409
|
-
ws.onmessage = (event) => {
|
|
430
|
+
ws.onmessage = (event: { data: unknown }) => {
|
|
410
431
|
try {
|
|
411
|
-
|
|
432
|
+
// Handle both browser (string) and Node.js (Buffer) message formats
|
|
433
|
+
const messageData = typeof event.data === 'string'
|
|
434
|
+
? event.data
|
|
435
|
+
: event.data instanceof Buffer
|
|
436
|
+
? event.data.toString()
|
|
437
|
+
: String(event.data);
|
|
438
|
+
const data = JSON.parse(messageData);
|
|
412
439
|
|
|
413
440
|
if (data.error) {
|
|
414
441
|
const error = this.parseError(data.error);
|
|
@@ -580,15 +607,21 @@ class MultiContextSession {
|
|
|
580
607
|
}
|
|
581
608
|
|
|
582
609
|
const url = `${wsUrl}/ws/tts/multi?${authParam}=${this.client.apiKey}`;
|
|
583
|
-
this.ws =
|
|
610
|
+
this.ws = createWs(url);
|
|
584
611
|
|
|
585
612
|
this.ws.onopen = () => {
|
|
586
613
|
// Connection established, ready to create contexts
|
|
587
614
|
};
|
|
588
615
|
|
|
589
|
-
this.ws.onmessage = (event) => {
|
|
616
|
+
this.ws.onmessage = (event: { data: unknown }) => {
|
|
590
617
|
try {
|
|
591
|
-
|
|
618
|
+
// Handle both browser (string) and Node.js (Buffer) message formats
|
|
619
|
+
const messageData = typeof event.data === 'string'
|
|
620
|
+
? event.data
|
|
621
|
+
: event.data instanceof Buffer
|
|
622
|
+
? event.data.toString()
|
|
623
|
+
: String(event.data);
|
|
624
|
+
const data = JSON.parse(messageData);
|
|
592
625
|
|
|
593
626
|
if (data.error) {
|
|
594
627
|
this.callbacks.onError?.(
|
|
@@ -669,7 +702,7 @@ class MultiContextSession {
|
|
|
669
702
|
voiceSettings?: import('./types').ContextVoiceSettings;
|
|
670
703
|
}
|
|
671
704
|
): void {
|
|
672
|
-
if (!this.ws || this.ws.readyState !==
|
|
705
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) {
|
|
673
706
|
throw new KugelAudioError('WebSocket not connected');
|
|
674
707
|
}
|
|
675
708
|
|
|
@@ -708,7 +741,7 @@ class MultiContextSession {
|
|
|
708
741
|
* Send text to a specific context.
|
|
709
742
|
*/
|
|
710
743
|
send(contextId: string, text: string, flush = false): void {
|
|
711
|
-
if (!this.ws || this.ws.readyState !==
|
|
744
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) {
|
|
712
745
|
throw new KugelAudioError('WebSocket not connected');
|
|
713
746
|
}
|
|
714
747
|
|
|
@@ -728,7 +761,7 @@ class MultiContextSession {
|
|
|
728
761
|
* Flush a context's buffer.
|
|
729
762
|
*/
|
|
730
763
|
flush(contextId: string): void {
|
|
731
|
-
if (!this.ws || this.ws.readyState !==
|
|
764
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
732
765
|
|
|
733
766
|
this.ws.send(JSON.stringify({
|
|
734
767
|
flush: true,
|
|
@@ -740,7 +773,7 @@ class MultiContextSession {
|
|
|
740
773
|
* Close a specific context.
|
|
741
774
|
*/
|
|
742
775
|
closeContext(contextId: string): void {
|
|
743
|
-
if (!this.ws || this.ws.readyState !==
|
|
776
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
744
777
|
|
|
745
778
|
this.ws.send(JSON.stringify({
|
|
746
779
|
close_context: true,
|
|
@@ -752,7 +785,7 @@ class MultiContextSession {
|
|
|
752
785
|
* Send keep-alive to reset a context's inactivity timeout.
|
|
753
786
|
*/
|
|
754
787
|
keepAlive(contextId: string): void {
|
|
755
|
-
if (!this.ws || this.ws.readyState !==
|
|
788
|
+
if (!this.ws || this.ws.readyState !== WS_OPEN) return;
|
|
756
789
|
|
|
757
790
|
this.ws.send(JSON.stringify({
|
|
758
791
|
text: '',
|
|
@@ -764,7 +797,7 @@ class MultiContextSession {
|
|
|
764
797
|
* Close the session and all contexts.
|
|
765
798
|
*/
|
|
766
799
|
close(): void {
|
|
767
|
-
if (this.ws && this.ws.readyState ===
|
|
800
|
+
if (this.ws && this.ws.readyState === WS_OPEN) {
|
|
768
801
|
this.ws.send(JSON.stringify({ close_socket: true }));
|
|
769
802
|
this.ws.close();
|
|
770
803
|
}
|
|
@@ -784,7 +817,7 @@ class MultiContextSession {
|
|
|
784
817
|
* Check if connected.
|
|
785
818
|
*/
|
|
786
819
|
get isConnected(): boolean {
|
|
787
|
-
return this.ws !== null && this.ws.readyState ===
|
|
820
|
+
return this.ws !== null && this.ws.readyState === WS_OPEN;
|
|
788
821
|
}
|
|
789
822
|
}
|
|
790
823
|
|
package/src/websocket.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket compatibility layer for browser and Node.js environments.
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: WebSocket resolution is lazy to avoid top-level side-effects
|
|
5
|
+
* that break server-side bundlers (Turbopack / Webpack) when this module
|
|
6
|
+
* is imported in a Node.js (API route) context.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
let _cachedWs: typeof WebSocket | null = null;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get the WebSocket constructor for the current environment.
|
|
13
|
+
* Uses native WebSocket in browsers, ws package in Node.js.
|
|
14
|
+
* Result is cached after first call.
|
|
15
|
+
*/
|
|
16
|
+
export function getWebSocket(): typeof WebSocket {
|
|
17
|
+
if (_cachedWs) return _cachedWs;
|
|
18
|
+
|
|
19
|
+
// Browser environment
|
|
20
|
+
if (typeof globalThis !== 'undefined' && typeof (globalThis as any).WebSocket !== 'undefined') {
|
|
21
|
+
_cachedWs = (globalThis as any).WebSocket;
|
|
22
|
+
return _cachedWs!;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Node.js environment - use ws package via dynamic require
|
|
26
|
+
try {
|
|
27
|
+
// Use Function constructor to hide require from static analysis by bundlers
|
|
28
|
+
// eslint-disable-next-line no-new-func
|
|
29
|
+
const _require = typeof require !== 'undefined'
|
|
30
|
+
? require
|
|
31
|
+
: Function('return typeof require !== "undefined" ? require : undefined')();
|
|
32
|
+
if (_require) {
|
|
33
|
+
const ws = _require('ws');
|
|
34
|
+
_cachedWs = ws.default || ws;
|
|
35
|
+
return _cachedWs!;
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
// Fall through to error
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
throw new Error(
|
|
42
|
+
'WebSocket not available. In Node.js, install the "ws" package: npm install ws'
|
|
43
|
+
);
|
|
44
|
+
}
|