@goodandready/dsh-voice 0.8.21 → 0.8.22

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.
@@ -274,7 +274,14 @@
274
274
  return () => clearInterval(timer)
275
275
  }, [ready])
276
276
 
277
- React.useEffect(() => { if (ready && draft === null) setDraft(JSON.parse(JSON.stringify(value))) }, [ready, draft, value])
277
+ const deepClone = (obj) => {
278
+ if (typeof structuredClone === 'function') {
279
+ try { return structuredClone(obj) } catch (e) { /* fallback */ }
280
+ }
281
+ try { return JSON.parse(JSON.stringify(obj)) } catch (e) { return Object.assign({}, obj) }
282
+ }
283
+
284
+ React.useEffect(() => { if (ready && draft === null) setDraft(deepClone(value)) }, [ready, draft, value])
278
285
 
279
286
  React.useEffect(() => {
280
287
  if (!ready) return
@@ -335,7 +342,7 @@
335
342
  }
336
343
 
337
344
  const setIn = (mode, key, v) => setDraft((d) => {
338
- const next = JSON.parse(JSON.stringify(d || {}))
345
+ const next = deepClone(d || {})
339
346
  next[mode] = next[mode] || {}
340
347
  next[mode][key] = v
341
348
  return next
package/lib/client.js CHANGED
@@ -1606,7 +1606,14 @@ window.__ModuleLoader__.load({
1606
1606
  return () => clearInterval(timer)
1607
1607
  }, [ready])
1608
1608
 
1609
- React.useEffect(() => { if (ready && draft === null) setDraft(JSON.parse(JSON.stringify(value))) }, [ready, draft, value])
1609
+ const deepClone = (obj) => {
1610
+ if (typeof structuredClone === 'function') {
1611
+ try { return structuredClone(obj) } catch (e) { /* fallback */ }
1612
+ }
1613
+ try { return JSON.parse(JSON.stringify(obj)) } catch (e) { return Object.assign({}, obj) }
1614
+ }
1615
+
1616
+ React.useEffect(() => { if (ready && draft === null) setDraft(deepClone(value)) }, [ready, draft, value])
1610
1617
 
1611
1618
  React.useEffect(() => {
1612
1619
  if (!ready) return
@@ -1667,7 +1674,7 @@ window.__ModuleLoader__.load({
1667
1674
  }
1668
1675
 
1669
1676
  const setIn = (mode, key, v) => setDraft((d) => {
1670
- const next = JSON.parse(JSON.stringify(d || {}))
1677
+ const next = deepClone(d || {})
1671
1678
  next[mode] = next[mode] || {}
1672
1679
  next[mode][key] = v
1673
1680
  return next
package/lib/providers.js CHANGED
@@ -113,6 +113,14 @@ async function readErrorDetail(res, defaultLabel) {
113
113
  return `${defaultLabel} ${detail}`
114
114
  }
115
115
 
116
+ async function safeJson(res, defaultLabel) {
117
+ try {
118
+ return await res.json()
119
+ } catch {
120
+ throw new Error(`${defaultLabel}: invalid JSON response`)
121
+ }
122
+ }
123
+
116
124
  export function makeProviders(deps, req) {
117
125
  const { resolveKey, fetchImpl, cfg } = deps
118
126
  const { bytes, mime, lang, signal, models } = req
@@ -134,7 +142,10 @@ export function makeProviders(deps, req) {
134
142
  signal,
135
143
  })
136
144
  if (!res.ok) throw new Error(await readErrorDetail(res, 'Deepgram'))
137
- const data = await res.json()
145
+ let data
146
+ try { data = await safeJson(res, 'Deepgram') } catch (e) {
147
+ return { ok: false, provider: 'deepgram', reason: e.message }
148
+ }
138
149
  const text = (data?.results?.channels?.[0]?.alternatives?.[0]?.transcript || '').trim()
139
150
  return { ok: text.length > 0, provider: 'deepgram', text, reason: text ? '' : 'empty transcript' }
140
151
  }
@@ -154,7 +165,10 @@ export function makeProviders(deps, req) {
154
165
  signal,
155
166
  })
156
167
  if (!res.ok) throw new Error(await readErrorDetail(res, 'Groq'))
157
- const data = await res.json()
168
+ let data
169
+ try { data = await safeJson(res, 'Groq') } catch (e) {
170
+ return { ok: false, provider: 'groq', reason: e.message }
171
+ }
158
172
  const text = (data?.text || '').trim()
159
173
  return { ok: text.length > 0, provider: 'groq', text, reason: text ? '' : 'empty transcript' }
160
174
  }
@@ -170,7 +184,10 @@ export function makeProviders(deps, req) {
170
184
  signal,
171
185
  })
172
186
  if (!res.ok) throw new Error(await readErrorDetail(res, 'HF'))
173
- const data = await res.json()
187
+ let data
188
+ try { data = await safeJson(res, 'HF') } catch (e) {
189
+ return { ok: false, provider: 'hf', reason: e.message }
190
+ }
174
191
  const text = (data?.text || '').trim()
175
192
  return { ok: text.length > 0, provider: 'hf', text, reason: text ? '' : 'empty transcript' }
176
193
  }
@@ -210,7 +227,10 @@ export function makeProviders(deps, req) {
210
227
  try { const e = await res.json(); if (e?.error) detail = e.error } catch { /* body is not json */ }
211
228
  return { ok: false, provider: 'local-whisper', reason: `local whisper: ${detail}` }
212
229
  }
213
- const data = await res.json()
230
+ let data
231
+ try { data = await safeJson(res, 'local whisper') } catch (e) {
232
+ return { ok: false, provider: 'local-whisper', reason: e.message }
233
+ }
214
234
  const text = (data?.text || '').trim()
215
235
  return { ok: text.length > 0, provider: 'local-whisper', text, reason: text ? '' : 'empty transcript' }
216
236
  }
@@ -298,7 +318,7 @@ export function makeProviders(deps, req) {
298
318
  method: 'POST', headers, body: form, signal,
299
319
  })
300
320
  if (!res.ok) throw new Error(await readErrorDetail(res, label))
301
- const data = await res.json()
321
+ const data = await safeJson(res, label)
302
322
  return (data?.text || '').trim()
303
323
  }
304
324
 
@@ -331,7 +351,7 @@ export function makeProviders(deps, req) {
331
351
  signal,
332
352
  })
333
353
  if (!res.ok) throw new Error(await readErrorDetail(res, label))
334
- const data = await res.json()
354
+ const data = await safeJson(res, label)
335
355
  return String(data?.choices?.[0]?.message?.content || '').trim()
336
356
  }
337
357
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@goodandready/dsh-voice",
3
- "version": "0.8.21",
3
+ "version": "0.8.22",
4
4
  "description": "Voice input for DeepSeek Harness: dictation chunked by pauses and voice messages, each with its own provider fallback chain (Deepgram, Groq, HuggingFace, local whisper.cpp, plus any OpenAI-compatible API of your own).",
5
5
  "license": "MIT",
6
6
  "type": "module",