@speechly/browser-client
Advanced tools
Comparing version 2.1.0-beta.4 to 2.1.0-beta.5
@@ -1,4 +0,4 @@ | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Speechly={})}(this,(function(t){"use strict";const e=new Error("Current device does not support microphone API"),i=new Error("AppId changed without project login"),n=16e3;class s{constructor(t,e){this.isFinalized=!1,this.words=[],this.entities=new Map,this.intent={intent:"",isFinal:!1},this.contextId=t,this.id=e}toSegment(){let t=0;const e=new Array(this.entities.size);return this.entities.forEach((i=>{e[t]=i,t++})),{id:this.id,contextId:this.contextId,isFinal:this.isFinalized,words:this.words,entities:e,intent:this.intent}}toString(){const t=this.toSegment(),e=t.words.filter((t=>t.value)).map((t=>({value:t.value,index:t.index}))),i=Object.assign(Object.assign({},t),{words:e});return JSON.stringify(i,null,2)}updateTranscript(t){return t.forEach((t=>{this.isFinalized&&!t.isFinal||(this.words[t.index]=t)})),this}updateEntities(t){return t.forEach((t=>{this.isFinalized&&!t.isFinal||this.entities.set(function(t){return`${t.startPosition.toString()}:${t.endPosition.toString()}`}(t),t)})),this}updateIntent(t){return this.isFinalized&&!t.isFinal||(this.intent=t),this}finalize(){return this.entities.forEach(((t,e)=>{t.isFinal||this.entities.delete(e)})),this.words=this.words.filter((t=>t.isFinal)),this.intent.isFinal||(this.intent.intent="",this.intent.isFinal=!0),this.isFinalized=!0,this}}function o(t,e,i,n){return new(i||(i=Promise))((function(s,o){function a(t){try{l(n.next(t))}catch(t){o(t)}}function d(t){try{l(n.throw(t))}catch(t){o(t)}}function l(t){var e;t.done?s(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(a,d)}l((n=n.apply(t,e||[])).next())}))}const a=new Error("Microphone is not initialized"),d=new Error("Microphone is already initialized"),l=new Error("Microphone consent is not given");var c,r,h;t.WebsocketResponseType=void 0,(c=t.WebsocketResponseType||(t.WebsocketResponseType={})).Started="started",c.Stopped="stopped",c.SegmentEnd="segment_end",c.Transcript="transcript",c.Entity="entity",c.Intent="intent",c.TentativeTranscript="tentative_transcript",c.TentativeEntities="tentative_entities",c.TentativeIntent="tentative_intent",t.WorkerSignal=void 0,(r=t.WorkerSignal||(t.WorkerSignal={})).Opened="WEBSOCKET_OPEN",r.Closed="WEBSOCKET_CLOSED",r.AudioProcessorReady="SOURCE_SAMPLE_RATE_SET_SUCCESS",r.VadSignalHigh="VadSignalHigh",r.VadSignalLow="VadSignalLow",t.ControllerSignal=void 0,(h=t.ControllerSignal||(t.ControllerSignal={})).connect="connect",h.initAudioProcessor="initAudioProcessor",h.adjustAudioProcessor="adjustAudioProcessor",h.SET_SHARED_ARRAY_BUFFERS="SET_SHARED_ARRAY_BUFFERS",h.CLOSE="CLOSE",h.START_CONTEXT="START_CONTEXT",h.SWITCH_CONTEXT="SWITCH_CONTEXT",h.STOP_CONTEXT="STOP_CONTEXT",h.AUDIO="AUDIO",h.startStream="startStream",h.stopStream="stopStream",h.setContextOptions="setContextOptions";const u=new Error("Current device does not support storage API"),p=new Error("Requested key was not present in storage"),b={enabled:!1,controlListening:!0,signalToNoiseDb:3,noiseGateDb:-24,noiseLearnHalftimeMillis:400,signalSearchFrames:5,signalActivation:.7,signalRelease:.2,signalSustainMillis:3e3};var m;t.DecoderState=void 0,(m=t.DecoderState||(t.DecoderState={}))[m.Failed=0]="Failed",m[m.Disconnected=1]="Disconnected",m[m.Connected=2]="Connected",m[m.Active=3]="Active";class Z{constructor(){this.stateChangeCbs=[],this.transcriptCbs=[],this.entityCbs=[],this.intentCbs=[],this.segmentChangeCbs=[],this.tentativeTranscriptCbs=[],this.tentativeEntityCbs=[],this.tentativeIntentCbs=[],this.contextStartedCbs=[],this.contextStoppedCbs=[],this.onVadStateChange=[]}}function G(t){var e;return null!==(e=y.get(t))&&void 0!==e?e:"unknown"}const y=new Map([[t.DecoderState.Failed,"Failed"],[t.DecoderState.Disconnected,"Disconnected"],[t.DecoderState.Connected,"Connected"],[t.DecoderState.Active,"Active"]]);var X,v=new Uint8Array(16);function R(){if(!X&&!(X="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return X(v)}var V=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function W(t){return"string"==typeof t&&V.test(t)}for(var S=[],C=0;C<256;++C)S.push((C+256).toString(16).substr(1));function g(t,e,i){var n=(t=t||{}).random||(t.rng||R)();if(n[6]=15&n[6]|64,n[8]=63&n[8]|128,e){i=i||0;for(var s=0;s<16;++s)e[i+s]=n[s];return e}return function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=(S[t[e+0]]+S[t[e+1]]+S[t[e+2]]+S[t[e+3]]+"-"+S[t[e+4]]+S[t[e+5]]+"-"+S[t[e+6]]+S[t[e+7]]+"-"+S[t[e+8]]+S[t[e+9]]+"-"+S[t[e+10]]+S[t[e+11]]+S[t[e+12]]+S[t[e+13]]+S[t[e+14]]+S[t[e+15]]).toLowerCase();if(!W(i))throw TypeError("Stringified UUID is invalid");return i}(n)}var z="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},Y={exports:{}}; | ||
!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).Speechly={})}(this,(function(t){"use strict";const e=new Error("Current device does not support microphone API"),i=new Error("AppId changed without project login"),n=16e3;class s{constructor(t,e){this.isFinalized=!1,this.words=[],this.entities=new Map,this.intent={intent:"",isFinal:!1},this.contextId=t,this.id=e}toSegment(){let t=0;const e=new Array(this.entities.size);return this.entities.forEach((i=>{e[t]=i,t++})),{id:this.id,contextId:this.contextId,isFinal:this.isFinalized,words:this.words,entities:e,intent:this.intent}}toString(){const t=this.toSegment(),e=t.words.filter((t=>t.value)).map((t=>({value:t.value,index:t.index}))),i=Object.assign(Object.assign({},t),{words:e});return JSON.stringify(i,null,2)}updateTranscript(t){return t.forEach((t=>{this.isFinalized&&!t.isFinal||(this.words[t.index]=t)})),this}updateEntities(t){return t.forEach((t=>{this.isFinalized&&!t.isFinal||this.entities.set(function(t){return`${t.startPosition.toString()}:${t.endPosition.toString()}`}(t),t)})),this}updateIntent(t){return this.isFinalized&&!t.isFinal||(this.intent=t),this}finalize(){return this.entities.forEach(((t,e)=>{t.isFinal||this.entities.delete(e)})),this.words=this.words.filter((t=>t.isFinal)),this.intent.isFinal||(this.intent.intent="",this.intent.isFinal=!0),this.isFinalized=!0,this}}function o(t,e,i,n){return new(i||(i=Promise))((function(s,o){function a(t){try{l(n.next(t))}catch(t){o(t)}}function d(t){try{l(n.throw(t))}catch(t){o(t)}}function l(t){var e;t.done?s(t.value):(e=t.value,e instanceof i?e:new i((function(t){t(e)}))).then(a,d)}l((n=n.apply(t,e||[])).next())}))}const a=new Error("Microphone is not initialized"),d=new Error("Microphone is already initialized"),l=new Error("Microphone consent is not given");var c,r,h;t.WebsocketResponseType=void 0,(c=t.WebsocketResponseType||(t.WebsocketResponseType={})).Started="started",c.Stopped="stopped",c.SegmentEnd="segment_end",c.Transcript="transcript",c.Entity="entity",c.Intent="intent",c.TentativeTranscript="tentative_transcript",c.TentativeEntities="tentative_entities",c.TentativeIntent="tentative_intent",t.WorkerSignal=void 0,(r=t.WorkerSignal||(t.WorkerSignal={})).Opened="WEBSOCKET_OPEN",r.Closed="WEBSOCKET_CLOSED",r.AudioProcessorReady="SOURCE_SAMPLE_RATE_SET_SUCCESS",r.VadSignalHigh="VadSignalHigh",r.VadSignalLow="VadSignalLow",t.ControllerSignal=void 0,(h=t.ControllerSignal||(t.ControllerSignal={})).connect="connect",h.initAudioProcessor="initAudioProcessor",h.adjustAudioProcessor="adjustAudioProcessor",h.SET_SHARED_ARRAY_BUFFERS="SET_SHARED_ARRAY_BUFFERS",h.CLOSE="CLOSE",h.START_CONTEXT="START_CONTEXT",h.SWITCH_CONTEXT="SWITCH_CONTEXT",h.STOP_CONTEXT="STOP_CONTEXT",h.AUDIO="AUDIO",h.startStream="startStream",h.stopStream="stopStream",h.setContextOptions="setContextOptions";const u=new Error("Current device does not support storage API"),p=new Error("Requested key was not present in storage"),b={enabled:!1,controlListening:!0,signalToNoiseDb:3,noiseGateDb:-24,noiseLearnHalftimeMillis:400,signalSearchFrames:5,signalActivation:.7,signalRelease:.2,signalSustainMillis:3e3};var m;t.DecoderState=void 0,(m=t.DecoderState||(t.DecoderState={}))[m.Failed=0]="Failed",m[m.Disconnected=1]="Disconnected",m[m.Connected=2]="Connected",m[m.Active=3]="Active";class Z{constructor(){this.stateChangeCbs=[],this.transcriptCbs=[],this.entityCbs=[],this.intentCbs=[],this.segmentChangeCbs=[],this.tentativeTranscriptCbs=[],this.tentativeEntityCbs=[],this.tentativeIntentCbs=[],this.contextStartedCbs=[],this.contextStoppedCbs=[],this.onVadStateChange=[]}}function G(t){var e;return null!==(e=y.get(t))&&void 0!==e?e:"unknown"}const y=new Map([[t.DecoderState.Failed,"Failed"],[t.DecoderState.Disconnected,"Disconnected"],[t.DecoderState.Connected,"Connected"],[t.DecoderState.Active,"Active"]]);var W,v=new Uint8Array(16);function R(){if(!W&&!(W="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto)))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return W(v)}var X=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;function V(t){return"string"==typeof t&&X.test(t)}for(var S=[],C=0;C<256;++C)S.push((C+256).toString(16).substr(1));function g(t,e,i){var n=(t=t||{}).random||(t.rng||R)();if(n[6]=15&n[6]|64,n[8]=63&n[8]|128,e){i=i||0;for(var s=0;s<16;++s)e[i+s]=n[s];return e}return function(t){var e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0,i=(S[t[e+0]]+S[t[e+1]]+S[t[e+2]]+S[t[e+3]]+"-"+S[t[e+4]]+S[t[e+5]]+"-"+S[t[e+6]]+S[t[e+7]]+"-"+S[t[e+8]]+S[t[e+9]]+"-"+S[t[e+10]]+S[t[e+11]]+S[t[e+12]]+S[t[e+13]]+S[t[e+14]]+S[t[e+15]]).toLowerCase();if(!V(i))throw TypeError("Stringified UUID is invalid");return i}(n)}var Y="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},z={exports:{}}; | ||
/*! http://mths.be/base64 v0.1.0 by @mathias | MIT license */ | ||
!function(t,e){!function(i){var n=e,s=t&&t.exports==n&&t,o="object"==typeof z&&z;o.global!==o&&o.window!==o||(i=o);var a=function(t){this.message=t};(a.prototype=new Error).name="InvalidCharacterError";var d=function(t){throw new a(t)},l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",c=/[\t\n\f\r ]/g,r={encode:function(t){t=String(t),/[^\0-\xFF]/.test(t)&&d("The string to be encoded contains characters outside of the Latin1 range.");for(var e,i,n,s,o=t.length%3,a="",c=-1,r=t.length-o;++c<r;)e=t.charCodeAt(c)<<16,i=t.charCodeAt(++c)<<8,n=t.charCodeAt(++c),a+=l.charAt((s=e+i+n)>>18&63)+l.charAt(s>>12&63)+l.charAt(s>>6&63)+l.charAt(63&s);return 2==o?(e=t.charCodeAt(c)<<8,i=t.charCodeAt(++c),a+=l.charAt((s=e+i)>>10)+l.charAt(s>>4&63)+l.charAt(s<<2&63)+"="):1==o&&(s=t.charCodeAt(c),a+=l.charAt(s>>2)+l.charAt(s<<4&63)+"=="),a},decode:function(t){var e=(t=String(t).replace(c,"")).length;e%4==0&&(e=(t=t.replace(/==?$/,"")).length),(e%4==1||/[^+a-zA-Z0-9/]/.test(t))&&d("Invalid character: the string to be decoded is not correctly encoded.");for(var i,n,s=0,o="",a=-1;++a<e;)n=l.indexOf(t.charAt(a)),i=s%4?64*i+n:n,s++%4&&(o+=String.fromCharCode(255&i>>(-2*s&6)));return o},version:"0.1.0"};if(n&&!n.nodeType)if(s)s.exports=r;else for(var h in r)r.hasOwnProperty(h)&&(n[h]=r[h]);else i.base64=r}(z)}(Y,Y.exports);function N(t,e,i,n,s=Date.now){const o=function(t){const e=t.split(".")[1];let i;try{i=JSON.parse(Y.exports.decode(e))}catch(t){throw new Error("Error decoding Speechly token!")}return{appId:i.appId,projectId:i.projectId,deviceId:i.deviceId,configId:i.configId,scopes:i.scope.split(" "),issuer:i.iss,audience:i.aud,expiresAtMs:1e3*i.exp}}(t);return!(o.expiresAtMs-s()<36e5)&&(o.appId===i&&o.projectId===e&&o.deviceId===n)}class T{constructor(){this.startCbs=[],this.stopCbs=[],this.onResponseCb=()=>{},this.onCloseCb=()=>{},this.onWebsocketMessage=e=>{const i=e.data;switch(i.type){case t.WorkerSignal.Opened:null!=this.resolveInitialization&&this.resolveInitialization();break;case t.WorkerSignal.Closed:this.onCloseCb({code:e.data.code,reason:e.data.reason,wasClean:e.data.wasClean});break;case t.WorkerSignal.AudioProcessorReady:null!=this.resolveSourceSampleRateSet&&this.resolveSourceSampleRateSet();break;case t.WebsocketResponseType.Started:this.onResponseCb(i),this.startCbs.forEach((t=>{try{t(void 0,i.audio_context)}catch(t){console.error('[SpeechlyClient] Error while invoking "onStart" callback:',t)}})),this.startCbs.length=0;break;case t.WebsocketResponseType.Stopped:this.onResponseCb(i),this.stopCbs.forEach((t=>{try{t(void 0,i.audio_context)}catch(t){console.error('[SpeechlyClient] Error while invoking "onStop" callback:',t)}})),this.stopCbs.length=0;break;default:this.onResponseCb(i)}},this.worker=new"Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwp2YXIgd29ya2VyX2NvZGU9ZnVuY3Rpb24odCl7InVzZSBzdHJpY3QiO2NsYXNzIGV7c3RhdGljIGRvd25zYW1wbGUodCxlLHM9MCxpPS0xLG89MCxyPS0xKXtpZihpPDAmJihpPXQubGVuZ3RoLXMpLHI8MCYmKHI9ZS5sZW5ndGgtbykscj5pKXRocm93IG5ldyBFcnJvcihgQ2FuJ3QgZG93bnNhbXBsZTogc291cmNlIGFycmF5IGxlbmd0aCAoJHtpfSkgaXMgc2hvcnRlciB0aGFuIGRlc3RpbmF0aW9uICgke3J9KWApO2lmKDA9PT1yKXRocm93IG5ldyBFcnJvcihgQ2FuJ3QgZG93bnNhbXBsZTogc291cmNlIGFycmF5IGxlbmd0aCAoJHtpfSkgY2FuJ3QgYmUgZG93bnNhbXBsZWQgdG8gemVyby1sZW5ndGggZGVzdGluYXRpb24uYCk7aWYoMD09PWkpdGhyb3cgbmV3IEVycm9yKCJDYW4ndCBkb3duc2FtcGxlOiBzb3VyY2UgcmFuZ2UgY2FuJ3QgYmUgemVybyBsZW5ndGguIik7aWYoMT09PWkpcmV0dXJuIHZvaWQoZVswXT10WzBdKTtsZXQgYT0wO2NvbnN0IG49KHItMSkvKGktMSk7bGV0IGg9MCxsPTA7Y29uc3QgZD1zK2k7Zm9yKDtzPGQ7cysrKXtjb25zdCBpPS41LU1hdGguYWJzKGEpO2grPXRbc10qaSxsKz1pLGErPW4sYT49LjUmJihhLT0xLGVbbysrXT1oL2wsaD0wLGw9MCl9bD4wJiYoZVtvKytdPWgvbCl9c3RhdGljIGdldEVuZXJneSh0LGU9MCxzPS0xKXtpZihzPDAmJihzPXQubGVuZ3RoLWUpLHM8PTApcmV0dXJuIDA7Y29uc3QgaT1lK3M7bGV0IG89MDtmb3IoO2U8aTtlKyspbys9dFtlXSp0W2VdO3JldHVybiBNYXRoLnNxcnQoby9zKX1zdGF0aWMgZ2V0QXVkaW9QZWFrKHQsZT0wLHM9LTEpe2lmKHM8MCYmKHM9dC5sZW5ndGgtZSksczw9MClyZXR1cm4gMDtjb25zdCBpPWUrcztsZXQgbz0wO2Zvcig7ZTxpO2UrKyl0W2VdPm8mJihvPXRbZV0pO3JldHVybiBvfXN0YXRpYyBjb252ZXJ0SW50MTZUb0Zsb2F0KHQsZSxzPTAsaT0tMSxvPTApe2k8MCYmKGk9dC5sZW5ndGgvMi1zKTtjb25zdCByPU1hdGgubWluKHQubGVuZ3RoLzItcyxlLmxlbmd0aC1vKTtpZigoaT1NYXRoLm1pbihpLHIpKTw9MClyZXR1cm4gMDtsZXQgYT0yKnM7Y29uc3Qgbj1hKzIqaTtmb3IoO2E8bjspZVtvKytdPSh0W2ErK10rKHRbYSsrXTw8OCkpLzMyNzY3O3JldHVybiBpfXN0YXRpYyBjb252ZXJ0RmxvYXRUb0ludDE2KHQsZSxzPTAsaT0tMSxvPTApe2k8MCYmKGk9dC5sZW5ndGgtcyk7Y29uc3Qgcj1zK2k7Zm9yKDtzPHI7KWVbbysrXT1+figzMjc2Nyp0W3MrK10pfXN0YXRpYyBlbmVyZ3lUb0RiKHQpe3JldHVybiAxMCpNYXRoLmxvZyh0KS9lLkxPR18yX1BMVVNfTE9HXzV9c3RhdGljIGRiVG9FbmVyZ3kodCl7cmV0dXJuIE1hdGgucG93KDEwLHQvMTApfX1lLkxPR18yX1BMVVNfTE9HXzU9TWF0aC5sb2coMikrTWF0aC5sb2coNSk7Y2xhc3Mgc3tjb25zdHJ1Y3Rvcih0LGUscyl7dGhpcy5pc0FjdGl2ZT0hMSx0aGlzLnN0cmVhbVNhbXBsZVBvcz0wLHRoaXMuc2FtcGxlc1NlbnQ9MCx0aGlzLnV0dGVyYW5jZVNlcmlhbD0tMSx0aGlzLnNlbmRBdWRpbz0odCxlLHMpPT57fSx0aGlzLm9uVmFkU2lnbmFsTG93PSgpPT57fSx0aGlzLm9uVmFkU2lnbmFsSGlnaD0oKT0+e30sdGhpcy5pbnB1dFNhbXBsZVJhdGU9MTZlMyx0aGlzLmludGVybmFsU2FtcGxlUmF0ZT0xNmUzLHRoaXMuaGlzdG9yeUZyYW1lcz01LHRoaXMuZnJhbWVNaWxsaXM9MzAsdGhpcy5jdXJyZW50RnJhbWVOdW1iZXI9MCx0aGlzLmZyYW1lU2FtcGxlUG9zPTAsdGhpcy5zdHJlYW1GcmFtZVBvcz0wLHRoaXMuaXNTaWduYWxEZXRlY3RlZD0hMSx0aGlzLmlucHV0U2FtcGxlUmF0ZT10LHRoaXMuaW50ZXJuYWxTYW1wbGVSYXRlPWUsdGhpcy5oaXN0b3J5RnJhbWVzPXMsdGhpcy5mcmFtZVNhbXBsZXM9fn4odGhpcy5pbnRlcm5hbFNhbXBsZVJhdGUqdGhpcy5mcmFtZU1pbGxpcy8xZTMpLHRoaXMuc2FtcGxlUmluZ0J1ZmZlcj1uZXcgRmxvYXQzMkFycmF5KHRoaXMuZnJhbWVTYW1wbGVzKnRoaXMuaGlzdG9yeUZyYW1lcyl9c3RhcnRDb250ZXh0KCl7dGhpcy5pc0FjdGl2ZT0hMCx0aGlzLnNhbXBsZXNTZW50PTAsdGhpcy51dHRlcmFuY2VTZXJpYWwrK31zdG9wQ29udGV4dCgpe3RoaXMuZmx1c2goKSx0aGlzLmlzQWN0aXZlPSExfXJlc2V0U3RyZWFtKCl7dmFyIHQ7dGhpcy5zdHJlYW1GcmFtZVBvcz0wLHRoaXMuc3RyZWFtU2FtcGxlUG9zPTAsdGhpcy5mcmFtZVNhbXBsZVBvcz0wLHRoaXMuY3VycmVudEZyYW1lTnVtYmVyPTAsdGhpcy51dHRlcmFuY2VTZXJpYWw9LTEsbnVsbD09PSh0PXRoaXMudmFkKXx8dm9pZCAwPT09dHx8dC5yZXNldFZBRCgpfWZsdXNoKCl7dGhpcy5wcm9jZXNzQXVkaW8odGhpcy5zYW1wbGVSaW5nQnVmZmVyLDAsdGhpcy5mcmFtZVNhbXBsZVBvcywhMCl9cHJvY2Vzc0F1ZGlvKHQscz0wLGk9LTEsbz0hMSl7aWYoaTwwJiYoaT10Lmxlbmd0aCksMD09PWkpcmV0dXJuO2xldCByPXM7Y29uc3QgYT1zK2k7Zm9yKDtyPGE7KXtjb25zdCBzPXRoaXMuY3VycmVudEZyYW1lTnVtYmVyKnRoaXMuZnJhbWVTYW1wbGVzO2lmKHRoaXMuaW5wdXRTYW1wbGVSYXRlPT09dGhpcy5pbnRlcm5hbFNhbXBsZVJhdGUpe2NvbnN0IGU9TWF0aC5taW4oYS1yLHRoaXMuZnJhbWVTYW1wbGVzLXRoaXMuZnJhbWVTYW1wbGVQb3MpLGk9dGhpcy5mcmFtZVNhbXBsZVBvcytlO2Zvcig7dGhpcy5mcmFtZVNhbXBsZVBvczxpOyl0aGlzLnNhbXBsZVJpbmdCdWZmZXJbcyt0aGlzLmZyYW1lU2FtcGxlUG9zKytdPXRbcisrXX1lbHNle2NvbnN0IGk9MSp0aGlzLmlucHV0U2FtcGxlUmF0ZS90aGlzLmludGVybmFsU2FtcGxlUmF0ZSxvPU1hdGgubWluKGEtcixNYXRoLnJvdW5kKGkqKHRoaXMuZnJhbWVTYW1wbGVzLXRoaXMuZnJhbWVTYW1wbGVQb3MpKSksbj1NYXRoLm1pbihNYXRoLnJvdW5kKChhLXIpL2kpLHRoaXMuZnJhbWVTYW1wbGVzLXRoaXMuZnJhbWVTYW1wbGVQb3MpO24+MCYmZS5kb3duc2FtcGxlKHQsdGhpcy5zYW1wbGVSaW5nQnVmZmVyLHIsbyxzK3RoaXMuZnJhbWVTYW1wbGVQb3Msbikscis9byx0aGlzLmZyYW1lU2FtcGxlUG9zKz1ufWlmKHRoaXMuZnJhbWVTYW1wbGVQb3M+dGhpcy5mcmFtZVNhbXBsZXMpdGhyb3cgbmV3IEVycm9yKGB0aGlzLmZyYW1lU2FtcGxlUG9zICgke3RoaXMuZnJhbWVTYW1wbGVQb3N9KSA+IHRoaXMuZnJhbWVTYW1wbGVzICgke3RoaXMuZnJhbWVTYW1wbGVzfSlgKTtpZih0aGlzLmZyYW1lU2FtcGxlUG9zPT09dGhpcy5mcmFtZVNhbXBsZXN8fG8pe2NvbnN0IHQ9bz90aGlzLmZyYW1lU2FtcGxlUG9zOnRoaXMuZnJhbWVTYW1wbGVzO2lmKG98fHRoaXMucHJvY2Vzc0ZyYW1lKHRoaXMuc2FtcGxlUmluZ0J1ZmZlcixzLHQpLHRoaXMuaXNBY3RpdmUpe2lmKDA9PT10aGlzLnNhbXBsZXNTZW50KXtjb25zdCB0PU1hdGgubWluKHRoaXMuc3RyZWFtRnJhbWVQb3MsdGhpcy5oaXN0b3J5RnJhbWVzLTEpO2xldCBlPSh0aGlzLmN1cnJlbnRGcmFtZU51bWJlcit0aGlzLmhpc3RvcnlGcmFtZXMtdCkldGhpcy5oaXN0b3J5RnJhbWVzO2Zvcig7ZSE9PXRoaXMuY3VycmVudEZyYW1lTnVtYmVyOyl0aGlzLnNlbmRBdWRpbyh0aGlzLnNhbXBsZVJpbmdCdWZmZXIsZSp0aGlzLmZyYW1lU2FtcGxlcyx0aGlzLmZyYW1lU2FtcGxlcyksdGhpcy5zYW1wbGVzU2VudCs9dGhpcy5mcmFtZVNhbXBsZXMsZT0oZSsxKSV0aGlzLmhpc3RvcnlGcmFtZXN9dGhpcy5zZW5kQXVkaW8odGhpcy5zYW1wbGVSaW5nQnVmZmVyLHMsdCksdGhpcy5zYW1wbGVzU2VudCs9dH10aGlzLmZyYW1lU2FtcGxlUG9zPT09dGhpcy5mcmFtZVNhbXBsZXMmJih0aGlzLmZyYW1lU2FtcGxlUG9zPTAsdGhpcy5zdHJlYW1GcmFtZVBvcys9MSx0aGlzLnN0cmVhbVNhbXBsZVBvcys9dCx0aGlzLmN1cnJlbnRGcmFtZU51bWJlcj0odGhpcy5jdXJyZW50RnJhbWVOdW1iZXIrMSkldGhpcy5oaXN0b3J5RnJhbWVzKX19fXByb2Nlc3NGcmFtZSh0LGU9MCxzPS0xKXt0aGlzLmFuYWx5emVBdWRpb0ZyYW1lKHQsZSxzKSx0aGlzLmF1dG9Db250cm9sTGlzdGVuaW5nKCl9YW5hbHl6ZUF1ZGlvRnJhbWUodCxlLHMpe3ZhciBpOyhudWxsPT09KGk9dGhpcy52YWQpfHx2b2lkIDA9PT1pP3ZvaWQgMDppLnZhZE9wdGlvbnMuZW5hYmxlZCkmJnRoaXMudmFkLnByb2Nlc3NGcmFtZSh0LGUscyl9YXV0b0NvbnRyb2xMaXN0ZW5pbmcoKXt2YXIgdDsobnVsbD09PSh0PXRoaXMudmFkKXx8dm9pZCAwPT09dD92b2lkIDA6dC52YWRPcHRpb25zLmVuYWJsZWQpJiYoIXRoaXMuaXNTaWduYWxEZXRlY3RlZCYmdGhpcy52YWQuaXNTaWduYWxEZXRlY3RlZCYmKHRoaXMub25WYWRTaWduYWxIaWdoKCksdGhpcy5pc1NpZ25hbERldGVjdGVkPSEwKSx0aGlzLmlzU2lnbmFsRGV0ZWN0ZWQmJiF0aGlzLnZhZC5pc1NpZ25hbERldGVjdGVkJiYodGhpcy5vblZhZFNpZ25hbExvdygpLHRoaXMuaXNTaWduYWxEZXRlY3RlZD0hMSkpfX1jbGFzcyBpe2NvbnN0cnVjdG9yKHQpe3RoaXMuaXNTaWduYWxEZXRlY3RlZD0hMSx0aGlzLnNpZ25hbERiPS05MCx0aGlzLm5vaXNlTGV2ZWxEYj0tOTAsdGhpcy5mcmFtZU1pbGxpcz0zMCx0aGlzLmVuZXJneT0wLHRoaXMuYmFzZWxpbmVFbmVyZ3k9LTEsdGhpcy5sb3VkRnJhbWVCaXRzPTAsdGhpcy52YWRTdXN0YWluTWlsbGlzTGVmdD0wLHRoaXMudmFkT3B0aW9ucz10fWFkanVzdFZhZE9wdGlvbnModCl7dGhpcy52YWRPcHRpb25zPU9iamVjdC5hc3NpZ24oT2JqZWN0LmFzc2lnbih7fSx0aGlzLnZhZE9wdGlvbnMpLHQpfXJlc2V0VkFEKCl7dGhpcy5pc1NpZ25hbERldGVjdGVkPSExLHRoaXMubG91ZEZyYW1lQml0cz0wLHRoaXMuZW5lcmd5PTAsdGhpcy5iYXNlbGluZUVuZXJneT0tMX1wcm9jZXNzRnJhbWUodCxzPTAsaT0tMSl7aWYoIXRoaXMudmFkT3B0aW9ucy5lbmFibGVkKXJldHVybiB2b2lkIHRoaXMucmVzZXRWQUQoKTt0aGlzLmVuZXJneT1lLmdldEVuZXJneSh0LHMsaSksdGhpcy5iYXNlbGluZUVuZXJneTwwJiYodGhpcy5iYXNlbGluZUVuZXJneT10aGlzLmVuZXJneSk7Y29uc3Qgbz10aGlzLmVuZXJneT5NYXRoLm1heChlLmRiVG9FbmVyZ3kodGhpcy52YWRPcHRpb25zLm5vaXNlR2F0ZURiKSx0aGlzLmJhc2VsaW5lRW5lcmd5KmUuZGJUb0VuZXJneSh0aGlzLnZhZE9wdGlvbnMuc2lnbmFsVG9Ob2lzZURiKSk7dGhpcy5wdXNoRnJhbWVIaXN0b3J5KG8pLHRoaXMuaXNTaWduYWxEZXRlY3RlZD10aGlzLmRldGVybWluZU5ld1NpZ25hbFN0YXRlKHRoaXMuaXNTaWduYWxEZXRlY3RlZCksdGhpcy5hZGFwdEJhY2tncm91bmROb2lzZSgpLHRoaXMuc2lnbmFsRGI9ZS5lbmVyZ3lUb0RiKHRoaXMuZW5lcmd5L3RoaXMuYmFzZWxpbmVFbmVyZ3kpLHRoaXMubm9pc2VMZXZlbERiPWUuZW5lcmd5VG9EYih0aGlzLmJhc2VsaW5lRW5lcmd5KX1kZXRlcm1pbmVOZXdTaWduYWxTdGF0ZSh0KXt0aGlzLnZhZFN1c3RhaW5NaWxsaXNMZWZ0PU1hdGgubWF4KHRoaXMudmFkU3VzdGFpbk1pbGxpc0xlZnQtdGhpcy5mcmFtZU1pbGxpcywwKTtjb25zdCBlPXRoaXMuY291bnRMb3VkRnJhbWVzKHRoaXMudmFkT3B0aW9ucy5zaWduYWxTZWFyY2hGcmFtZXMpLHM9TWF0aC5yb3VuZCh0aGlzLnZhZE9wdGlvbnMuc2lnbmFsQWN0aXZhdGlvbip0aGlzLnZhZE9wdGlvbnMuc2lnbmFsU2VhcmNoRnJhbWVzKSxpPU1hdGgucm91bmQodGhpcy52YWRPcHRpb25zLnNpZ25hbFJlbGVhc2UqdGhpcy52YWRPcHRpb25zLnNpZ25hbFNlYXJjaEZyYW1lcyk7cmV0dXJuIGU+PXM/KHRoaXMudmFkU3VzdGFpbk1pbGxpc0xlZnQ9dGhpcy52YWRPcHRpb25zLnNpZ25hbFN1c3RhaW5NaWxsaXMsITApOiEoZTw9aSYmMD09PXRoaXMudmFkU3VzdGFpbk1pbGxpc0xlZnQpJiZ0fWFkYXB0QmFja2dyb3VuZE5vaXNlKCl7aWYoIXRoaXMuaXNTaWduYWxEZXRlY3RlZCYmdGhpcy52YWRPcHRpb25zLm5vaXNlTGVhcm5IYWxmdGltZU1pbGxpcz4wKXt2YXIgdD1NYXRoLnBvdygyLC10aGlzLmZyYW1lTWlsbGlzL3RoaXMudmFkT3B0aW9ucy5ub2lzZUxlYXJuSGFsZnRpbWVNaWxsaXMpO3RoaXMuYmFzZWxpbmVFbmVyZ3k9dGhpcy5iYXNlbGluZUVuZXJneSp0K3RoaXMuZW5lcmd5KigxLXQpfX1wdXNoRnJhbWVIaXN0b3J5KHQpe3RoaXMubG91ZEZyYW1lQml0cz0odD8xOjApfHRoaXMubG91ZEZyYW1lQml0czw8MX1jb3VudExvdWRGcmFtZXModCl7bGV0IGU9MCxzPXRoaXMubG91ZEZyYW1lQml0cztmb3IoO3Q+MDspMT09KDEmcykmJmUrKyxzPj49MSx0LS07cmV0dXJuIGV9fXZhciBvLHIsYTshZnVuY3Rpb24odCl7dC5TdGFydGVkPSJzdGFydGVkIix0LlN0b3BwZWQ9InN0b3BwZWQiLHQuU2VnbWVudEVuZD0ic2VnbWVudF9lbmQiLHQuVHJhbnNjcmlwdD0idHJhbnNjcmlwdCIsdC5FbnRpdHk9ImVudGl0eSIsdC5JbnRlbnQ9ImludGVudCIsdC5UZW50YXRpdmVUcmFuc2NyaXB0PSJ0ZW50YXRpdmVfdHJhbnNjcmlwdCIsdC5UZW50YXRpdmVFbnRpdGllcz0idGVudGF0aXZlX2VudGl0aWVzIix0LlRlbnRhdGl2ZUludGVudD0idGVudGF0aXZlX2ludGVudCJ9KG98fChvPXt9KSksZnVuY3Rpb24odCl7dC5PcGVuZWQ9IldFQlNPQ0tFVF9PUEVOIix0LkNsb3NlZD0iV0VCU09DS0VUX0NMT1NFRCIsdC5BdWRpb1Byb2Nlc3NvclJlYWR5PSJTT1VSQ0VfU0FNUExFX1JBVEVfU0VUX1NVQ0NFU1MiLHQuVmFkU2lnbmFsSGlnaD0iVmFkU2lnbmFsSGlnaCIsdC5WYWRTaWduYWxMb3c9IlZhZFNpZ25hbExvdyJ9KHJ8fChyPXt9KSksZnVuY3Rpb24odCl7dC5jb25uZWN0PSJjb25uZWN0Iix0LmluaXRBdWRpb1Byb2Nlc3Nvcj0iaW5pdEF1ZGlvUHJvY2Vzc29yIix0LmFkanVzdEF1ZGlvUHJvY2Vzc29yPSJhZGp1c3RBdWRpb1Byb2Nlc3NvciIsdC5TRVRfU0hBUkVEX0FSUkFZX0JVRkZFUlM9IlNFVF9TSEFSRURfQVJSQVlfQlVGRkVSUyIsdC5DTE9TRT0iQ0xPU0UiLHQuU1RBUlRfQ09OVEVYVD0iU1RBUlRfQ09OVEVYVCIsdC5TV0lUQ0hfQ09OVEVYVD0iU1dJVENIX0NPTlRFWFQiLHQuU1RPUF9DT05URVhUPSJTVE9QX0NPTlRFWFQiLHQuQVVESU89IkFVRElPIix0LnN0YXJ0U3RyZWFtPSJzdGFydFN0cmVhbSIsdC5zdG9wU3RyZWFtPSJzdG9wU3RyZWFtIix0LnNldENvbnRleHRPcHRpb25zPSJzZXRDb250ZXh0T3B0aW9ucyJ9KGF8fChhPXt9KSk7Y29uc3Qgbj0wLGg9MSxsPTI7Y2xhc3MgZHtjb25zdHJ1Y3Rvcih0KXt0aGlzLnRhcmdldFNhbXBsZVJhdGU9MTZlMyx0aGlzLmlzQ29udGV4dFN0YXJ0ZWQ9ITEsdGhpcy5mcmFtZU1pbGxpcz0zMCx0aGlzLm91dHB1dEF1ZGlvRnJhbWU9bmV3IEludDE2QXJyYXkodGhpcy5mcmFtZU1pbGxpcyp0aGlzLnRhcmdldFNhbXBsZVJhdGUvMWUzKSx0aGlzLmRlYnVnPSExLHRoaXMub25XZWJzb2NrZXRDbG9zZT10PT57aWYoIXRoaXMud2Vic29ja2V0KXRocm93IEVycm9yKCJXZWJTb2NrZXQgaXMgdW5kZWZpbmVkIik7dGhpcy5kZWJ1ZyYmY29uc29sZS5sb2coIltXZWJTb2NrZXRDbGllbnRdIiwib25XZWJzb2NrZXRDbG9zZSIpLHRoaXMud2Vic29ja2V0LnJlbW92ZUV2ZW50TGlzdGVuZXIoIm9wZW4iLHRoaXMub25XZWJzb2NrZXRPcGVuKSx0aGlzLndlYnNvY2tldC5yZW1vdmVFdmVudExpc3RlbmVyKCJtZXNzYWdlIix0aGlzLm9uV2Vic29ja2V0TWVzc2FnZSksdGhpcy53ZWJzb2NrZXQucmVtb3ZlRXZlbnRMaXN0ZW5lcigiZXJyb3IiLHRoaXMub25XZWJzb2NrZXRFcnJvciksdGhpcy53ZWJzb2NrZXQucmVtb3ZlRXZlbnRMaXN0ZW5lcigiY2xvc2UiLHRoaXMub25XZWJzb2NrZXRDbG9zZSksdGhpcy53ZWJzb2NrZXQ9dm9pZCAwLHRoaXMud29ya2VyQ3R4LnBvc3RNZXNzYWdlKHt0eXBlOnIuQ2xvc2VkLGNvZGU6dC5jb2RlLHJlYXNvbjp0LnJlYXNvbix3YXNDbGVhbjp0Lndhc0NsZWFufSl9LHRoaXMub25XZWJzb2NrZXRPcGVuPXQ9Pnt0aGlzLmRlYnVnJiZjb25zb2xlLmxvZygiW1dlYlNvY2tldENsaWVudF0iLCJ3ZWJzb2NrZXQgb3BlbmVkIiksdGhpcy53b3JrZXJDdHgucG9zdE1lc3NhZ2Uoe3R5cGU6ci5PcGVuZWR9KX0sdGhpcy5vbldlYnNvY2tldEVycm9yPXQ9Pnt0aGlzLmRlYnVnJiZjb25zb2xlLmxvZygiW1dlYlNvY2tldENsaWVudF0iLCJ3ZWJzb2NrZXQgZXJyb3IiKX0sdGhpcy5vbldlYnNvY2tldE1lc3NhZ2U9dD0+e2xldCBlO3RyeXtlPUpTT04ucGFyc2UodC5kYXRhKX1jYXRjaCh0KXtyZXR1cm4gdm9pZCBjb25zb2xlLmVycm9yKCJbV2ViU29ja2V0Q2xpZW50XSIsImVycm9yIHBhcnNpbmcgcmVzcG9uc2UgZnJvbSB0aGUgc2VydmVyOiIsdCl9dGhpcy53b3JrZXJDdHgucG9zdE1lc3NhZ2UoZSl9LHRoaXMud29ya2VyQ3R4PXR9Y29ubmVjdCh0LGUscyxpKXt0aGlzLmRlYnVnPWksdGhpcy5kZWJ1ZyYmY29uc29sZS5sb2coIltXZWJTb2NrZXRDbGllbnRdIiwiY29ubmVjdGluZyB0byAiLHQpLHRoaXMudGFyZ2V0U2FtcGxlUmF0ZT1zLHRoaXMuaXNDb250ZXh0U3RhcnRlZD0hMSx0aGlzLndlYnNvY2tldD1uZXcgV2ViU29ja2V0KHQsZSksdGhpcy53ZWJzb2NrZXQuYWRkRXZlbnRMaXN0ZW5lcigib3BlbiIsdGhpcy5vbldlYnNvY2tldE9wZW4pLHRoaXMud2Vic29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoIm1lc3NhZ2UiLHRoaXMub25XZWJzb2NrZXRNZXNzYWdlKSx0aGlzLndlYnNvY2tldC5hZGRFdmVudExpc3RlbmVyKCJlcnJvciIsdGhpcy5vbldlYnNvY2tldEVycm9yKSx0aGlzLndlYnNvY2tldC5hZGRFdmVudExpc3RlbmVyKCJjbG9zZSIsdGhpcy5vbldlYnNvY2tldENsb3NlKX1pbml0QXVkaW9Qcm9jZXNzb3IodCxvKXt0aGlzLmF1ZGlvUHJvY2Vzc29yPW5ldyBzKHQsdGhpcy50YXJnZXRTYW1wbGVSYXRlLDUpLG8mJih0aGlzLmF1ZGlvUHJvY2Vzc29yLnZhZD1uZXcgaShvKSx0aGlzLmF1ZGlvUHJvY2Vzc29yLm9uVmFkU2lnbmFsSGlnaD0oKT0+e3ZhciB0LGUscztjb25zdCBpPW51bGw9PT0oZT1udWxsPT09KHQ9dGhpcy5hdWRpb1Byb2Nlc3Nvcil8fHZvaWQgMD09PXQ/dm9pZCAwOnQudmFkKXx8dm9pZCAwPT09ZT92b2lkIDA6ZS52YWRPcHRpb25zOyhudWxsPT1pP3ZvaWQgMDppLmVuYWJsZWQpJiYobnVsbD09aT92b2lkIDA6aS5jb250cm9sTGlzdGVuaW5nKSYmKChudWxsPT09KHM9dGhpcy5kZWZhdWx0Q29udGV4dE9wdGlvbnMpfHx2b2lkIDA9PT1zP3ZvaWQgMDpzLmltbWVkaWF0ZSk/dGhpcy5zdGFydENvbnRleHQodGhpcy52YWRDb250ZXh0T3B0aW9ucyk6dGhpcy53b3JrZXJDdHgucG9zdE1lc3NhZ2Uoe3R5cGU6ci5WYWRTaWduYWxIaWdofSkpfSx0aGlzLmF1ZGlvUHJvY2Vzc29yLm9uVmFkU2lnbmFsTG93PSgpPT57dmFyIHQsZSxzO2NvbnN0IGk9bnVsbD09PShlPW51bGw9PT0odD10aGlzLmF1ZGlvUHJvY2Vzc29yKXx8dm9pZCAwPT09dD92b2lkIDA6dC52YWQpfHx2b2lkIDA9PT1lP3ZvaWQgMDplLnZhZE9wdGlvbnM7KG51bGw9PWk/dm9pZCAwOmkuZW5hYmxlZCkmJihudWxsPT1pP3ZvaWQgMDppLmNvbnRyb2xMaXN0ZW5pbmcpJiYoKG51bGw9PT0ocz10aGlzLmRlZmF1bHRDb250ZXh0T3B0aW9ucyl8fHZvaWQgMD09PXM/dm9pZCAwOnMuaW1tZWRpYXRlKT90aGlzLnN0b3BDb250ZXh0KCk6dGhpcy53b3JrZXJDdHgucG9zdE1lc3NhZ2Uoe3R5cGU6ci5WYWRTaWduYWxMb3d9KSl9KSx0aGlzLmF1ZGlvUHJvY2Vzc29yLnNlbmRBdWRpbz0odCxzLGkpPT57ZS5jb252ZXJ0RmxvYXRUb0ludDE2KHQsdGhpcy5vdXRwdXRBdWRpb0ZyYW1lLHMsaSksdGhpcy5zZW5kKHRoaXMub3V0cHV0QXVkaW9GcmFtZSl9LHZvaWQgMCE9PXRoaXMud29ya2VyQ3R4JiZ0aGlzLndvcmtlckN0eC5wb3N0TWVzc2FnZSh7dHlwZTpyLkF1ZGlvUHJvY2Vzc29yUmVhZHl9KX1hZGp1c3RBdWRpb1Byb2Nlc3Nvcih0KXtpZighdGhpcy5hdWRpb1Byb2Nlc3Nvcil0aHJvdyBuZXcgRXJyb3IoIk5vIEF1ZGlvUHJvY2Vzc29yIik7aWYodC52YWQpe2lmKCF0aGlzLmF1ZGlvUHJvY2Vzc29yLnZhZCl0aHJvdyBuZXcgRXJyb3IoIk5vIFZBRCBpbiBBdWRpb1Byb2Nlc3Nvci4gRGlkIHlvdSBkZWZpbmUgYHZhZGAgaW4gQnJvd3NlckNsaWVudCBjb25zdHJ1Y3RvciBwYXJhbWV0ZXJzPyIpO3RoaXMuYXVkaW9Qcm9jZXNzb3IudmFkLmFkanVzdFZhZE9wdGlvbnModC52YWQpfX1zZXRTaGFyZWRBcnJheUJ1ZmZlcnModCxlKXt0aGlzLmNvbnRyb2xTQUI9bmV3IEludDMyQXJyYXkodCksdGhpcy5kYXRhU0FCPW5ldyBGbG9hdDMyQXJyYXkoZSk7Y29uc3Qgcz10aGlzLmRhdGFTQUIubGVuZ3RoLzMyO3RoaXMuZGVidWcmJmNvbnNvbGUubG9nKCJbV2ViU29ja2V0Q2xpZW50XSIsIkF1ZGlvIGhhbmRsZSBpbnRlcnZhbCIscywibXMiKSxzZXRJbnRlcnZhbCh0aGlzLnByb2Nlc3NBdWRpb1NBQi5iaW5kKHRoaXMpLHMpfXN0YXJ0U3RyZWFtKHQpe2lmKCF0aGlzLmF1ZGlvUHJvY2Vzc29yKXRocm93IG5ldyBFcnJvcigiTm8gQXVkaW9Qcm9jZXNzb3IiKTt0aGlzLnZhZENvbnRleHRPcHRpb25zPXQsdGhpcy5hdWRpb1Byb2Nlc3Nvci5yZXNldFN0cmVhbSgpfXN0b3BTdHJlYW0oKXtpZighdGhpcy5hdWRpb1Byb2Nlc3Nvcil0aHJvdyBuZXcgRXJyb3IoIk5vIEF1ZGlvUHJvY2Vzc29yIik7dGhpcy5pc0NvbnRleHRTdGFydGVkJiZ0aGlzLnN0b3BDb250ZXh0KCksdGhpcy52YWRDb250ZXh0T3B0aW9ucz12b2lkIDB9cHJvY2Vzc0F1ZGlvKHQpe2lmKCF0aGlzLmF1ZGlvUHJvY2Vzc29yKXRocm93IG5ldyBFcnJvcigiTm8gQXVkaW9Qcm9jZXNzb3IiKTt0aGlzLmF1ZGlvUHJvY2Vzc29yLnByb2Nlc3NBdWRpbyh0KX1wcm9jZXNzQXVkaW9TQUIoKXtpZighdGhpcy5jb250cm9sU0FCfHwhdGhpcy5kYXRhU0FCKXRocm93IG5ldyBFcnJvcigiTm8gU2hhcmVkQXJyYXlCdWZmZXJzIik7Y29uc3QgdD10aGlzLmNvbnRyb2xTQUJbaF07aWYoMD09PXRoaXMuY29udHJvbFNBQltsXSYmdD4wKXtjb25zdCBlPXRoaXMuZGF0YVNBQi5zdWJhcnJheSgwLHQpO3RoaXMuY29udHJvbFNBQltoXT0wLHRoaXMuY29udHJvbFNBQltuXT0wLGUubGVuZ3RoPjAmJnRoaXMucHJvY2Vzc0F1ZGlvKGUpfX1zdGFydENvbnRleHQodCl7dmFyIGU7aWYoIXRoaXMuYXVkaW9Qcm9jZXNzb3IpdGhyb3cgRXJyb3IoIk5vIEF1ZGlvUHJvY2Vzc29yIik7aWYodGhpcy5pc0NvbnRleHRTdGFydGVkKXJldHVybiB2b2lkIGNvbnNvbGUuZXJyb3IoIltXZWJTb2NrZXRDbGllbnRdIiwiY2FuJ3Qgc3RhcnQgY29udGV4dDogYWN0aXZlIGNvbnRleHQgZXhpc3RzIik7dGhpcy5hdWRpb1Byb2Nlc3Nvci5zdGFydENvbnRleHQoKSx0aGlzLmlzQ29udGV4dFN0YXJ0ZWQ9ITA7bGV0IHM9bnVsbCE9PShlPXRoaXMuZGVmYXVsdENvbnRleHRPcHRpb25zKSYmdm9pZCAwIT09ZT9lOnt9O3ZvaWQgMCE9PXQmJihzPU9iamVjdC5hc3NpZ24oT2JqZWN0LmFzc2lnbih7fSxzKSx0KSk7Y29uc3QgaT11KHMpO2kuZXZlbnQ9InN0YXJ0Iix0aGlzLnNlbmQoSlNPTi5zdHJpbmdpZnkoaSkpfXN0b3BDb250ZXh0KCl7aWYoIXRoaXMuYXVkaW9Qcm9jZXNzb3IpdGhyb3cgRXJyb3IoIk5vIEF1ZGlvUHJvY2Vzc29yIik7aWYoIXRoaXMuaXNDb250ZXh0U3RhcnRlZClyZXR1cm4gdm9pZCBjb25zb2xlLmVycm9yKCJbV2ViU29ja2V0Q2xpZW50XSIsImNhbid0IHN0b3AgY29udGV4dDogbm8gYWN0aXZlIGNvbnRleHQiKTt0aGlzLmF1ZGlvUHJvY2Vzc29yLnN0b3BDb250ZXh0KCksdGhpcy5pc0NvbnRleHRTdGFydGVkPSExO2NvbnN0IHQ9SlNPTi5zdHJpbmdpZnkoe2V2ZW50OiJzdG9wIn0pO3RoaXMuc2VuZCh0KX1zd2l0Y2hDb250ZXh0KHQpe2lmKCF0aGlzLndlYnNvY2tldCl0aHJvdyBFcnJvcigiV2ViU29ja2V0IGlzIHVuZGVmaW5lZCIpO2lmKCF0aGlzLmlzQ29udGV4dFN0YXJ0ZWQpcmV0dXJuIHZvaWQgY29uc29sZS5lcnJvcigiW1dlYlNvY2tldENsaWVudF0iLCJjYW4ndCBzd2l0Y2ggY29udGV4dDogbm8gYWN0aXZlIGNvbnRleHQiKTtpZih2b2lkIDA9PT0obnVsbD09dD92b2lkIDA6dC5hcHBJZCkpcmV0dXJuIHZvaWQgY29uc29sZS5lcnJvcigiW1dlYlNvY2tldENsaWVudF0iLCJjYW4ndCBzd2l0Y2ggY29udGV4dDogbmV3IGFwcCBpZCBpcyB1bmRlZmluZWQiKTtjb25zdCBlPUpTT04uc3RyaW5naWZ5KHtldmVudDoic3RvcCJ9KTt0aGlzLnNlbmQoZSk7Y29uc3Qgcz11KHQpO3MuZXZlbnQ9InN0YXJ0Iix0aGlzLnNlbmQoSlNPTi5zdHJpbmdpZnkocykpfWNsb3NlV2Vic29ja2V0KHQ9MTAwNSxlPSJObyBTdGF0dXMgUmVjZWl2ZWQiKXtpZih0aGlzLmRlYnVnJiZjb25zb2xlLmxvZygiW1dlYlNvY2tldENsaWVudF0iLCJXZWJzb2NrZXQgY2xvc2luZyIpLCF0aGlzLndlYnNvY2tldCl0aHJvdyBFcnJvcigiV2ViU29ja2V0IGlzIHVuZGVmaW5lZCIpO3RoaXMud2Vic29ja2V0LmNsb3NlKHQsZSl9c2VuZCh0KXtpZighdGhpcy53ZWJzb2NrZXQpdGhyb3cgbmV3IEVycm9yKCJObyBXZWJzb2NrZXQiKTtpZih0aGlzLndlYnNvY2tldC5yZWFkeVN0YXRlIT09dGhpcy53ZWJzb2NrZXQuT1BFTil0aHJvdyBuZXcgRXJyb3IoYEV4cGVjdGVkIE9QRU4gV2Vic29ja2V0IHN0YXRlLCBidXQgZ290ICR7dGhpcy53ZWJzb2NrZXQucmVhZHlTdGF0ZX1gKTt0cnl7dGhpcy53ZWJzb2NrZXQuc2VuZCh0KX1jYXRjaCh0KXtjb25zb2xlLmxvZygiW1dlYlNvY2tldENsaWVudF0iLCJzZXJ2ZXIgY29ubmVjdGlvbiBlcnJvciIsdCl9fXNldENvbnRleHRPcHRpb25zKHQpe3RoaXMuZGVmYXVsdENvbnRleHRPcHRpb25zPXR9fWNvbnN0IGM9c2VsZixtPW5ldyBkKGMpO2Z1bmN0aW9uIHUodCl7Y29uc3QgZT17b3B0aW9uczp7dGltZXpvbmU6W0ludGwuRGF0ZVRpbWVGb3JtYXQoKS5yZXNvbHZlZE9wdGlvbnMoKS50aW1lWm9uZV19fTtyZXR1cm4gdm9pZCAwPT09dHx8KGUub3B0aW9ucy52b2NhYnVsYXJ5PXQudm9jYWJ1bGFyeSxlLm9wdGlvbnMudm9jYWJ1bGFyeV9iaWFzPXQudm9jYWJ1bGFyeUJpYXMsZS5vcHRpb25zLnNpbGVuY2VfdHJpZ2dlcmVkX3NlZ21lbnRhdGlvbj10LnNpbGVuY2VUcmlnZ2VyZWRTZWdtZW50YXRpb24sdm9pZCAwIT09KG51bGw9PXQ/dm9pZCAwOnQudGltZXpvbmUpJiYoZS5vcHRpb25zLnRpbWV6b25lPW51bGw9PXQ/dm9pZCAwOnQudGltZXpvbmUpLHZvaWQgMCE9PXQuYXBwSWQmJihlLmFwcElkPXQuYXBwSWQpKSxlfXJldHVybiBjLm9ubWVzc2FnZT1mdW5jdGlvbih0KXtzd2l0Y2godC5kYXRhLnR5cGUpe2Nhc2UgYS5jb25uZWN0Om0uY29ubmVjdCh0LmRhdGEuYXBpVXJsLHQuZGF0YS5hdXRoVG9rZW4sdC5kYXRhLnRhcmdldFNhbXBsZVJhdGUsdC5kYXRhLmRlYnVnKTticmVhaztjYXNlIGEuaW5pdEF1ZGlvUHJvY2Vzc29yOm0uaW5pdEF1ZGlvUHJvY2Vzc29yKHQuZGF0YS5zb3VyY2VTYW1wbGVSYXRlLHQuZGF0YS52YWRPcHRpb25zKTticmVhaztjYXNlIGEuYWRqdXN0QXVkaW9Qcm9jZXNzb3I6bS5hZGp1c3RBdWRpb1Byb2Nlc3Nvcih0LmRhdGEucGFyYW1zKTticmVhaztjYXNlIGEuU0VUX1NIQVJFRF9BUlJBWV9CVUZGRVJTOm0uc2V0U2hhcmVkQXJyYXlCdWZmZXJzKHQuZGF0YS5jb250cm9sU0FCLHQuZGF0YS5kYXRhU0FCKTticmVhaztjYXNlIGEuQ0xPU0U6bS5jbG9zZVdlYnNvY2tldCgxZTMsIkNsb3NlIHJlcXVlc3RlZCBieSBjbGllbnQiKTticmVhaztjYXNlIGEuc3RhcnRTdHJlYW06bS5zdGFydFN0cmVhbSh0LmRhdGEub3B0aW9ucyk7YnJlYWs7Y2FzZSBhLnN0b3BTdHJlYW06bS5zdG9wU3RyZWFtKCk7YnJlYWs7Y2FzZSBhLlNUQVJUX0NPTlRFWFQ6bS5zdGFydENvbnRleHQodC5kYXRhLm9wdGlvbnMpO2JyZWFrO2Nhc2UgYS5TV0lUQ0hfQ09OVEVYVDptLnN3aXRjaENvbnRleHQodC5kYXRhLm9wdGlvbnMpO2JyZWFrO2Nhc2UgYS5TVE9QX0NPTlRFWFQ6bS5zdG9wQ29udGV4dCgpO2JyZWFrO2Nhc2UgYS5BVURJTzptLnByb2Nlc3NBdWRpbyh0LmRhdGEucGF5bG9hZCk7YnJlYWs7Y2FzZSBhLnNldENvbnRleHRPcHRpb25zOm0uc2V0Q29udGV4dE9wdGlvbnModC5kYXRhLm9wdGlvbnMpO2JyZWFrO2RlZmF1bHQ6Y29uc29sZS5sb2coIldPUktFUiIsdCl9fSx0LmNvbnRleHRPcHRpb25zVG9Nc2c9dSx0LmRlZmF1bHQ9ZCxPYmplY3QuZGVmaW5lUHJvcGVydHkodCwiX19lc01vZHVsZSIse3ZhbHVlOiEwfSksdH0oe30pOwoK",this.worker.addEventListener("message",this.onWebsocketMessage)}onResponse(t){this.onResponseCb=t}onClose(t){this.onCloseCb=t}initialize(e,i,n,s){return o(this,void 0,void 0,(function*(){return this.worker.postMessage({type:t.ControllerSignal.connect,apiUrl:e,authToken:i,targetSampleRate:n,debug:s}),this.startCbs=[],this.stopCbs=[],new Promise((t=>{this.resolveInitialization=t}))}))}initAudioProcessor(e,i){return o(this,void 0,void 0,(function*(){return this.worker.postMessage({type:t.ControllerSignal.initAudioProcessor,sourceSampleRate:e,vadOptions:i}),new Promise((t=>{this.resolveSourceSampleRateSet=t}))}))}adjustAudioProcessor(e){this.worker.postMessage({type:t.ControllerSignal.adjustAudioProcessor,params:e})}close(){return o(this,void 0,void 0,(function*(){return new Promise(((e,i)=>{this.worker.postMessage({type:t.ControllerSignal.CLOSE,code:1e3,message:"Client has ended the session"}),e()}))}))}startStream(e){return o(this,void 0,void 0,(function*(){this.worker.postMessage({type:t.ControllerSignal.startStream,options:e})}))}stopStream(){return o(this,void 0,void 0,(function*(){this.worker.postMessage({type:t.ControllerSignal.stopStream})}))}startContext(e){return o(this,void 0,void 0,(function*(){return new Promise(((i,n)=>{this.startCbs.push(((t,e)=>{void 0!==t?n(t):i(e)})),this.worker.postMessage({type:t.ControllerSignal.START_CONTEXT,options:e})}))}))}stopContext(){return o(this,void 0,void 0,(function*(){return new Promise(((e,i)=>{this.stopCbs.push(((t,n)=>{void 0!==t?i(t):e(n)})),this.worker.postMessage({type:t.ControllerSignal.STOP_CONTEXT})}))}))}switchContext(e){return o(this,void 0,void 0,(function*(){return new Promise(((i,n)=>{this.startCbs.push(((t,e)=>{void 0!==t?n(t):i(e)})),this.worker.postMessage({type:t.ControllerSignal.SWITCH_CONTEXT,options:e})}))}))}postMessage(t){this.worker.postMessage(t)}sendAudio(e){this.worker.postMessage({type:t.ControllerSignal.AUDIO,payload:e})}setContextOptions(e){return o(this,void 0,void 0,(function*(){this.worker.postMessage({type:t.ControllerSignal.setContextOptions,options:e})}))}}class F{constructor(){this.storage=window.localStorage}get(t){return this.storage.getItem(t)}set(t,e){this.storage.setItem(t,e)}getOrSet(t,e){let i=this.storage.getItem(t);return null===i&&(i=e(),this.storage.setItem(t,i)),i}}function x(t,e){return{intent:t.intent,isFinal:e}}const f="speechly-auth-token";class J{constructor(e){var i,o,a,d,l,c,r,h;if(this.activeContexts=new Map,this.maxReconnectAttemptCount=10,this.connectAttempt=0,this.connectPromise=null,this.cbs=[],this.state=t.DecoderState.Disconnected,this.handleWebsocketResponse=e=>{switch(this.debug&&console.log("[Decoder]","Received response",e),e.type){case t.WorkerSignal.VadSignalHigh:this.cbs.forEach((t=>t.onVadStateChange.forEach((t=>t(!0)))));break;case t.WorkerSignal.VadSignalLow:this.cbs.forEach((t=>t.onVadStateChange.forEach((t=>t(!1)))));break;case t.WebsocketResponseType.Started:this.activeContexts.set(e.audio_context,new Map),this.cbs.forEach((t=>t.contextStartedCbs.forEach((t=>t(e.audio_context)))));break;case t.WebsocketResponseType.Stopped:this.activeContexts.delete(e.audio_context),this.cbs.forEach((t=>t.contextStoppedCbs.forEach((t=>t(e.audio_context)))));break;default:this.handleSegmentUpdate(e)}},this.handleSegmentUpdate=e=>{var i;const{audio_context:n,segment_id:o,type:a}=e;let{data:d}=e;const l=this.activeContexts.get(n);if(void 0===l)return void console.warn("[Decoder]","Received response for non-existent context",n);let c=null!==(i=l.get(o))&&void 0!==i?i:new s(n,o);switch(a){case t.WebsocketResponseType.TentativeTranscript:const e=function(t){return t.words.map((({word:t,index:e,start_timestamp:i,end_timestamp:n})=>({value:t,index:e,startTimestamp:i,endTimestamp:n,isFinal:!1})))}(d),i=d.transcript;this.cbs.forEach((t=>t.tentativeTranscriptCbs.forEach((t=>t(n,o,e,i))))),c=c.updateTranscript(e);break;case t.WebsocketResponseType.Transcript:const s=function(t){return{value:t.word,index:t.index,startTimestamp:t.start_timestamp,endTimestamp:t.end_timestamp,isFinal:!0}}(d);this.cbs.forEach((t=>t.transcriptCbs.forEach((t=>t(n,o,s))))),c=c.updateTranscript([s]);break;case t.WebsocketResponseType.TentativeEntities:const a=function(t){return t.entities.map((({entity:t,value:e,start_position:i,end_position:n})=>({type:t,value:e,startPosition:i,endPosition:n,isFinal:!1})))}(d);this.cbs.forEach((t=>t.tentativeEntityCbs.forEach((t=>t(n,o,a))))),c=c.updateEntities(a);break;case t.WebsocketResponseType.Entity:const l=function(t){return{type:t.entity,value:t.value,startPosition:t.start_position,endPosition:t.end_position,isFinal:!0}}(d);this.cbs.forEach((t=>t.entityCbs.forEach((t=>t(n,o,l))))),c=c.updateEntities([l]);break;case t.WebsocketResponseType.TentativeIntent:const r=x(d,!1);this.cbs.forEach((t=>t.tentativeIntentCbs.forEach((t=>t(n,o,r))))),c=c.updateIntent(r);break;case t.WebsocketResponseType.Intent:const h=x(d,!0);this.cbs.forEach((t=>t.intentCbs.forEach((t=>t(n,o,h))))),c=c.updateIntent(h);break;case t.WebsocketResponseType.SegmentEnd:c=c.finalize()}l.set(o,c),this.activeContexts.set(n,l),this.logSegments&&console.info(c.toString()),this.cbs.forEach((t=>t.segmentChangeCbs.forEach((t=>t(c.toSegment())))))},this.handleWebsocketClosure=e=>{if(1e3===e.code)this.debug&&console.log("[Decoder]","Websocket closed",e);else{if(console.error("[Decoder]","Websocket closed due to error",e),void 0===this.deviceId)return void this.setState(t.DecoderState.Failed);this.setState(t.DecoderState.Disconnected),this.reconnect()}},this.logSegments=null!==(i=e.logSegments)&&void 0!==i&&i,this.appId=null!==(o=e.appId)&&void 0!==o?o:void 0,this.projectId=null!==(a=e.projectId)&&void 0!==a?a:void 0,this.sampleRate=null!==(d=e.sampleRate)&&void 0!==d?d:n,this.debug=null!==(l=e.debug)&&void 0!==l&&l,void 0!==this.appId&&void 0!==this.projectId)throw Error("[Decoder] You cannot use both appId and projectId at the same time");if(void 0===this.appId&&void 0===this.projectId)throw Error("[Decoder] Either an appId or a projectId is required");const u=null!==(c=e.apiUrl)&&void 0!==c?c:"https://api.speechly.com";this.apiUrl=function(t,e){const i=new URLSearchParams;return i.append("sampleRate",e.toString()),`${t}?${i.toString()}`}(u.replace("http","ws")+"/ws/v1",this.sampleRate),this.loginUrl=`${u}/login`,this.storage=null!==(r=e.storage)&&void 0!==r?r:new F,this.deviceId=this.storage.getOrSet("speechly-device-id",g),this.apiClient=new T,this.apiClient.onResponse(this.handleWebsocketResponse),this.apiClient.onClose(this.handleWebsocketClosure),(null===(h=e.connect)||void 0===h||h)&&this.connect()}getReconnectDelayMs(t){return 100*Math.pow(2,t)}sleep(t){return o(this,void 0,void 0,(function*(){return new Promise((e=>setTimeout(e,t)))}))}connect(){return o(this,void 0,void 0,(function*(){null===this.connectPromise&&(this.connectPromise=(()=>o(this,void 0,void 0,(function*(){const e=this.storage.get(f);if(null!=e&&N(e,this.projectId,this.appId,this.deviceId))this.authToken=e;else try{this.authToken=yield function(t,e,i,n,s=fetch,a=Date.now){var d;return o(this,void 0,void 0,(function*(){let o;o=void 0!==e?{projectId:e,deviceId:n}:{appId:i,deviceId:n};const l=yield s(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)}),c=yield l.json();if(200!==l.status)throw Error(null!==(d=c.error)&&void 0!==d?d:`Speechly API login request failed with ${l.status}`);if(void 0===c.access_token)throw Error("Invalid login response from Speechly API");if(!N(c.access_token,e,i,n,a))throw Error("Invalid token received from Speechly API");return c.access_token}))}(this.loginUrl,this.projectId,this.appId,this.deviceId),this.storage.set(f,this.authToken)}catch(e){throw this.setState(t.DecoderState.Failed),e}yield this.apiClient.initialize(this.apiUrl,this.authToken,this.sampleRate,this.debug),this.advanceState(t.DecoderState.Connected)})))()),yield this.connectPromise}))}adjustAudioProcessor(t){this.apiClient.adjustAudioProcessor(t)}close(){return o(this,void 0,void 0,(function*(){let e;try{yield this.apiClient.close()}catch(t){e=t.message}if(this.activeContexts.clear(),this.connectPromise=null,this.setState(t.DecoderState.Disconnected),void 0!==e)throw Error(e)}))}startStream(t){return o(this,void 0,void 0,(function*(){yield this.apiClient.startStream(t)}))}stopStream(){return o(this,void 0,void 0,(function*(){this.state===t.DecoderState.Active&&(yield this.stopContext(0)),yield this.apiClient.stopStream()}))}startContext(e){return o(this,void 0,void 0,(function*(){if(this.state===t.DecoderState.Failed)throw Error("[Decoder] startContext cannot be run in Failed state.");if(this.state<t.DecoderState.Connected)yield this.connect();else if(this.state>t.DecoderState.Connected)throw Error("[Decoder] Unable to complete startContext: Expected Connected state, but was in "+G(this.state)+".");let n;if(this.setState(t.DecoderState.Active),null!=this.projectId)n=yield this.apiClient.startContext(null==e?void 0:e.appId);else{if(null!=(null==e?void 0:e.appId)&&this.appId!==(null==e?void 0:e.appId))throw this.setState(t.DecoderState.Failed),i;n=yield this.apiClient.startContext()}if(this.state<t.DecoderState.Active)throw Error("[Decoder] Unable to complete startContext: Problem acquiring contextId");return n}))}sendAudio(t){this.apiClient.sendAudio(t)}stopContext(e){return o(this,void 0,void 0,(function*(){if(this.state===t.DecoderState.Failed)throw Error("[Decoder] stopContext cannot be run in unrecovable error state.");if(this.state!==t.DecoderState.Active)throw Error("[Decoder] Unable to complete stopContext: Expected Active state, but was in "+G(this.state)+".");e>0&&(yield this.sleep(e)),this.apiClient.stopContext(),this.setState(t.DecoderState.Connected)}))}switchContext(e){return o(this,void 0,void 0,(function*(){if(this.state!==t.DecoderState.Active)throw Error("[Decoder] Unable to complete switchContext: Expected Active state, but was in "+G(this.state)+".");const i=yield this.apiClient.switchContext(e);this.activeContexts.set(i,new Map)}))}registerListener(t){this.cbs.push(t)}initAudioProcessor(t,e){return o(this,void 0,void 0,(function*(){this.sampleRate=t,yield this.apiClient.initAudioProcessor(t,e)}))}useSharedArrayBuffers(t,e){this.apiClient.postMessage({type:"SET_SHARED_ARRAY_BUFFERS",controlSAB:t,dataSAB:e})}setContextOptions(t){return o(this,void 0,void 0,(function*(){yield this.apiClient.setContextOptions(t)}))}reconnect(){return o(this,void 0,void 0,(function*(){this.debug&&console.log("[Decoder]","Reconnecting...",this.connectAttempt),this.connectPromise=null,this.connectAttempt<this.maxReconnectAttemptCount?(yield this.sleep(this.getReconnectDelayMs(this.connectAttempt++)),yield this.connect()):console.error("[Decoder] Maximum reconnect count reached, giving up automatic reconnect.")}))}advanceState(t){this.state>=t||this.setState(t)}setState(t){this.state!==t&&(this.debug&&console.log("[Decoder]",G(this.state),"->",G(t)),this.state=t,this.cbs.forEach((e=>{var i;return null===(i=e.stateChangeCbs)||void 0===i?void 0:i.forEach((e=>e(t)))})))}}t.BrowserClient=class{constructor(t){var e,i;this.contextStopDelay=250,this.debug=!1,this.initialized=!1,this.isStreaming=!1,this.isStreamAutoStarted=!1,this.active=!1,this.listeningPromise=null,this.stats={maxSignalEnergy:0,sentSamples:0};const n=window.navigator.mediaDevices.getSupportedConstraints();this.nativeResamplingSupported=!0===n.sampleRate,this.isMobileSafari=["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].indexOf(navigator.platform)>=0||navigator.userAgent.includes("Mac")&&"ontouchend"in document,this.isSafari=this.isMobileSafari||void 0!==window.safari,this.useSAB=!this.isSafari,this.vadOptions=Object.assign(Object.assign({},b),t.vad),this.debug=null===(e=t.debug)||void 0===e||e,this.callbacks=new Z,this.callbacks.onVadStateChange.push(this.onVadStateChange.bind(this)),this.decoder=null!==(i=t.decoder)&&void 0!==i?i:new J(t),this.decoder.registerListener(this.callbacks)}onVadStateChange(t){var e;this.debug&&console.log("[BrowserClient]","onVadStateChange",t),(null===(e=this.vadOptions)||void 0===e?void 0:e.controlListening)&&(t?this.active||this.start():this.active&&this.stop(0))}initialize(t){var i,s;return o(this,void 0,void 0,(function*(){if(!this.initialized){this.initialized=!0,this.debug&&console.log("[BrowserClient]","initializing"),yield this.decoder.connect();try{const t={};if(this.nativeResamplingSupported&&(t.sampleRate=n),void 0!==window.webkitAudioContext)try{this.audioContext=new window.webkitAudioContext(t)}catch(t){this.debug&&console.log("[BrowserClient]","creating audioContext without samplerate conversion",t),this.audioContext=new window.webkitAudioContext}else this.audioContext=new window.AudioContext(t),void 0!==window.webkitAudioContext&&(yield this.audioContext.resume())}catch(t){throw e}if(this.isSafari||void 0===window.AudioWorkletNode){if(this.debug&&console.log("[BrowserClient]","using ScriptProcessorNode"),void 0!==window.webkitAudioContext){const t=this.audioContext.sampleRate/n,e=4096*Math.pow(2,Math.ceil(Math.log(t)/Math.log(2)));this.audioProcessor=this.audioContext.createScriptProcessor(e,1,1)}else this.audioProcessor=this.audioContext.createScriptProcessor(void 0,1,1);this.audioProcessor.connect(this.audioContext.destination),this.audioProcessor.addEventListener("audioprocess",(t=>{this.handleAudio(t.inputBuffer.getChannelData(0))}))}else{this.debug&&console.log("[BrowserClient]","using AudioWorkletNode");const t=new Blob(["\n// Indices for the Control SAB.\nconst CONTROL = {\n 'WRITE_INDEX': 0,\n 'FRAMES_AVAILABLE': 1,\n 'LOCK': 2,\n};\n\nclass SpeechlyProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n\n this._initialized = false;\n this.debug = false;\n this.port.onmessage = this._initialize.bind(this);\n }\n\n _initialize(event) {\n this.controlSAB = new Int32Array(event.data.controlSAB);\n this.dataSAB = new Float32Array(event.data.dataSAB);\n this.debug = event.data.debug;\n if (this.debug) {\n console.log('[BrowserClient AudioWorkletNode]', 'initializing audioworklet');\n }\n this.sharedBufferSize = this.dataSAB.length;\n this.buffer = new Float32Array(0);\n this._initialized = true;\n }\n\n _transferDataToSharedBuffer(data) {\n this.controlSAB[CONTROL.LOCK] = 1;\n let inputWriteIndex = this.controlSAB[CONTROL.WRITE_INDEX];\n if (this.controlSAB[CONTROL.FRAMES_AVAILABLE] > 0) {\n if (inputWriteIndex + data.length > this.sharedBufferSize) {\n // console.log('buffer overflow')\n inputWriteIndex = 0;\n }\n }\n this.dataSAB.set(data, inputWriteIndex);\n this.controlSAB[CONTROL.WRITE_INDEX] = inputWriteIndex + data.length;\n this.controlSAB[CONTROL.FRAMES_AVAILABLE] = inputWriteIndex + data.length;\n this.controlSAB[CONTROL.LOCK] = 0;\n }\n\n _pushData(data) {\n if (this.debug) {\n const signalEnergy = getStandardDeviation(data)\n this.port.postMessage({\n type: 'STATS',\n signalEnergy: signalEnergy,\n samples: data.length,\n });\n }\n\n if (this.buffer.length > this.sharedBufferSize) {\n const dataToTransfer = this.buffer.subarray(0, this.sharedBufferSize);\n this._transferDataToSharedBuffer(dataToTransfer);\n this.buffer = this.buffer.subarray(this.sharedBufferSize);\n }\n let concat = new Float32Array(this.buffer.length + data.length);\n concat.set(this.buffer);\n concat.set(data, this.buffer.length);\n this.buffer = concat;\n }\n\n process(inputs, outputs, parameters) {\n const inputChannelData = inputs[0][0];\n if (inputChannelData !== undefined) {\n if (this.controlSAB && this.dataSAB) {\n this._pushData(inputChannelData);\n } else {\n this.port.postMessage({\n type: 'DATA',\n frames: inputChannelData\n });\n }\n }\n\n return true;\n }\n}\n\nfunction getStandardDeviation(array) {\n const n = array.length\n const mean = array.reduce((a, b) => a + b) / n\n return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n)\n}\n\nregisterProcessor('speechly-worklet', SpeechlyProcessor);\n"],{type:"text/javascript"}),e=window.URL.createObjectURL(t);if(yield this.audioContext.audioWorklet.addModule(e),this.speechlyNode=new AudioWorkletNode(this.audioContext,"speechly-worklet"),this.speechlyNode.connect(this.audioContext.destination),this.useSAB&&void 0!==window.SharedArrayBuffer){this.debug&&console.log("[BrowserClient]","using SharedArrayBuffer");const t=new window.SharedArrayBuffer(4*Int32Array.BYTES_PER_ELEMENT),e=new window.SharedArrayBuffer(1024*Float32Array.BYTES_PER_ELEMENT);this.decoder.useSharedArrayBuffers(t,e),this.speechlyNode.port.postMessage({type:"SET_SHARED_ARRAY_BUFFERS",controlSAB:t,dataSAB:e,debug:this.debug})}else this.debug&&console.log("[BrowserClient]","can not use SharedArrayBuffer");this.speechlyNode.port.onmessage=t=>{switch(t.data.type){case"STATS":t.data.signalEnergy>this.stats.maxSignalEnergy&&(this.stats.maxSignalEnergy=t.data.signalEnergy),this.stats.sentSamples+=parseInt(t.data.samples);break;case"DATA":this.handleAudio(t.data.frames)}}}this.debug&&console.log("[BrowserClient]","audioContext sampleRate is",null===(i=this.audioContext)||void 0===i?void 0:i.sampleRate),yield this.decoder.initAudioProcessor(null===(s=this.audioContext)||void 0===s?void 0:s.sampleRate,this.vadOptions),this.vadOptions&&(yield this.startStream()),(null==t?void 0:t.mediaStream)&&(yield this.attach(null==t?void 0:t.mediaStream))}}))}adjustAudioProcessor(t){this.decoder.adjustAudioProcessor(t)}close(){var t,e,i;return o(this,void 0,void 0,(function*(){yield this.detach(),null!==this.speechlyNode&&(null===(t=this.speechlyNode)||void 0===t||t.port.close(),null===(e=this.speechlyNode)||void 0===e||e.disconnect()),void 0!==this.audioProcessor&&(null===(i=this.audioProcessor)||void 0===i||i.disconnect()),yield this.decoder.close(),this.initialized=!1}))}attach(t){var e,i,n,s,a,d;return o(this,void 0,void 0,(function*(){if(yield this.initialize(),yield this.detach(),this.stream=null===(e=this.audioContext)||void 0===e?void 0:e.createMediaStreamSource(t),"running"!==(null===(i=this.audioContext)||void 0===i?void 0:i.state)&&(this.debug&&console.log("[BrowserClient]","audioContext resume required, state is",null===(n=this.audioContext)||void 0===n?void 0:n.state),yield null===(s=this.audioContext)||void 0===s?void 0:s.resume()),this.speechlyNode)null===(a=this.stream)||void 0===a||a.connect(this.speechlyNode);else{if(!this.audioProcessor)throw Error("[BrowserClient] cannot attach to mediaStream, not initialized");null===(d=this.stream)||void 0===d||d.connect(this.audioProcessor)}}))}detach(){return o(this,void 0,void 0,(function*(){this.active&&(yield this.stop(0)),this.stream&&(this.stream.disconnect(),this.stream=void 0)}))}uploadAudioData(t,e){var i,n,s;return o(this,void 0,void 0,(function*(){yield this.initialize();const o=yield null===(i=this.audioContext)||void 0===i?void 0:i.decodeAudioData(t);if(void 0===o)throw Error("Could not decode audioData");const a=o.getChannelData(0);if(o.numberOfChannels>1){const t=o.getChannelData(1);for(let e=0;e<a.length;e++)a[e]=(a[e]+t[e])/2}let d;yield this.startStream({immediate:!0});const l=(null===(n=this.vadOptions)||void 0===n?void 0:n.enabled)&&(null===(s=this.vadOptions)||void 0===s?void 0:s.controlListening);let c;d=l?"multiple context ids":yield this.start(e);for(let t=0;t<a.length;t+=16e3){const e=t+16e3;c=e>a.length?a.slice(t):a.slice(t,e),this.handleAudio(c)}return l||(yield this.stop(0)),yield this.stopStream(),d}))}startStream(t){return o(this,void 0,void 0,(function*(){yield this.decoder.startStream(t),this.isStreaming=!0}))}stopStream(){return o(this,void 0,void 0,(function*(){yield this.decoder.stopStream(),this.isStreaming=!1,this.isStreamAutoStarted=!1}))}queueTask(t){return o(this,void 0,void 0,(function*(){const e=this.listeningPromise;return this.listeningPromise=(()=>o(this,void 0,void 0,(function*(){return yield e,t()})))(),this.listeningPromise}))}start(t){return o(this,void 0,void 0,(function*(){return yield this.queueTask((()=>o(this,void 0,void 0,(function*(){yield this.initialize(),this.isStreaming||(yield this.startStream(t),this.isStreamAutoStarted=!0);const e=this.decoder.startContext(t);return this.active=!0,e}))))}))}stop(t=this.contextStopDelay){return o(this,void 0,void 0,(function*(){return yield this.queueTask((()=>o(this,void 0,void 0,(function*(){let e=null;try{e=yield this.decoder.stopContext(t),this.isStreaming&&this.isStreamAutoStarted&&(yield this.stopStream()),0===this.stats.sentSamples&&console.warn("[BrowserClient]","audioContext contained no audio data")}catch(t){console.warn("[BrowserClient]","stop() failed",t)}finally{this.active=!1,this.stats.sentSamples=0}return e}))))}))}handleAudio(t){this.isStreaming&&(this.stats.sentSamples+=t.length,this.decoder.sendAudio(t))}isActive(){return this.active}onSegmentChange(t){this.callbacks.segmentChangeCbs.push(t)}onTranscript(t){this.callbacks.transcriptCbs.push(t)}onEntity(t){this.callbacks.entityCbs.push(t)}onIntent(t){this.callbacks.intentCbs.push(t)}onTentativeTranscript(t){this.callbacks.tentativeTranscriptCbs.push(t)}onTentativeEntities(t){this.callbacks.tentativeEntityCbs.push(t)}onTentativeIntent(t){this.callbacks.tentativeIntentCbs.push(t)}onStateChange(t){this.callbacks.stateChangeCbs.push(t)}},t.BrowserMicrophone=class{constructor(){this.muted=!1,this.initialized=!1;try{const t=window.navigator.mediaDevices.getSupportedConstraints();this.nativeResamplingSupported=!0===t.sampleRate,this.autoGainControlSupported=!0===t.autoGainControl}catch(t){this.nativeResamplingSupported=!1,this.autoGainControlSupported=!1}}initialize(){var t;return o(this,void 0,void 0,(function*(){if(this.initialized)return;if(void 0===(null===(t=window.navigator)||void 0===t?void 0:t.mediaDevices))throw e;const i={video:!1};this.nativeResamplingSupported||this.autoGainControlSupported?i.audio={sampleRate:n,autoGainControl:this.autoGainControlSupported}:i.audio=!0;try{this.mediaStream=yield window.navigator.mediaDevices.getUserMedia(i)}catch(t){throw console.error(t),l}this.initialized=!0,this.muted=!0}))}close(){return o(this,void 0,void 0,(function*(){if(!this.initialized)throw a;this.muted=!0;this.mediaStream.getTracks().forEach((t=>t.stop())),this.mediaStream=void 0,this.initialized=!1}))}isRecording(){return!this.muted}},t.CloudDecoder=J,t.DefaultSampleRate=n,t.ErrAlreadyInitialized=d,t.ErrAppIdChangeWithoutProjectLogin=i,t.ErrDeviceNotSupported=e,t.ErrKeyNotFound=p,t.ErrNoAudioConsent=l,t.ErrNoStorageSupport=u,t.ErrNotInitialized=a,t.EventCallbacks=Z,t.SegmentState=s,t.VadDefaultOptions=b,t.stateToString=G,Object.defineProperty(t,"__esModule",{value:!0})})); | ||
!function(t,e){!function(i){var n=e,s=t&&t.exports==n&&t,o="object"==typeof Y&&Y;o.global!==o&&o.window!==o||(i=o);var a=function(t){this.message=t};(a.prototype=new Error).name="InvalidCharacterError";var d=function(t){throw new a(t)},l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",c=/[\t\n\f\r ]/g,r={encode:function(t){t=String(t),/[^\0-\xFF]/.test(t)&&d("The string to be encoded contains characters outside of the Latin1 range.");for(var e,i,n,s,o=t.length%3,a="",c=-1,r=t.length-o;++c<r;)e=t.charCodeAt(c)<<16,i=t.charCodeAt(++c)<<8,n=t.charCodeAt(++c),a+=l.charAt((s=e+i+n)>>18&63)+l.charAt(s>>12&63)+l.charAt(s>>6&63)+l.charAt(63&s);return 2==o?(e=t.charCodeAt(c)<<8,i=t.charCodeAt(++c),a+=l.charAt((s=e+i)>>10)+l.charAt(s>>4&63)+l.charAt(s<<2&63)+"="):1==o&&(s=t.charCodeAt(c),a+=l.charAt(s>>2)+l.charAt(s<<4&63)+"=="),a},decode:function(t){var e=(t=String(t).replace(c,"")).length;e%4==0&&(e=(t=t.replace(/==?$/,"")).length),(e%4==1||/[^+a-zA-Z0-9/]/.test(t))&&d("Invalid character: the string to be decoded is not correctly encoded.");for(var i,n,s=0,o="",a=-1;++a<e;)n=l.indexOf(t.charAt(a)),i=s%4?64*i+n:n,s++%4&&(o+=String.fromCharCode(255&i>>(-2*s&6)));return o},version:"0.1.0"};if(n&&!n.nodeType)if(s)s.exports=r;else for(var h in r)r.hasOwnProperty(h)&&(n[h]=r[h]);else i.base64=r}(Y)}(z,z.exports);function N(t,e,i,n,s=Date.now){const o=function(t){const e=t.split(".")[1];let i;try{i=JSON.parse(z.exports.decode(e))}catch(t){throw new Error("Error decoding Speechly token!")}return{appId:i.appId,projectId:i.projectId,deviceId:i.deviceId,configId:i.configId,scopes:i.scope.split(" "),issuer:i.iss,audience:i.aud,expiresAtMs:1e3*i.exp}}(t);return!(o.expiresAtMs-s()<36e5)&&(o.appId===i&&o.projectId===e&&o.deviceId===n)}class T{constructor(){this.startCbs=[],this.stopCbs=[],this.onResponseCb=()=>{},this.onCloseCb=()=>{},this.onWebsocketMessage=e=>{const i=e.data;switch(i.type){case t.WorkerSignal.Opened:null!=this.resolveInitialization&&this.resolveInitialization();break;case t.WorkerSignal.Closed:this.onCloseCb({code:e.data.code,reason:e.data.reason,wasClean:e.data.wasClean});break;case t.WorkerSignal.AudioProcessorReady:null!=this.resolveSourceSampleRateSet&&this.resolveSourceSampleRateSet();break;case t.WebsocketResponseType.Started:this.onResponseCb(i),this.startCbs.forEach((t=>{try{t(void 0,i.audio_context)}catch(t){console.error('[SpeechlyClient] Error while invoking "onStart" callback:',t)}})),this.startCbs.length=0;break;case t.WebsocketResponseType.Stopped:this.onResponseCb(i),this.stopCbs.forEach((t=>{try{t(void 0,i.audio_context)}catch(t){console.error('[SpeechlyClient] Error while invoking "onStop" callback:',t)}})),this.stopCbs.length=0;break;default:this.onResponseCb(i)}},this.worker=new"Lyogcm9sbHVwLXBsdWdpbi13ZWItd29ya2VyLWxvYWRlciAqLwp2YXIgd29ya2VyX2NvZGU9ZnVuY3Rpb24odCl7InVzZSBzdHJpY3QiO2NsYXNzIGV7c3RhdGljIGRvd25zYW1wbGUodCxlLHM9MCxpPS0xLG89MCxyPS0xKXtpZihpPDAmJihpPXQubGVuZ3RoLXMpLHI8MCYmKHI9ZS5sZW5ndGgtbykscj5pKXRocm93IG5ldyBFcnJvcihgQ2FuJ3QgZG93bnNhbXBsZTogc291cmNlIGFycmF5IGxlbmd0aCAoJHtpfSkgaXMgc2hvcnRlciB0aGFuIGRlc3RpbmF0aW9uICgke3J9KWApO2lmKDA9PT1yKXRocm93IG5ldyBFcnJvcihgQ2FuJ3QgZG93bnNhbXBsZTogc291cmNlIGFycmF5IGxlbmd0aCAoJHtpfSkgY2FuJ3QgYmUgZG93bnNhbXBsZWQgdG8gemVyby1sZW5ndGggZGVzdGluYXRpb24uYCk7aWYoMD09PWkpdGhyb3cgbmV3IEVycm9yKCJDYW4ndCBkb3duc2FtcGxlOiBzb3VyY2UgcmFuZ2UgY2FuJ3QgYmUgemVybyBsZW5ndGguIik7aWYoMT09PWkpcmV0dXJuIHZvaWQoZVswXT10WzBdKTtsZXQgYT0wO2NvbnN0IG49KHItMSkvKGktMSk7bGV0IGg9MCxjPTA7Y29uc3QgbD1zK2k7Zm9yKDtzPGw7cysrKXtjb25zdCBpPS41LU1hdGguYWJzKGEpO2grPXRbc10qaSxjKz1pLGErPW4sYT49LjUmJihhLT0xLGVbbysrXT1oL2MsaD0wLGM9MCl9Yz4wJiYoZVtvKytdPWgvYyl9c3RhdGljIGdldEVuZXJneSh0LGU9MCxzPS0xKXtpZihzPDAmJihzPXQubGVuZ3RoLWUpLHM8PTApcmV0dXJuIDA7Y29uc3QgaT1lK3M7bGV0IG89MDtmb3IoO2U8aTtlKyspbys9dFtlXSp0W2VdO3JldHVybiBNYXRoLnNxcnQoby9zKX1zdGF0aWMgZ2V0QXVkaW9QZWFrKHQsZT0wLHM9LTEpe2lmKHM8MCYmKHM9dC5sZW5ndGgtZSksczw9MClyZXR1cm4gMDtjb25zdCBpPWUrcztsZXQgbz0wO2Zvcig7ZTxpO2UrKyl0W2VdPm8mJihvPXRbZV0pO3JldHVybiBvfXN0YXRpYyBjb252ZXJ0SW50MTZUb0Zsb2F0KHQsZSxzPTAsaT0tMSxvPTApe2k8MCYmKGk9dC5sZW5ndGgvMi1zKTtjb25zdCByPU1hdGgubWluKHQubGVuZ3RoLzItcyxlLmxlbmd0aC1vKTtpZigoaT1NYXRoLm1pbihpLHIpKTw9MClyZXR1cm4gMDtsZXQgYT0yKnM7Y29uc3Qgbj1hKzIqaTtmb3IoO2E8bjspZVtvKytdPSh0W2ErK10rKHRbYSsrXTw8OCkpLzMyNzY3O3JldHVybiBpfXN0YXRpYyBjb252ZXJ0RmxvYXRUb0ludDE2KHQsZSxzPTAsaT0tMSxvPTApe2k8MCYmKGk9dC5sZW5ndGgtcyk7Y29uc3Qgcj1zK2k7Zm9yKDtzPHI7KWVbbysrXT1+figzMjc2Nyp0W3MrK10pfXN0YXRpYyBlbmVyZ3lUb0RiKHQpe3JldHVybiAxMCpNYXRoLmxvZyh0KS9lLkxPR18yX1BMVVNfTE9HXzV9c3RhdGljIGRiVG9FbmVyZ3kodCl7cmV0dXJuIE1hdGgucG93KDEwLHQvMTApfX1lLkxPR18yX1BMVVNfTE9HXzU9TWF0aC5sb2coMikrTWF0aC5sb2coNSk7Y2xhc3Mgc3tjb25zdHJ1Y3Rvcih0LGUscyl7dGhpcy5pc0FjdGl2ZT0hMSx0aGlzLnN0cmVhbVNhbXBsZVBvcz0wLHRoaXMuc2FtcGxlc1NlbnQ9MCx0aGlzLnV0dGVyYW5jZVNlcmlhbD0tMSx0aGlzLm9uU2VuZEF1ZGlvPSh0LGUscyk9Pnt9LHRoaXMub25WYWRTdGF0ZUNoYW5nZT10PT57fSx0aGlzLmlucHV0U2FtcGxlUmF0ZT0xNmUzLHRoaXMuaW50ZXJuYWxTYW1wbGVSYXRlPTE2ZTMsdGhpcy5oaXN0b3J5RnJhbWVzPTUsdGhpcy5mcmFtZU1pbGxpcz0zMCx0aGlzLmN1cnJlbnRGcmFtZU51bWJlcj0wLHRoaXMuZnJhbWVTYW1wbGVQb3M9MCx0aGlzLnN0cmVhbUZyYW1lUG9zPTAsdGhpcy53YXNTaWduYWxEZXRlY3RlZD0hMSx0aGlzLmlucHV0U2FtcGxlUmF0ZT10LHRoaXMuaW50ZXJuYWxTYW1wbGVSYXRlPWUsdGhpcy5oaXN0b3J5RnJhbWVzPXMsdGhpcy5mcmFtZVNhbXBsZXM9fn4odGhpcy5pbnRlcm5hbFNhbXBsZVJhdGUqdGhpcy5mcmFtZU1pbGxpcy8xZTMpLHRoaXMuc2FtcGxlUmluZ0J1ZmZlcj1uZXcgRmxvYXQzMkFycmF5KHRoaXMuZnJhbWVTYW1wbGVzKnRoaXMuaGlzdG9yeUZyYW1lcyl9c3RhcnRDb250ZXh0KCl7dGhpcy5pc0FjdGl2ZT0hMCx0aGlzLnNhbXBsZXNTZW50PTAsdGhpcy51dHRlcmFuY2VTZXJpYWwrK31zdG9wQ29udGV4dCgpe3RoaXMuZmx1c2goKSx0aGlzLmlzQWN0aXZlPSExfXJlc2V0U3RyZWFtKCl7dmFyIHQ7dGhpcy5zdHJlYW1GcmFtZVBvcz0wLHRoaXMuc3RyZWFtU2FtcGxlUG9zPTAsdGhpcy5mcmFtZVNhbXBsZVBvcz0wLHRoaXMuY3VycmVudEZyYW1lTnVtYmVyPTAsdGhpcy51dHRlcmFuY2VTZXJpYWw9LTEsbnVsbD09PSh0PXRoaXMudmFkKXx8dm9pZCAwPT09dHx8dC5yZXNldFZBRCgpfWZsdXNoKCl7dGhpcy5wcm9jZXNzQXVkaW8odGhpcy5zYW1wbGVSaW5nQnVmZmVyLDAsdGhpcy5mcmFtZVNhbXBsZVBvcywhMCl9Z2V0U3RyZWFtUG9zaXRpb24oKXtyZXR1cm4gdGhpcy5zdHJlYW1TYW1wbGVQb3MvdGhpcy5pbnB1dFNhbXBsZVJhdGV9cHJvY2Vzc0F1ZGlvKHQscz0wLGk9LTEsbz0hMSl7aWYoaTwwJiYoaT10Lmxlbmd0aCksMD09PWkpcmV0dXJuO2xldCByPXM7Y29uc3QgYT1zK2k7Zm9yKDtyPGE7KXtjb25zdCBzPXRoaXMuY3VycmVudEZyYW1lTnVtYmVyKnRoaXMuZnJhbWVTYW1wbGVzO2lmKHRoaXMuaW5wdXRTYW1wbGVSYXRlPT09dGhpcy5pbnRlcm5hbFNhbXBsZVJhdGUpe2NvbnN0IGU9TWF0aC5taW4oYS1yLHRoaXMuZnJhbWVTYW1wbGVzLXRoaXMuZnJhbWVTYW1wbGVQb3MpLGk9dGhpcy5mcmFtZVNhbXBsZVBvcytlO2Zvcig7dGhpcy5mcmFtZVNhbXBsZVBvczxpOyl0aGlzLnNhbXBsZVJpbmdCdWZmZXJbcyt0aGlzLmZyYW1lU2FtcGxlUG9zKytdPXRbcisrXX1lbHNle2NvbnN0IGk9MSp0aGlzLmlucHV0U2FtcGxlUmF0ZS90aGlzLmludGVybmFsU2FtcGxlUmF0ZSxvPU1hdGgubWluKGEtcixNYXRoLnJvdW5kKGkqKHRoaXMuZnJhbWVTYW1wbGVzLXRoaXMuZnJhbWVTYW1wbGVQb3MpKSksbj1NYXRoLm1pbihNYXRoLnJvdW5kKChhLXIpL2kpLHRoaXMuZnJhbWVTYW1wbGVzLXRoaXMuZnJhbWVTYW1wbGVQb3MpO24+MCYmZS5kb3duc2FtcGxlKHQsdGhpcy5zYW1wbGVSaW5nQnVmZmVyLHIsbyxzK3RoaXMuZnJhbWVTYW1wbGVQb3Msbikscis9byx0aGlzLmZyYW1lU2FtcGxlUG9zKz1ufWlmKHRoaXMuZnJhbWVTYW1wbGVQb3M+dGhpcy5mcmFtZVNhbXBsZXMpdGhyb3cgbmV3IEVycm9yKGB0aGlzLmZyYW1lU2FtcGxlUG9zICgke3RoaXMuZnJhbWVTYW1wbGVQb3N9KSA+IHRoaXMuZnJhbWVTYW1wbGVzICgke3RoaXMuZnJhbWVTYW1wbGVzfSlgKTtpZih0aGlzLmZyYW1lU2FtcGxlUG9zPT09dGhpcy5mcmFtZVNhbXBsZXN8fG8pe2NvbnN0IHQ9bz90aGlzLmZyYW1lU2FtcGxlUG9zOnRoaXMuZnJhbWVTYW1wbGVzO2lmKG98fHRoaXMucHJvY2Vzc0ZyYW1lKHRoaXMuc2FtcGxlUmluZ0J1ZmZlcixzLHQpLHRoaXMuaXNBY3RpdmUpe2lmKDA9PT10aGlzLnNhbXBsZXNTZW50KXtjb25zdCB0PU1hdGgubWluKHRoaXMuc3RyZWFtRnJhbWVQb3MsdGhpcy5oaXN0b3J5RnJhbWVzLTEpO2xldCBlPSh0aGlzLmN1cnJlbnRGcmFtZU51bWJlcit0aGlzLmhpc3RvcnlGcmFtZXMtdCkldGhpcy5oaXN0b3J5RnJhbWVzO2Zvcig7ZSE9PXRoaXMuY3VycmVudEZyYW1lTnVtYmVyOyl0aGlzLm9uU2VuZEF1ZGlvKHRoaXMuc2FtcGxlUmluZ0J1ZmZlcixlKnRoaXMuZnJhbWVTYW1wbGVzLHRoaXMuZnJhbWVTYW1wbGVzKSx0aGlzLnNhbXBsZXNTZW50Kz10aGlzLmZyYW1lU2FtcGxlcyxlPShlKzEpJXRoaXMuaGlzdG9yeUZyYW1lc310aGlzLm9uU2VuZEF1ZGlvKHRoaXMuc2FtcGxlUmluZ0J1ZmZlcixzLHQpLHRoaXMuc2FtcGxlc1NlbnQrPXR9dGhpcy5mcmFtZVNhbXBsZVBvcz09PXRoaXMuZnJhbWVTYW1wbGVzJiYodGhpcy5mcmFtZVNhbXBsZVBvcz0wLHRoaXMuc3RyZWFtRnJhbWVQb3MrPTEsdGhpcy5zdHJlYW1TYW1wbGVQb3MrPXQsdGhpcy5jdXJyZW50RnJhbWVOdW1iZXI9KHRoaXMuY3VycmVudEZyYW1lTnVtYmVyKzEpJXRoaXMuaGlzdG9yeUZyYW1lcyl9fX1wcm9jZXNzRnJhbWUodCxlPTAscz0tMSl7dmFyIGk7KG51bGw9PT0oaT10aGlzLnZhZCl8fHZvaWQgMD09PWk/dm9pZCAwOmkudmFkT3B0aW9ucy5lbmFibGVkKSYmdGhpcy5wcm9jZXNzVmFkRnJhbWUodGhpcy52YWQsdCxlLHMpfXByb2Nlc3NWYWRGcmFtZSh0LGUscz0wLGk9LTEpe3QucHJvY2Vzc0ZyYW1lKGUscyxpKSx0LmlzU2lnbmFsRGV0ZWN0ZWQhPT10aGlzLndhc1NpZ25hbERldGVjdGVkJiYodGhpcy5vblZhZFN0YXRlQ2hhbmdlKHQuaXNTaWduYWxEZXRlY3RlZCksdGhpcy53YXNTaWduYWxEZXRlY3RlZD10LmlzU2lnbmFsRGV0ZWN0ZWQpfX1jbGFzcyBpe2NvbnN0cnVjdG9yKHQpe3RoaXMuaXNTaWduYWxEZXRlY3RlZD0hMSx0aGlzLnNpZ25hbERiPS05MCx0aGlzLm5vaXNlTGV2ZWxEYj0tOTAsdGhpcy5mcmFtZU1pbGxpcz0zMCx0aGlzLmVuZXJneT0wLHRoaXMuYmFzZWxpbmVFbmVyZ3k9LTEsdGhpcy5sb3VkRnJhbWVCaXRzPTAsdGhpcy52YWRTdXN0YWluTWlsbGlzTGVmdD0wLHRoaXMudmFkT3B0aW9ucz10fWFkanVzdFZhZE9wdGlvbnModCl7dGhpcy52YWRPcHRpb25zPU9iamVjdC5hc3NpZ24oT2JqZWN0LmFzc2lnbih7fSx0aGlzLnZhZE9wdGlvbnMpLHQpfXJlc2V0VkFEKCl7dGhpcy5pc1NpZ25hbERldGVjdGVkPSExLHRoaXMubG91ZEZyYW1lQml0cz0wLHRoaXMuZW5lcmd5PTAsdGhpcy5iYXNlbGluZUVuZXJneT0tMX1wcm9jZXNzRnJhbWUodCxzPTAsaT0tMSl7aWYoIXRoaXMudmFkT3B0aW9ucy5lbmFibGVkKXJldHVybiB2b2lkIHRoaXMucmVzZXRWQUQoKTt0aGlzLmVuZXJneT1lLmdldEVuZXJneSh0LHMsaSksdGhpcy5iYXNlbGluZUVuZXJneTwwJiYodGhpcy5iYXNlbGluZUVuZXJneT10aGlzLmVuZXJneSk7Y29uc3Qgbz10aGlzLmVuZXJneT5NYXRoLm1heChlLmRiVG9FbmVyZ3kodGhpcy52YWRPcHRpb25zLm5vaXNlR2F0ZURiKSx0aGlzLmJhc2VsaW5lRW5lcmd5KmUuZGJUb0VuZXJneSh0aGlzLnZhZE9wdGlvbnMuc2lnbmFsVG9Ob2lzZURiKSk7dGhpcy5wdXNoRnJhbWVIaXN0b3J5KG8pLHRoaXMuaXNTaWduYWxEZXRlY3RlZD10aGlzLmRldGVybWluZU5ld1NpZ25hbFN0YXRlKHRoaXMuaXNTaWduYWxEZXRlY3RlZCksdGhpcy5hZGFwdEJhY2tncm91bmROb2lzZSgpLHRoaXMuc2lnbmFsRGI9ZS5lbmVyZ3lUb0RiKHRoaXMuZW5lcmd5L3RoaXMuYmFzZWxpbmVFbmVyZ3kpLHRoaXMubm9pc2VMZXZlbERiPWUuZW5lcmd5VG9EYih0aGlzLmJhc2VsaW5lRW5lcmd5KX1kZXRlcm1pbmVOZXdTaWduYWxTdGF0ZSh0KXt0aGlzLnZhZFN1c3RhaW5NaWxsaXNMZWZ0PU1hdGgubWF4KHRoaXMudmFkU3VzdGFpbk1pbGxpc0xlZnQtdGhpcy5mcmFtZU1pbGxpcywwKTtjb25zdCBlPXRoaXMuY291bnRMb3VkRnJhbWVzKHRoaXMudmFkT3B0aW9ucy5zaWduYWxTZWFyY2hGcmFtZXMpLHM9TWF0aC5yb3VuZCh0aGlzLnZhZE9wdGlvbnMuc2lnbmFsQWN0aXZhdGlvbip0aGlzLnZhZE9wdGlvbnMuc2lnbmFsU2VhcmNoRnJhbWVzKSxpPU1hdGgucm91bmQodGhpcy52YWRPcHRpb25zLnNpZ25hbFJlbGVhc2UqdGhpcy52YWRPcHRpb25zLnNpZ25hbFNlYXJjaEZyYW1lcyk7cmV0dXJuIGU+PXM/KHRoaXMudmFkU3VzdGFpbk1pbGxpc0xlZnQ9dGhpcy52YWRPcHRpb25zLnNpZ25hbFN1c3RhaW5NaWxsaXMsITApOiEoZTw9aSYmMD09PXRoaXMudmFkU3VzdGFpbk1pbGxpc0xlZnQpJiZ0fWFkYXB0QmFja2dyb3VuZE5vaXNlKCl7aWYoIXRoaXMuaXNTaWduYWxEZXRlY3RlZCYmdGhpcy52YWRPcHRpb25zLm5vaXNlTGVhcm5IYWxmdGltZU1pbGxpcz4wKXt2YXIgdD1NYXRoLnBvdygyLC10aGlzLmZyYW1lTWlsbGlzL3RoaXMudmFkT3B0aW9ucy5ub2lzZUxlYXJuSGFsZnRpbWVNaWxsaXMpO3RoaXMuYmFzZWxpbmVFbmVyZ3k9dGhpcy5iYXNlbGluZUVuZXJneSp0K3RoaXMuZW5lcmd5KigxLXQpfX1wdXNoRnJhbWVIaXN0b3J5KHQpe3RoaXMubG91ZEZyYW1lQml0cz0odD8xOjApfHRoaXMubG91ZEZyYW1lQml0czw8MX1jb3VudExvdWRGcmFtZXModCl7bGV0IGU9MCxzPXRoaXMubG91ZEZyYW1lQml0cztmb3IoO3Q+MDspMT09KDEmcykmJmUrKyxzPj49MSx0LS07cmV0dXJuIGV9fXZhciBvLHIsYTshZnVuY3Rpb24odCl7dC5TdGFydGVkPSJzdGFydGVkIix0LlN0b3BwZWQ9InN0b3BwZWQiLHQuU2VnbWVudEVuZD0ic2VnbWVudF9lbmQiLHQuVHJhbnNjcmlwdD0idHJhbnNjcmlwdCIsdC5FbnRpdHk9ImVudGl0eSIsdC5JbnRlbnQ9ImludGVudCIsdC5UZW50YXRpdmVUcmFuc2NyaXB0PSJ0ZW50YXRpdmVfdHJhbnNjcmlwdCIsdC5UZW50YXRpdmVFbnRpdGllcz0idGVudGF0aXZlX2VudGl0aWVzIix0LlRlbnRhdGl2ZUludGVudD0idGVudGF0aXZlX2ludGVudCJ9KG98fChvPXt9KSksZnVuY3Rpb24odCl7dC5PcGVuZWQ9IldFQlNPQ0tFVF9PUEVOIix0LkNsb3NlZD0iV0VCU09DS0VUX0NMT1NFRCIsdC5BdWRpb1Byb2Nlc3NvclJlYWR5PSJTT1VSQ0VfU0FNUExFX1JBVEVfU0VUX1NVQ0NFU1MiLHQuVmFkU2lnbmFsSGlnaD0iVmFkU2lnbmFsSGlnaCIsdC5WYWRTaWduYWxMb3c9IlZhZFNpZ25hbExvdyJ9KHJ8fChyPXt9KSksZnVuY3Rpb24odCl7dC5jb25uZWN0PSJjb25uZWN0Iix0LmluaXRBdWRpb1Byb2Nlc3Nvcj0iaW5pdEF1ZGlvUHJvY2Vzc29yIix0LmFkanVzdEF1ZGlvUHJvY2Vzc29yPSJhZGp1c3RBdWRpb1Byb2Nlc3NvciIsdC5TRVRfU0hBUkVEX0FSUkFZX0JVRkZFUlM9IlNFVF9TSEFSRURfQVJSQVlfQlVGRkVSUyIsdC5DTE9TRT0iQ0xPU0UiLHQuU1RBUlRfQ09OVEVYVD0iU1RBUlRfQ09OVEVYVCIsdC5TV0lUQ0hfQ09OVEVYVD0iU1dJVENIX0NPTlRFWFQiLHQuU1RPUF9DT05URVhUPSJTVE9QX0NPTlRFWFQiLHQuQVVESU89IkFVRElPIix0LnN0YXJ0U3RyZWFtPSJzdGFydFN0cmVhbSIsdC5zdG9wU3RyZWFtPSJzdG9wU3RyZWFtIix0LnNldENvbnRleHRPcHRpb25zPSJzZXRDb250ZXh0T3B0aW9ucyJ9KGF8fChhPXt9KSk7Y29uc3Qgbj0wLGg9MSxjPTI7Y2xhc3MgbHtjb25zdHJ1Y3Rvcih0KXt0aGlzLnRhcmdldFNhbXBsZVJhdGU9MTZlMyx0aGlzLmlzQ29udGV4dFN0YXJ0ZWQ9ITEsdGhpcy5jb250ZXh0U3RhcnRUaW1lPTAsdGhpcy5pbW1lZGlhdGVNb2RlPSExLHRoaXMuZnJhbWVNaWxsaXM9MzAsdGhpcy5vdXRwdXRBdWRpb0ZyYW1lPW5ldyBJbnQxNkFycmF5KHRoaXMuZnJhbWVNaWxsaXMqdGhpcy50YXJnZXRTYW1wbGVSYXRlLzFlMyksdGhpcy5kZWJ1Zz0hMSx0aGlzLm9uV2Vic29ja2V0Q2xvc2U9dD0+e2lmKCF0aGlzLndlYnNvY2tldCl0aHJvdyBFcnJvcigiV2ViU29ja2V0IGlzIHVuZGVmaW5lZCIpO3RoaXMuZGVidWcmJmNvbnNvbGUubG9nKCJbV2ViU29ja2V0Q2xpZW50XSIsIm9uV2Vic29ja2V0Q2xvc2UiKSx0aGlzLndlYnNvY2tldC5yZW1vdmVFdmVudExpc3RlbmVyKCJvcGVuIix0aGlzLm9uV2Vic29ja2V0T3BlbiksdGhpcy53ZWJzb2NrZXQucmVtb3ZlRXZlbnRMaXN0ZW5lcigibWVzc2FnZSIsdGhpcy5vbldlYnNvY2tldE1lc3NhZ2UpLHRoaXMud2Vic29ja2V0LnJlbW92ZUV2ZW50TGlzdGVuZXIoImVycm9yIix0aGlzLm9uV2Vic29ja2V0RXJyb3IpLHRoaXMud2Vic29ja2V0LnJlbW92ZUV2ZW50TGlzdGVuZXIoImNsb3NlIix0aGlzLm9uV2Vic29ja2V0Q2xvc2UpLHRoaXMud2Vic29ja2V0PXZvaWQgMCx0aGlzLndvcmtlckN0eC5wb3N0TWVzc2FnZSh7dHlwZTpyLkNsb3NlZCxjb2RlOnQuY29kZSxyZWFzb246dC5yZWFzb24sd2FzQ2xlYW46dC53YXNDbGVhbn0pfSx0aGlzLm9uV2Vic29ja2V0T3Blbj10PT57dGhpcy5kZWJ1ZyYmY29uc29sZS5sb2coIltXZWJTb2NrZXRDbGllbnRdIiwid2Vic29ja2V0IG9wZW5lZCIpLHRoaXMud29ya2VyQ3R4LnBvc3RNZXNzYWdlKHt0eXBlOnIuT3BlbmVkfSl9LHRoaXMub25XZWJzb2NrZXRFcnJvcj10PT57dGhpcy5kZWJ1ZyYmY29uc29sZS5sb2coIltXZWJTb2NrZXRDbGllbnRdIiwid2Vic29ja2V0IGVycm9yIil9LHRoaXMub25XZWJzb2NrZXRNZXNzYWdlPXQ9PntsZXQgZTt0cnl7ZT1KU09OLnBhcnNlKHQuZGF0YSl9Y2F0Y2godCl7cmV0dXJuIHZvaWQgY29uc29sZS5lcnJvcigiW1dlYlNvY2tldENsaWVudF0iLCJlcnJvciBwYXJzaW5nIHJlc3BvbnNlIGZyb20gdGhlIHNlcnZlcjoiLHQpfXRoaXMud29ya2VyQ3R4LnBvc3RNZXNzYWdlKGUpfSx0aGlzLndvcmtlckN0eD10fWNvbm5lY3QodCxlLHMsaSl7dGhpcy5kZWJ1Zz1pLHRoaXMuZGVidWcmJmNvbnNvbGUubG9nKCJbV2ViU29ja2V0Q2xpZW50XSIsImNvbm5lY3RpbmcgdG8gIix0KSx0aGlzLnRhcmdldFNhbXBsZVJhdGU9cyx0aGlzLmlzQ29udGV4dFN0YXJ0ZWQ9ITEsdGhpcy53ZWJzb2NrZXQ9bmV3IFdlYlNvY2tldCh0LGUpLHRoaXMud2Vic29ja2V0LmFkZEV2ZW50TGlzdGVuZXIoIm9wZW4iLHRoaXMub25XZWJzb2NrZXRPcGVuKSx0aGlzLndlYnNvY2tldC5hZGRFdmVudExpc3RlbmVyKCJtZXNzYWdlIix0aGlzLm9uV2Vic29ja2V0TWVzc2FnZSksdGhpcy53ZWJzb2NrZXQuYWRkRXZlbnRMaXN0ZW5lcigiZXJyb3IiLHRoaXMub25XZWJzb2NrZXRFcnJvciksdGhpcy53ZWJzb2NrZXQuYWRkRXZlbnRMaXN0ZW5lcigiY2xvc2UiLHRoaXMub25XZWJzb2NrZXRDbG9zZSl9aW5pdEF1ZGlvUHJvY2Vzc29yKHQsbyl7dGhpcy5hdWRpb1Byb2Nlc3Nvcj1uZXcgcyh0LHRoaXMudGFyZ2V0U2FtcGxlUmF0ZSw1KSxvJiYodGhpcy5hdWRpb1Byb2Nlc3Nvci52YWQ9bmV3IGkobyksdGhpcy5hdWRpb1Byb2Nlc3Nvci5vblZhZFN0YXRlQ2hhbmdlPXQ9Pnt2YXIgZSxzO2NvbnN0IGk9bnVsbD09PShzPW51bGw9PT0oZT10aGlzLmF1ZGlvUHJvY2Vzc29yKXx8dm9pZCAwPT09ZT92b2lkIDA6ZS52YWQpfHx2b2lkIDA9PT1zP3ZvaWQgMDpzLnZhZE9wdGlvbnM7aSYmKHQmJih0aGlzLmltbWVkaWF0ZU1vZGU/aS5jb250cm9sTGlzdGVuaW5nJiZ0aGlzLnN0YXJ0Q29udGV4dCh0aGlzLmRlZmF1bHRDb250ZXh0T3B0aW9ucyk6dGhpcy53b3JrZXJDdHgucG9zdE1lc3NhZ2Uoe3R5cGU6ci5WYWRTaWduYWxIaWdofSkpLHR8fCh0aGlzLmltbWVkaWF0ZU1vZGU/aS5jb250cm9sTGlzdGVuaW5nJiZ0aGlzLnN0b3BDb250ZXh0KCk6dGhpcy53b3JrZXJDdHgucG9zdE1lc3NhZ2Uoe3R5cGU6ci5WYWRTaWduYWxMb3d9KSkpfSksdGhpcy5hdWRpb1Byb2Nlc3Nvci5vblNlbmRBdWRpbz0odCxzLGkpPT57ZS5jb252ZXJ0RmxvYXRUb0ludDE2KHQsdGhpcy5vdXRwdXRBdWRpb0ZyYW1lLHMsaSksdGhpcy5zZW5kKHRoaXMub3V0cHV0QXVkaW9GcmFtZSl9LHZvaWQgMCE9PXRoaXMud29ya2VyQ3R4JiZ0aGlzLndvcmtlckN0eC5wb3N0TWVzc2FnZSh7dHlwZTpyLkF1ZGlvUHJvY2Vzc29yUmVhZHl9KX1hZGp1c3RBdWRpb1Byb2Nlc3Nvcih0KXtpZighdGhpcy5hdWRpb1Byb2Nlc3Nvcil0aHJvdyBuZXcgRXJyb3IoIk5vIEF1ZGlvUHJvY2Vzc29yIik7aWYodm9pZCAwIT09dC5pbW1lZGlhdGUmJih0aGlzLmltbWVkaWF0ZU1vZGU9dC5pbW1lZGlhdGUpLHQudmFkKXtpZighdGhpcy5hdWRpb1Byb2Nlc3Nvci52YWQpdGhyb3cgbmV3IEVycm9yKCJObyBWQUQgaW4gQXVkaW9Qcm9jZXNzb3IuIERpZCB5b3UgZGVmaW5lIGB2YWRgIGluIEJyb3dzZXJDbGllbnQgY29uc3RydWN0b3IgcGFyYW1ldGVycz8iKTt0aGlzLmF1ZGlvUHJvY2Vzc29yLnZhZC5hZGp1c3RWYWRPcHRpb25zKHQudmFkKX19c2V0U2hhcmVkQXJyYXlCdWZmZXJzKHQsZSl7dGhpcy5jb250cm9sU0FCPW5ldyBJbnQzMkFycmF5KHQpLHRoaXMuZGF0YVNBQj1uZXcgRmxvYXQzMkFycmF5KGUpO2NvbnN0IHM9dGhpcy5kYXRhU0FCLmxlbmd0aC8zMjt0aGlzLmRlYnVnJiZjb25zb2xlLmxvZygiW1dlYlNvY2tldENsaWVudF0iLCJBdWRpbyBoYW5kbGUgaW50ZXJ2YWwiLHMsIm1zIiksc2V0SW50ZXJ2YWwodGhpcy5wcm9jZXNzQXVkaW9TQUIuYmluZCh0aGlzKSxzKX1zdGFydFN0cmVhbSgpe2lmKCF0aGlzLmF1ZGlvUHJvY2Vzc29yKXRocm93IG5ldyBFcnJvcigiTm8gQXVkaW9Qcm9jZXNzb3IiKTt0aGlzLmF1ZGlvUHJvY2Vzc29yLnJlc2V0U3RyZWFtKCl9c3RvcFN0cmVhbSgpe2lmKCF0aGlzLmF1ZGlvUHJvY2Vzc29yKXRocm93IG5ldyBFcnJvcigiTm8gQXVkaW9Qcm9jZXNzb3IiKTt0aGlzLmlzQ29udGV4dFN0YXJ0ZWQmJnRoaXMuc3RvcENvbnRleHQoKX1wcm9jZXNzQXVkaW8odCl7aWYoIXRoaXMuYXVkaW9Qcm9jZXNzb3IpdGhyb3cgbmV3IEVycm9yKCJObyBBdWRpb1Byb2Nlc3NvciIpO3RoaXMuYXVkaW9Qcm9jZXNzb3IucHJvY2Vzc0F1ZGlvKHQpfXByb2Nlc3NBdWRpb1NBQigpe2lmKCF0aGlzLmNvbnRyb2xTQUJ8fCF0aGlzLmRhdGFTQUIpdGhyb3cgbmV3IEVycm9yKCJObyBTaGFyZWRBcnJheUJ1ZmZlcnMiKTtjb25zdCB0PXRoaXMuY29udHJvbFNBQltoXTtpZigwPT09dGhpcy5jb250cm9sU0FCW2NdJiZ0PjApe2NvbnN0IGU9dGhpcy5kYXRhU0FCLnN1YmFycmF5KDAsdCk7dGhpcy5jb250cm9sU0FCW2hdPTAsdGhpcy5jb250cm9sU0FCW25dPTAsZS5sZW5ndGg+MCYmdGhpcy5wcm9jZXNzQXVkaW8oZSl9fXN0YXJ0Q29udGV4dCh0KXt2YXIgZTtpZighdGhpcy5hdWRpb1Byb2Nlc3Nvcil0aHJvdyBFcnJvcigiTm8gQXVkaW9Qcm9jZXNzb3IiKTtpZih0aGlzLmlzQ29udGV4dFN0YXJ0ZWQpcmV0dXJuIHZvaWQgY29uc29sZS5lcnJvcigiW1dlYlNvY2tldENsaWVudF0iLCJjYW4ndCBzdGFydCBjb250ZXh0OiBhY3RpdmUgY29udGV4dCBleGlzdHMiKTt0aGlzLmF1ZGlvUHJvY2Vzc29yLnN0YXJ0Q29udGV4dCgpLHRoaXMuaXNDb250ZXh0U3RhcnRlZD0hMCx0aGlzLmNvbnRleHRTdGFydFRpbWU9dGhpcy5hdWRpb1Byb2Nlc3Nvci5nZXRTdHJlYW1Qb3NpdGlvbigpO2xldCBzPW51bGwhPT0oZT10aGlzLmRlZmF1bHRDb250ZXh0T3B0aW9ucykmJnZvaWQgMCE9PWU/ZTp7fTt2b2lkIDAhPT10JiYocz1PYmplY3QuYXNzaWduKE9iamVjdC5hc3NpZ24oe30scyksdCkpO2NvbnN0IGk9UyhzKTtpLmV2ZW50PSJzdGFydCIsdGhpcy5zZW5kKEpTT04uc3RyaW5naWZ5KGkpKX1zdG9wQ29udGV4dCgpe2lmKCF0aGlzLmF1ZGlvUHJvY2Vzc29yKXRocm93IEVycm9yKCJObyBBdWRpb1Byb2Nlc3NvciIpO2lmKCF0aGlzLmlzQ29udGV4dFN0YXJ0ZWQpcmV0dXJuIHZvaWQgY29uc29sZS5lcnJvcigiW1dlYlNvY2tldENsaWVudF0iLCJjYW4ndCBzdG9wIGNvbnRleHQ6IG5vIGFjdGl2ZSBjb250ZXh0Iik7dGhpcy5hdWRpb1Byb2Nlc3Nvci5zdG9wQ29udGV4dCgpLHRoaXMuaXNDb250ZXh0U3RhcnRlZD0hMTtjb25zdCB0PUpTT04uc3RyaW5naWZ5KHtldmVudDoic3RvcCJ9KTt0aGlzLnNlbmQodCl9c3dpdGNoQ29udGV4dCh0KXtpZighdGhpcy53ZWJzb2NrZXQpdGhyb3cgRXJyb3IoIldlYlNvY2tldCBpcyB1bmRlZmluZWQiKTtpZighdGhpcy5pc0NvbnRleHRTdGFydGVkKXJldHVybiB2b2lkIGNvbnNvbGUuZXJyb3IoIltXZWJTb2NrZXRDbGllbnRdIiwiY2FuJ3Qgc3dpdGNoIGNvbnRleHQ6IG5vIGFjdGl2ZSBjb250ZXh0Iik7aWYodm9pZCAwPT09KG51bGw9PXQ/dm9pZCAwOnQuYXBwSWQpKXJldHVybiB2b2lkIGNvbnNvbGUuZXJyb3IoIltXZWJTb2NrZXRDbGllbnRdIiwiY2FuJ3Qgc3dpdGNoIGNvbnRleHQ6IG5ldyBhcHAgaWQgaXMgdW5kZWZpbmVkIik7Y29uc3QgZT1KU09OLnN0cmluZ2lmeSh7ZXZlbnQ6InN0b3AifSk7dGhpcy5zZW5kKGUpO2NvbnN0IHM9Uyh0KTtzLmV2ZW50PSJzdGFydCIsdGhpcy5zZW5kKEpTT04uc3RyaW5naWZ5KHMpKX1jbG9zZVdlYnNvY2tldCh0PTEwMDUsZT0iTm8gU3RhdHVzIFJlY2VpdmVkIil7aWYodGhpcy5kZWJ1ZyYmY29uc29sZS5sb2coIltXZWJTb2NrZXRDbGllbnRdIiwiV2Vic29ja2V0IGNsb3NpbmciKSwhdGhpcy53ZWJzb2NrZXQpdGhyb3cgRXJyb3IoIldlYlNvY2tldCBpcyB1bmRlZmluZWQiKTt0aGlzLndlYnNvY2tldC5jbG9zZSh0LGUpfXNlbmQodCl7aWYoIXRoaXMud2Vic29ja2V0KXRocm93IG5ldyBFcnJvcigiTm8gV2Vic29ja2V0Iik7aWYodGhpcy53ZWJzb2NrZXQucmVhZHlTdGF0ZSE9PXRoaXMud2Vic29ja2V0Lk9QRU4pdGhyb3cgbmV3IEVycm9yKGBFeHBlY3RlZCBPUEVOIFdlYnNvY2tldCBzdGF0ZSwgYnV0IGdvdCAke3RoaXMud2Vic29ja2V0LnJlYWR5U3RhdGV9YCk7dHJ5e3RoaXMud2Vic29ja2V0LnNlbmQodCl9Y2F0Y2godCl7Y29uc29sZS5sb2coIltXZWJTb2NrZXRDbGllbnRdIiwic2VydmVyIGNvbm5lY3Rpb24gZXJyb3IiLHQpfX1zZXRDb250ZXh0T3B0aW9ucyh0KXt0aGlzLmRlZmF1bHRDb250ZXh0T3B0aW9ucz10fX1jb25zdCBkPXNlbGYsbT1uZXcgbChkKTtmdW5jdGlvbiBTKHQpe2NvbnN0IGU9e29wdGlvbnM6e3RpbWV6b25lOltJbnRsLkRhdGVUaW1lRm9ybWF0KCkucmVzb2x2ZWRPcHRpb25zKCkudGltZVpvbmVdfX07cmV0dXJuIHZvaWQgMD09PXR8fChlLm9wdGlvbnMudm9jYWJ1bGFyeT10LnZvY2FidWxhcnksZS5vcHRpb25zLnZvY2FidWxhcnlfYmlhcz10LnZvY2FidWxhcnlCaWFzLGUub3B0aW9ucy5zaWxlbmNlX3RyaWdnZXJlZF9zZWdtZW50YXRpb249dC5zaWxlbmNlVHJpZ2dlcmVkU2VnbWVudGF0aW9uLHZvaWQgMCE9PShudWxsPT10P3ZvaWQgMDp0LnRpbWV6b25lKSYmKGUub3B0aW9ucy50aW1lem9uZT1udWxsPT10P3ZvaWQgMDp0LnRpbWV6b25lKSx2b2lkIDAhPT10LmFwcElkJiYoZS5hcHBJZD10LmFwcElkKSksZX1yZXR1cm4gZC5vbm1lc3NhZ2U9ZnVuY3Rpb24odCl7c3dpdGNoKHQuZGF0YS50eXBlKXtjYXNlIGEuY29ubmVjdDptLmNvbm5lY3QodC5kYXRhLmFwaVVybCx0LmRhdGEuYXV0aFRva2VuLHQuZGF0YS50YXJnZXRTYW1wbGVSYXRlLHQuZGF0YS5kZWJ1Zyk7YnJlYWs7Y2FzZSBhLmluaXRBdWRpb1Byb2Nlc3NvcjptLmluaXRBdWRpb1Byb2Nlc3Nvcih0LmRhdGEuc291cmNlU2FtcGxlUmF0ZSx0LmRhdGEudmFkT3B0aW9ucyk7YnJlYWs7Y2FzZSBhLmFkanVzdEF1ZGlvUHJvY2Vzc29yOm0uYWRqdXN0QXVkaW9Qcm9jZXNzb3IodC5kYXRhLnBhcmFtcyk7YnJlYWs7Y2FzZSBhLlNFVF9TSEFSRURfQVJSQVlfQlVGRkVSUzptLnNldFNoYXJlZEFycmF5QnVmZmVycyh0LmRhdGEuY29udHJvbFNBQix0LmRhdGEuZGF0YVNBQik7YnJlYWs7Y2FzZSBhLkNMT1NFOm0uY2xvc2VXZWJzb2NrZXQoMWUzLCJDbG9zZSByZXF1ZXN0ZWQgYnkgY2xpZW50Iik7YnJlYWs7Y2FzZSBhLnN0YXJ0U3RyZWFtOm0uc3RhcnRTdHJlYW0oKTticmVhaztjYXNlIGEuc3RvcFN0cmVhbTptLnN0b3BTdHJlYW0oKTticmVhaztjYXNlIGEuU1RBUlRfQ09OVEVYVDptLnN0YXJ0Q29udGV4dCh0LmRhdGEub3B0aW9ucyk7YnJlYWs7Y2FzZSBhLlNXSVRDSF9DT05URVhUOm0uc3dpdGNoQ29udGV4dCh0LmRhdGEub3B0aW9ucyk7YnJlYWs7Y2FzZSBhLlNUT1BfQ09OVEVYVDptLnN0b3BDb250ZXh0KCk7YnJlYWs7Y2FzZSBhLkFVRElPOm0ucHJvY2Vzc0F1ZGlvKHQuZGF0YS5wYXlsb2FkKTticmVhaztjYXNlIGEuc2V0Q29udGV4dE9wdGlvbnM6bS5zZXRDb250ZXh0T3B0aW9ucyh0LmRhdGEub3B0aW9ucyk7YnJlYWs7ZGVmYXVsdDpjb25zb2xlLmxvZygiV09SS0VSIix0KX19LHQuY29udGV4dE9wdGlvbnNUb01zZz1TLHQuZGVmYXVsdD1sLE9iamVjdC5kZWZpbmVQcm9wZXJ0eSh0LCJfX2VzTW9kdWxlIix7dmFsdWU6ITB9KSx0fSh7fSk7Cgo=",this.worker.addEventListener("message",this.onWebsocketMessage)}onResponse(t){this.onResponseCb=t}onClose(t){this.onCloseCb=t}initialize(e,i,n,s){return o(this,void 0,void 0,(function*(){return this.worker.postMessage({type:t.ControllerSignal.connect,apiUrl:e,authToken:i,targetSampleRate:n,debug:s}),this.startCbs=[],this.stopCbs=[],new Promise((t=>{this.resolveInitialization=t}))}))}initAudioProcessor(e,i){return o(this,void 0,void 0,(function*(){return this.worker.postMessage({type:t.ControllerSignal.initAudioProcessor,sourceSampleRate:e,vadOptions:i}),new Promise((t=>{this.resolveSourceSampleRateSet=t}))}))}adjustAudioProcessor(e){this.worker.postMessage({type:t.ControllerSignal.adjustAudioProcessor,params:e})}close(){return o(this,void 0,void 0,(function*(){return new Promise(((e,i)=>{this.worker.postMessage({type:t.ControllerSignal.CLOSE,code:1e3,message:"Client has ended the session"}),e()}))}))}startStream(){return o(this,void 0,void 0,(function*(){this.worker.postMessage({type:t.ControllerSignal.startStream})}))}stopStream(){return o(this,void 0,void 0,(function*(){this.worker.postMessage({type:t.ControllerSignal.stopStream})}))}startContext(e){return o(this,void 0,void 0,(function*(){return new Promise(((i,n)=>{this.startCbs.push(((t,e)=>{void 0!==t?n(t):i(e)})),this.worker.postMessage({type:t.ControllerSignal.START_CONTEXT,options:e})}))}))}stopContext(){return o(this,void 0,void 0,(function*(){return new Promise(((e,i)=>{this.stopCbs.push(((t,n)=>{void 0!==t?i(t):e(n)})),this.worker.postMessage({type:t.ControllerSignal.STOP_CONTEXT})}))}))}switchContext(e){return o(this,void 0,void 0,(function*(){return new Promise(((i,n)=>{this.startCbs.push(((t,e)=>{void 0!==t?n(t):i(e)})),this.worker.postMessage({type:t.ControllerSignal.SWITCH_CONTEXT,options:e})}))}))}postMessage(t){this.worker.postMessage(t)}sendAudio(e){this.worker.postMessage({type:t.ControllerSignal.AUDIO,payload:e})}setContextOptions(e){return o(this,void 0,void 0,(function*(){this.worker.postMessage({type:t.ControllerSignal.setContextOptions,options:e})}))}}class F{constructor(){this.storage=window.localStorage}get(t){return this.storage.getItem(t)}set(t,e){this.storage.setItem(t,e)}getOrSet(t,e){let i=this.storage.getItem(t);return null===i&&(i=e(),this.storage.setItem(t,i)),i}}function f(t,e){return{intent:t.intent,isFinal:e}}const L="speechly-auth-token";class x{constructor(e){var i,o,a,d,l,c,r,h;if(this.activeContexts=new Map,this.maxReconnectAttemptCount=10,this.connectAttempt=0,this.connectPromise=null,this.cbs=[],this.state=t.DecoderState.Disconnected,this.handleWebsocketResponse=e=>{switch(this.debug&&console.log("[Decoder]","Received response",e),e.type){case t.WorkerSignal.VadSignalHigh:this.cbs.forEach((t=>t.onVadStateChange.forEach((t=>t(!0)))));break;case t.WorkerSignal.VadSignalLow:this.cbs.forEach((t=>t.onVadStateChange.forEach((t=>t(!1)))));break;case t.WebsocketResponseType.Started:this.activeContexts.set(e.audio_context,new Map),this.cbs.forEach((t=>t.contextStartedCbs.forEach((t=>t(e.audio_context)))));break;case t.WebsocketResponseType.Stopped:this.cbs.forEach((t=>t.contextStoppedCbs.forEach((t=>t(e.audio_context))))),this.activeContexts.delete(e.audio_context);break;default:this.handleSegmentUpdate(e)}},this.handleSegmentUpdate=e=>{var i;const{audio_context:n,segment_id:o,type:a}=e;let{data:d}=e;const l=this.activeContexts.get(n);if(void 0===l)return void console.warn("[Decoder]","Received response for non-existent context",n);let c=null!==(i=l.get(o))&&void 0!==i?i:new s(n,o);switch(a){case t.WebsocketResponseType.TentativeTranscript:const e=function(t){return t.words.map((({word:t,index:e,start_timestamp:i,end_timestamp:n})=>({value:t,index:e,startTimestamp:i,endTimestamp:n,isFinal:!1})))}(d),i=d.transcript;this.cbs.forEach((t=>t.tentativeTranscriptCbs.forEach((t=>t(n,o,e,i))))),c=c.updateTranscript(e);break;case t.WebsocketResponseType.Transcript:const s=function(t){return{value:t.word,index:t.index,startTimestamp:t.start_timestamp,endTimestamp:t.end_timestamp,isFinal:!0}}(d);this.cbs.forEach((t=>t.transcriptCbs.forEach((t=>t(n,o,s))))),c=c.updateTranscript([s]);break;case t.WebsocketResponseType.TentativeEntities:const a=function(t){return t.entities.map((({entity:t,value:e,start_position:i,end_position:n})=>({type:t,value:e,startPosition:i,endPosition:n,isFinal:!1})))}(d);this.cbs.forEach((t=>t.tentativeEntityCbs.forEach((t=>t(n,o,a))))),c=c.updateEntities(a);break;case t.WebsocketResponseType.Entity:const l=function(t){return{type:t.entity,value:t.value,startPosition:t.start_position,endPosition:t.end_position,isFinal:!0}}(d);this.cbs.forEach((t=>t.entityCbs.forEach((t=>t(n,o,l))))),c=c.updateEntities([l]);break;case t.WebsocketResponseType.TentativeIntent:const r=f(d,!1);this.cbs.forEach((t=>t.tentativeIntentCbs.forEach((t=>t(n,o,r))))),c=c.updateIntent(r);break;case t.WebsocketResponseType.Intent:const h=f(d,!0);this.cbs.forEach((t=>t.intentCbs.forEach((t=>t(n,o,h))))),c=c.updateIntent(h);break;case t.WebsocketResponseType.SegmentEnd:c=c.finalize()}l.set(o,c),this.activeContexts.set(n,l),this.logSegments&&console.info(c.toString()),this.cbs.forEach((t=>t.segmentChangeCbs.forEach((t=>t(c.toSegment())))))},this.handleWebsocketClosure=e=>{if(1e3===e.code)this.debug&&console.log("[Decoder]","Websocket closed",e);else{if(console.error("[Decoder]","Websocket closed due to error",e),void 0===this.deviceId)return void this.setState(t.DecoderState.Failed);this.setState(t.DecoderState.Disconnected),this.reconnect()}},this.logSegments=null!==(i=e.logSegments)&&void 0!==i&&i,this.appId=null!==(o=e.appId)&&void 0!==o?o:void 0,this.projectId=null!==(a=e.projectId)&&void 0!==a?a:void 0,this.sampleRate=null!==(d=e.sampleRate)&&void 0!==d?d:n,this.debug=null!==(l=e.debug)&&void 0!==l&&l,void 0!==this.appId&&void 0!==this.projectId)throw Error("[Decoder] You cannot use both appId and projectId at the same time");if(void 0===this.appId&&void 0===this.projectId)throw Error("[Decoder] Either an appId or a projectId is required");const u=null!==(c=e.apiUrl)&&void 0!==c?c:"https://api.speechly.com";this.apiUrl=function(t,e){const i=new URLSearchParams;return i.append("sampleRate",e.toString()),`${t}?${i.toString()}`}(u.replace("http","ws")+"/ws/v1",this.sampleRate),this.loginUrl=`${u}/login`,this.storage=null!==(r=e.storage)&&void 0!==r?r:new F,this.deviceId=this.storage.getOrSet("speechly-device-id",g),this.apiClient=new T,this.apiClient.onResponse(this.handleWebsocketResponse),this.apiClient.onClose(this.handleWebsocketClosure),(null===(h=e.connect)||void 0===h||h)&&this.connect()}getReconnectDelayMs(t){return 100*Math.pow(2,t)}sleep(t){return o(this,void 0,void 0,(function*(){return new Promise((e=>setTimeout(e,t)))}))}connect(){return o(this,void 0,void 0,(function*(){null===this.connectPromise&&(this.connectPromise=(()=>o(this,void 0,void 0,(function*(){const e=this.storage.get(L);if(null!=e&&N(e,this.projectId,this.appId,this.deviceId))this.authToken=e;else try{this.authToken=yield function(t,e,i,n,s=fetch,a=Date.now){var d;return o(this,void 0,void 0,(function*(){let o;o=void 0!==e?{projectId:e,deviceId:n}:{appId:i,deviceId:n};const l=yield s(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(o)}),c=yield l.json();if(200!==l.status)throw Error(null!==(d=c.error)&&void 0!==d?d:`Speechly API login request failed with ${l.status}`);if(void 0===c.access_token)throw Error("Invalid login response from Speechly API");if(!N(c.access_token,e,i,n,a))throw Error("Invalid token received from Speechly API");return c.access_token}))}(this.loginUrl,this.projectId,this.appId,this.deviceId),this.storage.set(L,this.authToken)}catch(e){throw this.setState(t.DecoderState.Failed),e}yield this.apiClient.initialize(this.apiUrl,this.authToken,this.sampleRate,this.debug),this.advanceState(t.DecoderState.Connected)})))()),yield this.connectPromise}))}adjustAudioProcessor(t){this.apiClient.adjustAudioProcessor(t)}close(){return o(this,void 0,void 0,(function*(){let e;try{yield this.apiClient.close()}catch(t){e=t.message}if(this.activeContexts.clear(),this.connectPromise=null,this.setState(t.DecoderState.Disconnected),void 0!==e)throw Error(e)}))}startStream(){return o(this,void 0,void 0,(function*(){yield this.apiClient.startStream()}))}stopStream(){return o(this,void 0,void 0,(function*(){this.state===t.DecoderState.Active&&(yield this.stopContext(0)),yield this.apiClient.stopStream()}))}startContext(e){return o(this,void 0,void 0,(function*(){if(this.state===t.DecoderState.Failed)throw Error("[Decoder] startContext cannot be run in Failed state.");if(this.state<t.DecoderState.Connected)yield this.connect();else if(this.state>t.DecoderState.Connected)throw Error("[Decoder] Unable to complete startContext: Expected Connected state, but was in "+G(this.state)+".");let n;if(this.setState(t.DecoderState.Active),null!=this.projectId)n=yield this.apiClient.startContext(null==e?void 0:e.appId);else{if(null!=(null==e?void 0:e.appId)&&this.appId!==(null==e?void 0:e.appId))throw this.setState(t.DecoderState.Failed),i;n=yield this.apiClient.startContext()}if(this.state<t.DecoderState.Active)throw Error("[Decoder] Unable to complete startContext: Problem acquiring contextId");return n}))}sendAudio(t){this.apiClient.sendAudio(t)}stopContext(e){return o(this,void 0,void 0,(function*(){if(this.state===t.DecoderState.Failed)throw Error("[Decoder] stopContext cannot be run in unrecovable error state.");if(this.state!==t.DecoderState.Active)throw Error("[Decoder] Unable to complete stopContext: Expected Active state, but was in "+G(this.state)+".");e>0&&(yield this.sleep(e)),this.apiClient.stopContext(),this.setState(t.DecoderState.Connected)}))}switchContext(e){return o(this,void 0,void 0,(function*(){if(this.state!==t.DecoderState.Active)throw Error("[Decoder] Unable to complete switchContext: Expected Active state, but was in "+G(this.state)+".");const i=yield this.apiClient.switchContext(e);this.activeContexts.set(i,new Map)}))}registerListener(t){this.cbs.push(t)}initAudioProcessor(t,e){return o(this,void 0,void 0,(function*(){this.sampleRate=t,yield this.apiClient.initAudioProcessor(t,e)}))}useSharedArrayBuffers(t,e){this.apiClient.postMessage({type:"SET_SHARED_ARRAY_BUFFERS",controlSAB:t,dataSAB:e})}setContextOptions(t){return o(this,void 0,void 0,(function*(){yield this.apiClient.setContextOptions(t)}))}reconnect(){return o(this,void 0,void 0,(function*(){this.debug&&console.log("[Decoder]","Reconnecting...",this.connectAttempt),this.connectPromise=null,this.connectAttempt<this.maxReconnectAttemptCount?(yield this.sleep(this.getReconnectDelayMs(this.connectAttempt++)),yield this.connect()):console.error("[Decoder] Maximum reconnect count reached, giving up automatic reconnect.")}))}advanceState(t){this.state>=t||this.setState(t)}setState(t){this.state!==t&&(this.debug&&console.log("[Decoder]",G(this.state),"->",G(t)),this.state=t,this.cbs.forEach((e=>{var i;return null===(i=e.stateChangeCbs)||void 0===i?void 0:i.forEach((e=>e(t)))})))}}t.BrowserClient=class{constructor(t){var e,i;this.contextStopDelay=250,this.debug=!1,this.initialized=!1,this.isStreaming=!1,this.isStreamAutoStarted=!1,this.active=!1,this.listeningPromise=null,this.stats={maxSignalEnergy:0,sentSamples:0};const n=window.navigator.mediaDevices.getSupportedConstraints();this.nativeResamplingSupported=!0===n.sampleRate,this.isMobileSafari=["iPad Simulator","iPhone Simulator","iPod Simulator","iPad","iPhone","iPod"].indexOf(navigator.platform)>=0||navigator.userAgent.includes("Mac")&&"ontouchend"in document,this.isSafari=this.isMobileSafari||void 0!==window.safari,this.useSAB=!this.isSafari,this.vadOptions=Object.assign(Object.assign({},b),t.vad),this.debug=null===(e=t.debug)||void 0===e||e,this.callbacks=new Z,this.callbacks.onVadStateChange.push(this.autoControlListening.bind(this)),this.decoder=null!==(i=t.decoder)&&void 0!==i?i:new x(t),this.decoder.registerListener(this.callbacks)}initialize(t){var i,s;return o(this,void 0,void 0,(function*(){if(!this.initialized){this.initialized=!0,this.debug&&console.log("[BrowserClient]","initializing"),yield this.decoder.connect();try{const t={};if(this.nativeResamplingSupported&&(t.sampleRate=n),void 0!==window.webkitAudioContext)try{this.audioContext=new window.webkitAudioContext(t)}catch(t){this.debug&&console.log("[BrowserClient]","creating audioContext without samplerate conversion",t),this.audioContext=new window.webkitAudioContext}else this.audioContext=new window.AudioContext(t),void 0!==window.webkitAudioContext&&(yield this.audioContext.resume())}catch(t){throw e}if(this.isSafari||void 0===window.AudioWorkletNode){if(this.debug&&console.log("[BrowserClient]","using ScriptProcessorNode"),void 0!==window.webkitAudioContext){const t=this.audioContext.sampleRate/n,e=4096*Math.pow(2,Math.ceil(Math.log(t)/Math.log(2)));this.audioProcessor=this.audioContext.createScriptProcessor(e,1,1)}else this.audioProcessor=this.audioContext.createScriptProcessor(void 0,1,1);this.audioProcessor.connect(this.audioContext.destination),this.audioProcessor.addEventListener("audioprocess",(t=>{this.handleAudio(t.inputBuffer.getChannelData(0))}))}else{this.debug&&console.log("[BrowserClient]","using AudioWorkletNode");const t=new Blob(["\n// Indices for the Control SAB.\nconst CONTROL = {\n 'WRITE_INDEX': 0,\n 'FRAMES_AVAILABLE': 1,\n 'LOCK': 2,\n};\n\nclass SpeechlyProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n\n this._initialized = false;\n this.debug = false;\n this.port.onmessage = this._initialize.bind(this);\n }\n\n _initialize(event) {\n this.controlSAB = new Int32Array(event.data.controlSAB);\n this.dataSAB = new Float32Array(event.data.dataSAB);\n this.debug = event.data.debug;\n if (this.debug) {\n console.log('[BrowserClient AudioWorkletNode]', 'initializing audioworklet');\n }\n this.sharedBufferSize = this.dataSAB.length;\n this.buffer = new Float32Array(0);\n this._initialized = true;\n }\n\n _transferDataToSharedBuffer(data) {\n this.controlSAB[CONTROL.LOCK] = 1;\n let inputWriteIndex = this.controlSAB[CONTROL.WRITE_INDEX];\n if (this.controlSAB[CONTROL.FRAMES_AVAILABLE] > 0) {\n if (inputWriteIndex + data.length > this.sharedBufferSize) {\n // console.log('buffer overflow')\n inputWriteIndex = 0;\n }\n }\n this.dataSAB.set(data, inputWriteIndex);\n this.controlSAB[CONTROL.WRITE_INDEX] = inputWriteIndex + data.length;\n this.controlSAB[CONTROL.FRAMES_AVAILABLE] = inputWriteIndex + data.length;\n this.controlSAB[CONTROL.LOCK] = 0;\n }\n\n _pushData(data) {\n if (this.debug) {\n const signalEnergy = getStandardDeviation(data)\n this.port.postMessage({\n type: 'STATS',\n signalEnergy: signalEnergy,\n samples: data.length,\n });\n }\n\n if (this.buffer.length > this.sharedBufferSize) {\n const dataToTransfer = this.buffer.subarray(0, this.sharedBufferSize);\n this._transferDataToSharedBuffer(dataToTransfer);\n this.buffer = this.buffer.subarray(this.sharedBufferSize);\n }\n let concat = new Float32Array(this.buffer.length + data.length);\n concat.set(this.buffer);\n concat.set(data, this.buffer.length);\n this.buffer = concat;\n }\n\n process(inputs, outputs, parameters) {\n const inputChannelData = inputs[0][0];\n if (inputChannelData !== undefined) {\n if (this.controlSAB && this.dataSAB) {\n this._pushData(inputChannelData);\n } else {\n this.port.postMessage({\n type: 'DATA',\n frames: inputChannelData\n });\n }\n }\n\n return true;\n }\n}\n\nfunction getStandardDeviation(array) {\n const n = array.length\n const mean = array.reduce((a, b) => a + b) / n\n return Math.sqrt(array.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n)\n}\n\nregisterProcessor('speechly-worklet', SpeechlyProcessor);\n"],{type:"text/javascript"}),e=window.URL.createObjectURL(t);if(yield this.audioContext.audioWorklet.addModule(e),this.speechlyNode=new AudioWorkletNode(this.audioContext,"speechly-worklet"),this.speechlyNode.connect(this.audioContext.destination),this.useSAB&&void 0!==window.SharedArrayBuffer){this.debug&&console.log("[BrowserClient]","using SharedArrayBuffer");const t=new window.SharedArrayBuffer(4*Int32Array.BYTES_PER_ELEMENT),e=new window.SharedArrayBuffer(1024*Float32Array.BYTES_PER_ELEMENT);this.decoder.useSharedArrayBuffers(t,e),this.speechlyNode.port.postMessage({type:"SET_SHARED_ARRAY_BUFFERS",controlSAB:t,dataSAB:e,debug:this.debug})}else this.debug&&console.log("[BrowserClient]","can not use SharedArrayBuffer");this.speechlyNode.port.onmessage=t=>{switch(t.data.type){case"STATS":t.data.signalEnergy>this.stats.maxSignalEnergy&&(this.stats.maxSignalEnergy=t.data.signalEnergy),this.stats.sentSamples+=parseInt(t.data.samples);break;case"DATA":this.handleAudio(t.data.frames)}}}this.debug&&console.log("[BrowserClient]","audioContext sampleRate is",null===(i=this.audioContext)||void 0===i?void 0:i.sampleRate),yield this.decoder.initAudioProcessor(null===(s=this.audioContext)||void 0===s?void 0:s.sampleRate,this.vadOptions),this.vadOptions&&(yield this.startStream()),(null==t?void 0:t.mediaStream)&&(yield this.attach(null==t?void 0:t.mediaStream))}}))}adjustAudioProcessor(t){t.vad&&(this.vadOptions=Object.assign(Object.assign({},this.vadOptions),t.vad)),this.decoder.adjustAudioProcessor(t)}close(){var t,e,i;return o(this,void 0,void 0,(function*(){yield this.detach(),null!==this.speechlyNode&&(null===(t=this.speechlyNode)||void 0===t||t.port.close(),null===(e=this.speechlyNode)||void 0===e||e.disconnect()),void 0!==this.audioProcessor&&(null===(i=this.audioProcessor)||void 0===i||i.disconnect()),yield this.decoder.close(),this.initialized=!1}))}attach(t){var e,i,n,s,a,d;return o(this,void 0,void 0,(function*(){if(yield this.initialize(),yield this.detach(),this.stream=null===(e=this.audioContext)||void 0===e?void 0:e.createMediaStreamSource(t),"running"!==(null===(i=this.audioContext)||void 0===i?void 0:i.state)&&(this.debug&&console.log("[BrowserClient]","audioContext resume required, state is",null===(n=this.audioContext)||void 0===n?void 0:n.state),yield null===(s=this.audioContext)||void 0===s?void 0:s.resume()),this.speechlyNode)null===(a=this.stream)||void 0===a||a.connect(this.speechlyNode);else{if(!this.audioProcessor)throw Error("[BrowserClient] cannot attach to mediaStream, not initialized");null===(d=this.stream)||void 0===d||d.connect(this.audioProcessor)}}))}detach(){return o(this,void 0,void 0,(function*(){this.active&&(yield this.stop(0)),this.stream&&(this.stream.disconnect(),this.stream=void 0)}))}uploadAudioData(t,e){var i,n,s;return o(this,void 0,void 0,(function*(){yield this.initialize();const o=yield null===(i=this.audioContext)||void 0===i?void 0:i.decodeAudioData(t);if(void 0===o)throw Error("Could not decode audioData");const a=o.getChannelData(0);if(o.numberOfChannels>1){const t=o.getChannelData(1);for(let e=0;e<a.length;e++)a[e]=(a[e]+t[e])/2}let d;yield this.adjustAudioProcessor({immediate:!0}),yield this.startStream();const l=(null===(n=this.vadOptions)||void 0===n?void 0:n.enabled)&&(null===(s=this.vadOptions)||void 0===s?void 0:s.controlListening);let c;d=l?"multiple context ids":yield this.start(e);for(let t=0;t<a.length;t+=16e3){const e=t+16e3;c=e>a.length?a.slice(t):a.slice(t,e),this.handleAudio(c)}return l||(yield this.stop(0)),yield this.stopStream(),yield this.adjustAudioProcessor({immediate:!1}),d}))}startStream(t){return o(this,void 0,void 0,(function*(){yield this.decoder.startStream(t),this.isStreaming=!0}))}stopStream(){return o(this,void 0,void 0,(function*(){yield this.decoder.stopStream(),this.isStreaming=!1,this.isStreamAutoStarted=!1}))}queueTask(t){return o(this,void 0,void 0,(function*(){const e=this.listeningPromise;return this.listeningPromise=(()=>o(this,void 0,void 0,(function*(){return yield e,t()})))(),this.listeningPromise}))}start(t){return o(this,void 0,void 0,(function*(){this.active=!0;return yield this.queueTask((()=>o(this,void 0,void 0,(function*(){yield this.initialize(),this.isStreaming||(yield this.startStream(t),this.isStreamAutoStarted=!0);return this.decoder.startContext(t)}))))}))}stop(t=this.contextStopDelay){return o(this,void 0,void 0,(function*(){this.active=!1,yield this.queueTask((()=>o(this,void 0,void 0,(function*(){try{yield this.decoder.stopContext(t),this.isStreaming&&this.isStreamAutoStarted&&(yield this.stopStream()),0===this.stats.sentSamples&&console.warn("[BrowserClient]","audioContext contained no audio data")}catch(t){console.warn("[BrowserClient]","stop() failed",t)}finally{this.stats.sentSamples=0}}))))}))}autoControlListening(t){var e;this.debug&&console.log("[BrowserClient]","autoControlListening",t),(null===(e=this.vadOptions)||void 0===e?void 0:e.controlListening)&&(t?this.active||this.start():this.active&&this.stop(0))}handleAudio(t){this.isStreaming&&(this.stats.sentSamples+=t.length,this.decoder.sendAudio(t))}isActive(){return this.active}onStart(t){this.callbacks.contextStartedCbs.push(t)}onStop(t){this.callbacks.contextStoppedCbs.push(t)}onSegmentChange(t){this.callbacks.segmentChangeCbs.push(t)}onTranscript(t){this.callbacks.transcriptCbs.push(t)}onEntity(t){this.callbacks.entityCbs.push(t)}onIntent(t){this.callbacks.intentCbs.push(t)}onTentativeTranscript(t){this.callbacks.tentativeTranscriptCbs.push(t)}onTentativeEntities(t){this.callbacks.tentativeEntityCbs.push(t)}onTentativeIntent(t){this.callbacks.tentativeIntentCbs.push(t)}onStateChange(t){this.callbacks.stateChangeCbs.push(t)}},t.BrowserMicrophone=class{constructor(){this.muted=!1,this.initialized=!1;try{const t=window.navigator.mediaDevices.getSupportedConstraints();this.nativeResamplingSupported=!0===t.sampleRate,this.autoGainControlSupported=!0===t.autoGainControl}catch(t){this.nativeResamplingSupported=!1,this.autoGainControlSupported=!1}}initialize(){var t;return o(this,void 0,void 0,(function*(){if(this.initialized)return;if(void 0===(null===(t=window.navigator)||void 0===t?void 0:t.mediaDevices))throw e;const i={video:!1};this.nativeResamplingSupported||this.autoGainControlSupported?i.audio={sampleRate:n,autoGainControl:this.autoGainControlSupported}:i.audio=!0;try{this.mediaStream=yield window.navigator.mediaDevices.getUserMedia(i)}catch(t){throw console.error(t),l}this.initialized=!0,this.muted=!0}))}close(){return o(this,void 0,void 0,(function*(){if(!this.initialized)throw a;this.muted=!0;this.mediaStream.getTracks().forEach((t=>t.stop())),this.mediaStream=void 0,this.initialized=!1}))}isRecording(){return!this.muted}},t.CloudDecoder=x,t.DefaultSampleRate=n,t.ErrAlreadyInitialized=d,t.ErrAppIdChangeWithoutProjectLogin=i,t.ErrDeviceNotSupported=e,t.ErrKeyNotFound=p,t.ErrNoAudioConsent=l,t.ErrNoStorageSupport=u,t.ErrNotInitialized=a,t.EventCallbacks=Z,t.SegmentState=s,t.VadDefaultOptions=b,t.stateToString=G,Object.defineProperty(t,"__esModule",{value:!0})})); | ||
//# sourceMappingURL=speechly.umd.min.js.map |
@@ -1,4 +0,4 @@ | ||
import EnergyTresholdVAD from './EnergyTresholdVAD'; | ||
import EnergyThresholdVAD from './EnergyThresholdVAD'; | ||
declare class AudioProcessor { | ||
vad?: EnergyTresholdVAD; | ||
vad?: EnergyThresholdVAD; | ||
/** | ||
@@ -14,5 +14,4 @@ * Returns true when StartContext is called and expecting StopContext next | ||
utteranceSerial: number; | ||
sendAudio: (samples: Float32Array, startIndex: number, length: number) => void; | ||
onVadSignalLow: () => void; | ||
onVadSignalHigh: () => void; | ||
onSendAudio: (samples: Float32Array, startIndex: number, length: number) => void; | ||
onVadStateChange: (isSignalDetected: boolean) => void; | ||
private readonly inputSampleRate; | ||
@@ -27,3 +26,3 @@ private readonly internalSampleRate; | ||
private streamFramePos; | ||
private isSignalDetected; | ||
private wasSignalDetected; | ||
constructor(inputSampleRate: number, outputSampleRate: number, historyFrames: number); | ||
@@ -35,2 +34,6 @@ startContext(): void; | ||
/** | ||
* @returns current position in stream in seconds | ||
*/ | ||
getStreamPosition(): number; | ||
/** | ||
* Process speech audio samples from a microphone or other audio source. | ||
@@ -56,5 +59,4 @@ * | ||
private processFrame; | ||
private analyzeAudioFrame; | ||
private autoControlListening; | ||
private processVadFrame; | ||
} | ||
export default AudioProcessor; |
@@ -17,3 +17,3 @@ import { DecoderState, DecoderOptions, ContextOptions, AudioProcessorParameters } from './types'; | ||
private readonly callbacks; | ||
private readonly vadOptions?; | ||
private vadOptions; | ||
private audioContext?; | ||
@@ -35,3 +35,2 @@ private initialized; | ||
constructor(options: DecoderOptions); | ||
onVadStateChange(active: boolean): void; | ||
/** | ||
@@ -87,3 +86,3 @@ * Create an AudioContext for resampling audio. | ||
* @param options - any custom options for the audio processing. | ||
* @returns The contextId of the active audio context. | ||
* @returns The contextId of the active audio context | ||
*/ | ||
@@ -94,6 +93,5 @@ start(options?: ContextOptions): Promise<string>; | ||
* If there is no active audio context, a warning is logged to console. | ||
* | ||
* @returns The contextId of the stopped context, or null if no context is active. | ||
*/ | ||
stop(stopDelayMs?: number): Promise<string | null>; | ||
stop(stopDelayMs?: number): Promise<void>; | ||
private autoControlListening; | ||
private handleAudio; | ||
@@ -105,2 +103,12 @@ /** | ||
/** | ||
* Adds a listener for start events | ||
* @param cb - the callback to invoke on context start | ||
*/ | ||
onStart(cb: (contextId: string) => void): void; | ||
/** | ||
* Adds a listener for stop events | ||
* @param cb - the callback to invoke on context stop | ||
*/ | ||
onStop(cb: (contextId: string) => void): void; | ||
/** | ||
* Adds a listener for current segment change events. | ||
@@ -107,0 +115,0 @@ * @param cb - the callback to invoke on segment change events. |
@@ -45,3 +45,3 @@ import { DecoderOptions, DecoderState, EventCallbacks, ContextOptions, VadOptions, AudioProcessorParameters } from './types'; | ||
close(): Promise<void>; | ||
startStream(defaultContextOptions?: ContextOptions): Promise<void>; | ||
startStream(): Promise<void>; | ||
stopStream(): Promise<void>; | ||
@@ -48,0 +48,0 @@ /** |
@@ -105,5 +105,11 @@ import { Segment, Word, Entity, Intent } from '../speechly'; | ||
controlListening: boolean; | ||
/** | ||
* Set audio worker | ||
* to ‘immediate audio processor’ mode where it can control start/stop context internally at its own pace. | ||
*/ | ||
immediate?: boolean; | ||
} | ||
export interface AudioProcessorParameters { | ||
vad?: Partial<VadOptions>; | ||
immediate?: boolean; | ||
} | ||
@@ -151,7 +157,2 @@ /** | ||
/** | ||
* BrowserClient.uploadAudioData internally uses this to set audio worker | ||
* to ‘immediate audio processor’ mode where it can control start/stop context at its own pace. | ||
*/ | ||
immediate?: boolean; | ||
/** | ||
* Inference time vocabulary. | ||
@@ -158,0 +159,0 @@ */ |
@@ -243,3 +243,3 @@ import { AudioProcessorParameters, ContextOptions, VadOptions } from '../client'; | ||
postMessage(message: Object): void; | ||
startStream(defaultContextOptions?: ContextOptions): Promise<void>; | ||
startStream(): Promise<void>; | ||
stopStream(): Promise<void>; | ||
@@ -246,0 +246,0 @@ /** |
@@ -22,3 +22,3 @@ import { APIClient, ResponseCallback, CloseCallback } from './types'; | ||
close(): Promise<void>; | ||
startStream(defaultContextOptions?: ContextOptions): Promise<void>; | ||
startStream(): Promise<void>; | ||
stopStream(): Promise<void>; | ||
@@ -25,0 +25,0 @@ startContext(options?: ContextOptions): Promise<string>; |
@@ -6,2 +6,3 @@ import { AudioProcessorParameters, ContextOptions, VadOptions } from '../client'; | ||
private isContextStarted; | ||
private contextStartTime; | ||
private websocket?; | ||
@@ -11,2 +12,3 @@ private audioProcessor?; | ||
private dataSAB?; | ||
private immediateMode; | ||
private readonly frameMillis; | ||
@@ -16,3 +18,2 @@ private readonly outputAudioFrame; | ||
private defaultContextOptions?; | ||
private vadContextOptions?; | ||
constructor(ctx: Worker); | ||
@@ -27,3 +28,3 @@ connect(apiUrl: string, authToken: string, targetSampleRate: number, debug: boolean): void; | ||
setSharedArrayBuffers(controlSAB: number, dataSAB: number): void; | ||
startStream(vadContextOptions?: ContextOptions): void; | ||
startStream(): void; | ||
stopStream(): void; | ||
@@ -30,0 +31,0 @@ /** |
{ | ||
"name": "@speechly/browser-client", | ||
"version": "2.1.0-beta.4", | ||
"version": "2.1.0-beta.5", | ||
"description": "Browser client for Speechly API", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
import AudioTools from './AudioTools' | ||
import EnergyTresholdVAD from './EnergyTresholdVAD' | ||
import EnergyThresholdVAD from './EnergyThresholdVAD' | ||
class AudioProcessor { | ||
public vad?: EnergyTresholdVAD | ||
public vad?: EnergyThresholdVAD | ||
@@ -18,5 +18,4 @@ /** | ||
public utteranceSerial = -1 | ||
public sendAudio = (samples: Float32Array, startIndex: number, length: number): void => {} | ||
public onVadSignalLow = (): void => {} | ||
public onVadSignalHigh = (): void => {} | ||
public onSendAudio = (samples: Float32Array, startIndex: number, length: number): void => {} | ||
public onVadStateChange = (isSignalDetected: boolean): void => {} | ||
@@ -34,3 +33,3 @@ private readonly inputSampleRate: number = 16000 | ||
private streamFramePos: number = 0 | ||
private isSignalDetected: boolean = false | ||
private wasSignalDetected: boolean = false | ||
@@ -71,2 +70,9 @@ constructor(inputSampleRate: number, outputSampleRate: number, historyFrames: number) { | ||
/** | ||
* @returns current position in stream in seconds | ||
*/ | ||
public getStreamPosition(): number { | ||
return this.streamSamplePos / this.inputSampleRate | ||
} | ||
/** | ||
* Process speech audio samples from a microphone or other audio source. | ||
@@ -137,3 +143,3 @@ * | ||
while (historyFrameIndex !== this.currentFrameNumber) { | ||
this.sendAudio(this.sampleRingBuffer, historyFrameIndex * this.frameSamples, this.frameSamples) | ||
this.onSendAudio(this.sampleRingBuffer, historyFrameIndex * this.frameSamples, this.frameSamples) | ||
this.samplesSent += this.frameSamples | ||
@@ -143,3 +149,3 @@ historyFrameIndex = (historyFrameIndex + 1) % this.historyFrames | ||
} | ||
this.sendAudio(this.sampleRingBuffer, frameBase, subFrameSamples) | ||
this.onSendAudio(this.sampleRingBuffer, frameBase, subFrameSamples) | ||
this.samplesSent += subFrameSamples | ||
@@ -159,23 +165,12 @@ } | ||
private processFrame(floats: Float32Array, start = 0, length = -1): void { | ||
this.analyzeAudioFrame(floats, start, length) | ||
this.autoControlListening() | ||
} | ||
private analyzeAudioFrame(waveData: Float32Array, s: number, frameSamples: number): void { | ||
if (this.vad?.vadOptions.enabled) { | ||
this.vad.processFrame(waveData, s, frameSamples) | ||
this.processVadFrame(this.vad, floats, start, length) | ||
} | ||
} | ||
private autoControlListening(): void { | ||
if (this.vad?.vadOptions.enabled) { | ||
if (!this.isSignalDetected && this.vad.isSignalDetected) { | ||
this.onVadSignalHigh() | ||
this.isSignalDetected = true | ||
} | ||
if (this.isSignalDetected && !this.vad.isSignalDetected) { | ||
this.onVadSignalLow() | ||
this.isSignalDetected = false | ||
} | ||
private processVadFrame(vad: EnergyThresholdVAD, floats: Float32Array, start = 0, length = -1): void { | ||
vad.processFrame(floats, start, length) | ||
if (vad.isSignalDetected !== this.wasSignalDetected) { | ||
this.onVadStateChange(vad.isSignalDetected) | ||
this.wasSignalDetected = vad.isSignalDetected | ||
} | ||
@@ -182,0 +177,0 @@ } |
@@ -21,3 +21,3 @@ import { DecoderState, EventCallbacks, DecoderOptions, ContextOptions, VadOptions, VadDefaultOptions, AudioProcessorParameters } from './types' | ||
private readonly callbacks: EventCallbacks | ||
private readonly vadOptions?: VadOptions | ||
private vadOptions: VadOptions | ||
@@ -56,3 +56,3 @@ private audioContext?: AudioContext | ||
this.callbacks = new EventCallbacks() | ||
this.callbacks.onVadStateChange.push(this.onVadStateChange.bind(this)) | ||
this.callbacks.onVadStateChange.push(this.autoControlListening.bind(this)) | ||
this.decoder = options.decoder ?? new CloudDecoder(options) | ||
@@ -62,17 +62,2 @@ this.decoder.registerListener(this.callbacks) | ||
onVadStateChange(active: boolean): void { | ||
if (this.debug) { | ||
console.log('[BrowserClient]', 'onVadStateChange', active) | ||
} | ||
if (this.vadOptions?.controlListening) { | ||
if (active) { | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
if (!this.active) this.start() | ||
} else { | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
if (this.active) this.stop(0) | ||
} | ||
} | ||
} | ||
/** | ||
@@ -213,2 +198,5 @@ * Create an AudioContext for resampling audio. | ||
adjustAudioProcessor(ap: AudioProcessorParameters): void { | ||
if (ap.vad) { | ||
this.vadOptions = { ...this.vadOptions, ...ap.vad } | ||
} | ||
this.decoder.adjustAudioProcessor(ap) | ||
@@ -295,6 +283,8 @@ } | ||
await this.startStream({ immediate: true }) | ||
await this.adjustAudioProcessor({ immediate: true }) | ||
await this.startStream() | ||
let contextId: string | ||
const vadActive = this.vadOptions?.enabled && this.vadOptions?.controlListening | ||
if (!vadActive) { | ||
@@ -322,2 +312,3 @@ contextId = await this.start(options) | ||
await this.stopStream() | ||
await this.adjustAudioProcessor({ immediate: false }) | ||
@@ -361,5 +352,7 @@ return contextId | ||
* @param options - any custom options for the audio processing. | ||
* @returns The contextId of the active audio context. | ||
* @returns The contextId of the active audio context | ||
*/ | ||
async start(options?: ContextOptions): Promise<string> { | ||
this.active = true | ||
const promise = await this.queueTask(async () => { | ||
@@ -373,3 +366,2 @@ await this.initialize() | ||
const startPromise = this.decoder.startContext(options) | ||
this.active = true | ||
return startPromise | ||
@@ -383,10 +375,9 @@ }) | ||
* If there is no active audio context, a warning is logged to console. | ||
* | ||
* @returns The contextId of the stopped context, or null if no context is active. | ||
*/ | ||
async stop(stopDelayMs = this.contextStopDelay): Promise<string | null> { | ||
const contextId = await this.queueTask(async () => { | ||
let contextId = null | ||
async stop(stopDelayMs = this.contextStopDelay): Promise<void> { | ||
this.active = false | ||
await this.queueTask(async () => { | ||
try { | ||
contextId = await this.decoder.stopContext(stopDelayMs) | ||
await this.decoder.stopContext(stopDelayMs) | ||
if (this.isStreaming && this.isStreamAutoStarted) { | ||
@@ -403,10 +394,22 @@ // Automatically control streaming for backwards compability | ||
} finally { | ||
this.active = false | ||
this.stats.sentSamples = 0 | ||
} | ||
return contextId | ||
}) | ||
return contextId | ||
} | ||
private autoControlListening(vadActive: boolean): void { | ||
if (this.debug) { | ||
console.log('[BrowserClient]', 'autoControlListening', vadActive) | ||
} | ||
if (this.vadOptions?.controlListening) { | ||
if (vadActive) { | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
if (!this.active) this.start() | ||
} else { | ||
// eslint-disable-next-line @typescript-eslint/no-floating-promises | ||
if (this.active) this.stop(0) | ||
} | ||
} | ||
} | ||
private handleAudio(array: Float32Array): void { | ||
@@ -427,2 +430,18 @@ if (this.isStreaming) { | ||
/** | ||
* Adds a listener for start events | ||
* @param cb - the callback to invoke on context start | ||
*/ | ||
onStart(cb: (contextId: string) => void): void { | ||
this.callbacks.contextStartedCbs.push(cb) | ||
} | ||
/** | ||
* Adds a listener for stop events | ||
* @param cb - the callback to invoke on context stop | ||
*/ | ||
onStop(cb: (contextId: string) => void): void { | ||
this.callbacks.contextStoppedCbs.push(cb) | ||
} | ||
/** | ||
* Adds a listener for current segment change events. | ||
@@ -429,0 +448,0 @@ * @param cb - the callback to invoke on segment change events. |
@@ -160,4 +160,4 @@ import { v4 as uuidv4 } from 'uuid' | ||
async startStream(defaultContextOptions?: ContextOptions): Promise<void> { | ||
await this.apiClient.startStream(defaultContextOptions) | ||
async startStream(): Promise<void> { | ||
await this.apiClient.startStream() | ||
} | ||
@@ -294,4 +294,4 @@ | ||
case WebsocketResponseType.Stopped: { | ||
this.cbs.forEach(cb => cb.contextStoppedCbs.forEach(f => f(response.audio_context))) | ||
this.activeContexts.delete(response.audio_context) | ||
this.cbs.forEach(cb => cb.contextStoppedCbs.forEach(f => f(response.audio_context))) | ||
break | ||
@@ -298,0 +298,0 @@ } |
@@ -125,2 +125,8 @@ import { Segment, Word, Entity, Intent } from '../speechly' | ||
controlListening: boolean | ||
/** | ||
* Set audio worker | ||
* to ‘immediate audio processor’ mode where it can control start/stop context internally at its own pace. | ||
*/ | ||
immediate?: boolean | ||
} | ||
@@ -130,2 +136,3 @@ | ||
vad?: Partial<VadOptions> | ||
immediate?: boolean | ||
} | ||
@@ -189,8 +196,2 @@ | ||
/** | ||
* BrowserClient.uploadAudioData internally uses this to set audio worker | ||
* to ‘immediate audio processor’ mode where it can control start/stop context at its own pace. | ||
*/ | ||
immediate?: boolean | ||
/** | ||
* Inference time vocabulary. | ||
@@ -197,0 +198,0 @@ */ |
@@ -273,3 +273,3 @@ import { AudioProcessorParameters, ContextOptions, VadOptions } from '../client' | ||
startStream(defaultContextOptions?: ContextOptions): Promise<void> | ||
startStream(): Promise<void> | ||
@@ -276,0 +276,0 @@ stopStream(): Promise<void> |
@@ -84,4 +84,4 @@ import { APIClient, ResponseCallback, CloseCallback, WebsocketResponse, WebsocketResponseType, WorkerSignal, ControllerSignal } from './types' | ||
async startStream(defaultContextOptions?: ContextOptions): Promise<void> { | ||
this.worker.postMessage({ type: ControllerSignal.startStream, options: defaultContextOptions }) | ||
async startStream(): Promise<void> { | ||
this.worker.postMessage({ type: ControllerSignal.startStream }) | ||
} | ||
@@ -88,0 +88,0 @@ |
import AudioProcessor from '../audioprocessing/AudioProcessor' | ||
import EnergyTresholdVAD from '../audioprocessing/EnergyTresholdVAD' | ||
import EnergyThresholdVAD from '../audioprocessing/EnergyThresholdVAD' | ||
import AudioTools from '../audioprocessing/AudioTools' | ||
@@ -47,2 +47,3 @@ import { ControllerSignal, WebsocketResponseType, WorkerSignal } from './types' | ||
private isContextStarted: boolean = false | ||
private contextStartTime: number = 0 | ||
private websocket?: WebSocket | ||
@@ -53,2 +54,3 @@ private audioProcessor?: AudioProcessor | ||
private immediateMode = false | ||
private readonly frameMillis = 30 | ||
@@ -59,4 +61,3 @@ private readonly outputAudioFrame: Int16Array = new Int16Array(this.frameMillis * this.targetSampleRate / 1000) | ||
private defaultContextOptions?: ContextOptions // setContextOptions modifies | ||
private vadContextOptions?: ContextOptions // startStream modifies | ||
private defaultContextOptions?: ContextOptions | ||
@@ -85,23 +86,22 @@ constructor(ctx: Worker) { | ||
if (vadOptions) { | ||
this.audioProcessor.vad = new EnergyTresholdVAD(vadOptions) | ||
this.audioProcessor.vad = new EnergyThresholdVAD(vadOptions) | ||
this.audioProcessor.onVadSignalHigh = () => { | ||
this.audioProcessor.onVadStateChange = (isSignalDetected: boolean) => { | ||
const currentVadOptions = this.audioProcessor?.vad?.vadOptions | ||
if (!(currentVadOptions?.enabled && currentVadOptions?.controlListening)) return | ||
if (!currentVadOptions) return | ||
if (this.defaultContextOptions?.immediate) { | ||
this.startContext(this.vadContextOptions) | ||
} else { | ||
this.workerCtx.postMessage({ type: WorkerSignal.VadSignalHigh }) | ||
if (isSignalDetected) { | ||
if (!this.immediateMode) { | ||
this.workerCtx.postMessage({ type: WorkerSignal.VadSignalHigh }) | ||
} else if (currentVadOptions.controlListening) { | ||
this.startContext(this.defaultContextOptions) | ||
} | ||
} | ||
} | ||
this.audioProcessor.onVadSignalLow = () => { | ||
const currentVadOptions = this.audioProcessor?.vad?.vadOptions | ||
if (!(currentVadOptions?.enabled && currentVadOptions?.controlListening)) return | ||
if (this.defaultContextOptions?.immediate) { | ||
this.stopContext() | ||
} else { | ||
this.workerCtx.postMessage({ type: WorkerSignal.VadSignalLow }) | ||
if (!isSignalDetected) { | ||
if (!this.immediateMode) { | ||
this.workerCtx.postMessage({ type: WorkerSignal.VadSignalLow }) | ||
} else if (currentVadOptions.controlListening) { | ||
this.stopContext() | ||
} | ||
} | ||
@@ -111,3 +111,3 @@ } | ||
this.audioProcessor.sendAudio = (floats: Float32Array, startIndex: number, length: number) => { | ||
this.audioProcessor.onSendAudio = (floats: Float32Array, startIndex: number, length: number) => { | ||
AudioTools.convertFloatToInt16(floats, this.outputAudioFrame, startIndex, length) | ||
@@ -130,2 +130,6 @@ this.send(this.outputAudioFrame) | ||
if (ap.immediate !== undefined) { | ||
this.immediateMode = ap.immediate | ||
} | ||
if (ap.vad) { | ||
@@ -149,3 +153,3 @@ if (!this.audioProcessor.vad) { | ||
startStream(vadContextOptions?: ContextOptions): void { | ||
startStream(): void { | ||
if (!this.audioProcessor) { | ||
@@ -155,3 +159,2 @@ throw new Error('No AudioProcessor') | ||
this.vadContextOptions = vadContextOptions | ||
this.audioProcessor.resetStream() | ||
@@ -169,4 +172,2 @@ } | ||
} | ||
this.vadContextOptions = undefined | ||
} | ||
@@ -216,2 +217,3 @@ | ||
this.isContextStarted = true | ||
this.contextStartTime = this.audioProcessor.getStreamPosition() | ||
@@ -372,3 +374,3 @@ let options: ContextOptions = this.defaultContextOptions ?? {} | ||
case ControllerSignal.startStream: | ||
websocketClient.startStream(e.data.options) | ||
websocketClient.startStream() | ||
break | ||
@@ -375,0 +377,0 @@ case ControllerSignal.stopStream: |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
6636
461784