@volley/recognition-client-sdk 0.1.287 → 0.1.294
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 +680 -4683
- package/dist/index.js.map +4 -4
- package/dist/recog-client-sdk.browser.js +506 -800
- package/dist/recog-client-sdk.browser.js.map +4 -4
- package/dist/recognition-client.d.ts +1 -0
- package/dist/recognition-client.d.ts.map +1 -1
- package/dist/recognition-client.types.d.ts +18 -0
- package/dist/recognition-client.types.d.ts.map +1 -1
- package/dist/simplified-vgf-recognition-client.d.ts +19 -0
- package/dist/simplified-vgf-recognition-client.d.ts.map +1 -1
- package/dist/vgf-recognition-state.d.ts +1 -0
- package/dist/vgf-recognition-state.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/recognition-client.spec.ts +128 -0
- package/src/recognition-client.ts +23 -0
- package/src/recognition-client.types.ts +19 -0
- package/src/simplified-vgf-recognition-client.spec.ts +246 -0
- package/src/simplified-vgf-recognition-client.ts +58 -1
- package/src/vgf-recognition-state.ts +2 -1
|
@@ -88,6 +88,7 @@ export declare class RealTimeTwoWayWebSocketRecognitionClient extends WebSocketA
|
|
|
88
88
|
sendAudio(audioData: ArrayBuffer | ArrayBufferView | Blob): void;
|
|
89
89
|
private sendAudioInternal;
|
|
90
90
|
stopRecording(): Promise<void>;
|
|
91
|
+
stopAbnormally(): void;
|
|
91
92
|
getAudioUtteranceId(): string;
|
|
92
93
|
getUrl(): string;
|
|
93
94
|
getState(): ClientState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recognition-client.d.ts","sourceRoot":"","sources":["../src/recognition-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAML,KAAK,qBAAqB,EAS3B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,KAAK,EACV,kBAAkB,EAClB,uBAAuB,EACvB,8CAA8C,EAE/C,MAAM,+BAA+B,CAAC;AAUvC;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAgCD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAGxD,YAAY,EAAE,8CAA8C,EAAE,MAAM,+BAA+B,CAAC;AAgCpG;;;;;GAKG;AACH,qBAAa,wCACX,SAAQ,oBAAoB,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAC7C,YAAW,kBAAkB;IAE7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAK;IAE7C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,iBAAiB,CAA4B;IAGrD,OAAO,CAAC,iBAAiB,CAAS;IAGlC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,qBAAqB,CAAO;IACpC,OAAO,CAAC,iBAAiB,CAAK;gBAElB,MAAM,EAAE,8CAA8C;IA8ElE;;;;;;OAMG;IACH,OAAO,CAAC,GAAG;IAWX;;;OAGG;IACH,OAAO,CAAC,OAAO;IAmBA,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BvC;;;OAGG;YACW,gBAAgB;IAkIrB,SAAS,CAAC,SAAS,EAAE,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,IAAI;IAiBzE,OAAO,CAAC,iBAAiB;IAsCnB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"recognition-client.d.ts","sourceRoot":"","sources":["../src/recognition-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAML,KAAK,qBAAqB,EAS3B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAC5D,OAAO,KAAK,EACV,kBAAkB,EAClB,uBAAuB,EACvB,8CAA8C,EAE/C,MAAM,+BAA+B,CAAC;AAUvC;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3D;AAgCD;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,CAAC;AAGxD,YAAY,EAAE,8CAA8C,EAAE,MAAM,+BAA+B,CAAC;AAgCpG;;;;;GAKG;AACH,qBAAa,wCACX,SAAQ,oBAAoB,CAAC,MAAM,EAAE,GAAG,EAAE,GAAG,CAC7C,YAAW,kBAAkB;IAE7B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAK;IAE7C,OAAO,CAAC,MAAM,CAAiB;IAC/B,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,iBAAiB,CAA4B;IAGrD,OAAO,CAAC,iBAAiB,CAAS;IAGlC,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,qBAAqB,CAAO;IACpC,OAAO,CAAC,iBAAiB,CAAK;gBAElB,MAAM,EAAE,8CAA8C;IA8ElE;;;;;;OAMG;IACH,OAAO,CAAC,GAAG;IAWX;;;OAGG;IACH,OAAO,CAAC,OAAO;IAmBA,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BvC;;;OAGG;YACW,gBAAgB;IAkIrB,SAAS,CAAC,SAAS,EAAE,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,IAAI;IAiBzE,OAAO,CAAC,iBAAiB;IAsCnB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAoCpC,cAAc,IAAI,IAAI;IAwBtB,mBAAmB,IAAI,MAAM;IAI7B,MAAM,IAAI,MAAM;IAIhB,QAAQ,IAAI,WAAW;IAIvB,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,OAAO;IAIvB,UAAU,IAAI,OAAO;IAIrB,uBAAuB,IAAI,OAAO;IAIlC,mBAAmB,IAAI,OAAO;IAI9B,QAAQ,IAAI,uBAAuB;IAgBnC,SAAS,CAAC,WAAW,IAAI,IAAI;IAiE7B,SAAS,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAqB5D,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;cAYlB,SAAS,CAAC,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAE,GAAG,IAAI;IAQ/E;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAyB5B;;;OAGG;IACH,OAAO,CAAC,YAAY;CAuBrB"}
|
|
@@ -172,6 +172,24 @@ export interface IRecognitionClient {
|
|
|
172
172
|
* @returns Promise that resolves when final transcript is received
|
|
173
173
|
*/
|
|
174
174
|
stopRecording(): Promise<void>;
|
|
175
|
+
/**
|
|
176
|
+
* Force stop and immediately close connection without waiting for server
|
|
177
|
+
*
|
|
178
|
+
* WARNING: This is an abnormal shutdown that bypasses the graceful stop flow:
|
|
179
|
+
* - Does NOT wait for server to process remaining audio
|
|
180
|
+
* - Does NOT receive final transcript from server
|
|
181
|
+
* - Immediately closes WebSocket connection
|
|
182
|
+
* - Cleans up resources (buffers, listeners)
|
|
183
|
+
*
|
|
184
|
+
* Use Cases:
|
|
185
|
+
* - User explicitly cancels/abandons session
|
|
186
|
+
* - Timeout scenarios where waiting is not acceptable
|
|
187
|
+
* - Need immediate cleanup and can't wait for server
|
|
188
|
+
*
|
|
189
|
+
* RECOMMENDED: Use stopRecording() for normal shutdown.
|
|
190
|
+
* Only use this when immediate disconnection is required.
|
|
191
|
+
*/
|
|
192
|
+
stopAbnormally(): void;
|
|
175
193
|
/**
|
|
176
194
|
* Get the audio utterance ID for this session
|
|
177
195
|
* Available immediately after client construction.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recognition-client.types.d.ts","sourceRoot":"","sources":["../src/recognition-client.types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"recognition-client.types.d.ts","sourceRoot":"","sources":["../src/recognition-client.types.ts"],"names":[],"mappings":"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"}
|
|
@@ -46,6 +46,24 @@ export interface ISimplifiedVGFRecognitionClient {
|
|
|
46
46
|
* @returns Promise that resolves when transcription is complete
|
|
47
47
|
*/
|
|
48
48
|
stopRecording(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Force stop and immediately close connection without waiting for server
|
|
51
|
+
*
|
|
52
|
+
* WARNING: This is an abnormal shutdown that bypasses the graceful stop flow:
|
|
53
|
+
* - Does NOT wait for server to process remaining audio
|
|
54
|
+
* - Does NOT receive final transcript from server (VGF state set to empty)
|
|
55
|
+
* - Immediately closes WebSocket connection
|
|
56
|
+
* - Cleans up resources (buffers, listeners)
|
|
57
|
+
*
|
|
58
|
+
* Use Cases:
|
|
59
|
+
* - User explicitly cancels/abandons the session
|
|
60
|
+
* - Timeout scenarios where waiting is not acceptable
|
|
61
|
+
* - Need immediate cleanup and can't wait for server
|
|
62
|
+
*
|
|
63
|
+
* RECOMMENDED: Use stopRecording() for normal shutdown.
|
|
64
|
+
* Only use this when immediate disconnection is required.
|
|
65
|
+
*/
|
|
66
|
+
stopAbnormally(): void;
|
|
49
67
|
/**
|
|
50
68
|
* Get the current VGF recognition state
|
|
51
69
|
* @returns Current RecognitionState with all transcription data
|
|
@@ -97,6 +115,7 @@ export declare class SimplifiedVGFRecognitionClient implements ISimplifiedVGFRec
|
|
|
97
115
|
connect(): Promise<void>;
|
|
98
116
|
sendAudio(audioData: ArrayBuffer | ArrayBufferView | Blob): void;
|
|
99
117
|
stopRecording(): Promise<void>;
|
|
118
|
+
stopAbnormally(): void;
|
|
100
119
|
getAudioUtteranceId(): string;
|
|
101
120
|
getUrl(): string;
|
|
102
121
|
getState(): ClientState;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simplified-vgf-recognition-client.d.ts","sourceRoot":"","sources":["../src/simplified-vgf-recognition-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,
|
|
1
|
+
{"version":3,"file":"simplified-vgf-recognition-client.d.ts","sourceRoot":"","sources":["../src/simplified-vgf-recognition-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACH,gBAAgB,EAGnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAEH,wBAAwB,EACxB,WAAW,EACd,MAAM,+BAA+B,CAAC;AAWvC;;GAEG;AACH,MAAM,WAAW,yBAA0B,SAAQ,wBAAwB;IACvE;;;OAGG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAElD;;;OAGG;IACH,YAAY,CAAC,EAAE,gBAAgB,CAAC;CACnC;AAED;;;;;GAKG;AACH,MAAM,WAAW,+BAA+B;IAE5C;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEzB;;;OAGG;IACH,SAAS,CAAC,SAAS,EAAE,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,IAAI,CAAC;IAEjE;;;OAGG;IACH,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;;;OAgBG;IACH,cAAc,IAAI,IAAI,CAAC;IAGvB;;;OAGG;IACH,WAAW,IAAI,gBAAgB,CAAC;IAGhC;;OAEG;IACH,WAAW,IAAI,OAAO,CAAC;IAEvB;;OAEG;IACH,YAAY,IAAI,OAAO,CAAC;IAExB;;OAEG;IACH,UAAU,IAAI,OAAO,CAAC;IAEtB;;OAEG;IACH,uBAAuB,IAAI,OAAO,CAAC;IAEnC;;OAEG;IACH,mBAAmB,IAAI,OAAO,CAAC;IAG/B;;OAEG;IACH,mBAAmB,IAAI,MAAM,CAAC;IAE9B;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC;IAEjB;;OAEG;IACH,QAAQ,IAAI,WAAW,CAAC;CAE3B;AAED;;;GAGG;AACH,qBAAa,8BAA+B,YAAW,+BAA+B;IAClF,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,gBAAgB,CAAkB;IAC1C,OAAO,CAAC,mBAAmB,CAAkD;gBAEjE,MAAM,EAAE,yBAAyB;IAoGvC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAK9B,SAAS,CAAC,SAAS,EAAE,WAAW,GAAG,eAAe,GAAG,IAAI,GAAG,IAAI;IAc1D,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAOpC,cAAc,IAAI,IAAI;IAmCtB,mBAAmB,IAAI,MAAM;IAI7B,MAAM,IAAI,MAAM;IAIhB,QAAQ,IAAI,WAAW;IAIvB,WAAW,IAAI,OAAO;IAItB,YAAY,IAAI,OAAO;IAIvB,UAAU,IAAI,OAAO;IAIrB,uBAAuB,IAAI,OAAO;IAIlC,mBAAmB,IAAI,OAAO;IAM9B,WAAW,IAAI,gBAAgB;IAI/B,OAAO,CAAC,iBAAiB;CAK5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,yBAAyB,GAAG,+BAA+B,CAE5G"}
|
|
@@ -73,6 +73,7 @@ export declare const TranscriptionStatus: {
|
|
|
73
73
|
readonly NOT_STARTED: "NOT_STARTED";
|
|
74
74
|
readonly IN_PROGRESS: "IN_PROGRESS";
|
|
75
75
|
readonly FINALIZED: "FINALIZED";
|
|
76
|
+
readonly ABORTED: "ABORTED";
|
|
76
77
|
readonly ERROR: "ERROR";
|
|
77
78
|
};
|
|
78
79
|
export type TranscriptionStatusType = typeof TranscriptionStatus[keyof typeof TranscriptionStatus];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vgf-recognition-state.d.ts","sourceRoot":"","sources":["../src/vgf-recognition-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BpC,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAGxE,eAAO,MAAM,eAAe;;;;;CAKlB,CAAA;AAEV,MAAM,MAAM,mBAAmB,GAAG,OAAO,eAAe,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA;AAEtF,eAAO,MAAM,mBAAmB
|
|
1
|
+
{"version":3,"file":"vgf-recognition-state.d.ts","sourceRoot":"","sources":["../src/vgf-recognition-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0BpC,CAAA;AAEF,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAA;AAGxE,eAAO,MAAM,eAAe;;;;;CAKlB,CAAA;AAEV,MAAM,MAAM,mBAAmB,GAAG,OAAO,eAAe,CAAC,MAAM,OAAO,eAAe,CAAC,CAAA;AAEtF,eAAO,MAAM,mBAAmB;;;;;;CAMtB,CAAA;AAEV,MAAM,MAAM,uBAAuB,GAAG,OAAO,mBAAmB,CAAC,MAAM,OAAO,mBAAmB,CAAC,CAAA;AAGlG,wBAAgB,6BAA6B,CAAC,gBAAgB,EAAE,MAAM,GAAG,gBAAgB,CAOxF;AAGD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAa9F"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volley/recognition-client-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.294",
|
|
4
4
|
"description": "Recognition Service TypeScript/Node.js Client SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -53,9 +53,9 @@
|
|
|
53
53
|
"ts-jest": "^29.4.5",
|
|
54
54
|
"typescript": "^5.1.6",
|
|
55
55
|
"@recog/shared-config": "1.0.0",
|
|
56
|
-
"@recog/
|
|
56
|
+
"@recog/websocket": "1.0.0",
|
|
57
57
|
"@recog/shared-types": "1.0.0",
|
|
58
|
-
"@recog/
|
|
58
|
+
"@recog/shared-utils": "1.0.0"
|
|
59
59
|
},
|
|
60
60
|
"keywords": [
|
|
61
61
|
"recognition",
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { RealTimeTwoWayWebSocketRecognitionClient } from './recognition-client';
|
|
6
6
|
import { ClientState } from './recognition-client.types';
|
|
7
|
+
import { RecognitionResultTypeV1 } from '@recog/shared-types';
|
|
7
8
|
import { WebSocket as MockWebSocket } from 'ws';
|
|
8
9
|
|
|
9
10
|
// Mock WebSocket
|
|
@@ -771,4 +772,131 @@ describe('RealTimeTwoWayWebSocketRecognitionClient', () => {
|
|
|
771
772
|
});
|
|
772
773
|
messageHandler(readyMessage);
|
|
773
774
|
}
|
|
775
|
+
|
|
776
|
+
describe('stopAbnormally', () => {
|
|
777
|
+
beforeEach(() => {
|
|
778
|
+
// Recreate client with fresh mocks
|
|
779
|
+
jest.clearAllMocks();
|
|
780
|
+
|
|
781
|
+
client = new RealTimeTwoWayWebSocketRecognitionClient({
|
|
782
|
+
url: 'ws://localhost:3000',
|
|
783
|
+
onTranscript: jest.fn(),
|
|
784
|
+
onError: jest.fn(),
|
|
785
|
+
onConnected: jest.fn(),
|
|
786
|
+
onDisconnected: jest.fn()
|
|
787
|
+
});
|
|
788
|
+
|
|
789
|
+
// Access the mock WebSocket through the MockWebSocket constructor
|
|
790
|
+
const MockWsConstructor = MockWebSocket as jest.MockedClass<typeof MockWebSocket>;
|
|
791
|
+
mockWs = MockWsConstructor.mock.results[MockWsConstructor.mock.results.length - 1]?.value;
|
|
792
|
+
|
|
793
|
+
// Ensure mockWs has necessary methods
|
|
794
|
+
if (mockWs) {
|
|
795
|
+
mockWs.on = mockWs.on || jest.fn().mockReturnThis();
|
|
796
|
+
mockWs.send = mockWs.send || jest.fn();
|
|
797
|
+
mockWs.close = mockWs.close || jest.fn();
|
|
798
|
+
mockWs.readyState = MockWebSocket.CONNECTING;
|
|
799
|
+
}
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
it('should immediately close WebSocket connection', async () => {
|
|
803
|
+
await setupReadyClient();
|
|
804
|
+
expect(client.getState()).toBe(ClientState.READY);
|
|
805
|
+
|
|
806
|
+
client.stopAbnormally();
|
|
807
|
+
|
|
808
|
+
expect(mockWs.close).toHaveBeenCalledWith(1000, 'Client abnormal stop');
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
it('should update state to STOPPED', async () => {
|
|
812
|
+
await setupReadyClient();
|
|
813
|
+
|
|
814
|
+
client.stopAbnormally();
|
|
815
|
+
|
|
816
|
+
expect(client.getState()).toBe(ClientState.STOPPED);
|
|
817
|
+
});
|
|
818
|
+
|
|
819
|
+
it('should work from any state', () => {
|
|
820
|
+
// Test from INITIAL state
|
|
821
|
+
expect(client.getState()).toBe(ClientState.INITIAL);
|
|
822
|
+
client.stopAbnormally();
|
|
823
|
+
expect(client.getState()).toBe(ClientState.STOPPED);
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
it('should clean up resources', async () => {
|
|
827
|
+
await setupReadyClient();
|
|
828
|
+
|
|
829
|
+
// Send some audio to populate buffers
|
|
830
|
+
client.sendAudio(new ArrayBuffer(1000));
|
|
831
|
+
|
|
832
|
+
// Verify audio was sent
|
|
833
|
+
const statsBefore = client.getStats();
|
|
834
|
+
expect(statsBefore.audioBytesSent).toBeGreaterThan(0);
|
|
835
|
+
|
|
836
|
+
client.stopAbnormally();
|
|
837
|
+
|
|
838
|
+
// Cleanup resets stats
|
|
839
|
+
const statsAfter = client.getStats();
|
|
840
|
+
expect(statsAfter.audioBytesSent).toBe(0);
|
|
841
|
+
expect(statsAfter.audioChunksSent).toBe(0);
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('should not send stop signal to server (immediate disconnect)', async () => {
|
|
845
|
+
await setupReadyClient();
|
|
846
|
+
jest.clearAllMocks(); // Clear connection setup messages
|
|
847
|
+
|
|
848
|
+
client.stopAbnormally();
|
|
849
|
+
|
|
850
|
+
// Should NOT send stop recording signal (unlike stopRecording)
|
|
851
|
+
// Only closes the WebSocket
|
|
852
|
+
expect(mockWs.send).not.toHaveBeenCalled();
|
|
853
|
+
expect(mockWs.close).toHaveBeenCalled();
|
|
854
|
+
});
|
|
855
|
+
|
|
856
|
+
it('should differ from stopRecording behavior', async () => {
|
|
857
|
+
await setupReadyClient();
|
|
858
|
+
|
|
859
|
+
// stopRecording sends control signal and waits
|
|
860
|
+
jest.clearAllMocks();
|
|
861
|
+
const stopPromise = client.stopRecording();
|
|
862
|
+
|
|
863
|
+
// Verify control signal was sent
|
|
864
|
+
expect(mockWs.send).toHaveBeenCalled();
|
|
865
|
+
const sendCall = mockWs.send.mock.calls[0][0];
|
|
866
|
+
const message = JSON.parse(sendCall);
|
|
867
|
+
expect(message.data.signal).toBe('STOP_RECORDING');
|
|
868
|
+
|
|
869
|
+
// Complete the stop
|
|
870
|
+
const messageHandler = mockWs.on.mock.calls.find((call: any[]) => call[0] === 'message')[1];
|
|
871
|
+
messageHandler(JSON.stringify({
|
|
872
|
+
v: 1,
|
|
873
|
+
type: 'message',
|
|
874
|
+
data: {
|
|
875
|
+
type: RecognitionResultTypeV1.TRANSCRIPTION,
|
|
876
|
+
is_finished: true,
|
|
877
|
+
finalTranscript: 'test'
|
|
878
|
+
}
|
|
879
|
+
}));
|
|
880
|
+
await stopPromise;
|
|
881
|
+
|
|
882
|
+
// Now test stopAbnormally
|
|
883
|
+
await setupReadyClient();
|
|
884
|
+
jest.clearAllMocks();
|
|
885
|
+
|
|
886
|
+
client.stopAbnormally();
|
|
887
|
+
|
|
888
|
+
// stopAbnormally does NOT send messages, just closes
|
|
889
|
+
expect(mockWs.send).not.toHaveBeenCalled();
|
|
890
|
+
expect(mockWs.close).toHaveBeenCalled();
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
it('should be idempotent - safe to call multiple times', () => {
|
|
894
|
+
client.stopAbnormally();
|
|
895
|
+
expect(client.getState()).toBe(ClientState.STOPPED);
|
|
896
|
+
|
|
897
|
+
// Call again - should not throw
|
|
898
|
+
expect(() => client.stopAbnormally()).not.toThrow();
|
|
899
|
+
expect(client.getState()).toBe(ClientState.STOPPED);
|
|
900
|
+
});
|
|
901
|
+
});
|
|
774
902
|
});
|
|
@@ -544,6 +544,29 @@ export class RealTimeTwoWayWebSocketRecognitionClient
|
|
|
544
544
|
});
|
|
545
545
|
}
|
|
546
546
|
|
|
547
|
+
stopAbnormally(): void {
|
|
548
|
+
// Guard: If already in terminal state, do nothing
|
|
549
|
+
if (this.state === ClientState.STOPPED || this.state === ClientState.FAILED) {
|
|
550
|
+
this.log('debug', 'stopAbnormally called but already in terminal state', { state: this.state });
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
this.log('warn', 'Abnormal stop requested - closing connection immediately', { state: this.state });
|
|
555
|
+
|
|
556
|
+
// Update state to STOPPED (skip STOPPING)
|
|
557
|
+
this.state = ClientState.STOPPED;
|
|
558
|
+
|
|
559
|
+
// Clean up resources
|
|
560
|
+
this.cleanup();
|
|
561
|
+
|
|
562
|
+
// Close WebSocket connection immediately
|
|
563
|
+
// Code 1000 = Normal Closure (even though abnormal for us, it's normal for WebSocket spec)
|
|
564
|
+
// Type assertion needed because closeConnection is a newly added protected method
|
|
565
|
+
(this as any).closeConnection(1000, 'Client abnormal stop');
|
|
566
|
+
|
|
567
|
+
// Note: onDisconnected will be called by WebSocket close event
|
|
568
|
+
// which will call cleanup again (idempotent) and trigger onDisconnected callback
|
|
569
|
+
}
|
|
547
570
|
|
|
548
571
|
getAudioUtteranceId(): string {
|
|
549
572
|
return this.config.audioUtteranceId;
|
|
@@ -220,6 +220,25 @@ export interface IRecognitionClient {
|
|
|
220
220
|
*/
|
|
221
221
|
stopRecording(): Promise<void>;
|
|
222
222
|
|
|
223
|
+
/**
|
|
224
|
+
* Force stop and immediately close connection without waiting for server
|
|
225
|
+
*
|
|
226
|
+
* WARNING: This is an abnormal shutdown that bypasses the graceful stop flow:
|
|
227
|
+
* - Does NOT wait for server to process remaining audio
|
|
228
|
+
* - Does NOT receive final transcript from server
|
|
229
|
+
* - Immediately closes WebSocket connection
|
|
230
|
+
* - Cleans up resources (buffers, listeners)
|
|
231
|
+
*
|
|
232
|
+
* Use Cases:
|
|
233
|
+
* - User explicitly cancels/abandons session
|
|
234
|
+
* - Timeout scenarios where waiting is not acceptable
|
|
235
|
+
* - Need immediate cleanup and can't wait for server
|
|
236
|
+
*
|
|
237
|
+
* RECOMMENDED: Use stopRecording() for normal shutdown.
|
|
238
|
+
* Only use this when immediate disconnection is required.
|
|
239
|
+
*/
|
|
240
|
+
stopAbnormally(): void;
|
|
241
|
+
|
|
223
242
|
/**
|
|
224
243
|
* Get the audio utterance ID for this session
|
|
225
244
|
* Available immediately after client construction.
|
|
@@ -29,6 +29,7 @@ describe('SimplifiedVGFRecognitionClient', () => {
|
|
|
29
29
|
connect: jest.fn().mockResolvedValue(undefined),
|
|
30
30
|
sendAudio: jest.fn(),
|
|
31
31
|
stopRecording: jest.fn().mockResolvedValue(undefined),
|
|
32
|
+
stopAbnormally: jest.fn(),
|
|
32
33
|
getAudioUtteranceId: jest.fn().mockReturnValue('test-uuid'),
|
|
33
34
|
getState: jest.fn().mockReturnValue(ClientState.INITIAL),
|
|
34
35
|
isConnected: jest.fn().mockReturnValue(false),
|
|
@@ -674,4 +675,249 @@ describe('SimplifiedVGFRecognitionClient', () => {
|
|
|
674
675
|
expect(callbackState).not.toBe(currentState); // Different references
|
|
675
676
|
});
|
|
676
677
|
});
|
|
678
|
+
|
|
679
|
+
describe('stopAbnormally', () => {
|
|
680
|
+
beforeEach(() => {
|
|
681
|
+
simplifiedClient = new SimplifiedVGFRecognitionClient({
|
|
682
|
+
asrRequestConfig: {
|
|
683
|
+
provider: 'deepgram',
|
|
684
|
+
language: 'en',
|
|
685
|
+
sampleRate: 16000,
|
|
686
|
+
encoding: AudioEncoding.LINEAR16
|
|
687
|
+
},
|
|
688
|
+
onStateChange: stateChangeCallback
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('should immediately set state to ABORTED with empty transcript', () => {
|
|
693
|
+
// Start recording first
|
|
694
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
695
|
+
jest.clearAllMocks();
|
|
696
|
+
|
|
697
|
+
// Call stopAbnormally
|
|
698
|
+
simplifiedClient.stopAbnormally();
|
|
699
|
+
|
|
700
|
+
// Verify state was updated to ABORTED (not FINALIZED)
|
|
701
|
+
expect(stateChangeCallback).toHaveBeenCalledTimes(1);
|
|
702
|
+
const finalState = stateChangeCallback.mock.calls[0][0];
|
|
703
|
+
|
|
704
|
+
expect(finalState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
705
|
+
expect(finalState.finalTranscript).toBe('');
|
|
706
|
+
expect(finalState.startRecordingStatus).toBe(RecordingStatus.FINISHED);
|
|
707
|
+
expect(finalState.finalRecordingTimestamp).toBeDefined();
|
|
708
|
+
expect(finalState.finalTranscriptionTimestamp).toBeDefined();
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
it('should stop recording audio flag', () => {
|
|
712
|
+
// Start recording
|
|
713
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
714
|
+
|
|
715
|
+
// Call stopAbnormally
|
|
716
|
+
simplifiedClient.stopAbnormally();
|
|
717
|
+
|
|
718
|
+
// Send more audio - should not update recording status again
|
|
719
|
+
jest.clearAllMocks();
|
|
720
|
+
simplifiedClient.sendAudio(Buffer.from([4, 5, 6]));
|
|
721
|
+
|
|
722
|
+
// Verify recording status was set in sendAudio
|
|
723
|
+
const state = simplifiedClient.getVGFState();
|
|
724
|
+
expect(state.startRecordingStatus).toBe(RecordingStatus.RECORDING);
|
|
725
|
+
});
|
|
726
|
+
|
|
727
|
+
it('should be idempotent - calling twice does not change state again', () => {
|
|
728
|
+
// Start recording
|
|
729
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
730
|
+
jest.clearAllMocks();
|
|
731
|
+
|
|
732
|
+
// Call stopAbnormally first time
|
|
733
|
+
simplifiedClient.stopAbnormally();
|
|
734
|
+
expect(stateChangeCallback).toHaveBeenCalledTimes(1);
|
|
735
|
+
|
|
736
|
+
const firstCallState = stateChangeCallback.mock.calls[0][0];
|
|
737
|
+
jest.clearAllMocks();
|
|
738
|
+
|
|
739
|
+
// Call stopAbnormally second time
|
|
740
|
+
simplifiedClient.stopAbnormally();
|
|
741
|
+
|
|
742
|
+
// Should not trigger state change callback again (already aborted)
|
|
743
|
+
expect(stateChangeCallback).toHaveBeenCalledTimes(0);
|
|
744
|
+
|
|
745
|
+
const currentState = simplifiedClient.getVGFState();
|
|
746
|
+
expect(currentState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
747
|
+
expect(currentState.finalTranscript).toBe('');
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('should work even if called before any recording', () => {
|
|
751
|
+
// Call stopAbnormally without ever recording
|
|
752
|
+
simplifiedClient.stopAbnormally();
|
|
753
|
+
|
|
754
|
+
const state = simplifiedClient.getVGFState();
|
|
755
|
+
expect(state.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
756
|
+
expect(state.finalTranscript).toBe('');
|
|
757
|
+
expect(state.startRecordingStatus).toBe(RecordingStatus.FINISHED);
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
it('should preserve existing state fields except for overridden ones', () => {
|
|
761
|
+
// Set up some initial state by sending audio
|
|
762
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
763
|
+
|
|
764
|
+
const initialState = simplifiedClient.getVGFState();
|
|
765
|
+
const audioUtteranceId = initialState.audioUtteranceId;
|
|
766
|
+
|
|
767
|
+
// Call stopAbnormally
|
|
768
|
+
simplifiedClient.stopAbnormally();
|
|
769
|
+
|
|
770
|
+
const finalState = simplifiedClient.getVGFState();
|
|
771
|
+
|
|
772
|
+
// Should preserve audioUtteranceId and other non-overridden fields
|
|
773
|
+
expect(finalState.audioUtteranceId).toBe(audioUtteranceId);
|
|
774
|
+
|
|
775
|
+
// Should override these fields
|
|
776
|
+
expect(finalState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
777
|
+
expect(finalState.finalTranscript).toBe('');
|
|
778
|
+
expect(finalState.startRecordingStatus).toBe(RecordingStatus.FINISHED);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('should set both recording and transcription timestamps', () => {
|
|
782
|
+
const beforeTime = new Date().toISOString();
|
|
783
|
+
|
|
784
|
+
simplifiedClient.stopAbnormally();
|
|
785
|
+
|
|
786
|
+
const state = simplifiedClient.getVGFState();
|
|
787
|
+
const afterTime = new Date().toISOString();
|
|
788
|
+
|
|
789
|
+
// Timestamps should be set and within reasonable range
|
|
790
|
+
expect(state.finalRecordingTimestamp).toBeDefined();
|
|
791
|
+
expect(state.finalTranscriptionTimestamp).toBeDefined();
|
|
792
|
+
|
|
793
|
+
// Basic sanity check that timestamps are ISO strings
|
|
794
|
+
expect(state.finalRecordingTimestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
795
|
+
expect(state.finalTranscriptionTimestamp).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
796
|
+
|
|
797
|
+
// Timestamps should be close to current time
|
|
798
|
+
if (state.finalRecordingTimestamp) {
|
|
799
|
+
expect(state.finalRecordingTimestamp >= beforeTime).toBe(true);
|
|
800
|
+
expect(state.finalRecordingTimestamp <= afterTime).toBe(true);
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
it('should call underlying client stopAbnormally for cleanup', () => {
|
|
805
|
+
simplifiedClient.stopAbnormally();
|
|
806
|
+
|
|
807
|
+
// stopAbnormally on underlying client SHOULD be called for WebSocket cleanup
|
|
808
|
+
expect(mockClient.stopAbnormally).toHaveBeenCalled();
|
|
809
|
+
|
|
810
|
+
// stopRecording on underlying client should NOT be called
|
|
811
|
+
expect(mockClient.stopRecording).not.toHaveBeenCalled();
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
it('should differ from stopRecording behavior', async () => {
|
|
815
|
+
// Test that stopAbnormally and stopRecording behave differently
|
|
816
|
+
jest.clearAllMocks();
|
|
817
|
+
|
|
818
|
+
// Use the existing simplifiedClient for testing
|
|
819
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
820
|
+
|
|
821
|
+
// Test stopAbnormally - should NOT call underlying client
|
|
822
|
+
simplifiedClient.stopAbnormally();
|
|
823
|
+
expect(mockClient.stopRecording).not.toHaveBeenCalled();
|
|
824
|
+
|
|
825
|
+
// Create new client to test stopRecording
|
|
826
|
+
const client2 = new SimplifiedVGFRecognitionClient({
|
|
827
|
+
asrRequestConfig: {
|
|
828
|
+
provider: 'deepgram',
|
|
829
|
+
language: 'en',
|
|
830
|
+
sampleRate: 16000,
|
|
831
|
+
encoding: AudioEncoding.LINEAR16
|
|
832
|
+
},
|
|
833
|
+
onStateChange: jest.fn()
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
// Clear mocks to isolate client2's behavior
|
|
837
|
+
jest.clearAllMocks();
|
|
838
|
+
|
|
839
|
+
// Test stopRecording - SHOULD call underlying client
|
|
840
|
+
await client2.stopRecording();
|
|
841
|
+
expect(mockClient.stopRecording).toHaveBeenCalled();
|
|
842
|
+
});
|
|
843
|
+
|
|
844
|
+
it('should use ABORTED status to distinguish from normal completion', () => {
|
|
845
|
+
// Test that stopAbnormally uses ABORTED, not FINALIZED
|
|
846
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
847
|
+
|
|
848
|
+
// Abnormal stop - should set to ABORTED
|
|
849
|
+
simplifiedClient.stopAbnormally();
|
|
850
|
+
const abortedState = simplifiedClient.getVGFState();
|
|
851
|
+
|
|
852
|
+
// Verify ABORTED is used (not FINALIZED)
|
|
853
|
+
expect(abortedState.transcriptionStatus).toBe(TranscriptionStatus.ABORTED);
|
|
854
|
+
expect(abortedState.transcriptionStatus).not.toBe(TranscriptionStatus.FINALIZED);
|
|
855
|
+
expect(abortedState.finalTranscript).toBe(''); // Empty because cancelled
|
|
856
|
+
|
|
857
|
+
// ABORTED clearly indicates user cancelled, vs FINALIZED which means completed normally
|
|
858
|
+
});
|
|
859
|
+
|
|
860
|
+
describe('state guards', () => {
|
|
861
|
+
it('should do nothing if already fully stopped', () => {
|
|
862
|
+
// Setup: finalize state and mark underlying client as stopped
|
|
863
|
+
mockClient.getState.mockReturnValue(ClientState.STOPPED);
|
|
864
|
+
simplifiedClient.stopAbnormally();
|
|
865
|
+
|
|
866
|
+
// Clear mocks to test second call
|
|
867
|
+
jest.clearAllMocks();
|
|
868
|
+
|
|
869
|
+
// Call again - should return early and not call anything
|
|
870
|
+
simplifiedClient.stopAbnormally();
|
|
871
|
+
|
|
872
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
873
|
+
expect(mockClient.stopAbnormally).not.toHaveBeenCalled();
|
|
874
|
+
});
|
|
875
|
+
|
|
876
|
+
it('should not call underlying client if already in STOPPED state', () => {
|
|
877
|
+
// Mock underlying client as already stopped
|
|
878
|
+
mockClient.getState.mockReturnValue(ClientState.STOPPED);
|
|
879
|
+
|
|
880
|
+
// But VGF state not finalized yet
|
|
881
|
+
simplifiedClient.sendAudio(Buffer.from([1, 2, 3]));
|
|
882
|
+
jest.clearAllMocks();
|
|
883
|
+
|
|
884
|
+
simplifiedClient.stopAbnormally();
|
|
885
|
+
|
|
886
|
+
// Should finalize VGF state
|
|
887
|
+
expect(stateChangeCallback).toHaveBeenCalled();
|
|
888
|
+
// But should NOT call underlying client
|
|
889
|
+
expect(mockClient.stopAbnormally).not.toHaveBeenCalled();
|
|
890
|
+
});
|
|
891
|
+
|
|
892
|
+
it('should not call underlying client if already in FAILED state', () => {
|
|
893
|
+
// Mock underlying client as failed
|
|
894
|
+
mockClient.getState.mockReturnValue(ClientState.FAILED);
|
|
895
|
+
|
|
896
|
+
simplifiedClient.stopAbnormally();
|
|
897
|
+
|
|
898
|
+
// Should handle VGF state update
|
|
899
|
+
expect(stateChangeCallback).toHaveBeenCalled();
|
|
900
|
+
// But should NOT call underlying client (already in terminal state)
|
|
901
|
+
expect(mockClient.stopAbnormally).not.toHaveBeenCalled();
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
it('should only update VGF state if already finalized but client not stopped', () => {
|
|
905
|
+
// First call - fully stop
|
|
906
|
+
simplifiedClient.stopAbnormally();
|
|
907
|
+
const firstCallCount = stateChangeCallback.mock.calls.length;
|
|
908
|
+
|
|
909
|
+
// Mock underlying client reconnects (edge case)
|
|
910
|
+
mockClient.getState.mockReturnValue(ClientState.READY);
|
|
911
|
+
jest.clearAllMocks();
|
|
912
|
+
|
|
913
|
+
// Second call - VGF already finalized but client not stopped
|
|
914
|
+
simplifiedClient.stopAbnormally();
|
|
915
|
+
|
|
916
|
+
// Should NOT update VGF state (already finalized)
|
|
917
|
+
expect(stateChangeCallback).not.toHaveBeenCalled();
|
|
918
|
+
// But SHOULD call underlying client (not stopped)
|
|
919
|
+
expect(mockClient.stopAbnormally).toHaveBeenCalled();
|
|
920
|
+
});
|
|
921
|
+
});
|
|
922
|
+
});
|
|
677
923
|
});
|