@untemps/vocal 2.0.0-beta.2 → 2.0.0-beta.20
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/CHANGELOG.md +143 -0
- package/README.md +91 -8
- package/dist/index.cjs +2 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +110 -45
- package/package.json +9 -12
- package/dist/index.js +0 -2
- package/dist/index.umd.js +0 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,146 @@
|
|
|
1
|
+
# [2.0.0-beta.20](https://github.com/untemps/vocal/compare/v2.0.0-beta.19...v2.0.0-beta.20) (2026-05-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* Auto-restart recognition on silence in continuous mode ([#84](https://github.com/untemps/vocal/issues/84)) ([79a55f5](https://github.com/untemps/vocal/commit/79a55f5e295d2027a1473ce59872e6a09b4655c1))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### BREAKING CHANGES
|
|
10
|
+
|
|
11
|
+
* continuous mode now keeps the session alive across silence and aggregates results — semantics that callers using `continuous: true` must adapt to:
|
|
12
|
+
- Recording no longer ends after ~7s of silence; call `stop()` or `abort()` explicitly to terminate the session.
|
|
13
|
+
- A synthetic `result` event is emitted just before `end` on `stop()`, carrying the joined final transcripts. `event instanceof SpeechRecognitionEvent` returns `false` for this event — read the transcript through the listener's second argument (`(event, bestAlternative, alternatives) => ...`).
|
|
14
|
+
- Intermediate `end` and `start` events fired by the browser during silent restart cycles are no longer forwarded to user listeners. `isRecording` stays `true` across the cycle.
|
|
15
|
+
- `abort()` discards the aggregated buffer without emitting.
|
|
16
|
+
`continuous: false` consumers see no behavioural change.
|
|
17
|
+
|
|
18
|
+
# [2.0.0-beta.19](https://github.com/untemps/vocal/compare/v2.0.0-beta.18...v2.0.0-beta.19) (2026-05-17)
|
|
19
|
+
|
|
20
|
+
# [2.0.0-beta.18](https://github.com/untemps/vocal/compare/v2.0.0-beta.17...v2.0.0-beta.18) (2026-05-16)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### chore
|
|
24
|
+
|
|
25
|
+
* Remove UMD bundle from distribution ([#78](https://github.com/untemps/vocal/issues/78)) ([c0c819c](https://github.com/untemps/vocal/commit/c0c819c251cf4ee838463bf9dd6a960a70f6ad32))
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
### BREAKING CHANGES
|
|
29
|
+
|
|
30
|
+
* dist/index.umd.js is no longer published. Consumers loading via <script> tags or AMD loaders should use dist/index.es.js with a module-aware loader instead.
|
|
31
|
+
|
|
32
|
+
# [2.0.0-beta.17](https://github.com/untemps/vocal/compare/v2.0.0-beta.16...v2.0.0-beta.17) (2026-05-16)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
### Features
|
|
36
|
+
|
|
37
|
+
* Remove instance getter to prevent implementation leakage ([#77](https://github.com/untemps/vocal/issues/77)) ([93fc58f](https://github.com/untemps/vocal/commit/93fc58f46abe7fadced9c8f512dd69fb4865cd9c))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
### BREAKING CHANGES
|
|
41
|
+
|
|
42
|
+
* vocal.instance is removed. Consumers who accessed the raw SpeechRecognition object must migrate to Vocal API methods.
|
|
43
|
+
|
|
44
|
+
# [2.0.0-beta.16](https://github.com/untemps/vocal/compare/v2.0.0-beta.15...v2.0.0-beta.16) (2026-05-16)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
### Features
|
|
48
|
+
|
|
49
|
+
* Add once() method for one-shot event listener registration ([#76](https://github.com/untemps/vocal/issues/76)) ([8179643](https://github.com/untemps/vocal/commit/8179643f159e3ccf690f7ac2d8bd101568c6e5b3))
|
|
50
|
+
|
|
51
|
+
# [2.0.0-beta.15](https://github.com/untemps/vocal/compare/v2.0.0-beta.14...v2.0.0-beta.15) (2026-05-16)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
### Features
|
|
55
|
+
|
|
56
|
+
* Support multiple listeners per event type in addEventListener ([#75](https://github.com/untemps/vocal/issues/75)) ([97a435d](https://github.com/untemps/vocal/commit/97a435dc09105fadb9d8c22052ddaa75cbb6ee26))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
### BREAKING CHANGES
|
|
60
|
+
|
|
61
|
+
* addEventListener now stacks listeners instead of replacing. removeEventListener(eventType) removes all handlers for the type
|
|
62
|
+
removeEventListener(eventType, callback) removes only the specific callback.
|
|
63
|
+
|
|
64
|
+
# [2.0.0-beta.14](https://github.com/untemps/vocal/compare/v2.0.0-beta.13...v2.0.0-beta.14) (2026-05-16)
|
|
65
|
+
|
|
66
|
+
# [2.0.0-beta.13](https://github.com/untemps/vocal/compare/v2.0.0-beta.12...v2.0.0-beta.13) (2026-05-16)
|
|
67
|
+
|
|
68
|
+
# [2.0.0-beta.12](https://github.com/untemps/vocal/compare/v2.0.0-beta.11...v2.0.0-beta.12) (2026-05-16)
|
|
69
|
+
|
|
70
|
+
# [2.0.0-beta.11](https://github.com/untemps/vocal/compare/v2.0.0-beta.10...v2.0.0-beta.11) (2026-05-16)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
### Bug Fixes
|
|
74
|
+
|
|
75
|
+
* Throw on invalid event type in addEventListener and removeEventListener ([#69](https://github.com/untemps/vocal/issues/69)) ([a474718](https://github.com/untemps/vocal/commit/a474718fc7f36e4828a5430cf7c19b851401189d))
|
|
76
|
+
|
|
77
|
+
# [2.0.0-beta.10](https://github.com/untemps/vocal/compare/v2.0.0-beta.9...v2.0.0-beta.10) (2026-05-16)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
### Bug Fixes
|
|
81
|
+
|
|
82
|
+
* Remove internal end listener on cleanup ([#68](https://github.com/untemps/vocal/issues/68)) ([3179943](https://github.com/untemps/vocal/commit/31799433b054cca334d6159d8aae9e00c8971b6d))
|
|
83
|
+
|
|
84
|
+
# [2.0.0-beta.9](https://github.com/untemps/vocal/compare/v2.0.0-beta.8...v2.0.0-beta.9) (2026-05-16)
|
|
85
|
+
|
|
86
|
+
# [2.0.0-beta.8](https://github.com/untemps/vocal/compare/v2.0.0-beta.7...v2.0.0-beta.8) (2026-05-16)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
### Bug Fixes
|
|
90
|
+
|
|
91
|
+
* Return false from isSupported in non-browser environments ([#65](https://github.com/untemps/vocal/issues/65)) ([56f67cc](https://github.com/untemps/vocal/commit/56f67cc6cc9ff6288472d1461a71d3e0cbc128ed))
|
|
92
|
+
|
|
93
|
+
# [2.0.0-beta.7](https://github.com/untemps/vocal/compare/v2.0.0-beta.6...v2.0.0-beta.7) (2026-05-16)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
### Bug Fixes
|
|
97
|
+
|
|
98
|
+
* Use resultIndex to select current result in continuous mode ([#64](https://github.com/untemps/vocal/issues/64)) ([62d61c4](https://github.com/untemps/vocal/commit/62d61c41ec7713cb01d578568b462734324e722a))
|
|
99
|
+
|
|
100
|
+
# [2.0.0-beta.6](https://github.com/untemps/vocal/compare/v2.0.0-beta.5...v2.0.0-beta.6) (2026-05-16)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
### chore
|
|
104
|
+
|
|
105
|
+
* Add type module and rename CJS dist to index.cjs ([#45](https://github.com/untemps/vocal/issues/45)) ([e9923af](https://github.com/untemps/vocal/commit/e9923af7032fe48fc0b214bb77e3d6708a4b1adb))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
### BREAKING CHANGES
|
|
109
|
+
|
|
110
|
+
* "main" field: dist/index.js → dist/index.cjs. Consumers using the main field directly (not via the exports map) must update their import path.
|
|
111
|
+
Consumers using the exports map (require/import conditions) are not affected.
|
|
112
|
+
|
|
113
|
+
# [2.0.0-beta.5](https://github.com/untemps/vocal/compare/v2.0.0-beta.4...v2.0.0-beta.5) (2026-05-16)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
* refactor!: Select best RESULT transcript by confidence ([#44](https://github.com/untemps/vocal/issues/44)) ([4713366](https://github.com/untemps/vocal/commit/471336641a156623a17b6f7e0602658a3086381d))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
### BREAKING CHANGES
|
|
120
|
+
|
|
121
|
+
* The RESULT callback signature changes from (event, transcript: string, alternatives: string[]) to (event, bestAlternative: string, alternatives: string[]) where bestAlternative is the alternative with the highest confidence score instead of the first in the array.
|
|
122
|
+
Migration: no change needed if confidence ordering matches array order (standard browser behavior); replace transcript with bestAlternative if using the parameter name.
|
|
123
|
+
|
|
124
|
+
# [2.0.0-beta.4](https://github.com/untemps/vocal/compare/v2.0.0-beta.3...v2.0.0-beta.4) (2026-05-16)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
### Features
|
|
128
|
+
|
|
129
|
+
* start() rejects on error instead of always resolving ([#43](https://github.com/untemps/vocal/issues/43)) ([4414f11](https://github.com/untemps/vocal/commit/4414f11608e795b94845d06e6be53e8e5a76e022))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
### BREAKING CHANGES
|
|
133
|
+
|
|
134
|
+
* start(): no longer resolves when the microphone stream fails. Callers who did not handle rejections will receive an UnhandledPromiseRejection.
|
|
135
|
+
Migration: wrap await vocal.start() in try/catch, or use .catch().
|
|
136
|
+
|
|
137
|
+
# [2.0.0-beta.3](https://github.com/untemps/vocal/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2026-05-15)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
### Features
|
|
141
|
+
|
|
142
|
+
* Expose AbortSignal support in start() ([#42](https://github.com/untemps/vocal/issues/42)) ([a7f638b](https://github.com/untemps/vocal/commit/a7f638b541347a4377bce1f43a47aa5290ea2852))
|
|
143
|
+
|
|
1
144
|
# [2.0.0-beta.2](https://github.com/untemps/vocal/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2026-05-15)
|
|
2
145
|
|
|
3
146
|
|
package/README.md
CHANGED
|
@@ -33,11 +33,15 @@ const vocal = new Vocal(options)
|
|
|
33
33
|
// Subscribe to Vocal instance events (see below for all available events)
|
|
34
34
|
vocal.addEventListener('speechstart', (event) => console.log('Vocal starts recording'))
|
|
35
35
|
vocal.addEventListener('speechend', (event) => console.log('Vocal stops recording'))
|
|
36
|
-
vocal.addEventListener('result', (event,
|
|
37
|
-
vocal.addEventListener('error', (
|
|
38
|
-
|
|
39
|
-
// Start recording
|
|
40
|
-
|
|
36
|
+
vocal.addEventListener('result', (event, bestAlternative, alternatives) => console.log('Vocal catches a result:', bestAlternative, alternatives))
|
|
37
|
+
vocal.addEventListener('error', (event) => console.error(event.error, event.message))
|
|
38
|
+
|
|
39
|
+
// Start recording — rejects on error
|
|
40
|
+
try {
|
|
41
|
+
await vocal.start()
|
|
42
|
+
} catch (error) {
|
|
43
|
+
// handle error
|
|
44
|
+
}
|
|
41
45
|
|
|
42
46
|
// Stop/Pause recording
|
|
43
47
|
vocal.stop()
|
|
@@ -58,9 +62,22 @@ Please refer to [this section](https://developer.mozilla.org/en-US/docs/Web/API/
|
|
|
58
62
|
| ---------------- | ----------------- | ---------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
59
63
|
| grammars | SpeechGrammarList | null | Grammars understood by the recognition [JSpeech Grammar Format](https://www.w3.org/TR/jsgf/) |
|
|
60
64
|
| lang | string | 'en-US' | Language understood by the recognition [BCP 47 language tag](https://tools.ietf.org/html/bcp47) |
|
|
61
|
-
| continuous | boolean | false | Whether continuous results are returned for each recognition, or only a single result
|
|
65
|
+
| continuous | boolean | false | Whether continuous results are returned for each recognition, or only a single result (see [Continuous mode](#continuous-mode)) |
|
|
62
66
|
| interimResults | boolean | false | Whether interim results should be returned or not. Interim results are results that are not yet final |
|
|
63
67
|
| maxAlternatives | number | 1 | Maximum number of SpeechRecognitionAlternatives provided per result |
|
|
68
|
+
|
|
69
|
+
### Continuous mode
|
|
70
|
+
|
|
71
|
+
Browsers (notably Chrome) automatically end a recognition session after a few seconds of silence, even when `continuous` is `true`. Vocal transparently restarts the underlying engine after such a silence-induced `end`, so recording keeps running until `stop()` or `abort()` is explicitly called. The intermediate `end` and `start` events triggered by the restart are not forwarded to user listeners — `isRecording` stays `true` across the restart, and the cycle is throttled to at most one restart per second to avoid `InvalidStateError`.
|
|
72
|
+
|
|
73
|
+
The restart is disabled automatically when the recognition emits a fatal error (`not-allowed`, `service-not-allowed`, `audio-capture`).
|
|
74
|
+
|
|
75
|
+
#### Aggregated result on stop
|
|
76
|
+
|
|
77
|
+
To compensate for results being split across silent restart cycles, Vocal accumulates every final result (`isFinal: true`) received during a session. On explicit `stop()`, an extra `result` event is emitted just before `end`, carrying the joined transcripts as a single string. Interim results and `abort()` are excluded — `abort()` discards the buffer without emitting.
|
|
78
|
+
|
|
79
|
+
The aggregated event is a synthetic `Event` shaped to match `SpeechRecognitionEvent` (`resultIndex` + `results[0][0].transcript`); it is not a real `SpeechRecognitionEvent` instance, so `event instanceof SpeechRecognitionEvent` returns `false`. Read the transcript through the second argument of the listener (`bestAlternative`).
|
|
80
|
+
|
|
64
81
|
## Events
|
|
65
82
|
|
|
66
83
|
Events described below are those from the `SpeechRecognition` Web API.
|
|
@@ -73,7 +90,7 @@ Please refer to [this section](https://developer.mozilla.org/en-US/docs/Web/API/
|
|
|
73
90
|
| end | Fired when the recognition service has disconnected |
|
|
74
91
|
| error | Fired when a recognition error occurs |
|
|
75
92
|
| nomatch | Fired when the recognition service returns a final result with no significant recognition |
|
|
76
|
-
| result | Fired when the recognition service returns a result — callback receives `(event,
|
|
93
|
+
| result | Fired when the recognition service returns a result — callback receives `(event, bestAlternative: string, alternatives: string[])` where `bestAlternative` is the alternative with the highest confidence |
|
|
77
94
|
| soundend | Fired when any sound — recognisable or not — has stopped being detected |
|
|
78
95
|
| soundstart | Fired when any sound — recognisable or not — has been detected |
|
|
79
96
|
| speechend | Fired when speech recognized by the recognition service has stopped being detected |
|
|
@@ -85,5 +102,71 @@ Please refer to [this section](https://developer.mozilla.org/en-US/docs/Web/API/
|
|
|
85
102
|
| Getter | Type | Description |
|
|
86
103
|
| ----------- | ------------------------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
87
104
|
| isSupported | boolean | Whether the current environment supports the SpeechRecognition Web API (static) |
|
|
88
|
-
| instance | SpeechRecognition \| null | The underlying SpeechRecognition instance |
|
|
89
105
|
| isRecording | boolean | Whether recognition is currently active — `true` after `start()`, `false` after `stop()`, `abort()`, or `end` event |
|
|
106
|
+
|
|
107
|
+
## Methods
|
|
108
|
+
|
|
109
|
+
### `start({ signal? })`
|
|
110
|
+
|
|
111
|
+
| Parameter | Type | Default | Description |
|
|
112
|
+
| --------- | ------------- | ----------- | ----------------------------------------------------------------------------- |
|
|
113
|
+
| signal | AbortSignal | `undefined` | Cancels the in-flight microphone permission request when the signal is aborted |
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
const controller = new AbortController()
|
|
117
|
+
vocal.start({ signal: controller.signal })
|
|
118
|
+
|
|
119
|
+
// Cancel the permission request at any later point
|
|
120
|
+
controller.abort()
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### `stop()`
|
|
124
|
+
|
|
125
|
+
Stops recognition gracefully, allowing the current audio to be processed before disconnecting. Sets `isRecording` to `false`.
|
|
126
|
+
|
|
127
|
+
### `abort()`
|
|
128
|
+
|
|
129
|
+
Stops recognition immediately without processing pending audio. Sets `isRecording` to `false`.
|
|
130
|
+
|
|
131
|
+
### `addEventListener(eventType, callback)`
|
|
132
|
+
|
|
133
|
+
Registers a callback for the given event type. Multiple callbacks can be registered for the same type — they stack and all fire in registration order.
|
|
134
|
+
|
|
135
|
+
| Parameter | Type | Description |
|
|
136
|
+
| --------- | ------------------------------------------------- | ------------------------------------------ |
|
|
137
|
+
| eventType | `EventType` | One of the valid event type strings |
|
|
138
|
+
| callback | `ResultEventHandler \| ErrorEventHandler \| GenericEventHandler` | Callback invoked when the event fires |
|
|
139
|
+
|
|
140
|
+
Throws if `eventType` is not a valid `EventType`.
|
|
141
|
+
|
|
142
|
+
### `removeEventListener(eventType, callback?)`
|
|
143
|
+
|
|
144
|
+
Removes a listener for the given event type.
|
|
145
|
+
|
|
146
|
+
| Parameter | Type | Default | Description |
|
|
147
|
+
| --------- | ------------------------------------------------- | ----------- | ---------------------------------------------------- |
|
|
148
|
+
| eventType | `EventType` | | One of the valid event type strings |
|
|
149
|
+
| callback | `ResultEventHandler \| ErrorEventHandler \| GenericEventHandler` | `undefined` | Specific callback to remove. Omit to remove all listeners for this type |
|
|
150
|
+
|
|
151
|
+
Throws if `eventType` is not a valid `EventType`.
|
|
152
|
+
|
|
153
|
+
### `once(eventType, callback)`
|
|
154
|
+
|
|
155
|
+
Registers a one-shot listener that automatically unregisters itself after firing once.
|
|
156
|
+
|
|
157
|
+
| Parameter | Type | Description |
|
|
158
|
+
| --------- | ------------------------------------------------- | ------------------------------------------ |
|
|
159
|
+
| eventType | `EventType` | One of the valid event type strings |
|
|
160
|
+
| callback | `ResultEventHandler \| ErrorEventHandler \| GenericEventHandler` | Callback invoked once when the event fires |
|
|
161
|
+
|
|
162
|
+
```js
|
|
163
|
+
vocal.once('result', (event, bestAlternative, alternatives) => {
|
|
164
|
+
console.log(bestAlternative)
|
|
165
|
+
vocal.stop()
|
|
166
|
+
})
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### `cleanup()`
|
|
170
|
+
|
|
171
|
+
Stops recognition, removes all registered listeners, and releases the internal `SpeechRecognition` instance. The `Vocal` object cannot be reused after `cleanup()`.
|
|
172
|
+
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@untemps/user-permissions-utils`);var t=1e3,n=new Set([`not-allowed`,`service-not-allowed`,`audio-capture`]),r=class r{static defaultOptions={grammars:null,lang:`en-US`,continuous:!1,interimResults:!1,maxAlternatives:1};static eventTypes={AUDIO_END:`audioend`,AUDIO_START:`audiostart`,END:`end`,ERROR:`error`,NO_MATCH:`nomatch`,RESULT:`result`,SOUND_END:`soundend`,SOUND_START:`soundstart`,SPEECH_END:`speechend`,SPEECH_START:`speechstart`,START:`start`};static get isSupported(){return!!r._resolveSpeechRecognition()&&!!(0,e.isNavigatorPermissionsSupported)()&&!!(0,e.isNavigatorMediaDevicesSupported)()}static set isSupported(e){throw Error(`You cannot set isSupported directly.`)}_instance=null;_listeners={};_isRecording=!1;_explicitStop=!1;_lastStartedAt=0;_restartTimeoutId=null;_isRestarting=!1;_finalTranscripts=[];_onEnd=e=>{if(this._shouldAutoRestart()){let n=Math.max(0,t-(Date.now()-this._lastStartedAt));this._isRestarting=!0,this._restartTimeoutId=setTimeout(()=>this._restart(),n),e.stopImmediatePropagation();return}this._isRecording=!1};_onStart=e=>{this._isRestarting&&(e.stopImmediatePropagation(),queueMicrotask(()=>{this._isRestarting=!1}))};_onError=e=>{n.has(e.error)&&(this._explicitStop=!0,this._clearRestartTimeout(),this._isRecording=!1)};_onResult=e=>{let t=e,n=t.results?.[t.resultIndex];n?.isFinal&&this._finalTranscripts.push(r._pickBestAlternative(Array.from(n)).transcript)};constructor(e){let t=r._resolveSpeechRecognition();if(!t)throw new DOMException(`SpeechRecognition not supported`,`NOT_SUPPORTED_ERR`);this._instance=new t;let{grammars:n,...i}={...r.defaultOptions,...e??{}},a=this._instance;if(Object.assign(a,i),n)a.grammars=n;else{let e=r._resolveSpeechGrammarList();a.grammars=e?new e:null}this._instance.addEventListener(r.eventTypes.END,this._onEnd),this._instance.addEventListener(r.eventTypes.START,this._onStart),this._instance.addEventListener(r.eventTypes.ERROR,this._onError),this._instance.addEventListener(r.eventTypes.RESULT,this._onResult)}get isRecording(){return this._isRecording}set isRecording(e){throw Error(`You cannot set isRecording directly.`)}async start({signal:t}={}){if(this._instance)try{if(!await(0,e.getUserMediaStream)(`microphone`,{audio:!0},{signal:t}))throw Error(`Unable to retrieve the stream from media device`);this._explicitStop=!1,this._finalTranscripts=[],this._instance.start(),this._isRecording=!0,this._lastStartedAt=Date.now()}catch(e){if(e instanceof Error&&e.name===`AbortError`)return this;throw e}return this}stop(){return this._instance&&(this._explicitStop=!0,this._clearRestartTimeout(),this._emitAggregatedResult(),this._instance.stop(),this._isRecording=!1),this}abort(){return this._instance&&(this._explicitStop=!0,this._clearRestartTimeout(),this._instance.abort(),this._isRecording=!1,this._finalTranscripts=[]),this}addEventListener(e,t){if(!this._includesEventType(e))throw Error(this._unknownEventTypeMessage(e));if(this._instance){let n=n=>{if(this._isRestarting&&(e===r.eventTypes.END||e===r.eventTypes.START))return;let i=[];if(e===r.eventTypes.RESULT){let e=n;if(e.results?.length>0&&e.resultIndex<e.results.length){let t=Array.from(e.results[e.resultIndex]);i.push(r._pickBestAlternative(t).transcript,t.map(e=>e.transcript))}}t.call(this,n,...i)};this._instance.addEventListener(e,n),this._listeners[e]||(this._listeners[e]=[]),this._listeners[e].push({callback:t,handler:n})}return this}removeEventListener(e,t){if(!this._includesEventType(e))throw Error(this._unknownEventTypeMessage(e));let n=this._instance;if(n&&this._listeners[e])if(t!==void 0){let r=this._listeners[e].findIndex(e=>e.callback===t);r!==-1&&(n.removeEventListener(e,this._listeners[e][r].handler),this._listeners[e].splice(r,1),this._listeners[e].length===0&&delete this._listeners[e])}else this._listeners[e].forEach(({handler:t})=>n.removeEventListener(e,t)),delete this._listeners[e];return this}once(e,t){let n=(...r)=>{t.call(this,...r),this.removeEventListener(e,n)};return this.addEventListener(e,n)}cleanup(){return this.stop(),Object.keys(this._listeners).forEach(e=>this.removeEventListener(e)),this._instance?.removeEventListener(r.eventTypes.END,this._onEnd),this._instance?.removeEventListener(r.eventTypes.START,this._onStart),this._instance?.removeEventListener(r.eventTypes.ERROR,this._onError),this._instance?.removeEventListener(r.eventTypes.RESULT,this._onResult),this._instance=null,this}_restart=()=>{this._restartTimeoutId=null;try{this._instance.start(),this._lastStartedAt=Date.now()}catch{this._isRestarting=!1,this._isRecording=!1}};_emitAggregatedResult(){let e=this._finalTranscripts;if(this._finalTranscripts=[],e.length===0)return;let t=e.join(` `).trim(),n=Object.assign([{transcript:t,confidence:1}],{isFinal:!0}),i=Object.assign(new Event(r.eventTypes.RESULT),{resultIndex:0,results:[n]});[...this._listeners[r.eventTypes.RESULT]??[]].forEach(({handler:e})=>e(i))}static _pickBestAlternative(e){return e.reduce((e,t)=>(t.confidence??0)>(e.confidence??0)?t:e)}_shouldAutoRestart(){return!!this._instance&&!this._explicitStop&&this._instance.continuous}_clearRestartTimeout(){this._restartTimeoutId!==null&&(clearTimeout(this._restartTimeoutId),this._restartTimeoutId=null),this._isRestarting=!1}_includesEventType(e){return Object.values(r.eventTypes).includes(e)}_unknownEventTypeMessage(e){return`Unknown event type "${e}". Valid types are: ${Object.values(r.eventTypes).join(`, `)}.`}static _resolveSpeechRecognition(){if(!(typeof window>`u`))return window.SpeechRecognition??window.webkitSpeechRecognition??window.mozSpeechRecognition??window.msSpeechRecognition}static _resolveSpeechGrammarList(){return window.SpeechGrammarList??window.webkitSpeechGrammarList??window.mozSpeechGrammarList??window.msSpeechGrammarList}};exports.Vocal=r;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export { default as Vocal } from './Vocal';
|
|
2
|
-
export type { VocalOptions, EventType } from './Vocal';
|
|
2
|
+
export type { VocalOptions, EventType, ResultEventHandler, ErrorEventHandler, GenericEventHandler, EventHandlerFor, } from './Vocal';
|
package/dist/index.es.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { getUserMediaStream as e, isNavigatorMediaDevicesSupported as t, isNavigatorPermissionsSupported as n } from "@untemps/user-permissions-utils";
|
|
2
2
|
//#region src/Vocal.ts
|
|
3
|
-
var r =
|
|
3
|
+
var r = 1e3, i = new Set([
|
|
4
|
+
"not-allowed",
|
|
5
|
+
"service-not-allowed",
|
|
6
|
+
"audio-capture"
|
|
7
|
+
]), a = class a {
|
|
4
8
|
static defaultOptions = {
|
|
5
9
|
grammars: null,
|
|
6
10
|
lang: "en-US",
|
|
@@ -22,36 +26,53 @@ var r = class r {
|
|
|
22
26
|
START: "start"
|
|
23
27
|
};
|
|
24
28
|
static get isSupported() {
|
|
25
|
-
return !!
|
|
29
|
+
return !!a._resolveSpeechRecognition() && !!n() && !!t();
|
|
26
30
|
}
|
|
27
31
|
static set isSupported(e) {
|
|
28
32
|
throw Error("You cannot set isSupported directly.");
|
|
29
33
|
}
|
|
30
34
|
_instance = null;
|
|
31
|
-
_listeners =
|
|
35
|
+
_listeners = {};
|
|
32
36
|
_isRecording = !1;
|
|
37
|
+
_explicitStop = !1;
|
|
38
|
+
_lastStartedAt = 0;
|
|
39
|
+
_restartTimeoutId = null;
|
|
40
|
+
_isRestarting = !1;
|
|
41
|
+
_finalTranscripts = [];
|
|
42
|
+
_onEnd = (e) => {
|
|
43
|
+
if (this._shouldAutoRestart()) {
|
|
44
|
+
let t = Math.max(0, r - (Date.now() - this._lastStartedAt));
|
|
45
|
+
this._isRestarting = !0, this._restartTimeoutId = setTimeout(() => this._restart(), t), e.stopImmediatePropagation();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
this._isRecording = !1;
|
|
49
|
+
};
|
|
50
|
+
_onStart = (e) => {
|
|
51
|
+
this._isRestarting && (e.stopImmediatePropagation(), queueMicrotask(() => {
|
|
52
|
+
this._isRestarting = !1;
|
|
53
|
+
}));
|
|
54
|
+
};
|
|
55
|
+
_onError = (e) => {
|
|
56
|
+
i.has(e.error) && (this._explicitStop = !0, this._clearRestartTimeout(), this._isRecording = !1);
|
|
57
|
+
};
|
|
58
|
+
_onResult = (e) => {
|
|
59
|
+
let t = e, n = t.results?.[t.resultIndex];
|
|
60
|
+
n?.isFinal && this._finalTranscripts.push(a._pickBestAlternative(Array.from(n)).transcript);
|
|
61
|
+
};
|
|
33
62
|
constructor(e) {
|
|
34
|
-
let t =
|
|
63
|
+
let t = a._resolveSpeechRecognition();
|
|
35
64
|
if (!t) throw new DOMException("SpeechRecognition not supported", "NOT_SUPPORTED_ERR");
|
|
36
|
-
this._instance = new t()
|
|
37
|
-
let { grammars: n, ...
|
|
38
|
-
...
|
|
65
|
+
this._instance = new t();
|
|
66
|
+
let { grammars: n, ...r } = {
|
|
67
|
+
...a.defaultOptions,
|
|
39
68
|
...e ?? {}
|
|
40
|
-
},
|
|
41
|
-
if (Object.assign(
|
|
69
|
+
}, i = this._instance;
|
|
70
|
+
if (Object.assign(i, r), n) i.grammars = n;
|
|
42
71
|
else {
|
|
43
|
-
let e =
|
|
44
|
-
|
|
72
|
+
let e = a._resolveSpeechGrammarList();
|
|
73
|
+
i.grammars = e ? new e() : null;
|
|
45
74
|
}
|
|
46
|
-
this._instance.addEventListener(
|
|
47
|
-
this._isRecording = !1;
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
get instance() {
|
|
51
|
-
return this._instance;
|
|
52
|
-
}
|
|
53
|
-
set instance(e) {
|
|
54
|
-
throw Error("You cannot set instance directly.");
|
|
75
|
+
this._instance.addEventListener(a.eventTypes.END, this._onEnd), this._instance.addEventListener(a.eventTypes.START, this._onStart), this._instance.addEventListener(a.eventTypes.ERROR, this._onError), this._instance.addEventListener(a.eventTypes.RESULT, this._onResult);
|
|
55
76
|
}
|
|
56
77
|
get isRecording() {
|
|
57
78
|
return this._isRecording;
|
|
@@ -59,61 +80,105 @@ var r = class r {
|
|
|
59
80
|
set isRecording(e) {
|
|
60
81
|
throw Error("You cannot set isRecording directly.");
|
|
61
82
|
}
|
|
62
|
-
async start() {
|
|
83
|
+
async start({ signal: t } = {}) {
|
|
63
84
|
if (this._instance) try {
|
|
64
|
-
if (!await e("microphone", { audio: !0 })) throw Error("Unable to retrieve the stream from media device");
|
|
65
|
-
this._instance.start(), this._isRecording = !0;
|
|
85
|
+
if (!await e("microphone", { audio: !0 }, { signal: t })) throw Error("Unable to retrieve the stream from media device");
|
|
86
|
+
this._explicitStop = !1, this._finalTranscripts = [], this._instance.start(), this._isRecording = !0, this._lastStartedAt = Date.now();
|
|
66
87
|
} catch (e) {
|
|
67
|
-
|
|
68
|
-
|
|
88
|
+
if (e instanceof Error && e.name === "AbortError") return this;
|
|
89
|
+
throw e;
|
|
69
90
|
}
|
|
70
91
|
return this;
|
|
71
92
|
}
|
|
72
93
|
stop() {
|
|
73
|
-
return this._instance && (this._instance.stop(), this._isRecording = !1), this;
|
|
94
|
+
return this._instance && (this._explicitStop = !0, this._clearRestartTimeout(), this._emitAggregatedResult(), this._instance.stop(), this._isRecording = !1), this;
|
|
74
95
|
}
|
|
75
96
|
abort() {
|
|
76
|
-
return this._instance && (this._instance.abort(), this._isRecording = !1), this;
|
|
97
|
+
return this._instance && (this._explicitStop = !0, this._clearRestartTimeout(), this._instance.abort(), this._isRecording = !1, this._finalTranscripts = []), this;
|
|
77
98
|
}
|
|
78
99
|
addEventListener(e, t) {
|
|
79
|
-
if (this.
|
|
80
|
-
|
|
100
|
+
if (!this._includesEventType(e)) throw Error(this._unknownEventTypeMessage(e));
|
|
101
|
+
if (this._instance) {
|
|
81
102
|
let n = (n) => {
|
|
82
|
-
|
|
83
|
-
|
|
103
|
+
if (this._isRestarting && (e === a.eventTypes.END || e === a.eventTypes.START)) return;
|
|
104
|
+
let r = [];
|
|
105
|
+
if (e === a.eventTypes.RESULT) {
|
|
84
106
|
let e = n;
|
|
85
|
-
if (e.results?.length > 0) {
|
|
86
|
-
let t = Array.from(e.results[
|
|
87
|
-
|
|
107
|
+
if (e.results?.length > 0 && e.resultIndex < e.results.length) {
|
|
108
|
+
let t = Array.from(e.results[e.resultIndex]);
|
|
109
|
+
r.push(a._pickBestAlternative(t).transcript, t.map((e) => e.transcript));
|
|
88
110
|
}
|
|
89
111
|
}
|
|
90
|
-
t.
|
|
112
|
+
t.call(this, n, ...r);
|
|
91
113
|
};
|
|
92
|
-
this._instance.addEventListener(e, n), this._listeners[e] =
|
|
114
|
+
this._instance.addEventListener(e, n), this._listeners[e] || (this._listeners[e] = []), this._listeners[e].push({
|
|
115
|
+
callback: t,
|
|
116
|
+
handler: n
|
|
117
|
+
});
|
|
93
118
|
}
|
|
94
119
|
return this;
|
|
95
120
|
}
|
|
96
|
-
removeEventListener(e) {
|
|
97
|
-
if (this.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
121
|
+
removeEventListener(e, t) {
|
|
122
|
+
if (!this._includesEventType(e)) throw Error(this._unknownEventTypeMessage(e));
|
|
123
|
+
let n = this._instance;
|
|
124
|
+
if (n && this._listeners[e]) if (t !== void 0) {
|
|
125
|
+
let r = this._listeners[e].findIndex((e) => e.callback === t);
|
|
126
|
+
r !== -1 && (n.removeEventListener(e, this._listeners[e][r].handler), this._listeners[e].splice(r, 1), this._listeners[e].length === 0 && delete this._listeners[e]);
|
|
127
|
+
} else this._listeners[e].forEach(({ handler: t }) => n.removeEventListener(e, t)), delete this._listeners[e];
|
|
101
128
|
return this;
|
|
102
129
|
}
|
|
130
|
+
once(e, t) {
|
|
131
|
+
let n = (...r) => {
|
|
132
|
+
t.call(this, ...r), this.removeEventListener(e, n);
|
|
133
|
+
};
|
|
134
|
+
return this.addEventListener(e, n);
|
|
135
|
+
}
|
|
103
136
|
cleanup() {
|
|
104
|
-
return this.stop(), Object.keys(this._listeners).forEach((e) => this.removeEventListener(e)), this._instance = null, this;
|
|
137
|
+
return this.stop(), Object.keys(this._listeners).forEach((e) => this.removeEventListener(e)), this._instance?.removeEventListener(a.eventTypes.END, this._onEnd), this._instance?.removeEventListener(a.eventTypes.START, this._onStart), this._instance?.removeEventListener(a.eventTypes.ERROR, this._onError), this._instance?.removeEventListener(a.eventTypes.RESULT, this._onResult), this._instance = null, this;
|
|
138
|
+
}
|
|
139
|
+
_restart = () => {
|
|
140
|
+
this._restartTimeoutId = null;
|
|
141
|
+
try {
|
|
142
|
+
this._instance.start(), this._lastStartedAt = Date.now();
|
|
143
|
+
} catch {
|
|
144
|
+
this._isRestarting = !1, this._isRecording = !1;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
_emitAggregatedResult() {
|
|
148
|
+
let e = this._finalTranscripts;
|
|
149
|
+
if (this._finalTranscripts = [], e.length === 0) return;
|
|
150
|
+
let t = e.join(" ").trim(), n = Object.assign([{
|
|
151
|
+
transcript: t,
|
|
152
|
+
confidence: 1
|
|
153
|
+
}], { isFinal: !0 }), r = Object.assign(new Event(a.eventTypes.RESULT), {
|
|
154
|
+
resultIndex: 0,
|
|
155
|
+
results: [n]
|
|
156
|
+
});
|
|
157
|
+
[...this._listeners[a.eventTypes.RESULT] ?? []].forEach(({ handler: e }) => e(r));
|
|
158
|
+
}
|
|
159
|
+
static _pickBestAlternative(e) {
|
|
160
|
+
return e.reduce((e, t) => (t.confidence ?? 0) > (e.confidence ?? 0) ? t : e);
|
|
161
|
+
}
|
|
162
|
+
_shouldAutoRestart() {
|
|
163
|
+
return !!this._instance && !this._explicitStop && this._instance.continuous;
|
|
164
|
+
}
|
|
165
|
+
_clearRestartTimeout() {
|
|
166
|
+
this._restartTimeoutId !== null && (clearTimeout(this._restartTimeoutId), this._restartTimeoutId = null), this._isRestarting = !1;
|
|
105
167
|
}
|
|
106
168
|
_includesEventType(e) {
|
|
107
|
-
return Object.values(
|
|
169
|
+
return Object.values(a.eventTypes).includes(e);
|
|
170
|
+
}
|
|
171
|
+
_unknownEventTypeMessage(e) {
|
|
172
|
+
return `Unknown event type "${e}". Valid types are: ${Object.values(a.eventTypes).join(", ")}.`;
|
|
108
173
|
}
|
|
109
174
|
static _resolveSpeechRecognition() {
|
|
110
|
-
return window.SpeechRecognition ?? window.webkitSpeechRecognition ?? window.mozSpeechRecognition ?? window.msSpeechRecognition;
|
|
175
|
+
if (!(typeof window > "u")) return window.SpeechRecognition ?? window.webkitSpeechRecognition ?? window.mozSpeechRecognition ?? window.msSpeechRecognition;
|
|
111
176
|
}
|
|
112
177
|
static _resolveSpeechGrammarList() {
|
|
113
178
|
return window.SpeechGrammarList ?? window.webkitSpeechGrammarList ?? window.mozSpeechGrammarList ?? window.msSpeechGrammarList;
|
|
114
179
|
}
|
|
115
180
|
};
|
|
116
181
|
//#endregion
|
|
117
|
-
export {
|
|
182
|
+
export { a as Vocal };
|
|
118
183
|
|
|
119
184
|
//# sourceMappingURL=index.es.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@untemps/vocal",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.20",
|
|
4
4
|
"description": "Class wrapped around the SpeechRecognition Web API",
|
|
5
5
|
"repository": "git@github.com:untemps/vocal.git",
|
|
6
6
|
"keywords": [
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"author": "Vincent Le Badezet <v.lebadezet@untemps.net>",
|
|
14
14
|
"license": "MIT",
|
|
15
15
|
"private": false,
|
|
16
|
+
"type": "module",
|
|
16
17
|
"publishConfig": {
|
|
17
18
|
"access": "public"
|
|
18
19
|
},
|
|
@@ -20,20 +21,19 @@
|
|
|
20
21
|
"node": ">=22"
|
|
21
22
|
},
|
|
22
23
|
"files": [
|
|
23
|
-
"dist/index.
|
|
24
|
+
"dist/index.cjs",
|
|
24
25
|
"dist/index.es.js",
|
|
25
|
-
"dist/index.umd.js",
|
|
26
26
|
"dist/index.d.ts",
|
|
27
27
|
"CHANGELOG.md"
|
|
28
28
|
],
|
|
29
|
-
"main": "dist/index.
|
|
29
|
+
"main": "dist/index.cjs",
|
|
30
30
|
"module": "dist/index.es.js",
|
|
31
31
|
"types": "dist/index.d.ts",
|
|
32
32
|
"exports": {
|
|
33
33
|
".": {
|
|
34
34
|
"types": "./dist/index.d.ts",
|
|
35
35
|
"import": "./dist/index.es.js",
|
|
36
|
-
"require": "./dist/index.
|
|
36
|
+
"require": "./dist/index.cjs",
|
|
37
37
|
"default": "./dist/index.es.js"
|
|
38
38
|
}
|
|
39
39
|
},
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"vitest": "^4.1.5"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@untemps/user-permissions-utils": "^1.3.
|
|
63
|
+
"@untemps/user-permissions-utils": "^1.3.3"
|
|
64
64
|
},
|
|
65
65
|
"release": {
|
|
66
66
|
"branches": [
|
|
@@ -91,16 +91,12 @@
|
|
|
91
91
|
{
|
|
92
92
|
"assets": [
|
|
93
93
|
{
|
|
94
|
-
"path": "dist/index.
|
|
94
|
+
"path": "dist/index.cjs",
|
|
95
95
|
"label": "CJS distribution"
|
|
96
96
|
},
|
|
97
97
|
{
|
|
98
98
|
"path": "dist/index.es.js",
|
|
99
99
|
"label": "ES distribution"
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
"path": "dist/index.umd.js",
|
|
103
|
-
"label": "UMD distribution"
|
|
104
100
|
}
|
|
105
101
|
]
|
|
106
102
|
}
|
|
@@ -108,12 +104,13 @@
|
|
|
108
104
|
]
|
|
109
105
|
},
|
|
110
106
|
"scripts": {
|
|
107
|
+
"dev": "vite demo --config demo/vite.config.js",
|
|
111
108
|
"test": "vitest",
|
|
112
109
|
"test:ci": "vitest run --coverage",
|
|
113
110
|
"build": "vite build",
|
|
114
111
|
"typecheck": "tsc --noEmit",
|
|
115
112
|
"lint": "eslint src/ vitest.setup.ts",
|
|
116
113
|
"prepare": "husky",
|
|
117
|
-
"prettier": "prettier \"src/**/*.{ts,js}\" vitest.setup.ts --ignore-path ./.prettierignore --write"
|
|
114
|
+
"prettier": "prettier \"src/**/*.{ts,js}\" vitest.setup.ts \"*.{js,ts}\" --ignore-path ./.prettierignore --write"
|
|
118
115
|
}
|
|
119
116
|
}
|
package/dist/index.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@untemps/user-permissions-utils`);var t=class t{static defaultOptions={grammars:null,lang:`en-US`,continuous:!1,interimResults:!1,maxAlternatives:1};static eventTypes={AUDIO_END:`audioend`,AUDIO_START:`audiostart`,END:`end`,ERROR:`error`,NO_MATCH:`nomatch`,RESULT:`result`,SOUND_END:`soundend`,SOUND_START:`soundstart`,SPEECH_END:`speechend`,SPEECH_START:`speechstart`,START:`start`};static get isSupported(){return!!t._resolveSpeechRecognition()&&!!(0,e.isNavigatorPermissionsSupported)()&&!!(0,e.isNavigatorMediaDevicesSupported)()}static set isSupported(e){throw Error(`You cannot set isSupported directly.`)}_instance=null;_listeners=null;_isRecording=!1;constructor(e){let n=t._resolveSpeechRecognition();if(!n)throw new DOMException(`SpeechRecognition not supported`,`NOT_SUPPORTED_ERR`);this._instance=new n,this._listeners={};let{grammars:r,...i}={...t.defaultOptions,...e??{}},a=this._instance;if(Object.assign(a,i),r)a.grammars=r;else{let e=t._resolveSpeechGrammarList();a.grammars=e?new e:null}this._instance.addEventListener(`end`,()=>{this._isRecording=!1})}get instance(){return this._instance}set instance(e){throw Error(`You cannot set instance directly.`)}get isRecording(){return this._isRecording}set isRecording(e){throw Error(`You cannot set isRecording directly.`)}async start(){if(this._instance)try{if(!await(0,e.getUserMediaStream)(`microphone`,{audio:!0}))throw Error(`Unable to retrieve the stream from media device`);this._instance.start(),this._isRecording=!0}catch(e){let t=this._listeners?.error;t&&t(e)}return this}stop(){return this._instance&&(this._instance.stop(),this._isRecording=!1),this}abort(){return this._instance&&(this._instance.abort(),this._isRecording=!1),this}addEventListener(e,n){if(this._instance&&this._listeners&&this._includesEventType(e)){this._listeners[e]&&this.removeEventListener(e);let r=r=>{let i=[];if(e===t.eventTypes.RESULT){let e=r;if(e.results?.length>0){let t=Array.from(e.results[0],e=>e.transcript);i.push(t[0],t)}}n.apply(this,[r,...i])};this._instance.addEventListener(e,r),this._listeners[e]=r}return this}removeEventListener(e){if(this._instance&&this._listeners){let t=this._listeners[e];this._instance.removeEventListener(e,t),delete this._listeners[e]}return this}cleanup(){return this.stop(),Object.keys(this._listeners).forEach(e=>this.removeEventListener(e)),this._instance=null,this}_includesEventType(e){return Object.values(t.eventTypes).includes(e)}static _resolveSpeechRecognition(){return window.SpeechRecognition??window.webkitSpeechRecognition??window.mozSpeechRecognition??window.msSpeechRecognition}static _resolveSpeechGrammarList(){return window.SpeechGrammarList??window.webkitSpeechGrammarList??window.mozSpeechGrammarList??window.msSpeechGrammarList}};exports.Vocal=t;
|
|
2
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.umd.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`@untemps/user-permissions-utils`)):typeof define==`function`&&define.amd?define([`exports`,`@untemps/user-permissions-utils`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.Vocal={},e.UserPermissionsUtils))})(this,function(e,t){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`}),e.Vocal=class e{static defaultOptions={grammars:null,lang:`en-US`,continuous:!1,interimResults:!1,maxAlternatives:1};static eventTypes={AUDIO_END:`audioend`,AUDIO_START:`audiostart`,END:`end`,ERROR:`error`,NO_MATCH:`nomatch`,RESULT:`result`,SOUND_END:`soundend`,SOUND_START:`soundstart`,SPEECH_END:`speechend`,SPEECH_START:`speechstart`,START:`start`};static get isSupported(){return!!e._resolveSpeechRecognition()&&!!(0,t.isNavigatorPermissionsSupported)()&&!!(0,t.isNavigatorMediaDevicesSupported)()}static set isSupported(e){throw Error(`You cannot set isSupported directly.`)}_instance=null;_listeners=null;_isRecording=!1;constructor(t){let n=e._resolveSpeechRecognition();if(!n)throw new DOMException(`SpeechRecognition not supported`,`NOT_SUPPORTED_ERR`);this._instance=new n,this._listeners={};let{grammars:r,...i}={...e.defaultOptions,...t??{}},a=this._instance;if(Object.assign(a,i),r)a.grammars=r;else{let t=e._resolveSpeechGrammarList();a.grammars=t?new t:null}this._instance.addEventListener(`end`,()=>{this._isRecording=!1})}get instance(){return this._instance}set instance(e){throw Error(`You cannot set instance directly.`)}get isRecording(){return this._isRecording}set isRecording(e){throw Error(`You cannot set isRecording directly.`)}async start(){if(this._instance)try{if(!await(0,t.getUserMediaStream)(`microphone`,{audio:!0}))throw Error(`Unable to retrieve the stream from media device`);this._instance.start(),this._isRecording=!0}catch(e){let t=this._listeners?.error;t&&t(e)}return this}stop(){return this._instance&&(this._instance.stop(),this._isRecording=!1),this}abort(){return this._instance&&(this._instance.abort(),this._isRecording=!1),this}addEventListener(t,n){if(this._instance&&this._listeners&&this._includesEventType(t)){this._listeners[t]&&this.removeEventListener(t);let r=r=>{let i=[];if(t===e.eventTypes.RESULT){let e=r;if(e.results?.length>0){let t=Array.from(e.results[0],e=>e.transcript);i.push(t[0],t)}}n.apply(this,[r,...i])};this._instance.addEventListener(t,r),this._listeners[t]=r}return this}removeEventListener(e){if(this._instance&&this._listeners){let t=this._listeners[e];this._instance.removeEventListener(e,t),delete this._listeners[e]}return this}cleanup(){return this.stop(),Object.keys(this._listeners).forEach(e=>this.removeEventListener(e)),this._instance=null,this}_includesEventType(t){return Object.values(e.eventTypes).includes(t)}static _resolveSpeechRecognition(){return window.SpeechRecognition??window.webkitSpeechRecognition??window.mozSpeechRecognition??window.msSpeechRecognition}static _resolveSpeechGrammarList(){return window.SpeechGrammarList??window.webkitSpeechGrammarList??window.mozSpeechGrammarList??window.msSpeechGrammarList}}});
|
|
2
|
-
//# sourceMappingURL=index.umd.js.map
|