@soniox/node 2.0.0 → 2.0.1
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 +89 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +29 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +89 -9
- 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();
|