web-speech-cognitive-services
Version:
Polyfill Web Speech API with Cognitive Services Speech-to-Text service
1 lines • 102 kB
Source Map (JSON)
{"version":3,"sources":["../src/SpeechServices/SpeechToText/createSpeechRecognitionPonyfill.ts","../src/SpeechServices/patchOptions.ts","../src/SpeechServices/resolveFunctionOrReturnValue.ts","../src/SpeechServices/SpeechSDK.ts","../src/SpeechServices/SpeechToText/validation/credentialsSchema.ts","../src/SpeechServices/SpeechToText/validation/enableTelemetrySchema.ts","../src/SpeechServices/SpeechToText/validation/initialSilenceTimeoutSchema.ts","../src/SpeechServices/SpeechToText/validation/looseEventsSchema.ts","../src/SpeechServices/SpeechToText/validation/referenceGrammarsSchema.ts","../src/SpeechServices/SpeechToText/validation/speechRecognitionEndpointIdSchema.ts","../src/SpeechServices/SpeechToText/validation/textNormalizationSchema.ts","../src/SpeechServices/SpeechToText/createSpeechRecognitionPonyfillFromRecognizer.ts","../../../node_modules/p-defer/index.js","../src/Util/createPromiseQueue.js","../src/SpeechServices/SpeechToText/SpeechRecognitionAlternative.ts","../src/SpeechServices/SpeechToText/private/FakeArray.ts","../src/SpeechServices/SpeechToText/SpeechRecognitionResult.ts","../src/SpeechServices/SpeechToText/cognitiveServiceEventResultToWebSpeechRecognitionResult.ts","../src/SpeechServices/SpeechToText/cognitiveServicesAsyncToPromise.ts","../src/SpeechServices/SpeechToText/private/EventListenerMap.ts","../src/SpeechServices/SpeechToText/private/prepareAudioConfig.ts","../src/SpeechServices/SpeechToText/private/averageAmplitude.ts","../src/SpeechServices/SpeechToText/private/serializeRecognitionResult.ts","../src/SpeechServices/SpeechToText/SpeechGrammarList.ts","../src/SpeechServices/SpeechToText/SpeechRecognitionErrorEvent.ts","../src/SpeechServices/SpeechToText/SpeechRecognitionResultList.ts","../src/SpeechServices/SpeechToText/SpeechRecognitionEvent.ts","../src/SpeechServices/TextToSpeech/createSpeechSynthesisPonyfill.js","../src/SpeechServices/TextToSpeech/AudioContextQueue.js","../src/SpeechServices/TextToSpeech/AudioContextConsumer.js","../src/SpeechServices/TextToSpeech/SpeechSynthesisEvent.js","../src/SpeechServices/TextToSpeech/SpeechSynthesisUtterance.js","../src/SpeechServices/TextToSpeech/fetchSpeechData.js","../src/SpeechServices/TextToSpeech/buildSSML.js","../src/SpeechServices/TextToSpeech/isSSML.js","../src/SpeechServices/TextToSpeech/subscribeEvent.js","../src/SpeechServices/TextToSpeech/SpeechSynthesisVoice.js","../src/SpeechServices/TextToSpeech/fetchCustomVoices.js","../src/SpeechServices/TextToSpeech/fetchVoices.js","../src/SpeechServices/TextToSpeech.js","../src/SpeechServices/fetchAuthorizationToken.ts","../src/SpeechServices.ts"],"sourcesContent":["/* eslint class-methods-use-this: \"off\" */\n/* eslint complexity: [\"error\", 70] */\n/* eslint no-await-in-loop: \"off\" */\n/* eslint no-empty-function: \"off\" */\n/* eslint no-magic-numbers: [\"error\", { \"ignore\": [0, 100, 150] }] */\n\nimport { PropertyId } from 'microsoft-cognitiveservices-speech-sdk';\nimport patchOptions, { type PatchOptionsInit } from '../patchOptions';\nimport SpeechSDK from '../SpeechSDK';\nimport createSpeechRecognitionPonyfillFromRecognizer from './createSpeechRecognitionPonyfillFromRecognizer';\n\nconst { OutputFormat, SpeechConfig, SpeechRecognizer } = SpeechSDK;\n\nexport default function createSpeechRecognitionPonyfill(options: PatchOptionsInit) {\n const {\n audioConfig,\n enableTelemetry,\n fetchCredentials,\n initialSilenceTimeout,\n looseEvents,\n referenceGrammars,\n speechRecognitionEndpointId,\n textNormalization\n } = patchOptions(options);\n\n if (!audioConfig && (!window.navigator.mediaDevices || !window.navigator.mediaDevices.getUserMedia)) {\n throw new Error(\n 'web-speech-cognitive-services: This browser does not support Media Capture and Streams API and it will not work with Cognitive Services Speech Services.'\n );\n }\n\n const createRecognizer = async (lang: string) => {\n const credentials = await fetchCredentials();\n let speechConfig;\n\n if (typeof credentials.speechRecognitionHostname !== 'undefined') {\n const host = new URL('wss://localhost:443');\n\n host.hostname = credentials.speechRecognitionHostname;\n\n if (credentials.authorizationToken) {\n speechConfig = SpeechConfig.fromHost(host);\n speechConfig.authorizationToken = credentials.authorizationToken;\n } else {\n speechConfig = SpeechConfig.fromHost(host, credentials.subscriptionKey);\n }\n } else {\n speechConfig =\n typeof credentials.authorizationToken !== 'undefined'\n ? SpeechConfig.fromAuthorizationToken(credentials.authorizationToken, credentials.region)\n : SpeechConfig.fromSubscription(credentials.subscriptionKey, credentials.region);\n }\n\n if (speechRecognitionEndpointId) {\n speechConfig.endpointId = speechRecognitionEndpointId;\n }\n\n speechConfig.outputFormat = OutputFormat.Detailed;\n speechConfig.speechRecognitionLanguage = lang || 'en-US';\n typeof initialSilenceTimeout === 'number' &&\n speechConfig.setProperty(PropertyId.SpeechServiceConnection_InitialSilenceTimeoutMs, '' + initialSilenceTimeout);\n\n return new SpeechRecognizer(speechConfig, audioConfig);\n };\n\n return createSpeechRecognitionPonyfillFromRecognizer({\n createRecognizer,\n enableTelemetry,\n looseEvents,\n referenceGrammars,\n textNormalization\n });\n}\n","import { type AudioConfig as AudioConfigType } from 'microsoft-cognitiveservices-speech-sdk';\nimport { parse } from 'valibot';\n\nimport resolveFunctionOrReturnValue from './resolveFunctionOrReturnValue';\nimport SpeechSDK from './SpeechSDK';\nimport credentialsSchema, { type Credentials } from './SpeechToText/validation/credentialsSchema';\nimport enableTelemetrySchema, { EnableTelemetry } from './SpeechToText/validation/enableTelemetrySchema';\nimport initialSilenceTimeoutSchema, {\n InitialSilenceTimeout\n} from './SpeechToText/validation/initialSilenceTimeoutSchema';\nimport looseEventsSchema, { LooseEvents } from './SpeechToText/validation/looseEventsSchema';\nimport referenceGrammarsSchema, { ReferenceGrammars } from './SpeechToText/validation/referenceGrammarsSchema';\nimport speechRecognitionEndpointIdSchema, {\n SpeechRecognitionEndpointId\n} from './SpeechToText/validation/speechRecognitionEndpointIdSchema';\nimport textNormalizationSchema, { TextNormalization } from './SpeechToText/validation/textNormalizationSchema';\n\nconst { AudioConfig } = SpeechSDK;\n\nlet shouldWarnOnSubscriptionKey = true;\n\ntype PatchOptionsInit = {\n audioConfig?: AudioConfigType | undefined;\n credentials: (() => Credentials | Promise<Credentials>) | Credentials | Promise<Credentials>;\n enableTelemetry?: boolean | undefined;\n initialSilenceTimeout?: number | undefined;\n looseEvent?: boolean | undefined;\n looseEvents?: boolean | undefined;\n referenceGrammars?: readonly string[] | undefined;\n speechRecognitionEndpointId?: string | undefined;\n textNormalization?: 'display' | 'itn' | 'lexical' | 'maskeditn' | undefined;\n};\n\ntype PatchedOptions = Readonly<{\n audioConfig: AudioConfigType;\n enableTelemetry: EnableTelemetry;\n fetchCredentials: () => Promise<Credentials>;\n initialSilenceTimeout: InitialSilenceTimeout;\n looseEvents: LooseEvents;\n referenceGrammars: ReferenceGrammars;\n speechRecognitionEndpointId: SpeechRecognitionEndpointId;\n textNormalization: TextNormalization;\n}>;\n\nexport default function patchOptions(init: PatchOptionsInit): PatchedOptions {\n const {\n audioConfig,\n credentials,\n enableTelemetry,\n initialSilenceTimeout,\n looseEvent,\n referenceGrammars,\n speechRecognitionEndpointId,\n textNormalization\n } = init;\n\n let { looseEvents } = init;\n\n if (typeof looseEvent !== 'undefined') {\n console.warn('web-speech-cognitive-services: The option \"looseEvent\" should be named as \"looseEvents\".');\n\n looseEvents = looseEvent;\n }\n\n return Object.freeze({\n audioConfig: audioConfig || AudioConfig.fromDefaultMicrophoneInput(),\n // We set telemetry to true to honor the default telemetry settings of Speech SDK\n // https://github.com/Microsoft/cognitive-services-speech-sdk-js#data--telemetry\n enableTelemetry: parse(enableTelemetrySchema, enableTelemetry),\n fetchCredentials: async () => {\n const parsedCredentials = parse(credentialsSchema, await resolveFunctionOrReturnValue<Credentials>(credentials));\n\n if (shouldWarnOnSubscriptionKey && parsedCredentials.subscriptionKey) {\n console.warn(\n 'web-speech-cognitive-services: In production environment, subscription key should not be used, authorization token should be used instead.'\n );\n\n shouldWarnOnSubscriptionKey = false;\n }\n\n return parsedCredentials;\n },\n initialSilenceTimeout: parse(initialSilenceTimeoutSchema, initialSilenceTimeout),\n looseEvents: parse(looseEventsSchema, looseEvents),\n referenceGrammars: parse(referenceGrammarsSchema, referenceGrammars),\n speechRecognitionEndpointId: parse(speechRecognitionEndpointIdSchema, speechRecognitionEndpointId),\n textNormalization: parse(textNormalizationSchema, textNormalization)\n });\n}\n\nexport type { Credentials, PatchedOptions, PatchOptionsInit };\n","function isFunction(value: unknown): value is () => unknown {\n return typeof value === 'function';\n}\n\nexport default function resolveFunctionOrReturnValue<T>(\n fnOrValue: (() => Promise<T> | T) | Promise<T> | T\n): Promise<T> | T {\n return isFunction(fnOrValue) ? fnOrValue() : fnOrValue;\n}\n","// We are only importing what we need.\n\nimport {\n AudioConfig,\n OutputFormat,\n ResultReason,\n SpeechConfig,\n SpeechRecognizer\n} from 'microsoft-cognitiveservices-speech-sdk/distrib/lib/microsoft.cognitiveservices.speech.sdk';\n\nexport default {\n AudioConfig,\n OutputFormat,\n ResultReason,\n SpeechConfig,\n SpeechRecognizer\n};\n","import { type InferOutput, intersect, object, optional, pipe, readonly, string, undefined_, union } from 'valibot';\n\nconst credentialsSchema = pipe(\n intersect([\n union(\n [\n object({\n authorizationToken: string(),\n subscriptionKey: optional(undefined_('\"subscriptionKey\" must be unset when \"authorizationToken\" is set.'))\n }),\n object({\n authorizationToken: optional(undefined_('\"authorizationToken\" must be unset when \"subscriptionKey\" is set.')),\n subscriptionKey: string()\n })\n ],\n 'The object must either have either \"authorizationToken\" or \"subscriptionKey\" set, but not both.'\n ),\n union(\n [\n object({\n customVoiceHostname: optional(undefined_('\"customVoiceHostname\" must be unest when \"region\" is set.')),\n region: string(),\n speechRecognitionHostname: optional(\n undefined_('\"speechRecognitionHostname\" must be unest when \"region\" is set.')\n ),\n speechSynthesisHostname: optional(undefined_('\"speechSynthesisHostname\" must be unest when \"region\" is set.'))\n }),\n object({\n customVoiceHostname: optional(union([string(), undefined_()])),\n region: optional(undefined_('\"region\" must be unset when \"*Hostname\" is set.')),\n speechRecognitionHostname: string(),\n speechSynthesisHostname: string()\n })\n ],\n 'The object must either have either \"region\" or \"*Hostname\" set, but not both.'\n )\n ]),\n readonly()\n);\n\nexport default credentialsSchema;\n\nexport type Credentials = InferOutput<typeof credentialsSchema>;\n","import { boolean, type InferOutput, optional } from 'valibot';\n\nconst enableTelemetrySchema = optional(boolean());\n\nexport type EnableTelemetry = InferOutput<typeof enableTelemetrySchema>;\n\nexport default enableTelemetrySchema;\n","import { type InferOutput, maxValue, minValue, number, optional, pipe } from 'valibot';\n\n// 60_000 is an arbitrary value, we can set it to a larger number.\nconst initialSilenceTimeoutSchema = optional(pipe(number(), minValue(1), maxValue(60_000)));\n\nexport type InitialSilenceTimeout = InferOutput<typeof initialSilenceTimeoutSchema>;\n\nexport default initialSilenceTimeoutSchema;\n","import { type InferOutput, boolean, optional } from 'valibot';\n\nconst looseEventsSchema = optional(boolean(), false);\n\nexport type LooseEvents = InferOutput<typeof looseEventsSchema>;\n\nexport default looseEventsSchema;\n","import { array, type InferOutput, optional, pipe, string, transform } from 'valibot';\n\nconst referenceGrammarsSchema = pipe(\n optional(array(string()), []),\n // any(),\n // array(string()),\n // transform<string[], readonly string[]>(value => (Object.isFrozen(value) ? value : Object.freeze([...value])))\n transform<string[], readonly string[]>(value => (Object.isFrozen(value) ? value : Object.freeze([...value])))\n);\n\nexport type ReferenceGrammars = InferOutput<typeof referenceGrammarsSchema>;\n\nexport default referenceGrammarsSchema;\n","import { type InferOutput, optional, string } from 'valibot';\n\nconst speechRecognitionEndpointIdSchema = optional(string());\n\nexport type SpeechRecognitionEndpointId = InferOutput<typeof speechRecognitionEndpointIdSchema>;\n\nexport default speechRecognitionEndpointIdSchema;\n","import { type InferOutput, enum_, optional } from 'valibot';\n\nconst textNormalizationSchema = optional(\n enum_({\n display: 'display',\n itn: 'itn',\n lexical: 'lexical',\n maskeditn: 'maskeditn'\n }),\n 'display'\n);\n\nexport type TextNormalization = InferOutput<typeof textNormalizationSchema>;\n\nexport default textNormalizationSchema;\n","/* eslint class-methods-use-this: \"off\" */\n/* eslint complexity: [\"error\", 70] */\n/* eslint no-await-in-loop: \"off\" */\n/* eslint no-empty-function: \"off\" */\n/* eslint no-magic-numbers: [\"error\", { \"ignore\": [0, 100, 150] }] */\n\nimport {\n type CancellationEventArgs,\n type RecognitionEventArgs,\n type SessionEventArgs,\n type SpeechRecognitionEventArgs,\n type SpeechRecognizer as SpeechRecognizerType\n} from 'microsoft-cognitiveservices-speech-sdk';\nimport { type AudioConfigImpl } from 'microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioConfig';\nimport { boolean, function_, parse, undefined_, union } from 'valibot';\nimport createPromiseQueue from '../../Util/createPromiseQueue';\nimport SpeechSDK from '../SpeechSDK';\nimport cognitiveServiceEventResultToWebSpeechRecognitionResult from './cognitiveServiceEventResultToWebSpeechRecognitionResult';\nimport cognitiveServicesAsyncToPromise from './cognitiveServicesAsyncToPromise';\nimport EventListenerMap from './private/EventListenerMap';\nimport prepareAudioConfig from './private/prepareAudioConfig';\nimport serializeRecognitionResult from './private/serializeRecognitionResult';\nimport SpeechGrammarList from './SpeechGrammarList';\nimport SpeechRecognitionErrorEvent from './SpeechRecognitionErrorEvent';\nimport SpeechRecognitionEvent from './SpeechRecognitionEvent';\nimport { type SpeechRecognitionEventListenerMap } from './SpeechRecognitionEventListenerMap';\nimport type SpeechRecognitionResult from './SpeechRecognitionResult';\nimport SpeechRecognitionResultList from './SpeechRecognitionResultList';\nimport referenceGrammarsSchema from './validation/referenceGrammarsSchema';\nimport textNormalizationSchema from './validation/textNormalizationSchema';\n\n// https://docs.microsoft.com/en-us/javascript/api/microsoft-cognitiveservices-speech-sdk/speechconfig?view=azure-node-latest#outputformat\n// {\n// \"RecognitionStatus\": \"Success\",\n// \"Offset\": 900000,\n// \"Duration\": 49000000,\n// \"NBest\": [\n// {\n// \"Confidence\": 0.738919,\n// \"Lexical\": \"second\",\n// \"ITN\": \"second\",\n// \"MaskedITN\": \"second\",\n// \"Display\": \"Second.\"\n// }\n// ]\n// }\n\n// {\n// \"RecognitionStatus\": \"InitialSilenceTimeout\",\n// \"Offset\": 50000000,\n// \"Duration\": 0\n// }\n\nconst { ResultReason, SpeechRecognizer } = SpeechSDK;\n\ntype CreateSpeechRecognitionPonyfillFromRecognizerInit = {\n createRecognizer: (lang: string) => Promise<SpeechRecognizerType>;\n enableTelemetry: boolean | undefined;\n looseEvents: boolean;\n referenceGrammars?: readonly string[] | undefined;\n textNormalization: 'display' | 'itn' | 'lexical' | 'maskeditn';\n};\n\nconst enableTelemetrySchema = union([boolean(), undefined_()]);\n\nexport default function createSpeechRecognitionPonyfillFromRecognizer({\n createRecognizer,\n enableTelemetry,\n looseEvents,\n referenceGrammars,\n textNormalization\n}: CreateSpeechRecognitionPonyfillFromRecognizerInit) {\n createRecognizer = parse(function_(), createRecognizer) as typeof createRecognizer;\n enableTelemetry = parse(enableTelemetrySchema, enableTelemetry);\n looseEvents = parse(boolean(), looseEvents);\n referenceGrammars = parse(referenceGrammarsSchema, referenceGrammars);\n textNormalization = parse(textNormalizationSchema, textNormalization);\n\n // If enableTelemetry is set to null or non-boolean, we will default to true.\n typeof enableTelemetry !== 'undefined' && SpeechRecognizer.enableTelemetry(enableTelemetry);\n\n class SpeechRecognition extends EventTarget {\n #continuous = false;\n #eventListenerMap: SpeechRecognitionEventListenerMap = new EventListenerMap(this);\n #grammars: SpeechGrammarList = new SpeechGrammarList();\n #interimResults = false;\n #lang =\n typeof window !== 'undefined'\n ? window.document.documentElement.getAttribute('lang') || window.navigator.language\n : 'en-US';\n // eslint-disable-next-line no-magic-numbers\n #maxAlternatives: number = 1;\n\n emitCognitiveServices<T extends { type: string }>(type: string, event: T) {\n this.dispatchEvent(\n new SpeechRecognitionEvent('cognitiveservices', {\n data: {\n ...event,\n type\n }\n })\n );\n }\n\n get continuous(): boolean {\n return this.#continuous;\n }\n\n set continuous(value: boolean) {\n this.#continuous = value;\n }\n\n get grammars(): SpeechGrammarList {\n return this.#grammars;\n }\n\n set grammars(value: SpeechGrammarList) {\n if (value instanceof SpeechGrammarList) {\n this.#grammars = value;\n } else {\n throw new Error(`The provided value is not of type 'SpeechGrammarList'`);\n }\n }\n\n get interimResults() {\n return this.#interimResults;\n }\n\n set interimResults(value) {\n this.#interimResults = value;\n }\n\n get maxAlternatives() {\n return this.#maxAlternatives;\n }\n\n set maxAlternatives(value) {\n this.#maxAlternatives = value;\n }\n\n get lang() {\n return this.#lang;\n }\n\n set lang(value) {\n this.#lang = value;\n }\n\n get onaudioend(): ((event: SpeechRecognitionEvent<'audioend'>) => void) | undefined {\n return this.#eventListenerMap.getProperty('audioend');\n }\n\n set onaudioend(value: ((event: SpeechRecognitionEvent<'audioend'>) => void) | undefined) {\n this.#eventListenerMap.setProperty('audioend', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'audiostart'>) => void) | undefined } */\n get onaudiostart() {\n return this.#eventListenerMap.getProperty('audiostart');\n }\n\n set onaudiostart(\n /** @type { ((event: SpeechRecognitionEvent<'audiostart'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('audiostart', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'cognitiveservices'>) => void) | undefined } */\n get oncognitiveservices() {\n return this.#eventListenerMap.getProperty('cognitiveservices');\n }\n\n set oncognitiveservices(\n /** @type { ((event: SpeechRecognitionEvent<'cognitiveservices'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('cognitiveservices', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'end'>) => void) | undefined } */\n get onend() {\n return this.#eventListenerMap.getProperty('end');\n }\n\n set onend(\n /** @type { ((event: SpeechRecognitionEvent<'end'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('end', value);\n }\n\n /** @type { ((event: SpeechRecognitionErrorEvent) => void) | undefined } */\n get onerror() {\n return this.#eventListenerMap.getProperty('error');\n }\n\n set onerror(\n /** @type { ((event: SpeechRecognitionErrorEvent) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('error', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'result'>) => void) | undefined } */\n get onresult() {\n return this.#eventListenerMap.getProperty('result');\n }\n\n set onresult(\n /** @type { ((event: SpeechRecognitionEvent<'result'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('result', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'soundend'>) => void) | undefined } */\n get onsoundend() {\n return this.#eventListenerMap.getProperty('soundend');\n }\n\n set onsoundend(\n /** @type { ((event: SpeechRecognitionEvent<'soundend'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('soundend', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'soundstart'>) => void) | undefined } */\n get onsoundstart() {\n return this.#eventListenerMap.getProperty('soundstart');\n }\n\n set onsoundstart(\n /** @type { ((event: SpeechRecognitionEvent<'soundstart'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('soundstart', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'speechend'>) => void) | undefined } */\n get onspeechend() {\n return this.#eventListenerMap.getProperty('speechend');\n }\n\n set onspeechend(\n /** @type { ((event: SpeechRecognitionEvent<'speechend'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('speechend', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'speechstart'>) => void) | undefined } */\n get onspeechstart() {\n return this.#eventListenerMap.getProperty('speechstart');\n }\n\n set onspeechstart(\n /** @type { ((event: SpeechRecognitionEvent<'speechstart'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('speechstart', value);\n }\n\n /** @type { ((event: SpeechRecognitionEvent<'start'>) => void) | undefined } */\n get onstart() {\n return this.#eventListenerMap.getProperty('start');\n }\n\n set onstart(\n /** @type { ((event: SpeechRecognitionEvent<'start'>) => void) | undefined } */\n value\n ) {\n this.#eventListenerMap.setProperty('start', value);\n }\n\n abort: (() => void) | undefined;\n stop: (() => void) | undefined;\n\n start() {\n this._startOnce().catch(err => {\n this.dispatchEvent(\n new SpeechRecognitionErrorEvent('error', { error: err, message: err && (err.stack || err.message) })\n );\n });\n }\n\n async _startOnce() {\n // TODO: [P2] Should check if recognition is active, we should not start recognition twice\n const recognizer = await createRecognizer(this.lang);\n\n const { pause, unprepare } = prepareAudioConfig(recognizer['audioConfig']);\n\n try {\n const queue = createPromiseQueue();\n let soundStarted;\n let speechStarted;\n let stopping;\n\n const { detach: detachAudioConfigEvent } = (recognizer['audioConfig'] as AudioConfigImpl).events.attach(\n event => {\n const { name } = event;\n\n if (name === 'AudioSourceReadyEvent') {\n queue.push({ audioSourceReady: {} });\n } else if (name === 'AudioSourceOffEvent') {\n queue.push({ audioSourceOff: {} });\n } else if (name === 'FirstAudibleChunk') {\n queue.push({ firstAudibleChunk: {} });\n }\n }\n );\n\n recognizer.canceled = (_, { errorDetails, offset, reason, sessionId }: CancellationEventArgs) => {\n queue.push({\n canceled: {\n errorDetails,\n offset,\n reason,\n sessionId\n }\n });\n };\n\n recognizer.recognized = (_, { offset, result, sessionId }: SpeechRecognitionEventArgs) => {\n queue.push({\n recognized: {\n offset,\n result: serializeRecognitionResult(result),\n sessionId\n }\n });\n };\n\n recognizer.recognizing = (_, { offset, result, sessionId }: SpeechRecognitionEventArgs) => {\n queue.push({\n recognizing: {\n offset,\n result: serializeRecognitionResult(result),\n sessionId\n }\n });\n };\n\n recognizer.sessionStarted = (_, { sessionId }: SessionEventArgs) => {\n queue.push({ sessionStarted: { sessionId } });\n };\n\n recognizer.sessionStopped = (_, { sessionId }: SessionEventArgs) => {\n // \"sessionStopped\" is never fired, probably because we are using startContinuousRecognitionAsync instead of recognizeOnceAsync.\n queue.push({ sessionStopped: { sessionId } });\n };\n\n recognizer.speechStartDetected = (_, { offset, sessionId }: RecognitionEventArgs) => {\n queue.push({ speechStartDetected: { offset, sessionId } });\n };\n\n recognizer.speechEndDetected = (_, { sessionId }: RecognitionEventArgs) => {\n // \"speechEndDetected\" is never fired, probably because we are using startContinuousRecognitionAsync instead of recognizeOnceAsync.\n // Update: \"speechEndDetected\" is fired for DLSpeech.listenOnceAsync()\n queue.push({ speechEndDetected: { sessionId } });\n };\n\n const { phrases } = this.grammars;\n\n // HACK: We are using the internal of SpeechRecognizer because they did not expose it\n const { dynamicGrammar } = recognizer['privReco'];\n\n referenceGrammars && referenceGrammars.length && dynamicGrammar.addReferenceGrammar([...referenceGrammars]);\n phrases && phrases.length && dynamicGrammar.addPhrase([...phrases]);\n\n await cognitiveServicesAsyncToPromise<void>(recognizer.startContinuousRecognitionAsync, recognizer)();\n\n if (typeof recognizer.stopContinuousRecognitionAsync === 'function') {\n this.abort = () => queue.push({ abort: {} });\n this.stop = () => queue.push({ stop: {} });\n } else {\n this.abort = this.stop = undefined;\n }\n\n let audioStarted;\n let finalEvent: SpeechRecognitionErrorEvent | SpeechRecognitionEvent<'result'> | undefined = undefined;\n let finalizedResults: readonly SpeechRecognitionResult[] = [];\n\n for (let loop = 0; !stopping || audioStarted; loop++) {\n const event = await queue.shift();\n const {\n abort,\n audioSourceOff,\n audioSourceReady,\n canceled,\n firstAudibleChunk,\n recognized,\n recognizing,\n stop\n } = event;\n\n // We are emitting event \"cognitiveservices\" for debugging purpose.\n Object.keys(event).forEach(name => this.emitCognitiveServices(name, event[name]));\n\n const errorMessage = canceled && canceled.errorDetails;\n\n if (/Permission\\sdenied/u.test(errorMessage || '')) {\n // If microphone is not allowed, we should not emit \"start\" event.\n\n finalEvent = new SpeechRecognitionErrorEvent('error', { error: 'not-allowed' });\n\n break;\n }\n\n if (!loop) {\n this.dispatchEvent(new SpeechRecognitionEvent('start'));\n }\n\n if (errorMessage) {\n if (/1006/u.test(errorMessage)) {\n if (!audioStarted) {\n this.dispatchEvent(new SpeechRecognitionEvent('audiostart'));\n this.dispatchEvent(new SpeechRecognitionEvent('audioend'));\n }\n\n finalEvent = new SpeechRecognitionErrorEvent('error', { error: 'network' });\n } else {\n finalEvent = new SpeechRecognitionErrorEvent('error', { error: 'unknown' });\n }\n\n break;\n } else if (abort || stop) {\n if (abort) {\n finalEvent = new SpeechRecognitionErrorEvent('error', { error: 'aborted' });\n\n // If we are aborting, we will ignore lingering recognizing/recognized events. But if we are stopping, we need them.\n stopping = 'abort';\n } else {\n // When we pause, we will send { isEnd: true }, Speech Services will send us \"recognized\" event.\n pause();\n stopping = 'stop';\n }\n\n // Abort should not be dispatched without support of \"stopContinuousRecognitionAsync\".\n // But for defensive purpose, we make sure \"stopContinuousRecognitionAsync\" is available before we can call.\n if (abort && recognizer.stopContinuousRecognitionAsync) {\n await cognitiveServicesAsyncToPromise<void>(recognizer.stopContinuousRecognitionAsync, recognizer)();\n }\n } else if (audioSourceReady) {\n this.dispatchEvent(new SpeechRecognitionEvent('audiostart'));\n\n audioStarted = true;\n } else if (firstAudibleChunk) {\n this.dispatchEvent(new SpeechRecognitionEvent('soundstart'));\n\n soundStarted = true;\n } else if (audioSourceOff) {\n // Looks like we don't need this line and all the tests are still working.\n // Guessing probably stopping is already truthy.\n // stopping = true;\n\n speechStarted && this.dispatchEvent(new SpeechRecognitionEvent('speechend'));\n soundStarted && this.dispatchEvent(new SpeechRecognitionEvent('soundend'));\n audioStarted && this.dispatchEvent(new SpeechRecognitionEvent('audioend'));\n\n audioStarted = soundStarted = speechStarted = false;\n\n break;\n } else if (stopping !== 'abort') {\n if (recognized && recognized.result && recognized.result.reason === ResultReason.NoMatch) {\n // Quirks: 2024-11-19 with Speech SDK 1.41.0\n // When microphone is muted, `reason` is `NoMatch` (0) in both interactive mode and continuous mode.\n // After receiving this \"recognized but no match\" event, both modes will continue to recognize speech with \"speechStartDetected\" and \"recognizing\" events.\n // That means, we need to end this manually in interactive mode, and continuous-but-stopping mode.\n if (!this.continuous || stopping === 'stop') {\n // Empty result will turn into \"no-speech\" later in the code.\n finalEvent = new SpeechRecognitionEvent('result', {\n results: new SpeechRecognitionResultList(finalizedResults)\n });\n\n // Quirks: 2024-11-19 with Speech SDK 1.14.0\n // Speech SDK did not stop after NoMatch even in interactive mode.\n recognizer.stopContinuousRecognitionAsync &&\n (await cognitiveServicesAsyncToPromise<void>(\n recognizer.stopContinuousRecognitionAsync,\n recognizer\n )());\n\n // Quirks: 2024-11-19 with Speech SDK 1.14.0\n // After calling stopContinuousRecognitionAsync, no \"audioSourceOff\" is fired.\n\n break;\n }\n } else if (recognized || recognizing) {\n if (!audioStarted) {\n // Unconfirmed prevention of quirks\n this.dispatchEvent(new SpeechRecognitionEvent('audiostart'));\n\n audioStarted = true;\n }\n\n if (!soundStarted) {\n this.dispatchEvent(new SpeechRecognitionEvent('soundstart'));\n\n soundStarted = true;\n }\n\n if (!speechStarted) {\n this.dispatchEvent(new SpeechRecognitionEvent('speechstart'));\n\n speechStarted = true;\n }\n\n if (recognized) {\n const result = cognitiveServiceEventResultToWebSpeechRecognitionResult(recognized.result, {\n maxAlternatives: this.maxAlternatives,\n textNormalization\n });\n\n const recognizable = !!result[0]?.transcript;\n\n if (recognizable) {\n finalizedResults = [...finalizedResults, result];\n\n this.continuous &&\n this.dispatchEvent(\n new SpeechRecognitionEvent('result', {\n results: new SpeechRecognitionResultList(finalizedResults)\n })\n );\n }\n\n // If it is continuous, we just sent the finalized results. So we don't need to send it again after \"audioend\" event.\n if (this.continuous && recognizable) {\n finalEvent = undefined;\n } else {\n finalEvent = new SpeechRecognitionEvent('result', {\n results: new SpeechRecognitionResultList(finalizedResults)\n });\n }\n\n // If it is interactive, stop after first recognition.\n // If it is continuous and it is stopping, stop it too.\n if ((!this.continuous || stopping === 'stop') && recognizer.stopContinuousRecognitionAsync) {\n await cognitiveServicesAsyncToPromise<void>(recognizer.stopContinuousRecognitionAsync, recognizer)();\n }\n\n // If event order can be loosened, we can send the recognized event as soon as we receive it.\n // 1. If it is not recognizable (no-speech), we should send an \"error\" event just before \"end\" event. We will not loosen \"error\" events.\n if (looseEvents && finalEvent && recognizable) {\n this.dispatchEvent(finalEvent);\n finalEvent = undefined;\n }\n } else if (recognizing) {\n this.interimResults &&\n this.dispatchEvent(\n new SpeechRecognitionEvent('result', {\n results: new SpeechRecognitionResultList([\n ...finalizedResults,\n cognitiveServiceEventResultToWebSpeechRecognitionResult(recognizing.result, {\n maxAlternatives: this.maxAlternatives,\n textNormalization\n })\n ])\n })\n );\n }\n }\n }\n }\n\n if (speechStarted) {\n this.dispatchEvent(new SpeechRecognitionEvent('speechend'));\n }\n\n if (soundStarted) {\n this.dispatchEvent(new SpeechRecognitionEvent('soundend'));\n }\n\n if (audioStarted) {\n this.dispatchEvent(new SpeechRecognitionEvent('audioend'));\n }\n\n if (finalEvent) {\n if (finalEvent.type === 'result' && !finalEvent.results.length) {\n finalEvent = new SpeechRecognitionErrorEvent('error', { error: 'no-speech' });\n }\n\n this.dispatchEvent(finalEvent);\n }\n\n // Even though there is no \"start\" event emitted, we will still emit \"end\" event\n // This is mainly for \"microphone blocked\" story.\n this.dispatchEvent(new SpeechRecognitionEvent('end'));\n\n detachAudioConfigEvent();\n } catch (err) {\n // Logging out the erorr because Speech SDK would fail silently.\n console.error(err);\n\n throw err;\n } finally {\n unprepare();\n recognizer['dispose'](false);\n }\n }\n }\n\n return {\n SpeechGrammarList,\n SpeechRecognition,\n SpeechRecognitionEvent\n };\n}\n","export default function pDefer() {\n\tconst deferred = {};\n\n\tdeferred.promise = new Promise((resolve, reject) => {\n\t\tdeferred.resolve = resolve;\n\t\tdeferred.reject = reject;\n\t});\n\n\treturn deferred;\n}\n","import createDeferred from 'p-defer';\n\nexport default function () {\n let shiftDeferred;\n const queue = [];\n\n const push = value => {\n if (shiftDeferred) {\n const { resolve } = shiftDeferred;\n\n shiftDeferred = null;\n resolve(value);\n } else {\n queue.push(value);\n }\n };\n\n const shift = () => {\n if (queue.length) {\n return Promise.resolve(queue.shift());\n }\n\n return (shiftDeferred || (shiftDeferred = createDeferred())).promise;\n };\n\n return {\n push,\n shift\n };\n}\n","export type SpeechRecognitionAlternativeInit = {\n confidence: number;\n transcript: string;\n};\n\nexport default class SpeechRecognitionAlternative {\n constructor({ confidence, transcript }: SpeechRecognitionAlternativeInit) {\n this.#confidence = confidence;\n this.#transcript = transcript;\n }\n\n #confidence: number;\n #transcript: string;\n\n get confidence() {\n return this.#confidence;\n }\n\n get transcript() {\n return this.#transcript;\n }\n}\n","interface FakeArrayInterface<T> {\n [index: number]: T | undefined;\n get length(): number;\n}\n\nexport default class FakeArray<T> implements FakeArrayInterface<T> {\n constructor(array: readonly T[]) {\n if (!array) {\n throw new Error('array must be set.');\n }\n\n this.#array = array;\n\n for (const key in array) {\n Object.defineProperty(this, key, {\n enumerable: true,\n get() {\n return array[key];\n }\n });\n }\n }\n\n #array: readonly T[];\n [index: number]: T | undefined;\n [Symbol.iterator]() {\n return this.#array[Symbol.iterator]();\n }\n\n get length(): number {\n return this.#array.length;\n }\n}\n","import FakeArray from './private/FakeArray';\n\nexport type SpeechRecognitionResultInit = {\n isFinal: boolean;\n results: readonly SpeechRecognitionAlternative[];\n};\n\nexport default class SpeechRecognitionResult extends FakeArray<SpeechRecognitionAlternative> {\n constructor(init: SpeechRecognitionResultInit) {\n super(init.results);\n\n this.#isFinal = init.isFinal;\n }\n\n #isFinal: boolean;\n\n get isFinal(): boolean {\n return this.#isFinal;\n }\n}\n","import SpeechSDK from '../SpeechSDK';\n\nimport SpeechRecognitionAlternative from './SpeechRecognitionAlternative';\nimport SpeechRecognitionResult from './SpeechRecognitionResult';\nimport type { SerializedRecognitionResult } from './private/serializeRecognitionResult';\n\nconst {\n ResultReason: { RecognizingSpeech, RecognizedSpeech }\n} = SpeechSDK;\n\nexport default function (\n result: SerializedRecognitionResult,\n init?:\n | {\n maxAlternatives: number;\n textNormalization: 'display' | 'itn' | 'lexical' | 'maskeditn';\n }\n | undefined\n): SpeechRecognitionResult {\n const { maxAlternatives = Infinity, textNormalization = 'display' } = init || {};\n const json: {\n NBest: readonly {\n Confidence: number;\n Display: string;\n ITN: string;\n Lexical: string;\n MaskedITN: string;\n }[];\n } = typeof result.json === 'string' ? JSON.parse(result.json) : result.json;\n\n if (result.reason === RecognizingSpeech || (result.reason === RecognizedSpeech && !json.NBest)) {\n return new SpeechRecognitionResult({\n isFinal: result.reason === RecognizedSpeech,\n results: [\n new SpeechRecognitionAlternative({\n confidence: 0.5,\n transcript: result.text\n })\n ]\n });\n } else if (result.reason === RecognizedSpeech) {\n return new SpeechRecognitionResult({\n isFinal: true,\n results: (json.NBest || []).slice(0, maxAlternatives).map(\n ({ Confidence: confidence, Display: display, ITN: itn, Lexical: lexical, MaskedITN: maskedITN }) =>\n new SpeechRecognitionAlternative({\n confidence,\n transcript:\n textNormalization === 'itn'\n ? itn\n : textNormalization === 'lexical'\n ? lexical\n : textNormalization === 'maskeditn'\n ? maskedITN\n : display\n })\n )\n });\n }\n\n return new SpeechRecognitionResult({ isFinal: false, results: [] });\n}\n","/* eslint-disable @typescript-eslint/no-explicit-any */\nexport default function cognitiveServicesAsyncToPromise<\n R,\n T extends (resolve: (returnValue: R) => void, reject: (error: unknown) => void) => void = (\n resolve: (returnValue: R) => void,\n reject: (error: unknown) => void\n ) => void\n>(fn: T, context?: undefined | unknown): () => Promise<R>;\n\nexport default function cognitiveServicesAsyncToPromise<\n R,\n P0 = any,\n T extends (arg0: P0, resolve: (returnValue: R) => void, reject: (error: unknown) => void) => void = (\n arg0: P0,\n resolve: (returnValue: R) => void,\n reject: (error: unknown) => void\n ) => void\n>(fn: T, context?: undefined | unknown): (arg0: P0) => Promise<R>;\n\nexport default function cognitiveServicesAsyncToPromise<\n R,\n P0 = any,\n P1 = any,\n T extends (arg0: P0, arg1: P1, resolve: (returnValue: R) => void, reject: (error: unknown) => void) => void = (\n arg0: P0,\n arg1: P1,\n resolve: (returnValue: R) => void,\n reject: (error: unknown) => void\n ) => void\n>(fn: T, context?: undefined | unknown): (arg0: P0, arg1: P1) => Promise<R>;\n\nexport default function cognitiveServicesAsyncToPromise<\n R,\n P0 = any,\n P1 = any,\n P2 = any,\n T extends (\n arg0: P0,\n arg1: P1,\n arg2: P2,\n resolve: (returnValue: R) => void,\n reject: (error: unknown) => void\n ) => void = (\n arg0: P0,\n arg1: P1,\n arg2: P2,\n resolve: (returnValue: R) => void,\n reject: (error: unknown) => void\n ) => void\n>(fn: T, context?: undefined | unknown): (arg0: P0, arg1: P1, arg2: P2) => Promise<R>;\n\nexport default function cognitiveServicesAsyncToPromise<\n R,\n P0 = any,\n P1 = any,\n P2 = any,\n P3 = any,\n T extends (\n arg0: P0,\n arg1: P1,\n arg2: P2,\n arg3: P3,\n resolve: (returnValue: R) => void,\n reject: (error: unknown) => void\n ) => void = (\n arg0: P0,\n arg1: P1,\n arg2: P2,\n arg3: P3,\n resolve: (returnValue: R) => void,\n reject: (error: unknown) => void\n ) => void\n>(fn: T, context?: undefined | unknown): (arg0: P0, arg1: P1, arg2: P2, arg3: P3) => Promise<R>;\n\nexport default function cognitiveServicesAsyncToPromise<\n R,\n T extends (...args: any[]) => void = (...args: any[]) => void\n>(fn: T, context: undefined | unknown = undefined): (...args: Parameters<T>) => Promise<R> {\n return (...args: Parameters<T>) =>\n // eslint-disable-next-line prefer-spread\n new Promise<R>((resolve, reject) => fn.apply(context, [...args, resolve, reject] as unknown as Parameters<T>));\n}\n","type EventListener<T> = (event: T) => void;\n\nexport default class EventListenerMap<T extends string, EventMap extends { [Name in T]: unknown }> {\n constructor(eventTarget: EventTarget) {\n this.#eventTarget = eventTarget;\n this.#propertyMap = {};\n }\n\n #eventTarget: EventTarget;\n #propertyMap: { [Name in keyof EventMap]?: EventListener<EventMap[Name]> | undefined };\n\n getProperty<U extends T>(name: U): ((event: EventMap[U]) => void) | undefined {\n return this.#propertyMap[name];\n }\n\n setProperty<U extends T>(name: U, value: ((event: EventMap[U]) => void) | undefined) {\n const existing = this.#propertyMap[name];\n\n existing && this.#eventTarget.removeEventListener(name, existing as EventListener<Event>);\n\n if (value) {\n this.#eventTarget.addEventListener(name, value as EventListener<Event>);\n }\n\n this.#propertyMap[name] = value;\n }\n}\n","import { AudioSourceEvent } from 'microsoft-cognitiveservices-speech-sdk/distrib/lib/src/common/AudioSourceEvents';\nimport {\n type AudioConfig,\n type AudioConfigImpl\n} from 'microsoft-cognitiveservices-speech-sdk/distrib/lib/src/sdk/Audio/AudioConfig';\nimport averageAmplitude from './averageAmplitude';\n\nexport default function prepareAudioConfig(audioConfig: AudioConfig) {\n // Speech SDK also force cast AudioConfig to AudioConfigImpl and pass it to ServiceRecognizerBase to use attach() and other methods.\n // https://github.com/microsoft/cognitive-services-speech-sdk-js/blob/a6e9d2a202534565ccc97650861a6b296de48ecf/src/sdk/SpeechRecognizer.ts#L291C27-L291C43\n const audioConfigImpl = audioConfig as AudioConfigImpl;\n const originalAttach = audioConfigImpl.attach;\n const boundOriginalAttach = audioConfigImpl.attach.bind(audioConfigImpl);\n let firstChunk = false;\n let muted = false;\n\n // We modify \"attach\" function and detect when audible chunk is read.\n // We will only modify \"attach\" function once.\n audioConfigImpl.attach = async () => {\n const reader = await boundOriginalAttach('');\n\n return {\n ...reader,\n read: async () => {\n const chunk = await reader.read();\n\n // The magic number 150 is measured by:\n // 1. Set microphone volume to 0\n // 2. Observe the amplitude (100-110) for the first few chunks\n // (There is a short static caught when turning on the microphone)\n // 3. Set the number a bit higher than the observation\n if (!firstChunk && averageAmplitude(chunk.buffer) > 150) {\n audioConfigImpl.events.onEvent(new AudioSourceEvent('FirstAudibleChunk', ''));\n firstChunk = true;\n }\n\n if (muted) {\n return { buffer: new ArrayBuffer(0), isEnd: true, timeReceived: Date.now() };\n }\n\n return chunk;\n }\n };\n };\n\n return {\n audioConfig,\n pause: () => {\n muted = true;\n },\n unprepare: () => {\n audioConfigImpl.attach = originalAttach;\n }\n };\n}\n","export default function averageAmplitude(arrayBuffer: ArrayBuffer): number {\n const array = Array.from(new Int16Array(arrayBuffer));\n\n return array.reduce((averageAmplitude, amplitude) => averageAmplitude + Math.abs(amplitude), 0) / array.length;\n}\n","import { type SpeechRecognitionResult } from 'microsoft-cognitiveservices-speech-sdk';\n\nexport type SerializedRecognitionResult = Readonly<{\n duration: number;\n errorDetails: string;\n json: unknown;\n offset: number;\n properties: unknown;\n reason: number;\n resultId: string;\n text: string;\n}>;\n\nexport default function serializeRecognitionResult({\n duration,\n errorDetails,\n json,\n offset,\n properties,\n reason,\n resultId,\n text\n}: SpeechRecognitionResult): SerializedRecognitionResult {\n return Object.freeze({\n duration,\n errorDetails,\n json: json && JSON.parse(json),\n offset,\n properties,\n reason,\n resultId,\n text\n });\n}\n","interface W3CSpeechGrammar {\n src: string;\n weight: number;\n}\n\ninterface W3CSpeechGrammarList {\n readonly length: number;\n addFromString(string: string, weight?: number): void;\n addFromURI(src: string, weight?: number): void;\n item(index: number): W3CSpeechGrammar;\n [index: number]: W3CSpeechGrammar;\n}\n\n/* eslint class-methods-use-this: \"off\" */\n\nexport default class SpeechGrammarList implements W3CSpeechGrammarList {\n constructor() {\n this.#phrases = [];\n }\n\n addFromString() {\n throw new Error('JSGF is not supported');\n }\n\n addFromURI() {\n throw new Error('JSGF is not supported');\n }\n\n item(): W3CSpeechGrammar {\n throw new Error('JSGF is not supported');\n }\n\n get length(): number {\n throw new Error('JSGF is not supported');\n }\n\n [index: number]: { src: string; weight: number };\n\n #phrases: readonly string[];\n\n get phrases(): readonly string[] {\n return this.#phrases;\n }\n\n set phrases(value: readonly string[]) {\n if (Array.isArray(value)) {\n this.#phrases = Object.freeze([...value]);\n } else if (typeof value === 'string') {\n this.#phrases = Object.freeze([value]);\n } else {\n throw new Error(`The provided value is not an array or of type 'string'`);\n }\n }\n}\n","export type SpeechRecognitionErrorType =\n | 'aborted'\n | 'audio-capture'\n | 'bad-grammar'\n | 'language-not-supported'\n | 'network'\n | 'no-speech'\n | 'not-allowed'\n | 'service-not-allowed'\n | 'unknown';\n\nexport type SpeechRecognitionErrorEventInit = {\n error: SpeechRecognitionErrorType;\n message?: string | undefined;\n};\n\nexport default class SpeechRecognitionErrorEvent extends Event {\n constructor(type: 'error', { error, message }: SpeechRecognitionErrorEventInit) {\n super(type);\n\n this.#error = error;\n this.#message = message;\n }\n\n #error: SpeechRecognitionErrorType;\n #message: string | undefined;\n\n get error(): SpeechRecognitionErrorType {\n return this.#error;\n }\n\n get message(): strin