@soniox/node 2.0.0 → 2.0.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/dist/index.cjs +93 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -9
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +37 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +93 -13
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -342,10 +342,34 @@ var AsyncEventQueue = class {
|
|
|
342
342
|
return this.done;
|
|
343
343
|
}
|
|
344
344
|
/**
|
|
345
|
+
* Drop buffered events without ending the queue.
|
|
346
|
+
*
|
|
347
|
+
* Intended for owners that know their consumer has gone away (e.g. an
|
|
348
|
+
* async-iterator consumer broke out of its `for await` loop). The queue
|
|
349
|
+
* remains active and accepts future pushes. Callers must ensure no other
|
|
350
|
+
* iterator is concurrently consuming this queue, since this also drops
|
|
351
|
+
* events those consumers would have observed.
|
|
352
|
+
*/
|
|
353
|
+
clear() {
|
|
354
|
+
this.queue = [];
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
345
357
|
* Async iterator implementation.
|
|
358
|
+
*
|
|
359
|
+
* The returned iterator implements `return()` so consumers that exit
|
|
360
|
+
* `for await` early (via `break`, `throw`, or an outer `return`) cleanly
|
|
361
|
+
* release the iteration without further work. The queue itself is left
|
|
362
|
+
* in place — call {@link clear} or {@link end}/{@link abort} if buffered
|
|
363
|
+
* events should also be dropped.
|
|
346
364
|
*/
|
|
347
365
|
[Symbol.asyncIterator]() {
|
|
348
|
-
return {
|
|
366
|
+
return {
|
|
367
|
+
next: () => this.next(),
|
|
368
|
+
return: (value) => Promise.resolve({
|
|
369
|
+
value,
|
|
370
|
+
done: true
|
|
371
|
+
})
|
|
372
|
+
};
|
|
349
373
|
}
|
|
350
374
|
/**
|
|
351
375
|
* Get the next event from the queue.
|
|
@@ -695,6 +719,7 @@ function filterSpecialTokens(tokens) {
|
|
|
695
719
|
var RealtimeSttSession = class {
|
|
696
720
|
emitter = new TypedEmitter();
|
|
697
721
|
eventQueue = new AsyncEventQueue();
|
|
722
|
+
iteratorAttached = false;
|
|
698
723
|
apiKey;
|
|
699
724
|
wsBaseUrl;
|
|
700
725
|
config;
|
|
@@ -883,9 +908,26 @@ var RealtimeSttSession = class {
|
|
|
883
908
|
}
|
|
884
909
|
/**
|
|
885
910
|
* Async iterator for consuming events.
|
|
911
|
+
*
|
|
912
|
+
* The returned iterator's `return()` resets the internal iterator-attach
|
|
913
|
+
* flag and drops any buffered events, so consumers that exit `for await`
|
|
914
|
+
* early (via `break` etc.) stop accruing memory while the session keeps
|
|
915
|
+
* running.
|
|
886
916
|
*/
|
|
887
917
|
[Symbol.asyncIterator]() {
|
|
888
|
-
|
|
918
|
+
this.iteratorAttached = true;
|
|
919
|
+
const inner = this.eventQueue[Symbol.asyncIterator]();
|
|
920
|
+
return {
|
|
921
|
+
next: () => inner.next(),
|
|
922
|
+
return: (value) => {
|
|
923
|
+
this.iteratorAttached = false;
|
|
924
|
+
this.eventQueue.clear();
|
|
925
|
+
return inner.return?.(value) ?? Promise.resolve({
|
|
926
|
+
value,
|
|
927
|
+
done: true
|
|
928
|
+
});
|
|
929
|
+
}
|
|
930
|
+
};
|
|
889
931
|
}
|
|
890
932
|
/**
|
|
891
933
|
* @internal Debug-only: forcefully close the underlying WebSocket to
|
|
@@ -894,6 +936,15 @@ var RealtimeSttSession = class {
|
|
|
894
936
|
__debugForceDisconnect() {
|
|
895
937
|
this.ws?.close(4999, "debug: simulated disconnect");
|
|
896
938
|
}
|
|
939
|
+
/**
|
|
940
|
+
* Push an event to the async iterator queue only when a consumer has
|
|
941
|
+
* attached via `[Symbol.asyncIterator]()`. Listener-only consumers
|
|
942
|
+
* (the documented `.on()` pattern) never drain the queue, so pushing
|
|
943
|
+
* unconditionally would leak buffered events on long-running sessions.
|
|
944
|
+
*/
|
|
945
|
+
enqueueIfIterating(event) {
|
|
946
|
+
if (this.iteratorAttached) this.eventQueue.push(event);
|
|
947
|
+
}
|
|
897
948
|
async createWebSocket() {
|
|
898
949
|
return new Promise((resolve, reject) => {
|
|
899
950
|
try {
|
|
@@ -943,21 +994,21 @@ var RealtimeSttSession = class {
|
|
|
943
994
|
tokens: userTokens
|
|
944
995
|
};
|
|
945
996
|
this.emitter.emit("result", filteredResult);
|
|
946
|
-
this.
|
|
997
|
+
this.enqueueIfIterating({
|
|
947
998
|
kind: "result",
|
|
948
999
|
data: filteredResult
|
|
949
1000
|
});
|
|
950
1001
|
if (hasEndpoint) {
|
|
951
1002
|
this.emitter.emit("endpoint");
|
|
952
|
-
this.
|
|
1003
|
+
this.enqueueIfIterating({ kind: "endpoint" });
|
|
953
1004
|
}
|
|
954
1005
|
if (hasFinalized) {
|
|
955
1006
|
this.emitter.emit("finalized");
|
|
956
|
-
this.
|
|
1007
|
+
this.enqueueIfIterating({ kind: "finalized" });
|
|
957
1008
|
}
|
|
958
1009
|
if (result.finished) {
|
|
959
1010
|
this.emitter.emit("finished");
|
|
960
|
-
this.
|
|
1011
|
+
this.enqueueIfIterating({ kind: "finished" });
|
|
961
1012
|
this.settleFinish();
|
|
962
1013
|
this.cleanup("finished", void 0, "finished");
|
|
963
1014
|
}
|
|
@@ -1142,6 +1193,7 @@ var RealtimeTtsStream = class extends TypedEmitter {
|
|
|
1142
1193
|
streamId;
|
|
1143
1194
|
_state = "active";
|
|
1144
1195
|
audioQueue = new AsyncEventQueue();
|
|
1196
|
+
iteratorAttached = false;
|
|
1145
1197
|
connection;
|
|
1146
1198
|
ownsConnection;
|
|
1147
1199
|
/** @internal */
|
|
@@ -1220,9 +1272,37 @@ var RealtimeTtsStream = class extends TypedEmitter {
|
|
|
1220
1272
|
this._endStream();
|
|
1221
1273
|
if (this.ownsConnection) this.connection.close();
|
|
1222
1274
|
}
|
|
1223
|
-
/**
|
|
1275
|
+
/**
|
|
1276
|
+
* Async iterator that yields decoded audio chunks.
|
|
1277
|
+
*
|
|
1278
|
+
* The returned iterator's `return()` resets the internal iterator-attach
|
|
1279
|
+
* flag and drops any buffered audio, so consumers that exit `for await`
|
|
1280
|
+
* early (via `break` etc.) stop accruing memory while the stream keeps
|
|
1281
|
+
* receiving server audio.
|
|
1282
|
+
*/
|
|
1224
1283
|
[Symbol.asyncIterator]() {
|
|
1225
|
-
|
|
1284
|
+
this.iteratorAttached = true;
|
|
1285
|
+
const inner = this.audioQueue[Symbol.asyncIterator]();
|
|
1286
|
+
return {
|
|
1287
|
+
next: () => inner.next(),
|
|
1288
|
+
return: (value) => {
|
|
1289
|
+
this.iteratorAttached = false;
|
|
1290
|
+
this.audioQueue.clear();
|
|
1291
|
+
return inner.return?.(value) ?? Promise.resolve({
|
|
1292
|
+
value,
|
|
1293
|
+
done: true
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
/**
|
|
1299
|
+
* Push an audio chunk to the async iterator queue only when a consumer
|
|
1300
|
+
* has attached via `[Symbol.asyncIterator]()`. Listener-only consumers
|
|
1301
|
+
* (the documented `.on('audio', ...)` pattern) never drain the queue,
|
|
1302
|
+
* so pushing unconditionally would leak buffered chunks.
|
|
1303
|
+
*/
|
|
1304
|
+
enqueueIfIterating(chunk) {
|
|
1305
|
+
if (this.iteratorAttached) this.audioQueue.push(chunk);
|
|
1226
1306
|
}
|
|
1227
1307
|
/** @internal Dispatch a server event to this stream. */
|
|
1228
1308
|
_handleEvent(event) {
|
|
@@ -1239,7 +1319,7 @@ var RealtimeTtsStream = class extends TypedEmitter {
|
|
|
1239
1319
|
if (event.audio !== void 0) {
|
|
1240
1320
|
const chunk = decodeBase64ToUint8Array(event.audio);
|
|
1241
1321
|
this.emit("audio", chunk);
|
|
1242
|
-
this.
|
|
1322
|
+
this.enqueueIfIterating(chunk);
|
|
1243
1323
|
}
|
|
1244
1324
|
if (event.audio_end) this.emit("audioEnd");
|
|
1245
1325
|
if (event.terminated) this._endStream();
|
|
@@ -1651,7 +1731,7 @@ function getCommonValue(values) {
|
|
|
1651
1731
|
* Uses only `globalThis.fetch` — no Node-specific dependencies.
|
|
1652
1732
|
* Shared by both `@soniox/node` and `@soniox/client`.
|
|
1653
1733
|
*/
|
|
1654
|
-
const DEFAULT_MODEL = "tts-rt-v1
|
|
1734
|
+
const DEFAULT_MODEL = "tts-rt-v1";
|
|
1655
1735
|
const DEFAULT_LANGUAGE = "en";
|
|
1656
1736
|
const DEFAULT_AUDIO_FORMAT = "wav";
|
|
1657
1737
|
function buildPayload(options) {
|
|
@@ -4194,7 +4274,7 @@ function combineAbortSignals(...signals) {
|
|
|
4194
4274
|
* @example TTS (single stream)
|
|
4195
4275
|
* ```typescript
|
|
4196
4276
|
* const stream = await client.realtime.tts({
|
|
4197
|
-
* model: 'tts-rt-v1
|
|
4277
|
+
* model: 'tts-rt-v1',
|
|
4198
4278
|
* voice: 'Adrian',
|
|
4199
4279
|
* language: 'en',
|
|
4200
4280
|
* audio_format: 'wav',
|
|
@@ -4208,7 +4288,7 @@ function combineAbortSignals(...signals) {
|
|
|
4208
4288
|
* ```typescript
|
|
4209
4289
|
* const conn = await client.realtime.tts.multiStream();
|
|
4210
4290
|
* const stream = await conn.stream({
|
|
4211
|
-
* model: 'tts-rt-v1
|
|
4291
|
+
* model: 'tts-rt-v1',
|
|
4212
4292
|
* voice: 'Adrian',
|
|
4213
4293
|
* language: 'en',
|
|
4214
4294
|
* audio_format: 'wav',
|
|
@@ -4279,7 +4359,7 @@ var SonioxRealtimeApi = class {
|
|
|
4279
4359
|
*
|
|
4280
4360
|
* // WebSocket TTS
|
|
4281
4361
|
* const stream = await client.realtime.tts({
|
|
4282
|
-
* model: 'tts-rt-v1
|
|
4362
|
+
* model: 'tts-rt-v1',
|
|
4283
4363
|
* voice: 'Adrian',
|
|
4284
4364
|
* language: 'en',
|
|
4285
4365
|
* audio_format: 'wav',
|