@happyvertical/speech 0.101.3 → 0.102.1

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.
Files changed (63) hide show
  1. package/README.md +36 -0
  2. package/dist/adapters/local/audio.d.ts +11 -1
  3. package/dist/adapters/local/env.d.ts +6 -2
  4. package/dist/adapters/local/runtime.d.ts +8 -1
  5. package/dist/adapters/local/transcriber.d.ts +3 -3
  6. package/dist/adapters/local/types.d.ts +7 -1
  7. package/dist/adapters/local/worker.d.ts +13 -3
  8. package/dist/adapters/openai-compatible-transcriber.d.ts +1 -1
  9. package/dist/adapters/openai-compatible.d.ts +1 -1
  10. package/dist/adapters/openai-realtime.d.ts +13 -2
  11. package/dist/adapters/qwen3.d.ts +1 -1
  12. package/dist/adapters/studio-server.d.ts +1 -1
  13. package/dist/adapters/voxtral-realtime.d.ts +36 -2
  14. package/dist/browser.d.ts +8 -0
  15. package/dist/browser.d.ts.map +1 -0
  16. package/dist/browser.js +159 -0
  17. package/dist/browser.js.map +1 -0
  18. package/dist/chunks/{errors-CQBX3OPS.js → errors-Cs0vX-mV.js} +25 -2
  19. package/dist/chunks/errors-Cs0vX-mV.js.map +1 -0
  20. package/dist/chunks/{registry-C_5BakmM.js → registry-DFSF3WoM.js} +2 -2
  21. package/dist/chunks/{registry-C_5BakmM.js.map → registry-DFSF3WoM.js.map} +1 -1
  22. package/dist/chunks/wav-B0Djpj2r.js +232 -0
  23. package/dist/chunks/wav-B0Djpj2r.js.map +1 -0
  24. package/dist/chunks/{websocket-CR9uXAuS.js → websocket-YbAfY6YI.js} +2 -2
  25. package/dist/chunks/{websocket-CR9uXAuS.js.map → websocket-YbAfY6YI.js.map} +1 -1
  26. package/dist/conversation/openai-webrtc.d.ts +1 -1
  27. package/dist/conversation-server.d.ts +3 -2
  28. package/dist/conversation-server.js +2 -2
  29. package/dist/conversation.js +1 -1
  30. package/dist/index.d.ts +1 -0
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +35 -4
  33. package/dist/index.js.map +1 -1
  34. package/dist/local.d.ts +20 -2
  35. package/dist/local.js +2 -2
  36. package/dist/pcm.d.ts +8 -0
  37. package/dist/pcm.d.ts.map +1 -0
  38. package/dist/pcm.js +3 -0
  39. package/dist/shared/audio.d.ts +7 -1
  40. package/dist/shared/env.d.ts +8 -1
  41. package/dist/shared/errors.d.ts +18 -0
  42. package/dist/shared/errors.d.ts.map +1 -1
  43. package/dist/shared/factory.d.ts +2 -2
  44. package/dist/shared/http.d.ts +2 -2
  45. package/dist/shared/input-format.d.ts +20 -0
  46. package/dist/shared/input-format.d.ts.map +1 -0
  47. package/dist/shared/pcm-capture.d.ts +38 -0
  48. package/dist/shared/pcm-capture.d.ts.map +1 -0
  49. package/dist/shared/pcm.d.ts +6 -1
  50. package/dist/shared/realtime-auth.d.ts +10 -2
  51. package/dist/shared/realtime-session.d.ts +14 -5
  52. package/dist/shared/registry.d.ts +10 -2
  53. package/dist/shared/resample.d.ts +15 -0
  54. package/dist/shared/resample.d.ts.map +1 -0
  55. package/dist/shared/streaming-client-secret.d.ts +21 -3
  56. package/dist/shared/streaming-factory.d.ts +7 -3
  57. package/dist/shared/streaming-types.d.ts +10 -3
  58. package/dist/shared/turn-limit.d.ts +13 -1
  59. package/dist/shared/types.d.ts +4 -4
  60. package/dist/shared/wav.d.ts +52 -0
  61. package/dist/shared/wav.d.ts.map +1 -0
  62. package/package.json +9 -1
  63. package/dist/chunks/errors-CQBX3OPS.js.map +0 -1
@@ -0,0 +1,232 @@
1
+ import { o as WavFormatError } from "./errors-Cs0vX-mV.js";
2
+ //#region src/shared/resample.ts
3
+ /**
4
+ * Mono resampler: windowed-sinc (Hann, 16 zero crossings per side)
5
+ * interpolation. When downsampling, the kernel's cutoff is scaled to 95% of
6
+ * the target Nyquist frequency so content above it is attenuated before it can
7
+ * alias; when upsampling it is a plain band-limited interpolator. Edges treat
8
+ * missing samples as absent and renormalise the kernel, so DC gain stays 1.
9
+ * Quality is suited to speech recognition (passband flat to roughly 90% of
10
+ * Nyquist, stopband well below -40 dB), not mastering. Cost is
11
+ * O(output length x taps), taps ~ 32 x max(1, fromRate / toRate).
12
+ */
13
+ /** Largest output a single call may produce (samples). */
14
+ var MAX_RESAMPLE_OUTPUT_SAMPLES = 2 ** 28;
15
+ var ZERO_CROSSINGS = 16;
16
+ var DOWNSAMPLE_CUTOFF = .95;
17
+ function sinc(x) {
18
+ if (x === 0) return 1;
19
+ const px = Math.PI * x;
20
+ return Math.sin(px) / px;
21
+ }
22
+ /** Resamples mono float samples from `fromRate` to `toRate` (Hz). */
23
+ function resampleMono(samples, fromRate, toRate) {
24
+ for (const [name, rate] of [["fromRate", fromRate], ["toRate", toRate]]) if (!Number.isFinite(rate) || rate <= 0) throw new WavFormatError("invalid_argument", `${name} must be a positive finite number (got ${String(rate)})`);
25
+ const length = Math.floor(samples.length * toRate / fromRate);
26
+ if (length > MAX_RESAMPLE_OUTPUT_SAMPLES) throw new WavFormatError("invalid_argument", `resampled output would be ${length} samples; the limit is ${MAX_RESAMPLE_OUTPUT_SAMPLES}`);
27
+ if (fromRate === toRate) return samples.slice();
28
+ const out = new Float32Array(length);
29
+ const step = fromRate / toRate;
30
+ const cutoff = step > 1 ? DOWNSAMPLE_CUTOFF / step : 1;
31
+ const halfWidth = ZERO_CROSSINGS / cutoff;
32
+ const last = samples.length - 1;
33
+ for (let index = 0; index < length; index += 1) {
34
+ const center = index * step;
35
+ const first = Math.max(0, Math.ceil(center - halfWidth));
36
+ const end = Math.min(last, Math.floor(center + halfWidth));
37
+ let sum = 0;
38
+ let weights = 0;
39
+ for (let tap = first; tap <= end; tap += 1) {
40
+ const distance = tap - center;
41
+ const window = .5 + .5 * Math.cos(Math.PI * distance / halfWidth);
42
+ const weight = cutoff * sinc(cutoff * distance) * window;
43
+ sum += weight * samples[tap];
44
+ weights += weight;
45
+ }
46
+ out[index] = weights !== 0 ? sum / weights : 0;
47
+ }
48
+ return out;
49
+ }
50
+ //#endregion
51
+ //#region src/shared/wav.ts
52
+ /**
53
+ * Pure, dependency-free 16-bit PCM WAV helpers. No Node APIs and no DOM: they
54
+ * run in the browser, in Node and in workers. Exposed as
55
+ * `@happyvertical/speech/pcm`.
56
+ *
57
+ * `parseWavPcm16` is strict because it reads untrusted bytes: every declared
58
+ * size is checked against the buffer before it is trusted, nothing is
59
+ * allocated from a declared (rather than present) size, and work is linear in
60
+ * the input length.
61
+ */
62
+ var HEADER_BYTES = 44;
63
+ var MAX_UINT32 = 4294967295;
64
+ var FORMAT_PCM = 1;
65
+ var FORMAT_EXTENSIBLE = 65534;
66
+ /** `KSDATAFORMAT_SUBTYPE_PCM` minus its leading format tag: 00000000-0010-8000-00AA-00389B71. */
67
+ var PCM_GUID_TAIL = [
68
+ 0,
69
+ 0,
70
+ 0,
71
+ 0,
72
+ 16,
73
+ 0,
74
+ 128,
75
+ 0,
76
+ 0,
77
+ 170,
78
+ 0,
79
+ 56,
80
+ 155,
81
+ 113
82
+ ];
83
+ /**
84
+ * Converts float samples (-1..1) to 16-bit PCM (scale 32768, the inverse of
85
+ * `pcm16ToFloat32`, so Int16 round-trips exactly). Clamps; NaN becomes 0.
86
+ */
87
+ function float32ToPcm16(samples) {
88
+ const out = new Int16Array(samples.length);
89
+ for (let index = 0; index < samples.length; index += 1) {
90
+ const value = samples[index];
91
+ if (Number.isNaN(value)) continue;
92
+ out[index] = Math.min(32767, Math.round((value > 1 ? 1 : value < -1 ? -1 : value) * 32768));
93
+ }
94
+ return out;
95
+ }
96
+ /** Converts 16-bit PCM to float samples in -1..1 (divides by 32768). */
97
+ function pcm16ToFloat32(samples) {
98
+ const out = new Float32Array(samples.length);
99
+ for (let index = 0; index < samples.length; index += 1) out[index] = samples[index] / 32768;
100
+ return out;
101
+ }
102
+ function assertSampleRate(sampleRate, what) {
103
+ if (!Number.isInteger(sampleRate) || sampleRate < 1 || sampleRate * 2 > MAX_UINT32) throw new WavFormatError("invalid_argument", `${what} must be a positive integer sample rate (got ${String(sampleRate)})`);
104
+ }
105
+ function writeTag(view, offset, tag) {
106
+ for (let index = 0; index < tag.length; index += 1) view.setUint8(offset + index, tag.charCodeAt(index));
107
+ }
108
+ /**
109
+ * Encodes mono samples as a canonical 44-byte-header 16-bit PCM WAV. Float
110
+ * input is clamped and converted; `Int16Array` is written as is.
111
+ */
112
+ function encodeWavPcm16(samples, sampleRate) {
113
+ assertSampleRate(sampleRate, "sampleRate");
114
+ const dataBytes = samples.length * 2;
115
+ if (dataBytes + HEADER_BYTES - 8 > MAX_UINT32) throw new WavFormatError("invalid_argument", "too many samples for a single WAV (4 GiB limit)");
116
+ const pcm = samples instanceof Int16Array ? samples : float32ToPcm16(samples);
117
+ const bytes = new Uint8Array(HEADER_BYTES + dataBytes);
118
+ const view = new DataView(bytes.buffer);
119
+ writeTag(view, 0, "RIFF");
120
+ view.setUint32(4, 36 + dataBytes, true);
121
+ writeTag(view, 8, "WAVE");
122
+ writeTag(view, 12, "fmt ");
123
+ view.setUint32(16, 16, true);
124
+ view.setUint16(20, FORMAT_PCM, true);
125
+ view.setUint16(22, 1, true);
126
+ view.setUint32(24, sampleRate, true);
127
+ view.setUint32(28, sampleRate * 2, true);
128
+ view.setUint16(32, 2, true);
129
+ view.setUint16(34, 16, true);
130
+ writeTag(view, 36, "data");
131
+ view.setUint32(40, dataBytes, true);
132
+ for (let index = 0; index < pcm.length; index += 1) view.setInt16(HEADER_BYTES + index * 2, pcm[index], true);
133
+ return bytes;
134
+ }
135
+ function fail(reason, message) {
136
+ throw new WavFormatError(reason, message);
137
+ }
138
+ function tagAt(bytes, offset) {
139
+ return String.fromCharCode(bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3]);
140
+ }
141
+ function readFmt(view, bytes, body, size) {
142
+ if (size < 16) fail("malformed_chunk", `fmt chunk is ${size} bytes; at least 16 required`);
143
+ let format = view.getUint16(body, true);
144
+ const channels = view.getUint16(body + 2, true);
145
+ const sampleRate = view.getUint32(body + 4, true);
146
+ const byteRate = view.getUint32(body + 8, true);
147
+ const blockAlign = view.getUint16(body + 12, true);
148
+ const bits = view.getUint16(body + 14, true);
149
+ if (format === FORMAT_EXTENSIBLE) {
150
+ if (size < 40) fail("malformed_chunk", `WAVE_FORMAT_EXTENSIBLE fmt chunk is ${size} bytes; at least 40 required`);
151
+ const cbSize = view.getUint16(body + 16, true);
152
+ if (cbSize < 22 || 18 + cbSize > size) fail("malformed_chunk", `WAVE_FORMAT_EXTENSIBLE extension (${cbSize} bytes) is under 22 or runs past its fmt chunk`);
153
+ const validBits = view.getUint16(body + 18, true);
154
+ if (validBits !== 0 && validBits !== 16) fail("unsupported_bits", `WAVE_FORMAT_EXTENSIBLE valid bits is ${validBits}; only 16 supported`);
155
+ const guid = body + 24;
156
+ const subFormat = view.getUint16(guid, true);
157
+ const tailMatches = PCM_GUID_TAIL.every((byte, index) => bytes[guid + 2 + index] === byte);
158
+ if (subFormat !== FORMAT_PCM || !tailMatches) fail("unsupported_format", "WAVE_FORMAT_EXTENSIBLE subtype is not PCM");
159
+ format = FORMAT_PCM;
160
+ }
161
+ if (format !== FORMAT_PCM) fail("unsupported_format", `WAV format tag ${format} is not PCM`);
162
+ if (bits !== 16) fail("unsupported_bits", `WAV is ${bits}-bit; only 16-bit PCM is supported`);
163
+ if (channels < 1) fail("inconsistent_header", "WAV declares zero channels");
164
+ if (sampleRate < 1) fail("inconsistent_header", "WAV declares a zero sample rate");
165
+ if (blockAlign !== channels * 2) fail("inconsistent_header", `WAV block align ${blockAlign} is not channels x 2 (${channels * 2})`);
166
+ if (byteRate !== sampleRate * blockAlign) fail("inconsistent_header", "WAV byte rate is not sample rate x block align");
167
+ return {
168
+ channels,
169
+ sampleRate
170
+ };
171
+ }
172
+ /**
173
+ * Strictly parses a 16-bit PCM WAV. Rejects (with `WavFormatError`) anything
174
+ * that is not a well-formed RIFF/WAVE with a `fmt ` chunk before its `data`
175
+ * chunk, format tag 1 (or WAVE_FORMAT_EXTENSIBLE with the PCM subtype), 16
176
+ * bits, a consistent header, and a declared data length that lies within the
177
+ * buffer, is non-zero and is a whole number of frames. Nothing is clamped or
178
+ * guessed. Every chunk inside the declared RIFF is bounds-checked, including
179
+ * those after `data`; bytes beyond the RIFF are ignored.
180
+ */
181
+ function parseWavPcm16(bytes, expect = {}) {
182
+ if (bytes.length < 12 || tagAt(bytes, 0) !== "RIFF" || tagAt(bytes, 8) !== "WAVE") fail("not_riff", "not a RIFF/WAVE file");
183
+ const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
184
+ const riffEnd = 8 + view.getUint32(4, true);
185
+ if (riffEnd < 12) fail("malformed_chunk", "RIFF size is too small");
186
+ if (riffEnd > bytes.length) fail("truncated", `RIFF declares ${riffEnd} bytes but only ${bytes.length} are present`);
187
+ let fmt;
188
+ let dataAt;
189
+ let dataSize = 0;
190
+ let offset = 12;
191
+ while (offset < riffEnd) {
192
+ if (offset + 8 > riffEnd) fail("malformed_chunk", "chunk header runs past the end of the RIFF");
193
+ const id = tagAt(bytes, offset);
194
+ const size = view.getUint32(offset + 4, true);
195
+ const body = offset + 8;
196
+ if (body + size > riffEnd) fail("truncated", `${JSON.stringify(id)} chunk declares ${size} bytes but only ${riffEnd - body} remain`);
197
+ if (id === "fmt ") {
198
+ if (fmt) fail("duplicate_fmt", "WAV has more than one fmt chunk");
199
+ fmt = readFmt(view, bytes, body, size);
200
+ if (expect.sampleRate !== void 0 && fmt.sampleRate !== expect.sampleRate) fail("rate_mismatch", `WAV is ${fmt.sampleRate} Hz; expected ${expect.sampleRate} Hz`);
201
+ if (expect.channels !== void 0 && fmt.channels !== expect.channels) fail("channel_mismatch", `WAV has ${fmt.channels} channel(s); expected ${expect.channels}`);
202
+ } else if (id === "data") {
203
+ if (dataAt !== void 0) fail("malformed_chunk", "WAV has more than one data chunk");
204
+ if (!fmt) fail("data_before_fmt", "WAV data chunk precedes its fmt chunk");
205
+ if (size === 0) fail("empty_data", "WAV data chunk is empty");
206
+ const frameBytes = fmt.channels * 2;
207
+ if (size % frameBytes !== 0) fail("misaligned_data", `WAV data (${size} bytes) is not a whole number of ${frameBytes}-byte frames`);
208
+ dataAt = body;
209
+ dataSize = size;
210
+ }
211
+ const next = body + size + size % 2;
212
+ if (next > riffEnd) fail("truncated", `${JSON.stringify(id)} chunk is missing its pad byte`);
213
+ offset = next;
214
+ }
215
+ if (!fmt || dataAt === void 0) return fail(fmt ? "missing_data" : "missing_fmt", fmt ? "WAV has no data chunk" : "WAV has no fmt chunk");
216
+ const data = bytes.subarray(dataAt, dataAt + dataSize);
217
+ const samples = new Int16Array(dataSize / 2);
218
+ for (let index = 0; index < samples.length; index += 1) samples[index] = view.getInt16(dataAt + index * 2, true);
219
+ const frames = dataSize / (fmt.channels * 2);
220
+ return {
221
+ sampleRate: fmt.sampleRate,
222
+ channels: fmt.channels,
223
+ frames,
224
+ durationMs: Math.round(frames / fmt.sampleRate * 1e3),
225
+ samples,
226
+ data
227
+ };
228
+ }
229
+ //#endregion
230
+ export { MAX_RESAMPLE_OUTPUT_SAMPLES as a, pcm16ToFloat32 as i, float32ToPcm16 as n, resampleMono as o, parseWavPcm16 as r, encodeWavPcm16 as t };
231
+
232
+ //# sourceMappingURL=wav-B0Djpj2r.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wav-B0Djpj2r.js","names":[],"sources":["../../src/shared/resample.ts","../../src/shared/wav.ts"],"sourcesContent":["/**\n * Mono resampler: windowed-sinc (Hann, 16 zero crossings per side)\n * interpolation. When downsampling, the kernel's cutoff is scaled to 95% of\n * the target Nyquist frequency so content above it is attenuated before it can\n * alias; when upsampling it is a plain band-limited interpolator. Edges treat\n * missing samples as absent and renormalise the kernel, so DC gain stays 1.\n * Quality is suited to speech recognition (passband flat to roughly 90% of\n * Nyquist, stopband well below -40 dB), not mastering. Cost is\n * O(output length x taps), taps ~ 32 x max(1, fromRate / toRate).\n */\n\nimport { WavFormatError } from './errors.js';\n\n/** Largest output a single call may produce (samples). */\nexport const MAX_RESAMPLE_OUTPUT_SAMPLES = 2 ** 28;\n\nconst ZERO_CROSSINGS = 16;\nconst DOWNSAMPLE_CUTOFF = 0.95;\n\nfunction sinc(x: number): number {\n if (x === 0) return 1;\n const px = Math.PI * x;\n return Math.sin(px) / px;\n}\n\n/** Resamples mono float samples from `fromRate` to `toRate` (Hz). */\nexport function resampleMono(\n samples: Float32Array,\n fromRate: number,\n toRate: number,\n): Float32Array {\n for (const [name, rate] of [\n ['fromRate', fromRate],\n ['toRate', toRate],\n ] as const) {\n if (!Number.isFinite(rate) || rate <= 0) {\n throw new WavFormatError(\n 'invalid_argument',\n `${name} must be a positive finite number (got ${String(rate)})`,\n );\n }\n }\n const length = Math.floor((samples.length * toRate) / fromRate);\n if (length > MAX_RESAMPLE_OUTPUT_SAMPLES) {\n throw new WavFormatError(\n 'invalid_argument',\n `resampled output would be ${length} samples; the limit is ${MAX_RESAMPLE_OUTPUT_SAMPLES}`,\n );\n }\n if (fromRate === toRate) return samples.slice();\n const out = new Float32Array(length);\n const step = fromRate / toRate;\n // Cutoff relative to the input Nyquist; 1 means no low-pass.\n const cutoff = step > 1 ? DOWNSAMPLE_CUTOFF / step : 1;\n const halfWidth = ZERO_CROSSINGS / cutoff;\n const last = samples.length - 1;\n\n for (let index = 0; index < length; index += 1) {\n const center = index * step;\n const first = Math.max(0, Math.ceil(center - halfWidth));\n const end = Math.min(last, Math.floor(center + halfWidth));\n let sum = 0;\n let weights = 0;\n for (let tap = first; tap <= end; tap += 1) {\n const distance = tap - center;\n const window = 0.5 + 0.5 * Math.cos((Math.PI * distance) / halfWidth);\n const weight = cutoff * sinc(cutoff * distance) * window;\n sum += weight * samples[tap];\n weights += weight;\n }\n out[index] = weights !== 0 ? sum / weights : 0;\n }\n return out;\n}\n","/**\n * Pure, dependency-free 16-bit PCM WAV helpers. No Node APIs and no DOM: they\n * run in the browser, in Node and in workers. Exposed as\n * `@happyvertical/speech/pcm`.\n *\n * `parseWavPcm16` is strict because it reads untrusted bytes: every declared\n * size is checked against the buffer before it is trusted, nothing is\n * allocated from a declared (rather than present) size, and work is linear in\n * the input length.\n */\n\nimport { WavFormatError } from './errors.js';\n\nconst HEADER_BYTES = 44;\nconst MAX_UINT32 = 0xffff_ffff;\nconst FORMAT_PCM = 1;\nconst FORMAT_EXTENSIBLE = 0xfffe;\n/** `KSDATAFORMAT_SUBTYPE_PCM` minus its leading format tag: 00000000-0010-8000-00AA-00389B71. */\nconst PCM_GUID_TAIL = [\n 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b,\n 0x71,\n];\n\n/**\n * Converts float samples (-1..1) to 16-bit PCM (scale 32768, the inverse of\n * `pcm16ToFloat32`, so Int16 round-trips exactly). Clamps; NaN becomes 0.\n */\nexport function float32ToPcm16(samples: Float32Array): Int16Array {\n const out = new Int16Array(samples.length);\n for (let index = 0; index < samples.length; index += 1) {\n const value = samples[index];\n if (Number.isNaN(value)) continue; // stays 0\n const clamped = value > 1 ? 1 : value < -1 ? -1 : value;\n out[index] = Math.min(0x7fff, Math.round(clamped * 0x8000));\n }\n return out;\n}\n\n/** Converts 16-bit PCM to float samples in -1..1 (divides by 32768). */\nexport function pcm16ToFloat32(samples: Int16Array): Float32Array {\n const out = new Float32Array(samples.length);\n for (let index = 0; index < samples.length; index += 1) {\n out[index] = samples[index] / 0x8000;\n }\n return out;\n}\n\nfunction assertSampleRate(sampleRate: number, what: string): void {\n // byteRate = sampleRate * 2 must fit an unsigned 32-bit field.\n if (\n !Number.isInteger(sampleRate) ||\n sampleRate < 1 ||\n sampleRate * 2 > MAX_UINT32\n ) {\n throw new WavFormatError(\n 'invalid_argument',\n `${what} must be a positive integer sample rate (got ${String(sampleRate)})`,\n );\n }\n}\n\nfunction writeTag(view: DataView, offset: number, tag: string): void {\n for (let index = 0; index < tag.length; index += 1) {\n view.setUint8(offset + index, tag.charCodeAt(index));\n }\n}\n\n/**\n * Encodes mono samples as a canonical 44-byte-header 16-bit PCM WAV. Float\n * input is clamped and converted; `Int16Array` is written as is.\n */\nexport function encodeWavPcm16(\n samples: Float32Array | Int16Array,\n sampleRate: number,\n): Uint8Array {\n assertSampleRate(sampleRate, 'sampleRate');\n const dataBytes = samples.length * 2;\n if (dataBytes + HEADER_BYTES - 8 > MAX_UINT32) {\n throw new WavFormatError(\n 'invalid_argument',\n 'too many samples for a single WAV (4 GiB limit)',\n );\n }\n const pcm = samples instanceof Int16Array ? samples : float32ToPcm16(samples);\n const bytes = new Uint8Array(HEADER_BYTES + dataBytes);\n const view = new DataView(bytes.buffer);\n writeTag(view, 0, 'RIFF');\n view.setUint32(4, 36 + dataBytes, true);\n writeTag(view, 8, 'WAVE');\n writeTag(view, 12, 'fmt ');\n view.setUint32(16, 16, true);\n view.setUint16(20, FORMAT_PCM, true);\n view.setUint16(22, 1, true);\n view.setUint32(24, sampleRate, true);\n view.setUint32(28, sampleRate * 2, true);\n view.setUint16(32, 2, true);\n view.setUint16(34, 16, true);\n writeTag(view, 36, 'data');\n view.setUint32(40, dataBytes, true);\n for (let index = 0; index < pcm.length; index += 1) {\n view.setInt16(HEADER_BYTES + index * 2, pcm[index], true);\n }\n return bytes;\n}\n\n/** Constraints `parseWavPcm16` enforces on the declared format. */\nexport interface WavExpectation {\n /** Required sample rate in Hz. */\n sampleRate?: number;\n /** Required channel count. */\n channels?: number;\n}\n\n/** A validated 16-bit PCM WAV. */\nexport interface ParsedWavPcm16 {\n sampleRate: number;\n channels: number;\n /** Frames (one sample per channel). */\n frames: number;\n durationMs: number;\n /** Interleaved samples, decoded to native endianness (a copy). */\n samples: Int16Array;\n /** The little-endian PCM bytes, a view into the input. */\n data: Uint8Array;\n}\n\nfunction fail(\n reason: ConstructorParameters<typeof WavFormatError>[0],\n message: string,\n): never {\n throw new WavFormatError(reason, message);\n}\n\nfunction tagAt(bytes: Uint8Array, offset: number): string {\n return String.fromCharCode(\n bytes[offset],\n bytes[offset + 1],\n bytes[offset + 2],\n bytes[offset + 3],\n );\n}\n\ninterface Fmt {\n channels: number;\n sampleRate: number;\n}\n\nfunction readFmt(\n view: DataView,\n bytes: Uint8Array,\n body: number,\n size: number,\n): Fmt {\n if (size < 16) {\n fail('malformed_chunk', `fmt chunk is ${size} bytes; at least 16 required`);\n }\n let format = view.getUint16(body, true);\n const channels = view.getUint16(body + 2, true);\n const sampleRate = view.getUint32(body + 4, true);\n const byteRate = view.getUint32(body + 8, true);\n const blockAlign = view.getUint16(body + 12, true);\n const bits = view.getUint16(body + 14, true);\n\n if (format === FORMAT_EXTENSIBLE) {\n if (size < 40) {\n fail(\n 'malformed_chunk',\n `WAVE_FORMAT_EXTENSIBLE fmt chunk is ${size} bytes; at least 40 required`,\n );\n }\n const cbSize = view.getUint16(body + 16, true);\n if (cbSize < 22 || 18 + cbSize > size) {\n fail(\n 'malformed_chunk',\n `WAVE_FORMAT_EXTENSIBLE extension (${cbSize} bytes) is under 22 or runs past its fmt chunk`,\n );\n }\n const validBits = view.getUint16(body + 18, true);\n if (validBits !== 0 && validBits !== 16) {\n fail(\n 'unsupported_bits',\n `WAVE_FORMAT_EXTENSIBLE valid bits is ${validBits}; only 16 supported`,\n );\n }\n const guid = body + 24;\n const subFormat = view.getUint16(guid, true);\n const tailMatches = PCM_GUID_TAIL.every(\n (byte, index) => bytes[guid + 2 + index] === byte,\n );\n if (subFormat !== FORMAT_PCM || !tailMatches) {\n fail('unsupported_format', 'WAVE_FORMAT_EXTENSIBLE subtype is not PCM');\n }\n format = FORMAT_PCM;\n }\n\n if (format !== FORMAT_PCM) {\n fail('unsupported_format', `WAV format tag ${format} is not PCM`);\n }\n if (bits !== 16) {\n fail(\n 'unsupported_bits',\n `WAV is ${bits}-bit; only 16-bit PCM is supported`,\n );\n }\n if (channels < 1) {\n fail('inconsistent_header', 'WAV declares zero channels');\n }\n if (sampleRate < 1) {\n fail('inconsistent_header', 'WAV declares a zero sample rate');\n }\n if (blockAlign !== channels * 2) {\n fail(\n 'inconsistent_header',\n `WAV block align ${blockAlign} is not channels x 2 (${channels * 2})`,\n );\n }\n if (byteRate !== sampleRate * blockAlign) {\n fail(\n 'inconsistent_header',\n 'WAV byte rate is not sample rate x block align',\n );\n }\n return { channels, sampleRate };\n}\n\n/**\n * Strictly parses a 16-bit PCM WAV. Rejects (with `WavFormatError`) anything\n * that is not a well-formed RIFF/WAVE with a `fmt ` chunk before its `data`\n * chunk, format tag 1 (or WAVE_FORMAT_EXTENSIBLE with the PCM subtype), 16\n * bits, a consistent header, and a declared data length that lies within the\n * buffer, is non-zero and is a whole number of frames. Nothing is clamped or\n * guessed. Every chunk inside the declared RIFF is bounds-checked, including\n * those after `data`; bytes beyond the RIFF are ignored.\n */\nexport function parseWavPcm16(\n bytes: Uint8Array,\n expect: WavExpectation = {},\n): ParsedWavPcm16 {\n if (\n bytes.length < 12 ||\n tagAt(bytes, 0) !== 'RIFF' ||\n tagAt(bytes, 8) !== 'WAVE'\n ) {\n fail('not_riff', 'not a RIFF/WAVE file');\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const riffEnd = 8 + view.getUint32(4, true);\n if (riffEnd < 12) {\n fail('malformed_chunk', 'RIFF size is too small');\n }\n if (riffEnd > bytes.length) {\n fail(\n 'truncated',\n `RIFF declares ${riffEnd} bytes but only ${bytes.length} are present`,\n );\n }\n\n let fmt: Fmt | undefined;\n let dataAt: number | undefined;\n let dataSize = 0;\n let offset = 12;\n while (offset < riffEnd) {\n if (offset + 8 > riffEnd) {\n fail('malformed_chunk', 'chunk header runs past the end of the RIFF');\n }\n const id = tagAt(bytes, offset);\n const size = view.getUint32(offset + 4, true);\n const body = offset + 8;\n if (body + size > riffEnd) {\n fail(\n 'truncated',\n `${JSON.stringify(id)} chunk declares ${size} bytes but only ${riffEnd - body} remain`,\n );\n }\n\n if (id === 'fmt ') {\n if (fmt) fail('duplicate_fmt', 'WAV has more than one fmt chunk');\n fmt = readFmt(view, bytes, body, size);\n if (\n expect.sampleRate !== undefined &&\n fmt.sampleRate !== expect.sampleRate\n ) {\n fail(\n 'rate_mismatch',\n `WAV is ${fmt.sampleRate} Hz; expected ${expect.sampleRate} Hz`,\n );\n }\n if (expect.channels !== undefined && fmt.channels !== expect.channels) {\n fail(\n 'channel_mismatch',\n `WAV has ${fmt.channels} channel(s); expected ${expect.channels}`,\n );\n }\n } else if (id === 'data') {\n if (dataAt !== undefined) {\n fail('malformed_chunk', 'WAV has more than one data chunk');\n }\n if (!fmt)\n fail('data_before_fmt', 'WAV data chunk precedes its fmt chunk');\n if (size === 0) fail('empty_data', 'WAV data chunk is empty');\n const frameBytes = fmt.channels * 2;\n if (size % frameBytes !== 0) {\n fail(\n 'misaligned_data',\n `WAV data (${size} bytes) is not a whole number of ${frameBytes}-byte frames`,\n );\n }\n dataAt = body;\n dataSize = size;\n }\n // RIFF chunks are word aligned: an odd size is followed by one pad byte,\n // which must also lie inside the RIFF.\n const next = body + size + (size % 2);\n if (next > riffEnd) {\n fail('truncated', `${JSON.stringify(id)} chunk is missing its pad byte`);\n }\n offset = next;\n }\n if (!fmt || dataAt === undefined) {\n return fail(\n fmt ? 'missing_data' : 'missing_fmt',\n fmt ? 'WAV has no data chunk' : 'WAV has no fmt chunk',\n );\n }\n // Every chunk inside the RIFF has now been bounds-checked; decode.\n const data = bytes.subarray(dataAt, dataAt + dataSize);\n const samples = new Int16Array(dataSize / 2);\n for (let index = 0; index < samples.length; index += 1) {\n samples[index] = view.getInt16(dataAt + index * 2, true);\n }\n const frames = dataSize / (fmt.channels * 2);\n return {\n sampleRate: fmt.sampleRate,\n channels: fmt.channels,\n frames,\n durationMs: Math.round((frames / fmt.sampleRate) * 1000),\n samples,\n data,\n };\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { n as SpeechConfigurationError } from "./errors-CQBX3OPS.js";
1
+ import { r as SpeechConfigurationError } from "./errors-Cs0vX-mV.js";
2
2
  //#region src/shared/websocket.ts
3
3
  /**
4
4
  * Minimal WebSocket surface used by streaming adapters. Browser, Node 22+
@@ -41,4 +41,4 @@ function messageDataToText(data) {
41
41
  //#endregion
42
42
  export { toWebSocketUrl as i, messageDataToText as n, resolveWebSocketFactory as r, isBrowserRuntime as t };
43
43
 
44
- //# sourceMappingURL=websocket-CR9uXAuS.js.map
44
+ //# sourceMappingURL=websocket-YbAfY6YI.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket-CR9uXAuS.js","names":[],"sources":["../../src/shared/websocket.ts"],"sourcesContent":["/**\n * Minimal WebSocket surface used by streaming adapters. Browser, Node 22+\n * (`globalThis.WebSocket`), Deno, Bun, and the `ws` package all satisfy it.\n */\n\nimport { SpeechConfigurationError } from './errors.js';\n\n/** `WebSocket.OPEN`. */\nexport const WEBSOCKET_OPEN = 1;\n\nexport interface SpeechWebSocketMessageEvent {\n data: unknown;\n}\n\nexport interface SpeechWebSocketCloseEvent {\n code?: number;\n reason?: string;\n}\n\n/** The subset of the WHATWG `WebSocket` interface the adapters use. */\nexport interface SpeechWebSocket {\n readonly readyState: number;\n readonly bufferedAmount: number;\n binaryType?: string;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: 'open', listener: (event: unknown) => void): void;\n addEventListener(\n type: 'message',\n listener: (event: SpeechWebSocketMessageEvent) => void,\n ): void;\n addEventListener(type: 'error', listener: (event: unknown) => void): void;\n addEventListener(\n type: 'close',\n listener: (event: SpeechWebSocketCloseEvent) => void,\n ): void;\n}\n\n/** Handshake details passed to a {@link SpeechWebSocketFactory}. */\nexport interface SpeechWebSocketInit {\n protocols: string[];\n /** Handshake headers; empty in browsers. */\n headers: Record<string, string>;\n}\n\nexport type SpeechWebSocketFactory = (\n url: string,\n init: SpeechWebSocketInit,\n) => SpeechWebSocket;\n\n/**\n * A WebSocket constructor. Standard constructors accept `protocols`; Node's\n * built-in WebSocket also accepts `{ protocols, headers }`.\n */\nexport type SpeechWebSocketConstructor = new (\n url: string,\n protocols?:\n | string\n | string[]\n | { protocols?: string[]; headers?: Record<string, string> },\n) => SpeechWebSocket;\n\n/** True in browsers and web workers, where API keys must never be used. */\nexport function isBrowserRuntime(): boolean {\n const scope = globalThis as {\n document?: unknown;\n WorkerGlobalScope?: unknown;\n process?: { versions?: { node?: string } };\n };\n if (scope.process?.versions?.node) {\n return false;\n }\n return (\n typeof scope.document !== 'undefined' ||\n typeof scope.WorkerGlobalScope !== 'undefined'\n );\n}\n\n/**\n * Resolves a socket factory from explicit options, falling back to\n * `globalThis.WebSocket`. Headers are only passed when present, so standard\n * constructors that cannot send them are never handed an options object.\n */\nexport function resolveWebSocketFactory(\n adapter: string,\n options: {\n createWebSocket?: SpeechWebSocketFactory;\n WebSocket?: SpeechWebSocketConstructor;\n },\n): SpeechWebSocketFactory {\n if (options.createWebSocket) {\n return options.createWebSocket;\n }\n\n const Ctor =\n options.WebSocket ??\n (globalThis as { WebSocket?: SpeechWebSocketConstructor }).WebSocket;\n if (!Ctor) {\n throw new SpeechConfigurationError(\n 'No WebSocket implementation available; pass `WebSocket` or `createWebSocket`',\n adapter,\n );\n }\n\n return (url, init) =>\n Object.keys(init.headers).length > 0\n ? new Ctor(url, { protocols: init.protocols, headers: init.headers })\n : new Ctor(url, init.protocols);\n}\n\n/** Converts `http(s)://` to `ws(s)://`; leaves `ws(s)://` unchanged. */\nexport function toWebSocketUrl(url: string, adapter: string): string {\n const parsed = new URL(url);\n if (parsed.protocol === 'http:') {\n parsed.protocol = 'ws:';\n } else if (parsed.protocol === 'https:') {\n parsed.protocol = 'wss:';\n } else if (parsed.protocol !== 'ws:' && parsed.protocol !== 'wss:') {\n throw new SpeechConfigurationError(\n `Unsupported streaming URL protocol: ${parsed.protocol}`,\n adapter,\n );\n }\n return parsed.toString();\n}\n\n/** Converts a message payload to text; returns `undefined` for unsupported data. */\nexport function messageDataToText(data: unknown): string | undefined {\n if (typeof data === 'string') {\n return data;\n }\n if (data instanceof ArrayBuffer) {\n return new TextDecoder().decode(data);\n }\n if (ArrayBuffer.isView(data)) {\n return new TextDecoder().decode(data);\n }\n return undefined;\n}\n"],"mappings":";;;;;;;AA+DA,SAAgB,mBAA4B;CAC1C,MAAM,QAAQ;CAKd,IAAI,MAAM,SAAS,UAAU,MAC3B,OAAO;CAET,OACE,OAAO,MAAM,aAAa,eAC1B,OAAO,MAAM,sBAAsB;AAEvC;;;;;;AAOA,SAAgB,wBACd,SACA,SAIwB;CACxB,IAAI,QAAQ,iBACV,OAAO,QAAQ;CAGjB,MAAM,OACJ,QAAQ,aACP,WAA0D;CAC7D,IAAI,CAAC,MACH,MAAM,IAAI,yBACR,gFACA,OACF;CAGF,QAAQ,KAAK,SACX,OAAO,KAAK,KAAK,OAAO,CAAC,CAAC,SAAS,IAC/B,IAAI,KAAK,KAAK;EAAE,WAAW,KAAK;EAAW,SAAS,KAAK;CAAQ,CAAC,IAClE,IAAI,KAAK,KAAK,KAAK,SAAS;AACpC;;AAGA,SAAgB,eAAe,KAAa,SAAyB;CACnE,MAAM,SAAS,IAAI,IAAI,GAAG;CAC1B,IAAI,OAAO,aAAa,SACtB,OAAO,WAAW;MACb,IAAI,OAAO,aAAa,UAC7B,OAAO,WAAW;MACb,IAAI,OAAO,aAAa,SAAS,OAAO,aAAa,QAC1D,MAAM,IAAI,yBACR,uCAAuC,OAAO,YAC9C,OACF;CAEF,OAAO,OAAO,SAAS;AACzB;;AAGA,SAAgB,kBAAkB,MAAmC;CACnE,IAAI,OAAO,SAAS,UAClB,OAAO;CAET,IAAI,gBAAgB,aAClB,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI;CAEtC,IAAI,YAAY,OAAO,IAAI,GACzB,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI;AAGxC"}
1
+ {"version":3,"file":"websocket-YbAfY6YI.js","names":[],"sources":["../../src/shared/websocket.ts"],"sourcesContent":["/**\n * Minimal WebSocket surface used by streaming adapters. Browser, Node 22+\n * (`globalThis.WebSocket`), Deno, Bun, and the `ws` package all satisfy it.\n */\n\nimport { SpeechConfigurationError } from './errors.js';\n\n/** `WebSocket.OPEN`. */\nexport const WEBSOCKET_OPEN = 1;\n\nexport interface SpeechWebSocketMessageEvent {\n data: unknown;\n}\n\nexport interface SpeechWebSocketCloseEvent {\n code?: number;\n reason?: string;\n}\n\n/** The subset of the WHATWG `WebSocket` interface the adapters use. */\nexport interface SpeechWebSocket {\n readonly readyState: number;\n readonly bufferedAmount: number;\n binaryType?: string;\n send(data: string): void;\n close(code?: number, reason?: string): void;\n addEventListener(type: 'open', listener: (event: unknown) => void): void;\n addEventListener(\n type: 'message',\n listener: (event: SpeechWebSocketMessageEvent) => void,\n ): void;\n addEventListener(type: 'error', listener: (event: unknown) => void): void;\n addEventListener(\n type: 'close',\n listener: (event: SpeechWebSocketCloseEvent) => void,\n ): void;\n}\n\n/** Handshake details passed to a {@link SpeechWebSocketFactory}. */\nexport interface SpeechWebSocketInit {\n protocols: string[];\n /** Handshake headers; empty in browsers. */\n headers: Record<string, string>;\n}\n\nexport type SpeechWebSocketFactory = (\n url: string,\n init: SpeechWebSocketInit,\n) => SpeechWebSocket;\n\n/**\n * A WebSocket constructor. Standard constructors accept `protocols`; Node's\n * built-in WebSocket also accepts `{ protocols, headers }`.\n */\nexport type SpeechWebSocketConstructor = new (\n url: string,\n protocols?:\n | string\n | string[]\n | { protocols?: string[]; headers?: Record<string, string> },\n) => SpeechWebSocket;\n\n/** True in browsers and web workers, where API keys must never be used. */\nexport function isBrowserRuntime(): boolean {\n const scope = globalThis as {\n document?: unknown;\n WorkerGlobalScope?: unknown;\n process?: { versions?: { node?: string } };\n };\n if (scope.process?.versions?.node) {\n return false;\n }\n return (\n typeof scope.document !== 'undefined' ||\n typeof scope.WorkerGlobalScope !== 'undefined'\n );\n}\n\n/**\n * Resolves a socket factory from explicit options, falling back to\n * `globalThis.WebSocket`. Headers are only passed when present, so standard\n * constructors that cannot send them are never handed an options object.\n */\nexport function resolveWebSocketFactory(\n adapter: string,\n options: {\n createWebSocket?: SpeechWebSocketFactory;\n WebSocket?: SpeechWebSocketConstructor;\n },\n): SpeechWebSocketFactory {\n if (options.createWebSocket) {\n return options.createWebSocket;\n }\n\n const Ctor =\n options.WebSocket ??\n (globalThis as { WebSocket?: SpeechWebSocketConstructor }).WebSocket;\n if (!Ctor) {\n throw new SpeechConfigurationError(\n 'No WebSocket implementation available; pass `WebSocket` or `createWebSocket`',\n adapter,\n );\n }\n\n return (url, init) =>\n Object.keys(init.headers).length > 0\n ? new Ctor(url, { protocols: init.protocols, headers: init.headers })\n : new Ctor(url, init.protocols);\n}\n\n/** Converts `http(s)://` to `ws(s)://`; leaves `ws(s)://` unchanged. */\nexport function toWebSocketUrl(url: string, adapter: string): string {\n const parsed = new URL(url);\n if (parsed.protocol === 'http:') {\n parsed.protocol = 'ws:';\n } else if (parsed.protocol === 'https:') {\n parsed.protocol = 'wss:';\n } else if (parsed.protocol !== 'ws:' && parsed.protocol !== 'wss:') {\n throw new SpeechConfigurationError(\n `Unsupported streaming URL protocol: ${parsed.protocol}`,\n adapter,\n );\n }\n return parsed.toString();\n}\n\n/** Converts a message payload to text; returns `undefined` for unsupported data. */\nexport function messageDataToText(data: unknown): string | undefined {\n if (typeof data === 'string') {\n return data;\n }\n if (data instanceof ArrayBuffer) {\n return new TextDecoder().decode(data);\n }\n if (ArrayBuffer.isView(data)) {\n return new TextDecoder().decode(data);\n }\n return undefined;\n}\n"],"mappings":";;;;;;;AA+DA,SAAgB,mBAA4B;CAC1C,MAAM,QAAQ;CAKd,IAAI,MAAM,SAAS,UAAU,MAC3B,OAAO;CAET,OACE,OAAO,MAAM,aAAa,eAC1B,OAAO,MAAM,sBAAsB;AAEvC;;;;;;AAOA,SAAgB,wBACd,SACA,SAIwB;CACxB,IAAI,QAAQ,iBACV,OAAO,QAAQ;CAGjB,MAAM,OACJ,QAAQ,aACP,WAA0D;CAC7D,IAAI,CAAC,MACH,MAAM,IAAI,yBACR,gFACA,OACF;CAGF,QAAQ,KAAK,SACX,OAAO,KAAK,KAAK,OAAO,CAAC,CAAC,SAAS,IAC/B,IAAI,KAAK,KAAK;EAAE,WAAW,KAAK;EAAW,SAAS,KAAK;CAAQ,CAAC,IAClE,IAAI,KAAK,KAAK,KAAK,SAAS;AACpC;;AAGA,SAAgB,eAAe,KAAa,SAAyB;CACnE,MAAM,SAAS,IAAI,IAAI,GAAG;CAC1B,IAAI,OAAO,aAAa,SACtB,OAAO,WAAW;MACb,IAAI,OAAO,aAAa,UAC7B,OAAO,WAAW;MACb,IAAI,OAAO,aAAa,SAAS,OAAO,aAAa,QAC1D,MAAM,IAAI,yBACR,uCAAuC,OAAO,YAC9C,OACF;CAEF,OAAO,OAAO,SAAS;AACzB;;AAGA,SAAgB,kBAAkB,MAAmC;CACnE,IAAI,OAAO,SAAS,UAClB,OAAO;CAET,IAAI,gBAAgB,aAClB,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI;CAEtC,IAAI,YAAY,OAAO,IAAI,GACzB,OAAO,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI;AAGxC"}
@@ -1,4 +1,4 @@
1
- import { OpenAIWebRTCVoiceOptions, VoiceSession } from './types.js';
1
+ import type { OpenAIWebRTCVoiceOptions, VoiceSession } from './types.js';
2
2
  /** Browser WebRTC transport. No credentials, Node dependencies or globals are read at import. */
3
3
  export declare function createOpenAIWebRTCVoiceSession(options: OpenAIWebRTCVoiceOptions): VoiceSession;
4
4
  //# sourceMappingURL=openai-webrtc.d.ts.map
@@ -1,6 +1,7 @@
1
+ /** Server-only OpenAI conversational call lifecycle. Hosts own authorization and admission policy. */
1
2
  import { SpeechProviderError } from './shared/errors.js';
2
- import { StreamingTurnDetection } from './shared/streaming-types.js';
3
- import { SpeechFetch } from './shared/types.js';
3
+ import type { StreamingTurnDetection } from './shared/streaming-types.js';
4
+ import type { SpeechFetch } from './shared/types.js';
4
5
  export interface OpenAIVoiceCallOptions {
5
6
  apiKey: string;
6
7
  /** Fixed by the host, never copied from untrusted browser JSON. */
@@ -1,5 +1,5 @@
1
- import { i as SpeechProviderError, n as SpeechConfigurationError } from "./chunks/errors-CQBX3OPS.js";
2
- import { t as isBrowserRuntime } from "./chunks/websocket-CR9uXAuS.js";
1
+ import { a as SpeechProviderError, r as SpeechConfigurationError } from "./chunks/errors-Cs0vX-mV.js";
2
+ import { t as isBrowserRuntime } from "./chunks/websocket-YbAfY6YI.js";
3
3
  //#region src/conversation-server.ts
4
4
  /** Server-only OpenAI conversational call lifecycle. Hosts own authorization and admission policy. */
5
5
  /** Build the GA speech-to-speech session config. Transcription is an optional side channel. */
@@ -1,4 +1,4 @@
1
- import { i as SpeechProviderError, n as SpeechConfigurationError } from "./chunks/errors-CQBX3OPS.js";
1
+ import { a as SpeechProviderError, r as SpeechConfigurationError } from "./chunks/errors-Cs0vX-mV.js";
2
2
  //#region src/conversation/openai-webrtc.ts
3
3
  var ADAPTER = "openai-conversation";
4
4
  /** Browser WebRTC transport. No credentials, Node dependencies or globals are read at import. */
package/dist/index.d.ts CHANGED
@@ -27,6 +27,7 @@ export { DEFAULT_MAX_AUDIO_BYTES, mimeTypeToAudioExtension, } from './shared/aud
27
27
  export { type SpeechEnv, TRANSCRIBER_ENV_KEYS, } from './shared/env.js';
28
28
  export * from './shared/errors.js';
29
29
  export { getAvailableSpeechAdapters, getSpeech, getSpeechSynthesizer, getTranscriber, type SpeechFactoryContext, } from './shared/factory.js';
30
+ export { type TranscriberInputFormat, transcriberInputFormat, } from './shared/input-format.js';
30
31
  export { audioSecondsForBytes, bytesPerSecond, unwrapRawAudio, } from './shared/pcm.js';
31
32
  export { type RealtimeConnection, type RealtimeProtocol, type RealtimeProtocolEvent, type RealtimeSessionConfig, type RealtimeSessionInit, RealtimeTranscriptionSession, } from './shared/realtime-session.js';
32
33
  export { CLIENT_SECRET_TTL_RANGE, type CreateStreamingClientSecretOptions, createStreamingClientSecret, DEFAULT_CLIENT_SECRET_TTL_SECONDS, resolveClientSecretUrl, type StreamingClientSecretContext, type StreamingClientSecretResult, } from './shared/streaming-client-secret.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EACL,6BAA6B,EAC7B,2BAA2B,EAC3B,sBAAsB,EACtB,kCAAkC,EAClC,wBAAwB,EACxB,2BAA2B,EAC3B,kBAAkB,GACnB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,yBAAyB,EACzB,yBAAyB,EACzB,6BAA6B,EAC7B,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,KAAK,SAAS,EACd,oBAAoB,GACrB,MAAM,iBAAiB,CAAC;AACzB,cAAc,oBAAoB,CAAC;AACnC,OAAO,EACL,0BAA0B,EAC1B,SAAS,EACT,oBAAoB,EACpB,cAAc,EACd,KAAK,oBAAoB,GAC1B,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,uBAAuB,EACvB,KAAK,kCAAkC,EACvC,2BAA2B,EAC3B,iCAAiC,EACjC,sBAAsB,EACtB,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,GACjC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,mBAAmB,EACnB,8BAA8B,EAC9B,2BAA2B,EAC3B,KAAK,uBAAuB,EAC5B,KAAK,kCAAkC,EACvC,wBAAwB,GACzB,MAAM,+BAA+B,CAAC;AACvC,YAAY,EACV,8BAA8B,EAC9B,gCAAgC,EAChC,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,wBAAwB,EACxB,gBAAgB,EAChB,yBAAyB,EACzB,sBAAsB,EACtB,wBAAwB,EACxB,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,EAClB,iCAAiC,GAClC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,wBAAwB,EACxB,+BAA+B,EAC/B,kCAAkC,EAClC,+BAA+B,GAChC,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,gBAAgB,EAChB,2BAA2B,EAC3B,qBAAqB,EACrB,wCAAwC,EACxC,kCAAkC,EAClC,6BAA6B,EAC7B,MAAM,EACN,yBAAyB,EACzB,iBAAiB,EACjB,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,qBAAqB,EACrB,WAAW,EACX,mBAAmB,EACnB,WAAW,EACX,gBAAgB,EAChB,oCAAoC,EACpC,8BAA8B,EAC9B,gBAAgB,EAChB,iBAAiB,EACjB,oBAAoB,EACpB,WAAW,EACX,eAAe,EACf,oBAAoB,EACpB,2BAA2B,EAC3B,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,GACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,gBAAgB,EAChB,KAAK,eAAe,EACpB,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,mBAAmB,GACzB,MAAM,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { i as SpeechProviderError, n as SpeechConfigurationError, r as SpeechError, t as InvalidSpeechAdapterError } from "./chunks/errors-CQBX3OPS.js";
2
- import { i as toWebSocketUrl, n as messageDataToText, r as resolveWebSocketFactory, t as isBrowserRuntime } from "./chunks/websocket-CR9uXAuS.js";
3
- import { _ as mimeTypeToAudioExtension, a as TRANSCRIBER_ENV_KEYS, c as hasTranscriberEnv, d as parseOptionalInteger, f as readEnv, g as DEFAULT_MAX_AUDIO_BYTES, h as reportSpeechUsage, i as registeredOptionalTranscribers, l as mergeHeaderInits, m as audioSecondsFromProviderUsage, n as optionalTranscriberEntry, o as defaultEnv, p as resolveTranscriberConfig, s as hasAnyEnv, t as getOptionalTranscriberFactory, u as parseHeadersEnv, v as normalizeAudioInput, y as normalizeMimeType } from "./chunks/registry-C_5BakmM.js";
1
+ import { a as SpeechProviderError, i as SpeechError, n as PcmCaptureError, o as WavFormatError, r as SpeechConfigurationError, t as InvalidSpeechAdapterError } from "./chunks/errors-Cs0vX-mV.js";
2
+ import { i as toWebSocketUrl, n as messageDataToText, r as resolveWebSocketFactory, t as isBrowserRuntime } from "./chunks/websocket-YbAfY6YI.js";
3
+ import { _ as mimeTypeToAudioExtension, a as TRANSCRIBER_ENV_KEYS, c as hasTranscriberEnv, d as parseOptionalInteger, f as readEnv, g as DEFAULT_MAX_AUDIO_BYTES, h as reportSpeechUsage, i as registeredOptionalTranscribers, l as mergeHeaderInits, m as audioSecondsFromProviderUsage, n as optionalTranscriberEntry, o as defaultEnv, p as resolveTranscriberConfig, s as hasAnyEnv, t as getOptionalTranscriberFactory, u as parseHeadersEnv, v as normalizeAudioInput, y as normalizeMimeType } from "./chunks/registry-DFSF3WoM.js";
4
4
  //#region src/shared/retry.ts
5
5
  /**
6
6
  * Retry with exponential backoff for speech provider calls. Retries only
@@ -2406,6 +2406,37 @@ function hasSynthesizerEnv(env) {
2406
2406
  return hasAnyEnv(env, "HAVE_SPEECH_TTS_TYPE", "HAVE_SPEECH_TTS_ADAPTER", "HAVE_SPEECH_TTS_BASE_URL", "TTS_ADAPTER", "TTS_BASE_URL");
2407
2407
  }
2408
2408
  //#endregion
2409
+ //#region src/shared/input-format.ts
2410
+ /** The `openai-realtime` default wire format: `audio/pcm`, 24 kHz mono. */
2411
+ var OPENAI_REALTIME_PCM_RATE = 24e3;
2412
+ /**
2413
+ * Says what to record for a transcriber type. `pcm16` types reject compressed
2414
+ * containers (webm, ogg, mp3) and need 16-bit little-endian mono PCM, raw or in
2415
+ * a WAV, at exactly `sampleRate`; use `@happyvertical/speech/browser` or
2416
+ * `/pcm` to produce it. `compressed` types decode containers themselves
2417
+ * (HTTP APIs, and the on-device transcriber, which decodes with the platform).
2418
+ * For `openai-realtime` this is the default format; G.711 is an explicit
2419
+ * adapter option and not a capture format.
2420
+ */
2421
+ function transcriberInputFormat(type) {
2422
+ switch (type) {
2423
+ case "voxtral-realtime": return {
2424
+ kind: "pcm16",
2425
+ sampleRate: VOXTRAL_REALTIME_AUDIO_FORMAT.sampleRate ?? 16e3,
2426
+ channels: 1
2427
+ };
2428
+ case "openai-realtime": return {
2429
+ kind: "pcm16",
2430
+ sampleRate: OPENAI_REALTIME_PCM_RATE,
2431
+ channels: 1
2432
+ };
2433
+ case "studio-server":
2434
+ case "openai-compatible":
2435
+ case "local": return { kind: "compressed" };
2436
+ default: throw new Error(`Unknown transcriber type: ${String(type)}`);
2437
+ }
2438
+ }
2439
+ //#endregion
2409
2440
  //#region src/shared/streaming-client-secret.ts
2410
2441
  /**
2411
2442
  * Server-side minting of short-lived browser credentials for realtime
@@ -2537,6 +2568,6 @@ var ClientSecretClient = class extends HttpSpeechAdapter {
2537
2568
  }
2538
2569
  };
2539
2570
  //#endregion
2540
- export { CLIENT_SECRET_TTL_RANGE, DEFAULT_CLIENT_SECRET_TTL_SECONDS, DEFAULT_MAX_AUDIO_BYTES, DEFAULT_MAX_TURN_SECONDS, DEFAULT_ROLLOVER_MIN_SILENCE_MS, DEFAULT_ROLLOVER_SILENCE_THRESHOLD, DEFAULT_ROLLOVER_WINDOW_SECONDS, InvalidSpeechAdapterError, OPENAI_REALTIME_DEFAULT_MODEL, OPENAI_REALTIME_DEFAULT_URL, RealtimeTranscriptionSession, STREAMING_TRANSCRIBER_ENV_KEYS, STREAMING_TRANSCRIBER_TYPES, SpeechConfigurationError, SpeechError, SpeechProviderError, TRANSCRIBER_ENV_KEYS, VOXTRAL_REALTIME_AUDIO_FORMAT, VOXTRAL_REALTIME_DEFAULT_MODEL, audioSecondsForBytes, bytesPerSecond, createStreamingClientSecret, getAvailableSpeechAdapters, getSpeech, getSpeechSynthesizer, getStreamingTranscriber, getTranscriber, isBrowserRuntime, isStreamingTranscriberType, mimeTypeToAudioExtension, openAIRealtimeProtocol, openAIRealtimeTranscriptionSession, parseMaxTurnSeconds, parseOpenAIRealtimeEvent, parseVoxtralRealtimeEvent, resolveClientSecretUrl, resolveOpenAIRealtimeFormat, resolveRealtimeUrl, resolveVoxtralRealtimeUrl, unwrapRawAudio, voxtralRealtimeProtocol, wrapStreamingTranscriber };
2571
+ export { CLIENT_SECRET_TTL_RANGE, DEFAULT_CLIENT_SECRET_TTL_SECONDS, DEFAULT_MAX_AUDIO_BYTES, DEFAULT_MAX_TURN_SECONDS, DEFAULT_ROLLOVER_MIN_SILENCE_MS, DEFAULT_ROLLOVER_SILENCE_THRESHOLD, DEFAULT_ROLLOVER_WINDOW_SECONDS, InvalidSpeechAdapterError, OPENAI_REALTIME_DEFAULT_MODEL, OPENAI_REALTIME_DEFAULT_URL, PcmCaptureError, RealtimeTranscriptionSession, STREAMING_TRANSCRIBER_ENV_KEYS, STREAMING_TRANSCRIBER_TYPES, SpeechConfigurationError, SpeechError, SpeechProviderError, TRANSCRIBER_ENV_KEYS, VOXTRAL_REALTIME_AUDIO_FORMAT, VOXTRAL_REALTIME_DEFAULT_MODEL, WavFormatError, audioSecondsForBytes, bytesPerSecond, createStreamingClientSecret, getAvailableSpeechAdapters, getSpeech, getSpeechSynthesizer, getStreamingTranscriber, getTranscriber, isBrowserRuntime, isStreamingTranscriberType, mimeTypeToAudioExtension, openAIRealtimeProtocol, openAIRealtimeTranscriptionSession, parseMaxTurnSeconds, parseOpenAIRealtimeEvent, parseVoxtralRealtimeEvent, resolveClientSecretUrl, resolveOpenAIRealtimeFormat, resolveRealtimeUrl, resolveVoxtralRealtimeUrl, transcriberInputFormat, unwrapRawAudio, voxtralRealtimeProtocol, wrapStreamingTranscriber };
2541
2572
 
2542
2573
  //# sourceMappingURL=index.js.map