@mingxy/ocosay 1.0.0
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/README.md +556 -0
- package/TECH_PLAN.md +352 -0
- package/__mocks__/@opencode-ai/plugin.ts +32 -0
- package/dist/config.d.ts +26 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +95 -0
- package/dist/config.js.map +1 -0
- package/dist/core/backends/afplay-backend.d.ts +33 -0
- package/dist/core/backends/afplay-backend.d.ts.map +1 -0
- package/dist/core/backends/afplay-backend.js +144 -0
- package/dist/core/backends/afplay-backend.js.map +1 -0
- package/dist/core/backends/aplay-backend.d.ts +33 -0
- package/dist/core/backends/aplay-backend.d.ts.map +1 -0
- package/dist/core/backends/aplay-backend.js +142 -0
- package/dist/core/backends/aplay-backend.js.map +1 -0
- package/dist/core/backends/base.d.ts +94 -0
- package/dist/core/backends/base.d.ts.map +1 -0
- package/dist/core/backends/base.js +6 -0
- package/dist/core/backends/base.js.map +1 -0
- package/dist/core/backends/index.d.ts +29 -0
- package/dist/core/backends/index.d.ts.map +1 -0
- package/dist/core/backends/index.js +114 -0
- package/dist/core/backends/index.js.map +1 -0
- package/dist/core/backends/naudiodon-backend.d.ts +52 -0
- package/dist/core/backends/naudiodon-backend.d.ts.map +1 -0
- package/dist/core/backends/naudiodon-backend.js +123 -0
- package/dist/core/backends/naudiodon-backend.js.map +1 -0
- package/dist/core/backends/powershell-backend.d.ts +34 -0
- package/dist/core/backends/powershell-backend.d.ts.map +1 -0
- package/dist/core/backends/powershell-backend.js +154 -0
- package/dist/core/backends/powershell-backend.js.map +1 -0
- package/dist/core/player.d.ts +97 -0
- package/dist/core/player.d.ts.map +1 -0
- package/dist/core/player.js +268 -0
- package/dist/core/player.js.map +1 -0
- package/dist/core/speaker.d.ts +97 -0
- package/dist/core/speaker.d.ts.map +1 -0
- package/dist/core/speaker.js +218 -0
- package/dist/core/speaker.js.map +1 -0
- package/dist/core/stream-player.d.ts +107 -0
- package/dist/core/stream-player.d.ts.map +1 -0
- package/dist/core/stream-player.js +272 -0
- package/dist/core/stream-player.js.map +1 -0
- package/dist/core/stream-reader.d.ts +86 -0
- package/dist/core/stream-reader.d.ts.map +1 -0
- package/dist/core/stream-reader.js +172 -0
- package/dist/core/stream-reader.js.map +1 -0
- package/dist/core/streaming-synthesizer.d.ts +51 -0
- package/dist/core/streaming-synthesizer.d.ts.map +1 -0
- package/dist/core/streaming-synthesizer.js +103 -0
- package/dist/core/streaming-synthesizer.js.map +1 -0
- package/dist/core/types.d.ts +141 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +37 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +179 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +151 -0
- package/dist/plugin.js.map +1 -0
- package/dist/providers/base.d.ts +55 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +95 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/minimax.d.ts +84 -0
- package/dist/providers/minimax.d.ts.map +1 -0
- package/dist/providers/minimax.js +387 -0
- package/dist/providers/minimax.js.map +1 -0
- package/dist/tools/tts.d.ts +147 -0
- package/dist/tools/tts.d.ts.map +1 -0
- package/dist/tools/tts.js +232 -0
- package/dist/tools/tts.js.map +1 -0
- package/jest.config.js +15 -0
- package/package.json +49 -0
- package/src/config.ts +121 -0
- package/src/core/backends/afplay-backend.ts +162 -0
- package/src/core/backends/aplay-backend.ts +160 -0
- package/src/core/backends/base.ts +117 -0
- package/src/core/backends/index.ts +128 -0
- package/src/core/backends/naudiodon-backend.ts +164 -0
- package/src/core/backends/powershell-backend.ts +173 -0
- package/src/core/player.ts +322 -0
- package/src/core/speaker.ts +283 -0
- package/src/core/stream-player.ts +326 -0
- package/src/core/stream-reader.ts +190 -0
- package/src/core/streaming-synthesizer.ts +123 -0
- package/src/core/types.ts +185 -0
- package/src/index.ts +233 -0
- package/src/plugin.ts +166 -0
- package/src/providers/base.ts +150 -0
- package/src/providers/minimax.ts +515 -0
- package/src/tools/tts.ts +277 -0
- package/src/types/naudiodon.d.ts +19 -0
- package/tests/__mocks__/@opencode-ai/plugin.ts +32 -0
- package/tests/backends.test.ts +831 -0
- package/tests/index.test.ts +201 -0
- package/tests/integration-test.d.ts +6 -0
- package/tests/integration-test.d.ts.map +1 -0
- package/tests/integration-test.js +84 -0
- package/tests/integration-test.js.map +1 -0
- package/tests/integration-test.ts +93 -0
- package/tests/p1-fixes.test.ts +160 -0
- package/tests/plugin.test.ts +311 -0
- package/tests/provider.test.d.ts +2 -0
- package/tests/provider.test.d.ts.map +1 -0
- package/tests/provider.test.js +69 -0
- package/tests/provider.test.js.map +1 -0
- package/tests/provider.test.ts +87 -0
- package/tests/speaker.test.d.ts +2 -0
- package/tests/speaker.test.d.ts.map +1 -0
- package/tests/speaker.test.js +63 -0
- package/tests/speaker.test.js.map +1 -0
- package/tests/speaker.test.ts +232 -0
- package/tests/stream-player.test.ts +303 -0
- package/tests/stream-reader.test.ts +269 -0
- package/tests/streaming-synthesizer.test.ts +225 -0
- package/tests/tts-tools.test.ts +270 -0
- package/tests/types.test.d.ts +2 -0
- package/tests/types.test.d.ts.map +1 -0
- package/tests/types.test.js +61 -0
- package/tests/types.test.js.map +1 -0
- package/tests/types.test.ts +63 -0
- package/tsconfig.json +22 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { handleToolCall, ttsTools } from '../src/tools/tts'
|
|
2
|
+
import * as speaker from '../src/core/speaker'
|
|
3
|
+
import * as index from '../src/index'
|
|
4
|
+
import { TTSError, TTSErrorCode } from '../src/core/types'
|
|
5
|
+
|
|
6
|
+
jest.mock('../src/core/speaker')
|
|
7
|
+
jest.mock('../src/index')
|
|
8
|
+
|
|
9
|
+
const mockSpeaker = {
|
|
10
|
+
speak: jest.fn(),
|
|
11
|
+
stop: jest.fn(),
|
|
12
|
+
pause: jest.fn(),
|
|
13
|
+
resume: jest.fn(),
|
|
14
|
+
listVoices: jest.fn(),
|
|
15
|
+
getProviders: jest.fn().mockReturnValue(['minimax']),
|
|
16
|
+
isPlaying: jest.fn().mockReturnValue(true),
|
|
17
|
+
isPausedState: jest.fn().mockReturnValue(false),
|
|
18
|
+
destroy: jest.fn()
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
jest.clearAllMocks()
|
|
23
|
+
;(speaker.getDefaultSpeaker as jest.Mock).mockReturnValue(mockSpeaker)
|
|
24
|
+
;(speaker.speak as jest.Mock).mockResolvedValue(undefined)
|
|
25
|
+
;(speaker.stop as jest.Mock).mockResolvedValue(undefined)
|
|
26
|
+
;(speaker.pause as jest.Mock).mockImplementation(() => {})
|
|
27
|
+
;(speaker.resume as jest.Mock).mockImplementation(() => {})
|
|
28
|
+
;(speaker.listVoices as jest.Mock).mockResolvedValue([
|
|
29
|
+
{ id: 'voice1', name: 'Voice 1', language: 'zh-CN' }
|
|
30
|
+
])
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
describe('ttsTools', () => {
|
|
34
|
+
describe('tool definitions', () => {
|
|
35
|
+
it('should have 10 tools defined', () => {
|
|
36
|
+
expect(ttsTools).toHaveLength(10)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('should include tts_speak tool', () => {
|
|
40
|
+
const tool = ttsTools.find(t => t.name === 'tts_speak')
|
|
41
|
+
expect(tool).toBeDefined()
|
|
42
|
+
expect(tool!.input!.required).toContain('text')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should include tts_stop tool', () => {
|
|
46
|
+
const tool = ttsTools.find(t => t.name === 'tts_stop')
|
|
47
|
+
expect(tool).toBeDefined()
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
it('should include tts_pause tool', () => {
|
|
51
|
+
const tool = ttsTools.find(t => t.name === 'tts_pause')
|
|
52
|
+
expect(tool).toBeDefined()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('should include tts_resume tool', () => {
|
|
56
|
+
const tool = ttsTools.find(t => t.name === 'tts_resume')
|
|
57
|
+
expect(tool).toBeDefined()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should include tts_list_voices tool', () => {
|
|
61
|
+
const tool = ttsTools.find(t => t.name === 'tts_list_voices')
|
|
62
|
+
expect(tool).toBeDefined()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('should include tts_list_providers tool', () => {
|
|
66
|
+
const tool = ttsTools.find(t => t.name === 'tts_list_providers')
|
|
67
|
+
expect(tool).toBeDefined()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('should include tts_status tool', () => {
|
|
71
|
+
const tool = ttsTools.find(t => t.name === 'tts_status')
|
|
72
|
+
expect(tool).toBeDefined()
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('should include tts_stream_speak tool', () => {
|
|
76
|
+
const tool = ttsTools.find(t => t.name === 'tts_stream_speak')
|
|
77
|
+
expect(tool).toBeDefined()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('should include tts_stream_stop tool', () => {
|
|
81
|
+
const tool = ttsTools.find(t => t.name === 'tts_stream_stop')
|
|
82
|
+
expect(tool).toBeDefined()
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it('should include tts_stream_status tool', () => {
|
|
86
|
+
const tool = ttsTools.find(t => t.name === 'tts_stream_status')
|
|
87
|
+
expect(tool).toBeDefined()
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
describe('handleToolCall', () => {
|
|
92
|
+
describe('tts_speak', () => {
|
|
93
|
+
it('should call speak with correct arguments', async () => {
|
|
94
|
+
const result = await handleToolCall('tts_speak', {
|
|
95
|
+
text: 'Hello world',
|
|
96
|
+
provider: 'minimax',
|
|
97
|
+
voice: 'voice1',
|
|
98
|
+
model: 'stream',
|
|
99
|
+
speed: 1.0,
|
|
100
|
+
volume: 80,
|
|
101
|
+
pitch: 1.0
|
|
102
|
+
})
|
|
103
|
+
expect(speaker.speak).toHaveBeenCalledWith('Hello world', expect.objectContaining({
|
|
104
|
+
provider: 'minimax',
|
|
105
|
+
voice: 'voice1',
|
|
106
|
+
model: 'stream',
|
|
107
|
+
speed: 1.0,
|
|
108
|
+
volume: 80,
|
|
109
|
+
pitch: 1.0
|
|
110
|
+
}))
|
|
111
|
+
expect(result).toEqual({ success: true, message: 'Speech completed' })
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it('should return error on TTSError', async () => {
|
|
115
|
+
;(speaker.speak as jest.Mock).mockRejectedValue(
|
|
116
|
+
new TTSError('Test error', TTSErrorCode.AUTH, 'minimax')
|
|
117
|
+
)
|
|
118
|
+
const result = await handleToolCall('tts_speak', { text: 'Hello' })
|
|
119
|
+
expect(result.success).toBe(false)
|
|
120
|
+
expect(result.error).toBe('Test error')
|
|
121
|
+
expect(result.code).toBe(TTSErrorCode.AUTH)
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('should return error on generic error', async () => {
|
|
125
|
+
;(speaker.speak as jest.Mock).mockRejectedValue(new Error('Generic error'))
|
|
126
|
+
const result = await handleToolCall('tts_speak', { text: 'Hello' })
|
|
127
|
+
expect(result.success).toBe(false)
|
|
128
|
+
expect(result.error).toContain('Generic error')
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
describe('tts_stop', () => {
|
|
133
|
+
it('should call stop and return success', async () => {
|
|
134
|
+
const result = await handleToolCall('tts_stop')
|
|
135
|
+
expect(speaker.stop).toHaveBeenCalled()
|
|
136
|
+
expect(result).toEqual({ success: true, message: 'Stopped' })
|
|
137
|
+
})
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
describe('tts_pause', () => {
|
|
141
|
+
it('should call pause and return success', async () => {
|
|
142
|
+
const result = await handleToolCall('tts_pause')
|
|
143
|
+
expect(speaker.pause).toHaveBeenCalled()
|
|
144
|
+
expect(result).toEqual({ success: true, message: 'Paused' })
|
|
145
|
+
})
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
describe('tts_resume', () => {
|
|
149
|
+
it('should call resume and return success', async () => {
|
|
150
|
+
const result = await handleToolCall('tts_resume')
|
|
151
|
+
expect(speaker.resume).toHaveBeenCalled()
|
|
152
|
+
expect(result).toEqual({ success: true, message: 'Resumed' })
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
describe('tts_list_voices', () => {
|
|
157
|
+
it('should return voices list', async () => {
|
|
158
|
+
const result = await handleToolCall('tts_list_voices', { provider: 'minimax' })
|
|
159
|
+
expect(result.success).toBe(true)
|
|
160
|
+
expect(result.voices).toEqual([
|
|
161
|
+
{ id: 'voice1', name: 'Voice 1', language: 'zh-CN' }
|
|
162
|
+
])
|
|
163
|
+
})
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
describe('tts_list_providers', () => {
|
|
167
|
+
it('should return providers list', async () => {
|
|
168
|
+
const result = await handleToolCall('tts_list_providers')
|
|
169
|
+
expect(result.success).toBe(true)
|
|
170
|
+
expect(result.providers).toEqual(['minimax'])
|
|
171
|
+
})
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
describe('tts_status', () => {
|
|
175
|
+
it('should return playing status', async () => {
|
|
176
|
+
const result = await handleToolCall('tts_status')
|
|
177
|
+
expect(result.success).toBe(true)
|
|
178
|
+
expect(result.isPlaying).toBe(true)
|
|
179
|
+
expect(result.isPaused).toBe(false)
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
it('should return paused status', async () => {
|
|
183
|
+
mockSpeaker.isPausedState.mockReturnValue(true)
|
|
184
|
+
const result = await handleToolCall('tts_status')
|
|
185
|
+
expect(result.success).toBe(true)
|
|
186
|
+
expect(result.isPlaying).toBe(true)
|
|
187
|
+
expect(result.isPaused).toBe(true)
|
|
188
|
+
})
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
describe('tts_stream_speak', () => {
|
|
192
|
+
it('should throw when autoRead not enabled', async () => {
|
|
193
|
+
;(index.isAutoReadEnabled as jest.Mock).mockReturnValue(false)
|
|
194
|
+
const result = await handleToolCall('tts_stream_speak', { text: 'Hello' })
|
|
195
|
+
expect(result.success).toBe(false)
|
|
196
|
+
expect(result.error).toContain('autoRead must be enabled')
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
it('should throw when stream not initialized', async () => {
|
|
200
|
+
;(index.isAutoReadEnabled as jest.Mock).mockReturnValue(true)
|
|
201
|
+
;(index.isStreamEnabled as jest.Mock).mockReturnValue(false)
|
|
202
|
+
const result = await handleToolCall('tts_stream_speak', { text: 'Hello' })
|
|
203
|
+
expect(result.success).toBe(false)
|
|
204
|
+
expect(result.error).toContain('Stream components not initialized')
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
it('should start stream speak when enabled', async () => {
|
|
208
|
+
;(index.isAutoReadEnabled as jest.Mock).mockReturnValue(true)
|
|
209
|
+
;(index.isStreamEnabled as jest.Mock).mockReturnValue(true)
|
|
210
|
+
;(index.getStreamReader as jest.Mock).mockReturnValue({ start: jest.fn() })
|
|
211
|
+
;(index.getStreamingSynthesizer as jest.Mock).mockReturnValue({ synthesize: jest.fn() })
|
|
212
|
+
|
|
213
|
+
const result = await handleToolCall('tts_stream_speak', { text: 'Hello' })
|
|
214
|
+
expect(result.success).toBe(true)
|
|
215
|
+
expect(result.message).toBe('Stream speak started')
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
describe('tts_stream_stop', () => {
|
|
220
|
+
it('should throw when stream not enabled', async () => {
|
|
221
|
+
;(index.isStreamEnabled as jest.Mock).mockReturnValue(false)
|
|
222
|
+
const result = await handleToolCall('tts_stream_stop')
|
|
223
|
+
expect(result.success).toBe(false)
|
|
224
|
+
expect(result.error).toContain('Stream mode is not enabled')
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
it('should stop stream player', async () => {
|
|
228
|
+
;(index.isStreamEnabled as jest.Mock).mockReturnValue(true)
|
|
229
|
+
;(index.getStreamPlayer as jest.Mock).mockReturnValue({ stop: jest.fn() })
|
|
230
|
+
|
|
231
|
+
const result = await handleToolCall('tts_stream_stop')
|
|
232
|
+
expect(result.success).toBe(true)
|
|
233
|
+
expect(result.message).toBe('Stream stopped')
|
|
234
|
+
})
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
describe('tts_stream_status', () => {
|
|
238
|
+
it('should return not_initialized when stream not enabled', async () => {
|
|
239
|
+
;(index.isStreamEnabled as jest.Mock).mockReturnValue(false)
|
|
240
|
+
const result = await handleToolCall('tts_stream_status')
|
|
241
|
+
expect(result.success).toBe(true)
|
|
242
|
+
expect(result.isActive).toBe(false)
|
|
243
|
+
expect(result.state).toBe('not_initialized')
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
it('should return stream status when enabled', async () => {
|
|
247
|
+
;(index.isStreamEnabled as jest.Mock).mockReturnValue(true)
|
|
248
|
+
;(index.getStreamStatus as jest.Mock).mockReturnValue({
|
|
249
|
+
isActive: true,
|
|
250
|
+
bytesWritten: 1024,
|
|
251
|
+
state: 'buffering'
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
const result = await handleToolCall('tts_stream_status')
|
|
255
|
+
expect(result.success).toBe(true)
|
|
256
|
+
expect(result.isActive).toBe(true)
|
|
257
|
+
expect(result.bytesWritten).toBe(1024)
|
|
258
|
+
expect(result.state).toBe('buffering')
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
describe('unknown tool', () => {
|
|
263
|
+
it('should throw error for unknown tool', async () => {
|
|
264
|
+
const result = await handleToolCall('unknown_tool')
|
|
265
|
+
expect(result.success).toBe(false)
|
|
266
|
+
expect(result.error).toContain('Unknown tool')
|
|
267
|
+
})
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.test.d.ts","sourceRoot":"","sources":["types.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const types_1 = require("../src/core/types");
|
|
4
|
+
describe('TTS Types', () => {
|
|
5
|
+
describe('TTSError', () => {
|
|
6
|
+
it('should create error with correct properties', () => {
|
|
7
|
+
const error = new types_1.TTSError('Test error', types_1.TTSErrorCode.AUTH, 'test-provider');
|
|
8
|
+
expect(error.message).toBe('Test error');
|
|
9
|
+
expect(error.code).toBe(types_1.TTSErrorCode.AUTH);
|
|
10
|
+
expect(error.provider).toBe('test-provider');
|
|
11
|
+
expect(error.name).toBe('TTSError');
|
|
12
|
+
});
|
|
13
|
+
it('should include details when provided', () => {
|
|
14
|
+
const details = { extra: 'info' };
|
|
15
|
+
const error = new types_1.TTSError('Error', types_1.TTSErrorCode.NETWORK, 'test', details);
|
|
16
|
+
expect(error.details).toEqual(details);
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
describe('Voice', () => {
|
|
20
|
+
it('should have correct structure', () => {
|
|
21
|
+
const voice = {
|
|
22
|
+
id: 'test-voice',
|
|
23
|
+
name: 'Test Voice',
|
|
24
|
+
language: 'zh-CN',
|
|
25
|
+
gender: 'male'
|
|
26
|
+
};
|
|
27
|
+
expect(voice.id).toBe('test-voice');
|
|
28
|
+
expect(voice.name).toBe('Test Voice');
|
|
29
|
+
expect(voice.language).toBe('zh-CN');
|
|
30
|
+
expect(voice.gender).toBe('male');
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('SpeakOptions', () => {
|
|
34
|
+
it('should accept all optional parameters', () => {
|
|
35
|
+
const options = {
|
|
36
|
+
voice: 'voice-1',
|
|
37
|
+
model: 'sync',
|
|
38
|
+
speed: 1.0,
|
|
39
|
+
volume: 80,
|
|
40
|
+
pitch: 1.2
|
|
41
|
+
};
|
|
42
|
+
expect(options.voice).toBe('voice-1');
|
|
43
|
+
expect(options.model).toBe('sync');
|
|
44
|
+
expect(options.speed).toBe(1.0);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('AudioResult', () => {
|
|
48
|
+
it('should have correct structure for buffer', () => {
|
|
49
|
+
const result = {
|
|
50
|
+
audioData: Buffer.from([1, 2, 3]),
|
|
51
|
+
format: 'mp3',
|
|
52
|
+
isStream: false,
|
|
53
|
+
duration: 5.5
|
|
54
|
+
};
|
|
55
|
+
expect(Buffer.isBuffer(result.audioData)).toBe(true);
|
|
56
|
+
expect(result.format).toBe('mp3');
|
|
57
|
+
expect(result.isStream).toBe(false);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
//# sourceMappingURL=types.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.test.js","sourceRoot":"","sources":["types.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { TTSError, TTSErrorCode, Voice, TTSCapabilities, SpeakOptions, AudioResult } from '../src/core/types'
|
|
2
|
+
|
|
3
|
+
describe('TTS Types', () => {
|
|
4
|
+
describe('TTSError', () => {
|
|
5
|
+
it('should create error with correct properties', () => {
|
|
6
|
+
const error = new TTSError('Test error', TTSErrorCode.AUTH, 'test-provider')
|
|
7
|
+
expect(error.message).toBe('Test error')
|
|
8
|
+
expect(error.code).toBe(TTSErrorCode.AUTH)
|
|
9
|
+
expect(error.provider).toBe('test-provider')
|
|
10
|
+
expect(error.name).toBe('TTSError')
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should include details when provided', () => {
|
|
14
|
+
const details = { extra: 'info' }
|
|
15
|
+
const error = new TTSError('Error', TTSErrorCode.NETWORK, 'test', details)
|
|
16
|
+
expect(error.details).toEqual(details)
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
describe('Voice', () => {
|
|
21
|
+
it('should have correct structure', () => {
|
|
22
|
+
const voice: Voice = {
|
|
23
|
+
id: 'test-voice',
|
|
24
|
+
name: 'Test Voice',
|
|
25
|
+
language: 'zh-CN',
|
|
26
|
+
gender: 'male'
|
|
27
|
+
}
|
|
28
|
+
expect(voice.id).toBe('test-voice')
|
|
29
|
+
expect(voice.name).toBe('Test Voice')
|
|
30
|
+
expect(voice.language).toBe('zh-CN')
|
|
31
|
+
expect(voice.gender).toBe('male')
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
describe('SpeakOptions', () => {
|
|
36
|
+
it('should accept all optional parameters', () => {
|
|
37
|
+
const options: SpeakOptions = {
|
|
38
|
+
voice: 'voice-1',
|
|
39
|
+
model: 'sync',
|
|
40
|
+
speed: 1.0,
|
|
41
|
+
volume: 80,
|
|
42
|
+
pitch: 1.2
|
|
43
|
+
}
|
|
44
|
+
expect(options.voice).toBe('voice-1')
|
|
45
|
+
expect(options.model).toBe('sync')
|
|
46
|
+
expect(options.speed).toBe(1.0)
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
describe('AudioResult', () => {
|
|
51
|
+
it('should have correct structure for buffer', () => {
|
|
52
|
+
const result: AudioResult = {
|
|
53
|
+
audioData: Buffer.from([1, 2, 3]),
|
|
54
|
+
format: 'mp3',
|
|
55
|
+
isStream: false,
|
|
56
|
+
duration: 5.5
|
|
57
|
+
}
|
|
58
|
+
expect(Buffer.isBuffer(result.audioData)).toBe(true)
|
|
59
|
+
expect(result.format).toBe('mp3')
|
|
60
|
+
expect(result.isStream).toBe(false)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
})
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ES2022",
|
|
5
|
+
"lib": ["ES2022"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"moduleResolution": "bundler",
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"types": ["node", "jest"],
|
|
18
|
+
"typeRoots": ["./src/types", "./node_modules/@types"]
|
|
19
|
+
},
|
|
20
|
+
"include": ["src/**/*"],
|
|
21
|
+
"exclude": ["node_modules", "dist"]
|
|
22
|
+
}
|