@volley/recognition-client-sdk 0.1.200 → 0.1.210

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volley/recognition-client-sdk",
3
- "version": "0.1.200",
3
+ "version": "0.1.210",
4
4
  "description": "Recognition Service TypeScript/Node.js Client SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -28,14 +28,7 @@
28
28
  "sideEffects": false,
29
29
  "publishConfig": {
30
30
  "access": "public",
31
- "provenance": false
32
- },
33
- "scripts": {
34
- "build": "tsup",
35
- "dev": "tsup --watch",
36
- "test": "jest --passWithNoTests",
37
- "lint": "eslint src --ext .ts",
38
- "prepublishOnly": "pnpm build"
31
+ "provenance": true
39
32
  },
40
33
  "dependencies": {
41
34
  "uuid": "^11.0.0",
@@ -43,12 +36,9 @@
43
36
  "zod": "^3.22.4"
44
37
  },
45
38
  "devDependencies": {
46
- "@recog/shared-config": "workspace:*",
47
- "@recog/shared-types": "workspace:*",
48
- "@recog/shared-utils": "workspace:*",
49
- "@recog/websocket": "workspace:*",
50
39
  "@semantic-release/changelog": "^6.0.3",
51
40
  "@semantic-release/commit-analyzer": "^13.0.1",
41
+ "@semantic-release/exec": "^7.1.0",
52
42
  "@semantic-release/git": "^10.0.1",
53
43
  "@semantic-release/github": "^12.0.0",
54
44
  "@semantic-release/npm": "^13.1.1",
@@ -62,12 +52,22 @@
62
52
  "semantic-release": "^25.0.1",
63
53
  "ts-jest": "^29.4.5",
64
54
  "tsup": "^8.5.0",
65
- "typescript": "^5.1.6"
55
+ "typescript": "^5.1.6",
56
+ "@recog/shared-config": "1.0.0",
57
+ "@recog/shared-types": "1.0.0",
58
+ "@recog/websocket": "1.0.0",
59
+ "@recog/shared-utils": "1.0.0"
66
60
  },
67
61
  "keywords": [
68
62
  "recognition",
69
63
  "sdk",
70
64
  "audio",
71
65
  "speech"
72
- ]
73
- }
66
+ ],
67
+ "scripts": {
68
+ "build": "tsup",
69
+ "dev": "tsup --watch",
70
+ "test": "jest --passWithNoTests",
71
+ "lint": "eslint src --ext .ts"
72
+ }
73
+ }
@@ -0,0 +1,265 @@
1
+ /**
2
+ * Unit tests for ConfigBuilder
3
+ */
4
+
5
+ import { ConfigBuilder } from './config-builder.js';
6
+ import { RecognitionProvider, RecognitionContextTypeV1 } from '@recog/shared-types';
7
+
8
+ describe('ConfigBuilder', () => {
9
+ it('should build empty config', () => {
10
+ const config = new ConfigBuilder().build();
11
+ expect(config).toBeDefined();
12
+ });
13
+
14
+ it('should set url', () => {
15
+ const config = new ConfigBuilder()
16
+ .url('ws://localhost:3101/ws/v1/recognize')
17
+ .build();
18
+ expect(config.url).toBe('ws://localhost:3101/ws/v1/recognize');
19
+ });
20
+
21
+ it('should set asrRequestConfig', () => {
22
+ const asrConfig = {
23
+ provider: RecognitionProvider.DEEPGRAM,
24
+ model: 'nova-2-general',
25
+ language: 'en-US',
26
+ sampleRate: 16000,
27
+ encoding: 'linear16'
28
+ };
29
+ const config = new ConfigBuilder()
30
+ .asrRequestConfig(asrConfig)
31
+ .build();
32
+ expect(config.asrRequestConfig).toEqual(asrConfig);
33
+ });
34
+
35
+ it('should set gameContext', () => {
36
+ const gameContext = {
37
+ type: RecognitionContextTypeV1.GAME_CONTEXT as const,
38
+ gameId: 'test-game',
39
+ gamePhase: 'test-phase'
40
+ };
41
+ const config = new ConfigBuilder()
42
+ .gameContext(gameContext)
43
+ .build();
44
+ expect(config.gameContext).toEqual(gameContext);
45
+ });
46
+
47
+ it('should set audioUtteranceId', () => {
48
+ const config = new ConfigBuilder()
49
+ .audioUtteranceId('test-utterance-id')
50
+ .build();
51
+ expect(config.audioUtteranceId).toBe('test-utterance-id');
52
+ });
53
+
54
+ it('should set callbackUrls', () => {
55
+ const callbackUrls = [
56
+ { url: 'https://example.com/callback', event: 'transcript' as const }
57
+ ];
58
+ const config = new ConfigBuilder()
59
+ .callbackUrls(callbackUrls)
60
+ .build();
61
+ expect(config.callbackUrls).toEqual(callbackUrls);
62
+ });
63
+
64
+ it('should set userId', () => {
65
+ const config = new ConfigBuilder()
66
+ .userId('user-123')
67
+ .build();
68
+ expect(config.userId).toBe('user-123');
69
+ });
70
+
71
+ it('should set gameSessionId', () => {
72
+ const config = new ConfigBuilder()
73
+ .gameSessionId('session-456')
74
+ .build();
75
+ expect(config.gameSessionId).toBe('session-456');
76
+ });
77
+
78
+ it('should set deviceId', () => {
79
+ const config = new ConfigBuilder()
80
+ .deviceId('device-789')
81
+ .build();
82
+ expect(config.deviceId).toBe('device-789');
83
+ });
84
+
85
+ it('should set accountId', () => {
86
+ const config = new ConfigBuilder()
87
+ .accountId('account-abc')
88
+ .build();
89
+ expect(config.accountId).toBe('account-abc');
90
+ });
91
+
92
+ it('should set questionAnswerId', () => {
93
+ const config = new ConfigBuilder()
94
+ .questionAnswerId('qa-xyz')
95
+ .build();
96
+ expect(config.questionAnswerId).toBe('qa-xyz');
97
+ });
98
+
99
+ it('should set platform', () => {
100
+ const config = new ConfigBuilder()
101
+ .platform('ios')
102
+ .build();
103
+ expect(config.platform).toBe('ios');
104
+ });
105
+
106
+ it('should set onTranscript callback', () => {
107
+ const callback = jest.fn();
108
+ const config = new ConfigBuilder()
109
+ .onTranscript(callback)
110
+ .build();
111
+ expect(config.onTranscript).toBe(callback);
112
+ });
113
+
114
+ it('should set onMetadata callback', () => {
115
+ const callback = jest.fn();
116
+ const config = new ConfigBuilder()
117
+ .onMetadata(callback)
118
+ .build();
119
+ expect(config.onMetadata).toBe(callback);
120
+ });
121
+
122
+ it('should set onError callback', () => {
123
+ const callback = jest.fn();
124
+ const config = new ConfigBuilder()
125
+ .onError(callback)
126
+ .build();
127
+ expect(config.onError).toBe(callback);
128
+ });
129
+
130
+ it('should set onConnected callback', () => {
131
+ const callback = jest.fn();
132
+ const config = new ConfigBuilder()
133
+ .onConnected(callback)
134
+ .build();
135
+ expect(config.onConnected).toBe(callback);
136
+ });
137
+
138
+ it('should set onDisconnected callback', () => {
139
+ const callback = jest.fn();
140
+ const config = new ConfigBuilder()
141
+ .onDisconnected(callback)
142
+ .build();
143
+ expect(config.onDisconnected).toBe(callback);
144
+ });
145
+
146
+ it('should set highWaterMark', () => {
147
+ const config = new ConfigBuilder()
148
+ .highWaterMark(1000)
149
+ .build();
150
+ expect(config.highWaterMark).toBe(1000);
151
+ });
152
+
153
+ it('should set lowWaterMark', () => {
154
+ const config = new ConfigBuilder()
155
+ .lowWaterMark(500)
156
+ .build();
157
+ expect(config.lowWaterMark).toBe(500);
158
+ });
159
+
160
+ it('should set maxBufferDurationSec', () => {
161
+ const config = new ConfigBuilder()
162
+ .maxBufferDurationSec(10)
163
+ .build();
164
+ expect(config.maxBufferDurationSec).toBe(10);
165
+ });
166
+
167
+ it('should set chunksPerSecond', () => {
168
+ const config = new ConfigBuilder()
169
+ .chunksPerSecond(50)
170
+ .build();
171
+ expect(config.chunksPerSecond).toBe(50);
172
+ });
173
+
174
+ it('should set logger', () => {
175
+ const logger = jest.fn();
176
+ const config = new ConfigBuilder()
177
+ .logger(logger)
178
+ .build();
179
+ expect(config.logger).toBe(logger);
180
+ });
181
+
182
+ it('should support method chaining', () => {
183
+ const config = new ConfigBuilder()
184
+ .url('ws://localhost:3101/ws/v1/recognize')
185
+ .audioUtteranceId('test-id')
186
+ .userId('user-123')
187
+ .gameSessionId('session-456')
188
+ .deviceId('device-789')
189
+ .platform('ios')
190
+ .highWaterMark(1000)
191
+ .lowWaterMark(500)
192
+ .build();
193
+
194
+ expect(config.url).toBe('ws://localhost:3101/ws/v1/recognize');
195
+ expect(config.audioUtteranceId).toBe('test-id');
196
+ expect(config.userId).toBe('user-123');
197
+ expect(config.gameSessionId).toBe('session-456');
198
+ expect(config.deviceId).toBe('device-789');
199
+ expect(config.platform).toBe('ios');
200
+ expect(config.highWaterMark).toBe(1000);
201
+ expect(config.lowWaterMark).toBe(500);
202
+ });
203
+
204
+ it('should build complete configuration', () => {
205
+ const onTranscript = jest.fn();
206
+ const onError = jest.fn();
207
+ const onConnected = jest.fn();
208
+ const onDisconnected = jest.fn();
209
+ const logger = jest.fn();
210
+
211
+ const config = new ConfigBuilder()
212
+ .url('ws://localhost:3101/ws/v1/recognize')
213
+ .asrRequestConfig({
214
+ provider: RecognitionProvider.DEEPGRAM,
215
+ model: 'nova-2-general',
216
+ language: 'en-US',
217
+ sampleRate: 16000,
218
+ encoding: 'linear16'
219
+ })
220
+ .gameContext({
221
+ type: RecognitionContextTypeV1.GAME_CONTEXT as const,
222
+ gameId: 'test-game',
223
+ gamePhase: 'test-phase'
224
+ })
225
+ .audioUtteranceId('test-utterance-id')
226
+ .userId('user-123')
227
+ .gameSessionId('session-456')
228
+ .deviceId('device-789')
229
+ .accountId('account-abc')
230
+ .questionAnswerId('qa-xyz')
231
+ .platform('ios')
232
+ .onTranscript(onTranscript)
233
+ .onError(onError)
234
+ .onConnected(onConnected)
235
+ .onDisconnected(onDisconnected)
236
+ .highWaterMark(1000)
237
+ .lowWaterMark(500)
238
+ .maxBufferDurationSec(10)
239
+ .chunksPerSecond(50)
240
+ .logger(logger)
241
+ .build();
242
+
243
+ expect(config).toMatchObject({
244
+ url: 'ws://localhost:3101/ws/v1/recognize',
245
+ audioUtteranceId: 'test-utterance-id',
246
+ userId: 'user-123',
247
+ gameSessionId: 'session-456',
248
+ deviceId: 'device-789',
249
+ accountId: 'account-abc',
250
+ questionAnswerId: 'qa-xyz',
251
+ platform: 'ios',
252
+ highWaterMark: 1000,
253
+ lowWaterMark: 500,
254
+ maxBufferDurationSec: 10,
255
+ chunksPerSecond: 50
256
+ });
257
+ expect(config.asrRequestConfig?.provider).toBe(RecognitionProvider.DEEPGRAM);
258
+ expect(config.gameContext?.gameId).toBe('test-game');
259
+ expect(config.onTranscript).toBe(onTranscript);
260
+ expect(config.onError).toBe(onError);
261
+ expect(config.onConnected).toBe(onConnected);
262
+ expect(config.onDisconnected).toBe(onDisconnected);
263
+ expect(config.logger).toBe(logger);
264
+ });
265
+ });
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Unit tests for Factory functions
3
+ */
4
+
5
+ import { createClient, createClientWithBuilder } from './factory.js';
6
+ import { ConfigBuilder } from './config-builder.js';
7
+ import { RecognitionProvider, RecognitionContextTypeV1 } from '@recog/shared-types';
8
+
9
+ // Mock the RealTimeTwoWayWebSocketRecognitionClient
10
+ jest.mock('./recognition-client.js', () => ({
11
+ RealTimeTwoWayWebSocketRecognitionClient: jest.fn().mockImplementation((config) => ({
12
+ config,
13
+ connect: jest.fn(),
14
+ disconnect: jest.fn(),
15
+ sendAudio: jest.fn(),
16
+ startRecognition: jest.fn(),
17
+ stopRecognition: jest.fn()
18
+ }))
19
+ }));
20
+
21
+ describe('Factory Functions', () => {
22
+ describe('createClient', () => {
23
+ it('should create client with config object', () => {
24
+ const config = {
25
+ url: 'ws://localhost:3101/ws/v1/recognize',
26
+ audioUtteranceId: 'test-id',
27
+ asrRequestConfig: {
28
+ provider: RecognitionProvider.DEEPGRAM,
29
+ model: 'nova-2-general',
30
+ language: 'en-US',
31
+ sampleRate: 16000,
32
+ encoding: 'linear16'
33
+ }
34
+ };
35
+
36
+ const client = createClient(config);
37
+ expect(client).toBeDefined();
38
+ expect(client.connect).toBeDefined();
39
+ expect(client.sendAudio).toBeDefined();
40
+ });
41
+
42
+ it('should create client with minimal config', () => {
43
+ const config = {
44
+ url: 'ws://localhost:3101/ws/v1/recognize',
45
+ audioUtteranceId: 'test-id'
46
+ };
47
+
48
+ const client = createClient(config);
49
+ expect(client).toBeDefined();
50
+ });
51
+
52
+ it('should create client with callbacks', () => {
53
+ const onTranscript = jest.fn();
54
+ const onError = jest.fn();
55
+ const onConnected = jest.fn();
56
+
57
+ const config = {
58
+ url: 'ws://localhost:3101/ws/v1/recognize',
59
+ audioUtteranceId: 'test-id',
60
+ onTranscript,
61
+ onError,
62
+ onConnected
63
+ };
64
+
65
+ const client = createClient(config);
66
+ expect(client).toBeDefined();
67
+ });
68
+ });
69
+
70
+ describe('createClientWithBuilder', () => {
71
+ it('should create client using builder pattern', () => {
72
+ const client = createClientWithBuilder((builder) =>
73
+ builder
74
+ .url('ws://localhost:3101/ws/v1/recognize')
75
+ .audioUtteranceId('test-id')
76
+ );
77
+
78
+ expect(client).toBeDefined();
79
+ expect(client.connect).toBeDefined();
80
+ });
81
+
82
+ it('should create client with multiple builder methods', () => {
83
+ const onTranscript = jest.fn();
84
+ const onError = jest.fn();
85
+
86
+ const client = createClientWithBuilder((builder) =>
87
+ builder
88
+ .url('ws://localhost:3101/ws/v1/recognize')
89
+ .audioUtteranceId('test-id')
90
+ .userId('user-123')
91
+ .gameSessionId('session-456')
92
+ .onTranscript(onTranscript)
93
+ .onError(onError)
94
+ );
95
+
96
+ expect(client).toBeDefined();
97
+ });
98
+
99
+ it('should create client with ASR config via builder', () => {
100
+ const client = createClientWithBuilder((builder) =>
101
+ builder
102
+ .url('ws://localhost:3101/ws/v1/recognize')
103
+ .audioUtteranceId('test-id')
104
+ .asrRequestConfig({
105
+ provider: RecognitionProvider.DEEPGRAM,
106
+ model: 'nova-2-general',
107
+ language: 'en-US',
108
+ sampleRate: 16000,
109
+ encoding: 'linear16'
110
+ })
111
+ );
112
+
113
+ expect(client).toBeDefined();
114
+ });
115
+
116
+ it('should create client with game context via builder', () => {
117
+ const client = createClientWithBuilder((builder) =>
118
+ builder
119
+ .url('ws://localhost:3101/ws/v1/recognize')
120
+ .audioUtteranceId('test-id')
121
+ .gameContext({
122
+ type: RecognitionContextTypeV1.GAME_CONTEXT as const,
123
+ gameId: 'test-game',
124
+ gamePhase: 'test-phase'
125
+ })
126
+ );
127
+
128
+ expect(client).toBeDefined();
129
+ });
130
+
131
+ it('should create client with all optional fields via builder', () => {
132
+ const onTranscript = jest.fn();
133
+ const onMetadata = jest.fn();
134
+ const onError = jest.fn();
135
+ const onConnected = jest.fn();
136
+ const onDisconnected = jest.fn();
137
+ const logger = jest.fn();
138
+
139
+ const client = createClientWithBuilder((builder) =>
140
+ builder
141
+ .url('ws://localhost:3101/ws/v1/recognize')
142
+ .audioUtteranceId('test-id')
143
+ .userId('user-123')
144
+ .gameSessionId('session-456')
145
+ .deviceId('device-789')
146
+ .accountId('account-abc')
147
+ .questionAnswerId('qa-xyz')
148
+ .platform('ios')
149
+ .asrRequestConfig({
150
+ provider: RecognitionProvider.DEEPGRAM,
151
+ model: 'nova-2-general',
152
+ language: 'en-US',
153
+ sampleRate: 16000,
154
+ encoding: 'linear16'
155
+ })
156
+ .gameContext({
157
+ type: RecognitionContextTypeV1.GAME_CONTEXT as const,
158
+ gameId: 'test-game',
159
+ gamePhase: 'test-phase'
160
+ })
161
+ .onTranscript(onTranscript)
162
+ .onMetadata(onMetadata)
163
+ .onError(onError)
164
+ .onConnected(onConnected)
165
+ .onDisconnected(onDisconnected)
166
+ .highWaterMark(1000)
167
+ .lowWaterMark(500)
168
+ .maxBufferDurationSec(10)
169
+ .chunksPerSecond(50)
170
+ .logger(logger)
171
+ );
172
+
173
+ expect(client).toBeDefined();
174
+ });
175
+
176
+ it('should accept builder as parameter', () => {
177
+ const builder = new ConfigBuilder();
178
+ const client = createClientWithBuilder((b) => b.url('ws://localhost:3101').audioUtteranceId('test-id'));
179
+ expect(client).toBeDefined();
180
+ });
181
+ });
182
+
183
+ describe('Factory Function Equivalence', () => {
184
+ it('should produce equivalent clients from config and builder', () => {
185
+ const config = {
186
+ url: 'ws://localhost:3101/ws/v1/recognize',
187
+ audioUtteranceId: 'test-id',
188
+ userId: 'user-123'
189
+ };
190
+
191
+ const clientFromConfig = createClient(config);
192
+ const clientFromBuilder = createClientWithBuilder((builder) =>
193
+ builder
194
+ .url('ws://localhost:3101/ws/v1/recognize')
195
+ .audioUtteranceId('test-id')
196
+ .userId('user-123')
197
+ );
198
+
199
+ expect(clientFromConfig).toBeDefined();
200
+ expect(clientFromBuilder).toBeDefined();
201
+ });
202
+
203
+ it('should handle empty config gracefully', () => {
204
+ const config = {
205
+ url: 'ws://localhost:3101/ws/v1/recognize',
206
+ audioUtteranceId: 'test-id'
207
+ };
208
+
209
+ const client = createClient(config);
210
+ expect(client).toBeDefined();
211
+ expect(typeof client.connect).toBe('function');
212
+ });
213
+
214
+ });
215
+ });
package/src/factory.ts CHANGED
@@ -13,9 +13,13 @@ import type { IRecognitionClient, RealTimeTwoWayWebSocketRecognitionClientConfig
13
13
  * ```typescript
14
14
  * const client = createClient({
15
15
  * url: 'ws://localhost:3101/ws/v1/recognize',
16
+ * audioUtteranceId: 'unique-id',
16
17
  * onTranscript: (result) => console.log(result)
17
18
  * });
18
19
  * ```
20
+ *
21
+ * @param config - Client configuration
22
+ * @returns Configured recognition client instance
19
23
  */
20
24
  export function createClient(config: RealTimeTwoWayWebSocketRecognitionClientConfig): IRecognitionClient {
21
25
  return new RealTimeTwoWayWebSocketRecognitionClient(config);