@voiceinput/core 0.1.0-beta.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hirad Arshadiyarahmadi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # `@voiceinput/core`
2
+
3
+ Framework-neutral voice sessions, browser audio capture, and cursor-safe text
4
+ ownership. React applications normally use `@voiceinput/react`, which composes
5
+ these primitives.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @voiceinput/core@next
11
+ ```
12
+
13
+ ## Session
14
+
15
+ `createVoiceInputSession` coordinates option validation, two-phase audio
16
+ startup, provider streaming, duration limits, normalized state, and cleanup.
17
+
18
+ ```ts
19
+ import {
20
+ createBrowserAudioSource,
21
+ createVoiceInputSession,
22
+ } from "@voiceinput/core";
23
+
24
+ const session = createVoiceInputSession({
25
+ provider,
26
+ audioSource: createBrowserAudioSource(),
27
+ language: "en-CA",
28
+ vocabulary: ["VoiceInput"],
29
+ endpointing: { silenceMs: 650 },
30
+ connectionTimeoutMs: 15_000,
31
+ maxDurationMs: 5 * 60 * 1_000,
32
+ });
33
+
34
+ const unsubscribe = session.subscribe((event) => {
35
+ console.log(event.type);
36
+ });
37
+
38
+ await session.start();
39
+ await session.stop();
40
+ unsubscribe();
41
+ ```
42
+
43
+ Actions are `start`, `stop`, `cancel`, and `toggle`. `stop` is graceful and
44
+ preserves trusted final text; `cancel` aborts immediately. The default maximum
45
+ duration is five minutes, with one warning 30 seconds before cutoff.
46
+
47
+ Once microphone audio is acquired, provider connection and audio activation must
48
+ complete within `connectionTimeoutMs` (15 seconds by default). Expiry aborts the
49
+ full run, releases acquired audio, reports a retryable `network-error`, and
50
+ permits a fresh `start()`.
51
+
52
+ The immutable snapshot exposes `status`, `transcript`, `interimTranscript`,
53
+ `finalTranscript`, and `error`. Status values are `idle`,
54
+ `requesting-permission`, `connecting`, `listening`, `stopping`, `processing`,
55
+ and `error`. Stop reasons are `user`, `max-duration`, `replaced`, `max-length`,
56
+ `target-unavailable`, and `backgrounded`. A `text-limit` event reports a
57
+ constrained insertion.
58
+
59
+ Final parts use the same boundary policy as field insertion: outer provider
60
+ whitespace is normalized, word boundaries are added when needed, punctuation is
61
+ kept adjacent, empty parts are ignored, and consecutive Han, Hiragana, and
62
+ Katakana parts are not separated. `finalTranscript` is cumulative; `transcript`
63
+ adds the current normalized interim part. Session `final` events expose the raw
64
+ provider part as `text` and the cumulative normalized value as `transcript`.
65
+
66
+ ## Audio source lifecycle
67
+
68
+ `VoiceAudioSource.prepare({ sampleRate, abortSignal, onAcquired })` returns a
69
+ `PreparedVoiceAudioSource` with a PCM16 stream plus `start`, `stop`, and
70
+ `abort`. Preparation can request permission, but audio delivery starts only
71
+ after the provider connects. Custom sources must call the optional
72
+ `onAcquired()` callback as soon as they hold live audio resources; this starts
73
+ the connection deadline even if later preparation is still pending.
74
+
75
+ `createBrowserAudioSource` supplies the production browser implementation:
76
+
77
+ ```ts
78
+ const audioSource = createBrowserAudioSource({
79
+ constraints: { echoCancellation: true },
80
+ frameDurationMs: 20,
81
+ // Optional: use a self-hosted module under strict CSP.
82
+ workletModuleUrl: "/voiceinput-worklet.js",
83
+ });
84
+ ```
85
+
86
+ It captures mono audio through an `AudioWorklet`, resamples to the adapter's
87
+ declared rate, emits `Int16Array` frames, resumes suspended Safari contexts, and
88
+ tears down tracks, nodes, and contexts on every terminal path.
89
+
90
+ The default worklet uses a temporary Blob URL. For a policy without `blob:`,
91
+ write `VOICE_INPUT_AUDIO_WORKLET_SOURCE` to a same-origin JavaScript asset at
92
+ build time and pass its URL as `workletModuleUrl`. See the
93
+ [Content Security Policy guide](https://github.com/VoiceInput/voiceinput/blob/main/docs/content-security-policy.md)
94
+ for the copy script and exact directives.
95
+
96
+ Use `getBrowserVoiceInputSupport()` for a capability report. It checks secure
97
+ context, media devices, `getUserMedia`, `AudioContext`, and `AudioWorklet`.
98
+ `normalizeBrowserAudioError(error)` converts browser failures into
99
+ `VoiceInputError`.
100
+
101
+ ## Text ownership engine
102
+
103
+ `createVoiceInputTextEngine` inserts transcript text without taking ownership of
104
+ unrelated user content:
105
+
106
+ ```ts
107
+ const engine = createVoiceInputTextEngine({
108
+ interimBehavior: "inline",
109
+ transformTranscript: async (text) => text.trim(),
110
+ transformTimeoutMs: 10_000,
111
+ });
112
+
113
+ engine.setTarget(textarea);
114
+ engine.captureSelection();
115
+ engine.begin();
116
+ engine.applyInterim("draft");
117
+ engine.applyFinal("final text");
118
+ const completion = engine.complete();
119
+ await completion.result;
120
+ ```
121
+
122
+ Supported targets are `<textarea>` and `<input>` types `text`, `search`, `url`,
123
+ and `tel`. The engine tracks provisional, finalized, frozen, and transformed
124
+ spans. If a user edits or moves the caret, it freezes text it can no longer
125
+ prove ownership of and re-anchors later speech. Uncontrolled targets receive a
126
+ bubbling native `input` event.
127
+
128
+ For a controlled target, provide `getValue` and `onValueChange`, then pass each
129
+ committed application value to `reconcileControlledValue`.
130
+
131
+ `interimBehavior: "inline"` inserts replaceable interim text. `"expose"` keeps
132
+ interim text out of the field while still reporting it in snapshots.
133
+
134
+ ## Public API
135
+
136
+ Session and errors:
137
+
138
+ - `createVoiceInputSession`
139
+ - `VoiceInputSession`, `CreateVoiceInputSessionOptions`
140
+ - `VoiceInputSnapshot`, `VoiceInputStatus`, `VoiceInputSessionEvent`
141
+ - `VoiceInputStopReason`
142
+ - `VoiceAudioSource`, `VoiceAudioSourcePrepareOptions`
143
+ - `PreparedVoiceAudioSource`
144
+ - `VoiceInputError`, `VoiceInputErrorCode`, `VoiceInputErrorOptions`
145
+
146
+ Browser audio:
147
+
148
+ - `createBrowserAudioSource`, `CreateBrowserAudioSourceOptions`
149
+ - `getBrowserVoiceInputSupport`
150
+ - `BrowserVoiceInputSupport`, `BrowserVoiceInputCapability`
151
+ - `normalizeBrowserAudioError`
152
+
153
+ Text ownership:
154
+
155
+ - `createVoiceInputTextEngine`, `CreateVoiceInputTextEngineOptions`
156
+ - `VoiceInputTextEngine`, `VoiceInputTextEngineSnapshot`
157
+ - `VoiceInputTextTarget`
158
+ - `VoiceInputTextSelection`
159
+ - `VoiceInputTextSpan`, `VoiceInputTextSpanState`
160
+ - `VoiceInputControlledTextBinding`
161
+ - `VoiceInputInterimBehavior`
162
+ - `VoiceInputTransformTranscript`
163
+ - `VoiceInputTextCompletion`
164
+
165
+ ## Provider boundary
166
+
167
+ The session accepts any `VoiceInputProviderV1`. Provider-specific models,
168
+ tokens, and settings belong in adapter factories, not core options. See the
169
+ [`@voiceinput/provider` guide](https://github.com/VoiceInput/voiceinput/blob/main/packages/provider/README.md)
170
+ to implement an adapter.
171
+
172
+ ## Configuration, segments and history
173
+
174
+ `session.updateOptions(options)` supplies configuration for the next recording;
175
+ active recording configuration is unchanged. `textEngine.updateOptions(options)`
176
+ similarly samples interim and transform settings on the next `begin()`.
177
+
178
+ Pass the provider's `segmentId` as the second argument to `applyInterim` and
179
+ `applyFinal`. Official adapters provide identifiers for every transcript part.
180
+ Omitting it retains sequential compatibility: every final closes the current
181
+ implicit segment. This legacy mode cannot distinguish duplicate final delivery.
182
+
183
+ The text engine exposes `undo()`, `redo()`, `isWritable()`, and `subscribe()`.
184
+ Subscribers receive `text-limit`, `target-unavailable`, and `reset` events. See
185
+ the [editing contract](../../docs/editing-contract.md) for behavior.
186
+
187
+ Capture starts while connecting and queues up to fifteen seconds of PCM in
188
+ memory. It drains in order once connected; overflow or sustained transport
189
+ stalls stop with a recoverable error. Recording duration includes buffered
190
+ capture. Backgrounding stops capture; unexpected AudioContext or track
191
+ interruption is a recoverable audio error. Audio and transcript data are never
192
+ persisted by core.