• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

semperai / vad-ts / 18363907063

09 Oct 2025 02:44AM UTC coverage: 73.936%. First build
18363907063

Pull #1

github

web-flow
Merge 79ce1f1ec into b562b936a
Pull Request #1: Typescript Port

187 of 199 branches covered (93.97%)

Branch coverage included in aggregate %.

460 of 637 new or added lines in 8 files covered. (72.21%)

1098 of 1539 relevant lines covered (71.35%)

893.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.0
/packages/web/src/validation.ts
1
import { log } from "./logging"
1✔
2

3
/**
4
 * Error types that can occur during VAD initialization or operation
5
 */
6
export class VADError extends Error {
1✔
7
  constructor(
1✔
8
    message: string,
11✔
9
    public readonly code: string,
11✔
10
    public readonly cause?: Error
11✔
11
  ) {
11✔
12
    super(message)
11✔
13
    this.name = "VADError"
11✔
14
  }
11✔
15
}
1✔
16

17
export class AudioConstraintsError extends VADError {
1✔
18
  constructor(message: string, cause?: Error) {
1✔
19
    super(message, "AUDIO_CONSTRAINTS_ERROR", cause)
6✔
20
    this.name = "AudioConstraintsError"
6✔
21
  }
6✔
22
}
1✔
23

24
export class ModelLoadError extends VADError {
1✔
25
  constructor(message: string, cause?: Error) {
1✔
26
    super(message, "MODEL_LOAD_ERROR", cause)
1✔
27
    this.name = "ModelLoadError"
1✔
28
  }
1✔
29
}
1✔
30

31
export class WorkletLoadError extends VADError {
1✔
32
  constructor(message: string, cause?: Error) {
1✔
33
    super(message, "WORKLET_LOAD_ERROR", cause)
1✔
34
    this.name = "WorkletLoadError"
1✔
35
  }
1✔
36
}
1✔
37

38
export class AudioContextError extends VADError {
1✔
39
  constructor(message: string, cause?: Error) {
1✔
40
    super(message, "AUDIO_CONTEXT_ERROR", cause)
3✔
41
    this.name = "AudioContextError"
3✔
42
  }
3✔
43
}
1✔
44

45
/**
46
 * Validate audio constraints before requesting user media
47
 */
48
export function validateAudioConstraints(
1✔
49
  constraints: MediaTrackConstraints
10✔
50
): void {
10✔
51
  log.debug("Validating audio constraints:", constraints)
10✔
52

53
  // Check channel count
54
  if (constraints.channelCount) {
10✔
55
    const channelCount =
3✔
56
      typeof constraints.channelCount === "number"
3✔
57
        ? constraints.channelCount
2✔
58
        : (constraints.channelCount as ConstrainULongRange).ideal ??
1!
NEW
59
          (constraints.channelCount as ConstrainULongRange).exact
×
60

61
    if (channelCount && channelCount !== 1) {
3✔
62
      log.warn(
2✔
63
        `VAD works best with mono audio (channelCount: 1), got ${channelCount}`
2✔
64
      )
2✔
65
    }
2✔
66
  }
3✔
67

68
  // Check sample rate
69
  if (constraints.sampleRate) {
10✔
70
    const sampleRate =
6✔
71
      typeof constraints.sampleRate === "number"
6✔
72
        ? constraints.sampleRate
4✔
73
        : (constraints.sampleRate as ConstrainULongRange).ideal ??
2✔
74
          (constraints.sampleRate as ConstrainULongRange).exact
2✔
75

76
    if (sampleRate && sampleRate < 16000) {
6✔
77
      throw new AudioConstraintsError(
3✔
78
        `Sample rate must be at least 16000 Hz, got ${sampleRate} Hz`
3✔
79
      )
3✔
80
    }
3✔
81
  }
6✔
82

83
  // Warn about audio processing flags that might interfere with VAD
84
  if (constraints.noiseSuppression === false) {
10✔
85
    log.warn(
1✔
86
      "Noise suppression is disabled. This may reduce VAD accuracy in noisy environments."
1✔
87
    )
1✔
88
  }
1✔
89

90
  if (constraints.echoCancellation === false) {
10✔
91
    log.warn(
1✔
92
      "Echo cancellation is disabled. This may cause false positives if system audio is playing."
1✔
93
    )
1✔
94
  }
1✔
95

96
  log.debug("Audio constraints validation passed")
7✔
97
}
7✔
98

99
/**
100
 * Check if getUserMedia is available in the current environment
101
 */
102
export function checkUserMediaSupport(): void {
1✔
103
  if (!navigator?.mediaDevices?.getUserMedia) {
3✔
104
    throw new AudioConstraintsError(
2✔
105
      "getUserMedia is not supported in this browser. VAD requires microphone access."
2✔
106
    )
2✔
107
  }
2✔
108

109
  log.debug("getUserMedia support confirmed")
1✔
110
}
1✔
111

112
/**
113
 * Check if AudioWorklet is supported
114
 */
115
export function checkAudioWorkletSupport(ctx: AudioContext): boolean {
1✔
116
  const hasWorklet =
2✔
117
    "audioWorklet" in ctx && typeof AudioWorkletNode === "function"
2✔
118

119
  if (!hasWorklet) {
2✔
120
    log.warn(
1✔
121
      "AudioWorklet is not supported. Falling back to ScriptProcessorNode (deprecated)."
1✔
122
    )
1✔
123
  } else {
1✔
124
    log.debug("AudioWorklet support confirmed")
1✔
125
  }
1✔
126

127
  return hasWorklet
2✔
128
}
2✔
129

130
/**
131
 * Validate model file URL
132
 */
133
export function validateModelURL(url: string): void {
1✔
NEW
134
  try {
×
NEW
135
    new URL(url, window.location.origin)
×
NEW
136
  } catch (e) {
×
NEW
137
    throw new ModelLoadError(`Invalid model URL: ${url}`, e as Error)
×
NEW
138
  }
×
NEW
139
}
×
140

141
/**
142
 * Validate worklet file URL
143
 */
144
export function validateWorkletURL(url: string): void {
1✔
NEW
145
  try {
×
NEW
146
    new URL(url, window.location.origin)
×
NEW
147
  } catch (e) {
×
NEW
148
    throw new WorkletLoadError(`Invalid worklet URL: ${url}`, e as Error)
×
NEW
149
  }
×
NEW
150
}
×
151

152
/**
153
 * Validate AudioContext state
154
 */
155
export function validateAudioContextState(ctx: AudioContext): void {
1✔
156
  if (ctx.state === "closed") {
4✔
157
    throw new AudioContextError(
2✔
158
      "AudioContext is closed. Cannot initialize VAD."
2✔
159
    )
2✔
160
  }
2✔
161

162
  if (ctx.state === "suspended") {
4✔
163
    log.warn(
1✔
164
      "AudioContext is suspended. It will be resumed when VAD starts."
1✔
165
    )
1✔
166
  }
1✔
167

168
  log.debug("AudioContext state validated:", ctx.state)
2✔
169
}
2✔
170

171
/**
172
 * Check browser compatibility for VAD features
173
 */
174
export interface BrowserCompatibility {
175
  getUserMedia: boolean
176
  audioWorklet: boolean
177
  audioContext: boolean
178
  onnxRuntime: boolean
179
  warnings: string[]
180
}
181

182
export function checkBrowserCompatibility(): BrowserCompatibility {
1✔
183
  const warnings: string[] = []
6✔
184

185
  const getUserMedia = !!navigator?.mediaDevices?.getUserMedia
6✔
186
  if (!getUserMedia) {
6✔
187
    warnings.push("getUserMedia API not available")
1✔
188
  }
1✔
189

190
  const audioContext = typeof AudioContext !== "undefined"
6✔
191
  if (!audioContext) {
6!
NEW
192
    warnings.push("AudioContext API not available")
×
NEW
193
  }
×
194

195
  const audioWorklet =
6✔
196
    audioContext &&
6✔
197
    "audioWorklet" in AudioContext.prototype &&
6!
NEW
198
    typeof AudioWorkletNode === "function"
×
199
  if (!audioWorklet) {
6✔
200
    warnings.push("AudioWorklet API not available (will use fallback)")
6✔
201
  }
6✔
202

203
  // Check for ONNX Runtime dependencies
204
  const onnxRuntime =
6✔
205
    typeof WebAssembly !== "undefined" &&
6✔
206
    typeof SharedArrayBuffer !== "undefined"
5✔
207
  if (!onnxRuntime) {
6✔
208
    warnings.push(
2✔
209
      "WebAssembly or SharedArrayBuffer not available. ONNX Runtime may not work."
2✔
210
    )
2✔
211
  }
2✔
212

213
  const result: BrowserCompatibility = {
6✔
214
    getUserMedia,
6✔
215
    audioWorklet,
6✔
216
    audioContext,
6✔
217
    onnxRuntime,
6✔
218
    warnings,
6✔
219
  }
6✔
220

221
  if (warnings.length > 0) {
6✔
222
    log.warn("Browser compatibility warnings:", warnings)
6✔
223
  } else {
6!
NEW
224
    log.info("Browser is fully compatible with VAD")
×
NEW
225
  }
×
226

227
  return result
6✔
228
}
6✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc