@untemps/vocal 2.0.0-beta.21 → 2.0.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +55 -25
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.es.js +122 -159
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,35 @@
|
|
|
1
|
+
# [2.0.0-beta.22](https://github.com/untemps/vocal/compare/v2.0.0-beta.21...v2.0.0-beta.22) (2026-05-22)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Code Refactoring
|
|
5
|
+
|
|
6
|
+
* Move from class-based to functional API ([#88](https://github.com/untemps/vocal/issues/88)) ([1161ce8](https://github.com/untemps/vocal/commit/1161ce8ce983a7f216b5a11803bbc5ad90a68dc5))
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### BREAKING CHANGES
|
|
10
|
+
|
|
11
|
+
* every public entry point changes shape:
|
|
12
|
+
- `new Vocal(options)` → `createVocal(options)`
|
|
13
|
+
- `Vocal.isSupported` (static getter) → `isSupported()` (function)
|
|
14
|
+
- `Vocal.eventTypes` (static) → `eventTypes` (named export)
|
|
15
|
+
- `vocal.addEventListener(type, cb)` → `vocal.on(type, cb)`
|
|
16
|
+
- `vocal.removeEventListener(type, cb?)` → `vocal.off(type, cb?)`
|
|
17
|
+
- Side-effect methods (`stop`, `abort`, `on`, `off`, `cleanup`) now
|
|
18
|
+
return `void` instead of `this` — chaining is no longer supported.
|
|
19
|
+
- The `Vocal` class is no longer exported; the new `VocalInstance`
|
|
20
|
+
interface describes the object returned by `createVocal()`.
|
|
21
|
+
Migration:
|
|
22
|
+
// before
|
|
23
|
+
import { Vocal } from '@untemps/vocal'
|
|
24
|
+
if (!Vocal.isSupported) throw new Error()
|
|
25
|
+
const vocal = new Vocal({ lang: 'fr-FR' })
|
|
26
|
+
vocal.addEventListener('result', cb)
|
|
27
|
+
// after
|
|
28
|
+
import { createVocal, isSupported } from '@untemps/vocal'
|
|
29
|
+
if (!isSupported()) throw new Error()
|
|
30
|
+
const vocal = createVocal({ lang: 'fr-FR' })
|
|
31
|
+
vocal.on('result', cb)
|
|
32
|
+
|
|
1
33
|
# [2.0.0-beta.21](https://github.com/untemps/vocal/compare/v2.0.0-beta.20...v2.0.0-beta.21) (2026-05-20)
|
|
2
34
|
|
|
3
35
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @untemps/vocal
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Functional wrapper around the SpeechRecognition Web API
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|

|
|
@@ -14,27 +14,22 @@ yarn add @untemps/vocal
|
|
|
14
14
|
|
|
15
15
|
## Basic Usage
|
|
16
16
|
|
|
17
|
-
Import `Vocal` to a file.
|
|
18
|
-
|
|
19
17
|
```javascript
|
|
20
|
-
import {
|
|
18
|
+
import { createVocal, isSupported } from '@untemps/vocal'
|
|
21
19
|
|
|
22
20
|
// Check whether SpeechRecognition, Permissions and MediaDevices interfaces are supported
|
|
23
|
-
if (!
|
|
21
|
+
if (!isSupported()) {
|
|
24
22
|
throw new Error('Vocal is not supported')
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
// Create a Vocal instance (see below for all available option properties)
|
|
28
|
-
const
|
|
29
|
-
lang: 'fr-FR',
|
|
30
|
-
}
|
|
31
|
-
const vocal = new Vocal(options)
|
|
26
|
+
const vocal = createVocal({ lang: 'fr-FR' })
|
|
32
27
|
|
|
33
|
-
// Subscribe to
|
|
34
|
-
vocal.
|
|
35
|
-
vocal.
|
|
36
|
-
vocal.
|
|
37
|
-
vocal.
|
|
28
|
+
// Subscribe to instance events (see below for all available events)
|
|
29
|
+
vocal.on('speechstart', (event) => console.log('Vocal starts recording'))
|
|
30
|
+
vocal.on('speechend', (event) => console.log('Vocal stops recording'))
|
|
31
|
+
vocal.on('result', (event, bestAlternative, alternatives) => console.log('Vocal catches a result:', bestAlternative, alternatives))
|
|
32
|
+
vocal.on('error', (event) => console.error(event.error, event.message))
|
|
38
33
|
|
|
39
34
|
// Start recording — rejects on error
|
|
40
35
|
try {
|
|
@@ -49,7 +44,7 @@ vocal.stop()
|
|
|
49
44
|
// Abort recording entirely
|
|
50
45
|
vocal.abort()
|
|
51
46
|
|
|
52
|
-
// Remove all attached listeners and
|
|
47
|
+
// Remove all attached listeners and release the internal SpeechRecognition instance
|
|
53
48
|
vocal.cleanup()
|
|
54
49
|
```
|
|
55
50
|
|
|
@@ -97,17 +92,33 @@ Please refer to [this section](https://developer.mozilla.org/en-US/docs/Web/API/
|
|
|
97
92
|
| speechstart | Fired when sound recognized by the recognition service as speech has been detected |
|
|
98
93
|
| start | fired when the recognition service has begun listening to incoming audio |
|
|
99
94
|
|
|
100
|
-
|
|
95
|
+
For convenience, `eventTypes` is exported as a constant map so consumers can reference type strings symbolically:
|
|
96
|
+
|
|
97
|
+
```js
|
|
98
|
+
import { eventTypes } from '@untemps/vocal'
|
|
99
|
+
vocal.on(eventTypes.RESULT, handler)
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Top-level exports
|
|
103
|
+
|
|
104
|
+
| Export | Kind | Description |
|
|
105
|
+
| ------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
106
|
+
| `createVocal` | function | Factory that returns a `VocalInstance`. See [Methods](#methods). |
|
|
107
|
+
| `isSupported` | function | Returns `true` if the current environment supports the SpeechRecognition Web API. Call it (it is **not** a getter). |
|
|
108
|
+
| `eventTypes` | const | Map of valid event type strings (e.g. `eventTypes.RESULT === 'result'`). |
|
|
101
109
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
|
105
|
-
|
|
|
110
|
+
## Instance getter
|
|
111
|
+
|
|
112
|
+
| Getter | Type | Description |
|
|
113
|
+
| ----------- | --------- | -------------------------------------------------------------------------------------------------------------------- |
|
|
114
|
+
| isRecording | boolean | Whether recognition is currently active — `true` after `start()`, `false` after `stop()`, `abort()`, or `end` event |
|
|
106
115
|
|
|
107
116
|
## Methods
|
|
108
117
|
|
|
109
118
|
### `start({ signal? })`
|
|
110
119
|
|
|
120
|
+
Starts recognition. Resolves once the engine is active. Rejects if microphone permission cannot be obtained.
|
|
121
|
+
|
|
111
122
|
| Parameter | Type | Default | Description |
|
|
112
123
|
| --------- | ------------- | ----------- | ----------------------------------------------------------------------------- |
|
|
113
124
|
| signal | AbortSignal | `undefined` | Cancels the in-flight microphone permission request when the signal is aborted |
|
|
@@ -122,13 +133,13 @@ controller.abort()
|
|
|
122
133
|
|
|
123
134
|
### `stop()`
|
|
124
135
|
|
|
125
|
-
Stops recognition gracefully, allowing the current audio to be processed before disconnecting. Sets `isRecording` to `false`.
|
|
136
|
+
Stops recognition gracefully, allowing the current audio to be processed before disconnecting. Sets `isRecording` to `false`. In continuous mode, emits the aggregated `result` event just before `end`.
|
|
126
137
|
|
|
127
138
|
### `abort()`
|
|
128
139
|
|
|
129
|
-
Stops recognition immediately without processing pending audio. Sets `isRecording` to `false`.
|
|
140
|
+
Stops recognition immediately without processing pending audio. Sets `isRecording` to `false`. Discards any aggregated transcript without emitting.
|
|
130
141
|
|
|
131
|
-
### `
|
|
142
|
+
### `on(eventType, callback)`
|
|
132
143
|
|
|
133
144
|
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
145
|
|
|
@@ -139,7 +150,7 @@ Registers a callback for the given event type. Multiple callbacks can be registe
|
|
|
139
150
|
|
|
140
151
|
Throws if `eventType` is not a valid `EventType`.
|
|
141
152
|
|
|
142
|
-
### `
|
|
153
|
+
### `off(eventType, callback?)`
|
|
143
154
|
|
|
144
155
|
Removes a listener for the given event type.
|
|
145
156
|
|
|
@@ -152,5 +163,24 @@ Throws if `eventType` is not a valid `EventType`.
|
|
|
152
163
|
|
|
153
164
|
### `cleanup()`
|
|
154
165
|
|
|
155
|
-
Stops recognition, removes all registered listeners, and releases the internal `SpeechRecognition` instance. The `
|
|
166
|
+
Stops recognition, removes all registered listeners, and releases the internal `SpeechRecognition` instance. The returned `VocalInstance` cannot be reused after `cleanup()`.
|
|
167
|
+
|
|
168
|
+
## Migration from the class-based API (v1.x)
|
|
169
|
+
|
|
170
|
+
```js
|
|
171
|
+
// Before
|
|
172
|
+
import { Vocal } from '@untemps/vocal'
|
|
173
|
+
if (!Vocal.isSupported) throw new Error()
|
|
174
|
+
const vocal = new Vocal({ lang: 'fr-FR' })
|
|
175
|
+
vocal.addEventListener('result', cb)
|
|
176
|
+
vocal.removeEventListener('result', cb)
|
|
177
|
+
|
|
178
|
+
// After
|
|
179
|
+
import { createVocal, isSupported } from '@untemps/vocal'
|
|
180
|
+
if (!isSupported()) throw new Error()
|
|
181
|
+
const vocal = createVocal({ lang: 'fr-FR' })
|
|
182
|
+
vocal.on('result', cb)
|
|
183
|
+
vocal.off('result', cb)
|
|
184
|
+
```
|
|
156
185
|
|
|
186
|
+
Side-effect methods (`stop`, `abort`, `on`, `off`, `cleanup`) now return `void` — method chaining is no longer supported. `Vocal.eventTypes` is now exported as the top-level `eventTypes` const.
|
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@untemps/user-permissions-utils`);var t=
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@untemps/user-permissions-utils`);var t={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`},n=1e3,r=new Set([`not-allowed`,`service-not-allowed`,`audio-capture`]),i={grammars:null,lang:`en-US`,continuous:!1,interimResults:!1,maxAlternatives:1},a=()=>{if(!(typeof window>`u`))return window.SpeechRecognition??window.webkitSpeechRecognition??window.mozSpeechRecognition??window.msSpeechRecognition},o=()=>window.SpeechGrammarList??window.webkitSpeechGrammarList??window.mozSpeechGrammarList??window.msSpeechGrammarList,s=e=>e.reduce((e,t)=>(t.confidence??0)>(e.confidence??0)?t:e),c=e=>Object.values(t).includes(e),l=e=>`Unknown event type "${e}". Valid types are: ${Object.values(t).join(`, `)}.`,u=()=>!!a()&&!!(0,e.isNavigatorPermissionsSupported)()&&!!(0,e.isNavigatorMediaDevicesSupported)(),d=u=>{let d=a();if(!d)throw new DOMException(`SpeechRecognition not supported`,`NOT_SUPPORTED_ERR`);let f=new d,p={},m=!1,h=!1,g=0,_=null,v=!1,y=[],b={...i,...u??{}};if(f.lang=b.lang,f.continuous=b.continuous,f.interimResults=b.interimResults,f.maxAlternatives=b.maxAlternatives,b.grammars)f.grammars=b.grammars;else{let e=o();f.grammars=e?new e:null}let x=()=>{_!==null&&(clearTimeout(_),_=null),v=!1},S=()=>!!f&&!h&&f.continuous,C=()=>{_=null;try{f.start(),g=Date.now()}catch{v=!1,m=!1}},w=()=>{let e=y;if(y=[],e.length===0||!p[t.RESULT]?.length)return;let n=e.join(` `).trim(),r=Object.assign([{transcript:n,confidence:1}],{isFinal:!0}),i=Object.assign(new Event(t.RESULT),{resultIndex:0,results:[r]});[...p[t.RESULT]].forEach(({handler:e})=>e(i))},T=[[t.END,e=>{if(S()){let t=Math.max(0,n-(Date.now()-g));v=!0,_=setTimeout(C,t),e.stopImmediatePropagation();return}m=!1}],[t.START,e=>{v&&(e.stopImmediatePropagation(),queueMicrotask(()=>{v=!1}))}],[t.ERROR,e=>{r.has(e.error)&&(h=!0,x(),m=!1)}],[t.RESULT,e=>{let t=e,n=t.results?.[t.resultIndex];n?.isFinal&&y.push(s(Array.from(n)).transcript)}]];T.forEach(([e,t])=>f.addEventListener(e,t));let E=async({signal:t}={})=>{if(f)try{let n=await(0,e.getUserMediaStream)(`microphone`,{audio:!0},{signal:t});if(t?.aborted)return;if(!n)throw Error(`Unable to retrieve the stream from media device`);h=!1,y=[],f.start(),m=!0,g=Date.now()}catch(e){if(e instanceof Error&&e.name===`AbortError`)return;throw e}},D=()=>{f&&(h=!0,x(),w(),f.stop(),m=!1)},O=()=>{f&&(h=!0,x(),f.abort(),m=!1,y=[])},k=(e,n)=>{if(!c(e))throw Error(l(e));if(!f)return;let r=r=>{if(v&&(e===t.END||e===t.START))return;if(e!==t.RESULT){n(r);return}let i=r;if(!(i.results?.length>0)||i.resultIndex>=i.results.length){n(r);return}let a=Array.from(i.results[i.resultIndex]);n(r,s(a).transcript,a.map(e=>e.transcript))};f.addEventListener(e,r),p[e]||(p[e]=[]),p[e].push({callback:n,handler:r})},A=(e,t)=>{if(!c(e))throw Error(l(e));if(!(!f||!p[e]))if(t!==void 0){let n=p[e].findIndex(e=>e.callback===t);n!==-1&&(f.removeEventListener(e,p[e][n].handler),p[e].splice(n,1),p[e].length===0&&delete p[e])}else p[e].forEach(({handler:t})=>f.removeEventListener(e,t)),delete p[e]};return{get isRecording(){return m},start:E,stop:D,abort:O,on:k,off:A,cleanup:()=>{D(),Object.keys(p).forEach(e=>A(e)),T.forEach(([e,t])=>f?.removeEventListener(e,t)),f=null}}};exports.createVocal=d,exports.eventTypes=t,exports.isSupported=u;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type { VocalOptions, EventType, ResultEventHandler, ErrorEventHandler, GenericEventHandler, EventHandlerFor, } from './Vocal';
|
|
1
|
+
export { createVocal, isSupported, eventTypes } from './Vocal';
|
|
2
|
+
export type { VocalOptions, VocalInstance, EventType, ResultEventHandler, ErrorEventHandler, GenericEventHandler, EventHandlerFor, } from './Vocal';
|
package/dist/index.es.js
CHANGED
|
@@ -1,178 +1,141 @@
|
|
|
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 = {
|
|
4
|
+
AUDIO_END: "audioend",
|
|
5
|
+
AUDIO_START: "audiostart",
|
|
6
|
+
END: "end",
|
|
7
|
+
ERROR: "error",
|
|
8
|
+
NO_MATCH: "nomatch",
|
|
9
|
+
RESULT: "result",
|
|
10
|
+
SOUND_END: "soundend",
|
|
11
|
+
SOUND_START: "soundstart",
|
|
12
|
+
SPEECH_END: "speechend",
|
|
13
|
+
SPEECH_START: "speechstart",
|
|
14
|
+
START: "start"
|
|
15
|
+
}, i = 1e3, a = new Set([
|
|
4
16
|
"not-allowed",
|
|
5
17
|
"service-not-allowed",
|
|
6
18
|
"audio-capture"
|
|
7
|
-
]),
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
]), o = {
|
|
20
|
+
grammars: null,
|
|
21
|
+
lang: "en-US",
|
|
22
|
+
continuous: !1,
|
|
23
|
+
interimResults: !1,
|
|
24
|
+
maxAlternatives: 1
|
|
25
|
+
}, s = () => {
|
|
26
|
+
if (!(typeof window > "u")) return window.SpeechRecognition ?? window.webkitSpeechRecognition ?? window.mozSpeechRecognition ?? window.msSpeechRecognition;
|
|
27
|
+
}, c = () => window.SpeechGrammarList ?? window.webkitSpeechGrammarList ?? window.mozSpeechGrammarList ?? window.msSpeechGrammarList, l = (e) => e.reduce((e, t) => (t.confidence ?? 0) > (e.confidence ?? 0) ? t : e), u = (e) => Object.values(r).includes(e), d = (e) => `Unknown event type "${e}". Valid types are: ${Object.values(r).join(", ")}.`, f = () => !!s() && !!n() && !!t(), p = (t) => {
|
|
28
|
+
let n = s();
|
|
29
|
+
if (!n) throw new DOMException("SpeechRecognition not supported", "NOT_SUPPORTED_ERR");
|
|
30
|
+
let f = new n(), p = {}, m = !1, h = !1, g = 0, _ = null, v = !1, y = [], b = {
|
|
31
|
+
...o,
|
|
32
|
+
...t ?? {}
|
|
14
33
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
SPEECH_END: "speechend",
|
|
25
|
-
SPEECH_START: "speechstart",
|
|
26
|
-
START: "start"
|
|
27
|
-
};
|
|
28
|
-
static get isSupported() {
|
|
29
|
-
return !!a._resolveSpeechRecognition() && !!n() && !!t();
|
|
30
|
-
}
|
|
31
|
-
static set isSupported(e) {
|
|
32
|
-
throw Error("You cannot set isSupported directly.");
|
|
33
|
-
}
|
|
34
|
-
_instance = null;
|
|
35
|
-
_listeners = {};
|
|
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
|
-
};
|
|
62
|
-
constructor(e) {
|
|
63
|
-
let t = a._resolveSpeechRecognition();
|
|
64
|
-
if (!t) throw new DOMException("SpeechRecognition not supported", "NOT_SUPPORTED_ERR");
|
|
65
|
-
this._instance = new t();
|
|
66
|
-
let { grammars: n, ...r } = {
|
|
67
|
-
...a.defaultOptions,
|
|
68
|
-
...e ?? {}
|
|
69
|
-
}, i = this._instance;
|
|
70
|
-
if (Object.assign(i, r), n) i.grammars = n;
|
|
71
|
-
else {
|
|
72
|
-
let e = a._resolveSpeechGrammarList();
|
|
73
|
-
i.grammars = e ? new e() : null;
|
|
74
|
-
}
|
|
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);
|
|
76
|
-
}
|
|
77
|
-
get isRecording() {
|
|
78
|
-
return this._isRecording;
|
|
79
|
-
}
|
|
80
|
-
set isRecording(e) {
|
|
81
|
-
throw Error("You cannot set isRecording directly.");
|
|
82
|
-
}
|
|
83
|
-
async start({ signal: t } = {}) {
|
|
84
|
-
if (this._instance) try {
|
|
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();
|
|
87
|
-
} catch (e) {
|
|
88
|
-
if (e instanceof Error && e.name === "AbortError") return this;
|
|
89
|
-
throw e;
|
|
90
|
-
}
|
|
91
|
-
return this;
|
|
92
|
-
}
|
|
93
|
-
stop() {
|
|
94
|
-
return this._instance && (this._explicitStop = !0, this._clearRestartTimeout(), this._emitAggregatedResult(), this._instance.stop(), this._isRecording = !1), this;
|
|
95
|
-
}
|
|
96
|
-
abort() {
|
|
97
|
-
return this._instance && (this._explicitStop = !0, this._clearRestartTimeout(), this._instance.abort(), this._isRecording = !1, this._finalTranscripts = []), this;
|
|
98
|
-
}
|
|
99
|
-
addEventListener(e, t) {
|
|
100
|
-
if (!this._includesEventType(e)) throw Error(this._unknownEventTypeMessage(e));
|
|
101
|
-
if (this._instance) {
|
|
102
|
-
let n = (n) => {
|
|
103
|
-
if (this._isRestarting && (e === a.eventTypes.END || e === a.eventTypes.START)) return;
|
|
104
|
-
let r = [];
|
|
105
|
-
if (e === a.eventTypes.RESULT) {
|
|
106
|
-
let e = n;
|
|
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));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
t.call(this, n, ...r);
|
|
113
|
-
};
|
|
114
|
-
this._instance.addEventListener(e, n), this._listeners[e] || (this._listeners[e] = []), this._listeners[e].push({
|
|
115
|
-
callback: t,
|
|
116
|
-
handler: n
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
return this;
|
|
120
|
-
}
|
|
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];
|
|
128
|
-
return this;
|
|
129
|
-
}
|
|
130
|
-
cleanup() {
|
|
131
|
-
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;
|
|
132
|
-
}
|
|
133
|
-
_restart = () => {
|
|
134
|
-
this._restartTimeoutId = null;
|
|
34
|
+
if (f.lang = b.lang, f.continuous = b.continuous, f.interimResults = b.interimResults, f.maxAlternatives = b.maxAlternatives, b.grammars) f.grammars = b.grammars;
|
|
35
|
+
else {
|
|
36
|
+
let e = c();
|
|
37
|
+
f.grammars = e ? new e() : null;
|
|
38
|
+
}
|
|
39
|
+
let x = () => {
|
|
40
|
+
_ !== null && (clearTimeout(_), _ = null), v = !1;
|
|
41
|
+
}, S = () => !!f && !h && f.continuous, C = () => {
|
|
42
|
+
_ = null;
|
|
135
43
|
try {
|
|
136
|
-
|
|
44
|
+
f.start(), g = Date.now();
|
|
137
45
|
} catch {
|
|
138
|
-
|
|
46
|
+
v = !1, m = !1;
|
|
139
47
|
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
if (this._finalTranscripts = [], e.length === 0) return;
|
|
48
|
+
}, w = () => {
|
|
49
|
+
let e = y;
|
|
50
|
+
if (y = [], e.length === 0 || !p[r.RESULT]?.length) return;
|
|
144
51
|
let t = e.join(" ").trim(), n = Object.assign([{
|
|
145
52
|
transcript: t,
|
|
146
53
|
confidence: 1
|
|
147
|
-
}], { isFinal: !0 }),
|
|
54
|
+
}], { isFinal: !0 }), i = Object.assign(new Event(r.RESULT), {
|
|
148
55
|
resultIndex: 0,
|
|
149
56
|
results: [n]
|
|
150
57
|
});
|
|
151
|
-
[...
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
58
|
+
[...p[r.RESULT]].forEach(({ handler: e }) => e(i));
|
|
59
|
+
}, T = [
|
|
60
|
+
[r.END, (e) => {
|
|
61
|
+
if (S()) {
|
|
62
|
+
let t = Math.max(0, i - (Date.now() - g));
|
|
63
|
+
v = !0, _ = setTimeout(C, t), e.stopImmediatePropagation();
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
m = !1;
|
|
67
|
+
}],
|
|
68
|
+
[r.START, (e) => {
|
|
69
|
+
v && (e.stopImmediatePropagation(), queueMicrotask(() => {
|
|
70
|
+
v = !1;
|
|
71
|
+
}));
|
|
72
|
+
}],
|
|
73
|
+
[r.ERROR, (e) => {
|
|
74
|
+
a.has(e.error) && (h = !0, x(), m = !1);
|
|
75
|
+
}],
|
|
76
|
+
[r.RESULT, (e) => {
|
|
77
|
+
let t = e, n = t.results?.[t.resultIndex];
|
|
78
|
+
n?.isFinal && y.push(l(Array.from(n)).transcript);
|
|
79
|
+
}]
|
|
80
|
+
];
|
|
81
|
+
T.forEach(([e, t]) => f.addEventListener(e, t));
|
|
82
|
+
let E = async ({ signal: t } = {}) => {
|
|
83
|
+
if (f) try {
|
|
84
|
+
let n = await e("microphone", { audio: !0 }, { signal: t });
|
|
85
|
+
if (t?.aborted) return;
|
|
86
|
+
if (!n) throw Error("Unable to retrieve the stream from media device");
|
|
87
|
+
h = !1, y = [], f.start(), m = !0, g = Date.now();
|
|
88
|
+
} catch (e) {
|
|
89
|
+
if (e instanceof Error && e.name === "AbortError") return;
|
|
90
|
+
throw e;
|
|
91
|
+
}
|
|
92
|
+
}, D = () => {
|
|
93
|
+
f && (h = !0, x(), w(), f.stop(), m = !1);
|
|
94
|
+
}, O = () => {
|
|
95
|
+
f && (h = !0, x(), f.abort(), m = !1, y = []);
|
|
96
|
+
}, k = (e, t) => {
|
|
97
|
+
if (!u(e)) throw Error(d(e));
|
|
98
|
+
if (!f) return;
|
|
99
|
+
let n = (n) => {
|
|
100
|
+
if (v && (e === r.END || e === r.START)) return;
|
|
101
|
+
if (e !== r.RESULT) {
|
|
102
|
+
t(n);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
let i = n;
|
|
106
|
+
if (!(i.results?.length > 0) || i.resultIndex >= i.results.length) {
|
|
107
|
+
t(n);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
let a = Array.from(i.results[i.resultIndex]);
|
|
111
|
+
t(n, l(a).transcript, a.map((e) => e.transcript));
|
|
112
|
+
};
|
|
113
|
+
f.addEventListener(e, n), p[e] || (p[e] = []), p[e].push({
|
|
114
|
+
callback: t,
|
|
115
|
+
handler: n
|
|
116
|
+
});
|
|
117
|
+
}, A = (e, t) => {
|
|
118
|
+
if (!u(e)) throw Error(d(e));
|
|
119
|
+
if (!(!f || !p[e])) if (t !== void 0) {
|
|
120
|
+
let n = p[e].findIndex((e) => e.callback === t);
|
|
121
|
+
n !== -1 && (f.removeEventListener(e, p[e][n].handler), p[e].splice(n, 1), p[e].length === 0 && delete p[e]);
|
|
122
|
+
} else p[e].forEach(({ handler: t }) => f.removeEventListener(e, t)), delete p[e];
|
|
123
|
+
};
|
|
124
|
+
return {
|
|
125
|
+
get isRecording() {
|
|
126
|
+
return m;
|
|
127
|
+
},
|
|
128
|
+
start: E,
|
|
129
|
+
stop: D,
|
|
130
|
+
abort: O,
|
|
131
|
+
on: k,
|
|
132
|
+
off: A,
|
|
133
|
+
cleanup: () => {
|
|
134
|
+
D(), Object.keys(p).forEach((e) => A(e)), T.forEach(([e, t]) => f?.removeEventListener(e, t)), f = null;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
174
137
|
};
|
|
175
138
|
//#endregion
|
|
176
|
-
export {
|
|
139
|
+
export { p as createVocal, r as eventTypes, f as isSupported };
|
|
177
140
|
|
|
178
141
|
//# sourceMappingURL=index.es.js.map
|