From c2b43b69bc0c9f8aab49b4e4f95064346b0d8658 Mon Sep 17 00:00:00 2001 From: Kevin Nagurski Date: Tue, 24 Jan 2023 16:18:06 +0000 Subject: [PATCH] feat: [FFM-6489]: Allow side-loading of evaluations (#64) --- README.md | 6 +- dist/sdk.cjs.js | 12 +-- dist/sdk.client-iife.js | 12 +-- dist/sdk.client.js | 12 +-- dist/sdk.esm.js | 12 +-- dist/types.d.ts | 13 ++- examples/preact/package-lock.json | 2 +- examples/react-redux/package-lock.json | 2 +- examples/react/package-lock.json | 2 +- package-lock.json | 4 +- package.json | 3 +- src/index.ts | 106 ++++++++++++------------- src/types.ts | 13 ++- 13 files changed, 110 insertions(+), 89 deletions(-) diff --git a/README.md b/README.md index aa75d4a8..627694a6 100644 --- a/README.md +++ b/README.md @@ -120,22 +120,20 @@ In case you want to import this library directly (without having to use npm or y ```html ``` If you need to support old browsers which don't support ES Module: ```html - + ``` -Remember to change the version `1.7.0` in the unpkg url accordingly. - ## License Apache version 2. diff --git a/dist/sdk.cjs.js b/dist/sdk.cjs.js index 2e2cf5f9..4c5d9395 100644 --- a/dist/sdk.cjs.js +++ b/dist/sdk.cjs.js @@ -1,9 +1,9 @@ -var Ze=Object.create,Ae=Object.defineProperty,et=Object.getPrototypeOf,tt=Object.prototype.hasOwnProperty,nt=Object.getOwnPropertyNames,rt=Object.getOwnPropertyDescriptor;var q=Object.assign,Ue=a=>Ae(a,"__esModule",{value:!0});var it=(a,l)=>()=>(l||(l={exports:{}},a(l.exports,l)),l.exports),at=(a,l)=>{for(var S in l)Ae(a,S,{get:l[S],enumerable:!0})},ot=(a,l,S)=>{if(l&&typeof l=="object"||typeof l=="function")for(let g of nt(l))!tt.call(a,g)&&g!=="default"&&Ae(a,g,{get:()=>l[g],enumerable:!(S=rt(l,g))||S.enumerable});return a},Le=a=>ot(Ue(Ae(a!=null?Ze(et(a)):{},"default",a&&a.__esModule&&"default"in a?{get:()=>a.default,enumerable:!0}:{value:a,enumerable:!0})),a);var Oe=(a,l,S)=>new Promise((g,Y)=>{var z=U=>{try{H(S.next(U))}catch(O){Y(O)}},x=U=>{try{H(S.throw(U))}catch(O){Y(O)}},H=U=>U.done?g(U.value):Promise.resolve(U.value).then(z,x);H((S=S.apply(a,l)).next())});var Be=it((Pe,De)=>{(function(a){"use strict";var l=a.setTimeout,S=a.clearTimeout,g=a.XMLHttpRequest,Y=a.XDomainRequest,z=a.ActiveXObject,x=a.EventSource,H=a.document,U=a.Promise,O=a.fetch,ie=a.Response,ae=a.TextDecoder,ve=a.TextEncoder,b=a.AbortController;if(typeof window!="undefined"&&typeof H!="undefined"&&!("readyState"in H)&&H.body==null&&(H.readyState="loading",window.addEventListener("load",function(e){H.readyState="complete"},!1)),g==null&&z!=null&&(g=function(){return new z("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function n(){}return n.prototype=e,new n}),Date.now||(Date.now=function(){return new Date().getTime()}),b==null){var D=O;O=function(e,n){var r=n.signal;return D(e,{headers:n.headers,credentials:n.credentials,cache:n.cache}).then(function(t){var d=t.body.getReader();return r._reader=d,r._aborted&&r._reader.cancel(),{status:t.status,statusText:t.statusText,headers:t.headers,body:{getReader:function(){return d}}}})},b=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function L(){this.bitsNeeded=0,this.codePoint=0}L.prototype.decode=function(e){function n(p,E,c){if(c===1)return p>=128>>E&&p<=2048>>E&&p<=57344>>E&&p<=65536>>E&&p<>6>15?3:E>31?2:1;if(p===6*2)return E>15?3:2;if(p===6*3)return 3;throw new Error}for(var t=65533,d="",f=this.bitsNeeded,v=this.codePoint,y=0;y191||!n(v<<6|h&63,f-6,r(f,v)))&&(f=0,v=t,d+=String.fromCharCode(v)),f===0?(h>=0&&h<=127?(f=0,v=h):h>=192&&h<=223?(f=6*1,v=h&31):h>=224&&h<=239?(f=6*2,v=h&15):h>=240&&h<=247?(f=6*3,v=h&7):(f=0,v=t),f!==0&&!n(v,f,r(f,v))&&(f=0,v=t)):(f-=6,v=v<<6|h&63),f===0&&(v<=65535?d+=String.fromCharCode(v):(d+=String.fromCharCode(55296+(v-65535-1>>10)),d+=String.fromCharCode(56320+(v-65535-1&1023))))}return this.bitsNeeded=f,this.codePoint=v,d};var R=function(){try{return new ae().decode(new ve().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(ae==null||ve==null||!R())&&(ae=L);var M=function(){};function Q(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=M,this.onload=M,this.onerror=M,this.onreadystatechange=M,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=M}Q.prototype.open=function(e,n){this._abort(!0);var r=this,t=this._xhr,d=1,f=0;this._abort=function(c){r._sendTimeout!==0&&(S(r._sendTimeout),r._sendTimeout=0),(d===1||d===2||d===3)&&(d=4,t.onload=M,t.onerror=M,t.onabort=M,t.onprogress=M,t.onreadystatechange=M,t.abort(),f!==0&&(S(f),f=0),c||(r.readyState=4,r.onabort(null),r.onreadystatechange())),d=0};var v=function(){if(d===1){var c=0,m="",ee=void 0;if("contentType"in t)c=200,m="OK",ee=t.contentType;else try{c=t.status,m=t.statusText,ee=t.getResponseHeader("Content-Type")}catch(me){c=0,m="",ee=void 0}c!==0&&(d=2,r.readyState=2,r.status=c,r.statusText=m,r._contentType=ee,r.onreadystatechange())}},y=function(){if(v(),d===2||d===3){d=3;var c="";try{c=t.responseText}catch(m){}r.readyState=3,r.responseText=c,r.onprogress()}},h=function(c,m){if((m==null||m.preventDefault==null)&&(m={preventDefault:M}),y(),d===1||d===2||d===3){if(d=4,f!==0&&(S(f),f=0),r.readyState=4,c==="load")r.onload(m);else if(c==="error")r.onerror(m);else if(c==="abort")r.onabort(m);else throw new TypeError;r.onreadystatechange()}},p=function(c){t!=null&&(t.readyState===4?(!("onload"in t)||!("onerror"in t)||!("onabort"in t))&&h(t.responseText===""?"error":"load",c):t.readyState===3?"onprogress"in t||y():t.readyState===2&&v())},E=function(){f=l(function(){E()},500),t.readyState===3&&y()};"onload"in t&&(t.onload=function(c){h("load",c)}),"onerror"in t&&(t.onerror=function(c){h("error",c)}),"onabort"in t&&(t.onabort=function(c){h("abort",c)}),"onprogress"in t&&(t.onprogress=y),"onreadystatechange"in t&&(t.onreadystatechange=function(c){p(c)}),("contentType"in t||!("ontimeout"in g.prototype))&&(n+=(n.indexOf("?")===-1?"?":"&")+"padding=true"),t.open(e,n,!0),"readyState"in t&&(f=l(function(){E()},0))},Q.prototype.abort=function(){this._abort(!1)},Q.prototype.getResponseHeader=function(e){return this._contentType},Q.prototype.setRequestHeader=function(e,n){var r=this._xhr;"setRequestHeader"in r&&r.setRequestHeader(e,n)},Q.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},Q.prototype.send=function(){if((!("ontimeout"in g.prototype)||!("sendAsBinary"in g.prototype)&&!("mozAnon"in g.prototype))&&H!=null&&H.readyState!=null&&H.readyState!=="complete"){var e=this;e._sendTimeout=l(function(){e._sendTimeout=0,e.send()},4);return}var n=this._xhr;"withCredentials"in n&&(n.withCredentials=this.withCredentials);try{n.send(void 0)}catch(r){throw r}};function se(e){return e.replace(/[A-Z]/g,function(n){return String.fromCharCode(n.charCodeAt(0)+32)})}function fe(e){for(var n=Object.create(null),r=e.split(`\r -`),t=0;tV,initialize:()=>ct});var Ke=Le(require("jwt-decode")),Ge=Le(require("mitt")),Xe=Le(Be());var V;(function(a){a.READY="ready",a.CONNECTED="connected",a.DISCONNECTED="disconnected",a.CHANGED="changed",a.ERROR="error"})(V||(V={}));var Ne=6e4,$e={debug:!1,baseUrl:"https://config.ff.harness.io/api/1.0",eventUrl:"https://events.ff.harness.io/api/1.0",eventsSyncInterval:Ne,streamEnabled:!0,allAttributesPrivate:!1,privateAttributeNames:[]},ue=(a,...l)=>console.error(`[FF-SDK] ${a}`,...l);var st="1.7.0",ft=500,_e=globalThis.fetch,dt=Xe.EventSourcePolyfill,be=!!globalThis.Proxy,Fe=a=>{let{value:l}=a;try{switch(a.kind.toLowerCase()){case"int":case"number":l=Number(l);break;case"boolean":l=l.toString().toLowerCase()==="true";break;case"json":l=JSON.parse(l);break}}catch(S){ue(S)}return l},ct=(a,l,S)=>{let g=!1,Y,z,x,H,U,O=!0,ie={},ae=()=>{O=!1},ve=()=>{O=!0},b=[],D=(0,Ge.default)(),L=q(q({},$e),S);L.eventsSyncInterval{L.debug&&console.debug(`[FF-SDK] ${o}`,...s)},M=o=>{if(O){let s=Date.now();s-o.lastAccessed>ft&&(o.count++,o.lastAccessed=s)}};globalThis.onbeforeunload=()=>{b.length&&globalThis.localStorage&&(ae(),globalThis.localStorage.HARNESS_FF_METRICS=JSON.stringify(b),ve())};let Q=(o,s)=>Oe(void 0,null,function*(){return(yield(yield _e(`${s.baseUrl}/client/auth`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:o,target:q(q({},l),{identifier:String(l.identifier)})})})).json()).authToken}),se=0,fe=()=>{if(b.length){R("Sending metrics...",{metrics:b,evaluations:N});let o={metricsData:b.map(s=>({timestamp:Date.now(),count:s.count,metricsType:"FFMETRICS",attributes:[{key:"featureIdentifier",value:s.featureIdentifier},{key:"featureName",value:s.featureIdentifier},{key:"variationIdentifier",value:s.variationIdentifier},{key:"target",value:l.identifier},{key:"SDK_NAME",value:"JavaScript"},{key:"SDK_LANGUAGE",value:"JavaScript"},{key:"SDK_TYPE",value:"client"},{key:"SDK_VERSION",value:st}]}))};_e(`${L.eventUrl}/metrics/${Y}?cluster=${z}`,{method:"POST",headers:q({"Content-Type":"application/json"},ie),body:JSON.stringify(o)}).then(()=>{b=[],se=0}).catch(s=>{se++&&(b=[],se=0),R(s)}).finally(()=>{U=window.setTimeout(fe,L.eventsSyncInterval)})}else U=window.setTimeout(fe,L.eventsSyncInterval)},N={},he=o=>{R("Sending event for",o.flag),be?D.emit(V.CHANGED,new Proxy(o,{get(s,i){var u;if(s.hasOwnProperty(i)&&i==="value"){let w=s.flag,T=o.value,_=b.find(B=>B.featureIdentifier===w&&B.featureValue===T);_?(M(_),_.variationIdentifier=((u=N[w])==null?void 0:u.identifier)||""):b.push({featureIdentifier:w,featureValue:String(T),variationIdentifier:N[w].identifier||"",count:O?1:0,lastAccessed:Date.now()}),R("Metrics event: Flag",i,"has been read with value via stream update",T)}return i==="value"?Fe(o):o[i]}})):D.emit(V.CHANGED,{deleted:o.deleted,flag:o.flag,value:Fe(o)})},pe=function(){return be?new Proxy({},{get(o,s){var u,w,T;let i=o[s];if(o.hasOwnProperty(s)){let _=o[s],B=b.find(Ce=>Ce.featureIdentifier===s&&_===Ce.featureValue);B?(B.variationIdentifier=((u=N[s])==null?void 0:u.identifier)||"",M(B)):b.push({featureIdentifier:s,featureValue:_,variationIdentifier:((w=N[s])==null?void 0:w.identifier)||"",count:O?1:0,lastAccessed:Date.now()}),R("Metrics event: Flag:",s,"has been read with value:",_,"variationIdentifier:",(T=N[s])==null?void 0:T.identifier)}return i}}):{}},I=pe();Q(a,L).then(o=>{if(g)return;H=o;let s=(0,Ke.default)(o);if(ie={Authorization:`Bearer ${H}`,"Harness-AccountID":s.accountID,"Harness-EnvironmentID":s.environmentIdentifier},R("Authenticated",s),globalThis.localStorage&&globalThis.localStorage.HARNESS_FF_METRICS)try{delete globalThis.localStorage.HARNESS_FF_METRICS,R("Picking up metrics from previous session")}catch(i){}U=window.setTimeout(fe,L.eventsSyncInterval),Y=s.environment,z=s.clusterIdentifier,ge().then(()=>{R("Fetch all flags ok",I)}).then(()=>{g||we()}).then(()=>{g||(R("Event stream ready",{storage:I}),D.emit(V.READY,q({},I)),be||Object.keys(I).forEach(i=>{var u;b.push({featureIdentifier:i,featureValue:I[i],variationIdentifier:((u=N[i])==null?void 0:u.identifier)||"",count:O?1:0,lastAccessed:Date.now()})}))}).catch(i=>{D.emit(V.ERROR,i)})}).catch(o=>{ue("Authentication error: ",o),D.emit(V.ERROR,o)});let ge=()=>Oe(void 0,null,function*(){try{(yield(yield _e(`${L.baseUrl}/client/env/${Y}/target/${l.identifier}/evaluations?cluster=${z}`,{headers:ie})).json()).forEach(i=>{let u=Fe(i),w=I[i.flag];u!==w&&(R("Flag variation has changed for ",i.identifier),I[i.flag]=u,N[i.flag]=q(q({},i),{value:u}),he(i))})}catch(o){return ue("Features fetch operation error: ",o),D.emit(V.ERROR,o),o}}),Z=o=>Oe(void 0,null,function*(){var s;try{let i=yield _e(`${L.baseUrl}/client/env/${Y}/target/${l.identifier}/evaluations/${o}?cluster=${z}`,{headers:ie});if(i.ok){let u=yield i.json(),w=Fe(u);if(ae(),I[o]=w,N[o]=q(q({},u),{value:w}),ve(),he(u),!be){let T=u.flag,_=b.find(B=>B.featureIdentifier===T&&B.featureValue===u.value);_?(M(_),_.variationIdentifier=((s=N[T])==null?void 0:s.identifier)||""):b.push({featureIdentifier:T,featureValue:String(u.value),variationIdentifier:N[T].identifier||"",count:O?1:0,lastAccessed:Date.now()})}}else D.emit(V.ERROR,i)}catch(i){ue("Feature fetch operation error: ",i),D.emit(V.ERROR,i)}}),we=()=>{if(!L.streamEnabled){R("Stream is disabled by configuration. Note: Polling is not yet supported");return}x=new dt(`${L.baseUrl}/stream?cluster=${z}`,{headers:q({"API-Key":a},ie)}),x.onopen=i=>{R("Stream connected",i),D.emit(V.CONNECTED)},x.onclose=i=>{R("Stream disconnected"),D.emit(V.DISCONNECTED)},x.onerror=i=>{ue("Stream has issue",i),D.emit(V.ERROR,i)};let o=i=>{switch(i.event){case"create":setTimeout(()=>Z(i.identifier),1e3);break;case"patch":Z(i.identifier);break;case"delete":delete I[i.identifier],D.emit(V.CHANGED,{flag:i.identifier,value:void 0,deleted:!0}),R("Evaluation deleted",{message:i,storage:I});break}},s=i=>{i.event==="patch"&&ge()};x.addEventListener("*",i=>{let u=JSON.parse(i.data);R("Received event from stream: ",u),u.domain==="flag"?o(u):u.domain==="target-segment"&&s(u)})},Ee=(o,s)=>D.on(o,s),Te=(o,s)=>{o?D.off(o,s):J()},ye=(o,s)=>{var u;let i=I[o];if(!be&&i!==void 0){let w=i,T=o,_=b.find(B=>B.featureIdentifier===T&&B.featureValue===w);_?(M(_),_.variationIdentifier=((u=N[T])==null?void 0:u.identifier)||""):b.push({featureIdentifier:T,featureValue:w,count:O?1:0,variationIdentifier:N[T].identifier||"",lastAccessed:Date.now()})}return i!==void 0?i:s},J=()=>{g=!0,R("Closing event stream"),I=pe(),N={},clearTimeout(U),D.all.clear(),typeof(x==null?void 0:x.close)=="function"&&x.close()};return{on:Ee,off:Te,variation:ye,close:J}}; +var Ze=Object.create,_e=Object.defineProperty,et=Object.getPrototypeOf,tt=Object.prototype.hasOwnProperty,nt=Object.getOwnPropertyNames,rt=Object.getOwnPropertyDescriptor;var J=Object.assign,Ge=a=>_e(a,"__esModule",{value:!0});var it=(a,c)=>()=>(c||(c={exports:{}},a(c.exports,c)),c.exports),at=(a,c)=>{for(var S in c)_e(a,S,{get:c[S],enumerable:!0})},ot=(a,c,S)=>{if(c&&typeof c=="object"||typeof c=="function")for(let p of nt(c))!tt.call(a,p)&&p!=="default"&&_e(a,p,{get:()=>c[p],enumerable:!(S=rt(c,p))||S.enumerable});return a},ke=a=>ot(Ge(_e(a!=null?Ze(et(a)):{},"default",a&&a.__esModule&&"default"in a?{get:()=>a.default,enumerable:!0}:{value:a,enumerable:!0})),a);var Ie=(a,c,S)=>new Promise((p,Y)=>{var W=V=>{try{F(S.next(V))}catch(D){Y(D)}},_=V=>{try{F(S.throw(V))}catch(D){Y(D)}},F=V=>V.done?p(V.value):Promise.resolve(V.value).then(W,_);F((S=S.apply(a,c)).next())});var Be=it((Le,Fe)=>{(function(a){"use strict";var c=a.setTimeout,S=a.clearTimeout,p=a.XMLHttpRequest,Y=a.XDomainRequest,W=a.ActiveXObject,_=a.EventSource,F=a.document,V=a.Promise,D=a.fetch,ae=a.Response,se=a.TextDecoder,Ee=a.TextEncoder,O=a.AbortController;if(typeof window!="undefined"&&typeof F!="undefined"&&!("readyState"in F)&&F.body==null&&(F.readyState="loading",window.addEventListener("load",function(e){F.readyState="complete"},!1)),p==null&&W!=null&&(p=function(){return new W("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function n(){}return n.prototype=e,new n}),Date.now||(Date.now=function(){return new Date().getTime()}),O==null){var g=D;D=function(e,n){var o=n.signal;return g(e,{headers:n.headers,credentials:n.credentials,cache:n.cache}).then(function(t){var f=t.body.getReader();return o._reader=f,o._aborted&&o._reader.cancel(),{status:t.status,statusText:t.statusText,headers:t.headers,body:{getReader:function(){return f}}}})},O=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function N(){this.bitsNeeded=0,this.codePoint=0}N.prototype.decode=function(e){function n(h,E,d){if(d===1)return h>=128>>E&&h<=2048>>E&&h<=57344>>E&&h<=65536>>E&&h<>6>15?3:E>31?2:1;if(h===6*2)return E>15?3:2;if(h===6*3)return 3;throw new Error}for(var t=65533,f="",s=this.bitsNeeded,u=this.codePoint,m=0;m191||!n(u<<6|v&63,s-6,o(s,u)))&&(s=0,u=t,f+=String.fromCharCode(u)),s===0?(v>=0&&v<=127?(s=0,u=v):v>=192&&v<=223?(s=6*1,u=v&31):v>=224&&v<=239?(s=6*2,u=v&15):v>=240&&v<=247?(s=6*3,u=v&7):(s=0,u=t),s!==0&&!n(u,s,o(s,u))&&(s=0,u=t)):(s-=6,u=u<<6|v&63),s===0&&(u<=65535?f+=String.fromCharCode(u):(f+=String.fromCharCode(55296+(u-65535-1>>10)),f+=String.fromCharCode(56320+(u-65535-1&1023))))}return this.bitsNeeded=s,this.codePoint=u,f};var w=function(){try{return new se().decode(new Ee().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(se==null||Ee==null||!w())&&(se=N);var x=function(){};function Q(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=x,this.onload=x,this.onerror=x,this.onreadystatechange=x,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=x}Q.prototype.open=function(e,n){this._abort(!0);var o=this,t=this._xhr,f=1,s=0;this._abort=function(d){o._sendTimeout!==0&&(S(o._sendTimeout),o._sendTimeout=0),(f===1||f===2||f===3)&&(f=4,t.onload=x,t.onerror=x,t.onabort=x,t.onprogress=x,t.onreadystatechange=x,t.abort(),s!==0&&(S(s),s=0),d||(o.readyState=4,o.onabort(null),o.onreadystatechange())),f=0};var u=function(){if(f===1){var d=0,R="",te=void 0;if("contentType"in t)d=200,R="OK",te=t.contentType;else try{d=t.status,R=t.statusText,te=t.getResponseHeader("Content-Type")}catch(be){d=0,R="",te=void 0}d!==0&&(f=2,o.readyState=2,o.status=d,o.statusText=R,o._contentType=te,o.onreadystatechange())}},m=function(){if(u(),f===2||f===3){f=3;var d="";try{d=t.responseText}catch(R){}o.readyState=3,o.responseText=d,o.onprogress()}},v=function(d,R){if((R==null||R.preventDefault==null)&&(R={preventDefault:x}),m(),f===1||f===2||f===3){if(f=4,s!==0&&(S(s),s=0),o.readyState=4,d==="load")o.onload(R);else if(d==="error")o.onerror(R);else if(d==="abort")o.onabort(R);else throw new TypeError;o.onreadystatechange()}},h=function(d){t!=null&&(t.readyState===4?(!("onload"in t)||!("onerror"in t)||!("onabort"in t))&&v(t.responseText===""?"error":"load",d):t.readyState===3?"onprogress"in t||m():t.readyState===2&&u())},E=function(){s=c(function(){E()},500),t.readyState===3&&m()};"onload"in t&&(t.onload=function(d){v("load",d)}),"onerror"in t&&(t.onerror=function(d){v("error",d)}),"onabort"in t&&(t.onabort=function(d){v("abort",d)}),"onprogress"in t&&(t.onprogress=m),"onreadystatechange"in t&&(t.onreadystatechange=function(d){h(d)}),("contentType"in t||!("ontimeout"in p.prototype))&&(n+=(n.indexOf("?")===-1?"?":"&")+"padding=true"),t.open(e,n,!0),"readyState"in t&&(s=c(function(){E()},0))},Q.prototype.abort=function(){this._abort(!1)},Q.prototype.getResponseHeader=function(e){return this._contentType},Q.prototype.setRequestHeader=function(e,n){var o=this._xhr;"setRequestHeader"in o&&o.setRequestHeader(e,n)},Q.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},Q.prototype.send=function(){if((!("ontimeout"in p.prototype)||!("sendAsBinary"in p.prototype)&&!("mozAnon"in p.prototype))&&F!=null&&F.readyState!=null&&F.readyState!=="complete"){var e=this;e._sendTimeout=c(function(){e._sendTimeout=0,e.send()},4);return}var n=this._xhr;"withCredentials"in n&&(n.withCredentials=this.withCredentials);try{n.send(void 0)}catch(o){throw o}};function ce(e){return e.replace(/[A-Z]/g,function(n){return String.fromCharCode(n.charCodeAt(0)+32)})}function le(e){for(var n=Object.create(null),o=e.split(`\r +`),t=0;ty,initialize:()=>ct});var Ke=ke(require("jwt-decode")),Xe=ke(require("mitt")),qe=ke(Be());var y;(function(a){a.READY="ready",a.CONNECTED="connected",a.DISCONNECTED="disconnected",a.CHANGED="changed",a.ERROR="error",a.ERROR_METRICS="metrics error",a.ERROR_AUTH="auth error",a.ERROR_FETCH_FLAGS="fetch flags error",a.ERROR_FETCH_FLAG="fetch flag error",a.ERROR_STREAM="stream error"})(y||(y={}));var De=6e4,$e={debug:!1,baseUrl:"https://config.ff.harness.io/api/1.0",eventUrl:"https://events.ff.harness.io/api/1.0",eventsSyncInterval:De,streamEnabled:!0,allAttributesPrivate:!1,privateAttributeNames:[]},oe=(a,...c)=>console.error(`[FF-SDK] ${a}`,...c);var st="1.8.0",ft=500,Ne=globalThis.fetch,dt=qe.EventSourcePolyfill,Ve=!!globalThis.Proxy,je=a=>{let{value:c}=a;try{switch(a.kind.toLowerCase()){case"int":case"number":c=Number(c);break;case"boolean":c=c.toString().toLowerCase()==="true";break;case"json":c=JSON.parse(c);break}}catch(S){oe(S)}return c},ct=(a,c,S)=>{let p=!1,Y,W,_,F,V,D=!0,ae={},se=()=>{D=!1},Ee=()=>{D=!0},O=[],g=(0,Xe.default)(),N=J(J({},$e),S);N.eventsSyncInterval{N.debug&&console.debug(`[FF-SDK] ${r}`,...i)},x=r=>{if(D){let i=Date.now();i-r.lastAccessed>ft&&(r.count++,r.lastAccessed=i)}};globalThis.onbeforeunload=()=>{O.length&&globalThis.localStorage&&(se(),globalThis.localStorage.HARNESS_FF_METRICS=JSON.stringify(O),Ee())};let Q=(r,i)=>Ie(void 0,null,function*(){return(yield(yield Ne(`${i.baseUrl}/client/auth`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r,target:J(J({},c),{identifier:String(c.identifier)})})})).json()).authToken}),ce=0,le=()=>{if(O.length){w("Sending metrics...",{metrics:O,evaluations:H});let r={metricsData:O.map(i=>({timestamp:Date.now(),count:i.count,metricsType:"FFMETRICS",attributes:[{key:"featureIdentifier",value:i.featureIdentifier},{key:"featureName",value:i.featureIdentifier},{key:"variationIdentifier",value:i.variationIdentifier},{key:"target",value:c.identifier},{key:"SDK_NAME",value:"JavaScript"},{key:"SDK_LANGUAGE",value:"JavaScript"},{key:"SDK_TYPE",value:"client"},{key:"SDK_VERSION",value:st}]}))};Ne(`${N.eventUrl}/metrics/${Y}?cluster=${W}`,{method:"POST",headers:J({"Content-Type":"application/json"},ae),body:JSON.stringify(r)}).then(()=>{O=[],ce=0}).catch(i=>{ce++&&(O=[],ce=0),w(i),g.emit(y.ERROR_METRICS,i)}).finally(()=>{V=window.setTimeout(le,N.eventsSyncInterval)})}else V=window.setTimeout(le,N.eventsSyncInterval)},H={},ye=r=>{w("Sending event for",r.flag),Ve?g.emit(y.CHANGED,new Proxy(r,{get(i,l){var T;if(i.hasOwnProperty(l)&&l==="value"){let $=i.flag,j=r.value,P=O.find(X=>X.featureIdentifier===$&&X.featureValue===j);P?(x(P),P.variationIdentifier=((T=H[$])==null?void 0:T.identifier)||""):O.push({featureIdentifier:$,featureValue:String(j),variationIdentifier:H[$].identifier||"",count:D?1:0,lastAccessed:Date.now()}),w("Metrics event: Flag",l,"has been read with value via stream update",j)}return l==="value"?je(r):r[l]}})):g.emit(y.CHANGED,{deleted:r.deleted,flag:r.flag,value:je(r)})},me=function(){return Ve?new Proxy({},{get(r,i){var T,$,j;let l=r[i];if(r.hasOwnProperty(i)){let P=r[i],X=O.find(ve=>ve.featureIdentifier===i&&P===ve.featureValue);X?(X.variationIdentifier=((T=H[i])==null?void 0:T.identifier)||"",x(X)):O.push({featureIdentifier:i,featureValue:P,variationIdentifier:(($=H[i])==null?void 0:$.identifier)||"",count:D?1:0,lastAccessed:Date.now()}),w("Metrics event: Flag:",i,"has been read with value:",P,"variationIdentifier:",(j=H[i])==null?void 0:j.identifier)}return l}}):{}},I=me();Q(a,N).then(r=>{if(p)return;F=r;let i=(0,Ke.default)(r);if(ae={Authorization:`Bearer ${F}`,"Harness-AccountID":i.accountID,"Harness-EnvironmentID":i.environmentIdentifier},w("Authenticated",i),globalThis.localStorage&&globalThis.localStorage.HARNESS_FF_METRICS)try{delete globalThis.localStorage.HARNESS_FF_METRICS,w("Picking up metrics from previous session")}catch(T){}V=window.setTimeout(le,N.eventsSyncInterval),Y=i.environment,W=i.clusterIdentifier;let l=!!Object.keys(H).length;Re().then(()=>{w("Fetch all flags ok",I)}).then(()=>{p||Se()}).then(()=>{p||(w("Event stream ready",{storage:I}),l||g.emit(y.READY,J({},I)))}).catch(T=>{g.emit(y.ERROR,T)})}).catch(r=>{oe("Authentication error: ",r),g.emit(y.ERROR_AUTH,r),g.emit(y.ERROR,r)});let Re=()=>Ie(void 0,null,function*(){try{let r=yield Ne(`${N.baseUrl}/client/env/${Y}/target/${c.identifier}/evaluations?cluster=${W}`,{headers:ae});r.ok?(yield r.json()).forEach(ue):(oe("Features fetch operation error: ",r),g.emit(y.ERROR_FETCH_FLAGS,r),g.emit(y.ERROR,r))}catch(r){return oe("Features fetch operation error: ",r),g.emit(y.ERROR_FETCH_FLAGS,r),g.emit(y.ERROR,r),r}}),Z=r=>Ie(void 0,null,function*(){try{let i=yield Ne(`${N.baseUrl}/client/env/${Y}/target/${c.identifier}/evaluations/${r}?cluster=${W}`,{headers:ae});if(i.ok){let l=yield i.json();ue(l),ye(l)}else oe("Feature fetch operation error: ",i),g.emit(y.ERROR_FETCH_FLAG,i),g.emit(y.ERROR,i)}catch(i){oe("Feature fetch operation error: ",i),g.emit(y.ERROR_FETCH_FLAG,i),g.emit(y.ERROR,i)}}),ue=r=>{se();let i=je(r);i!==I[r.flag]&&(w("Flag variation has changed for ",r.identifier),I[r.flag]=i,H[r.flag]=J(J({},r),{value:i}),ye(r)),Ee()},Se=()=>{if(!N.streamEnabled){w("Stream is disabled by configuration. Note: Polling is not yet supported");return}_=new dt(`${N.baseUrl}/stream?cluster=${W}`,{headers:J({"API-Key":a},ae)}),_.onopen=l=>{w("Stream connected",l),g.emit(y.CONNECTED)},_.onclose=l=>{w("Stream disconnected"),g.emit(y.DISCONNECTED)},_.onerror=l=>{oe("Stream has issue",l),g.emit(y.ERROR_STREAM,l),g.emit(y.ERROR,l)};let r=l=>{switch(l.event){case"create":setTimeout(()=>Z(l.identifier),1e3);break;case"patch":Z(l.identifier);break;case"delete":delete I[l.identifier],g.emit(y.CHANGED,{flag:l.identifier,value:void 0,deleted:!0}),w("Evaluation deleted",{message:l,storage:I});break}},i=l=>{l.event==="patch"&&Re()};_.addEventListener("*",l=>{let T=JSON.parse(l.data);w("Received event from stream: ",T),T.domain==="flag"?r(T):T.domain==="target-segment"&&i(T)})},Ce=(r,i)=>g.on(r,i),Te=(r,i)=>{r?g.off(r,i):ee()},z=(r,i)=>{var T;let l=I[r];if(!Ve&&l!==void 0){let $=l,j=r,P=O.find(X=>X.featureIdentifier===j&&X.featureValue===$);P?(x(P),P.variationIdentifier=((T=H[j])==null?void 0:T.identifier)||""):O.push({featureIdentifier:j,featureValue:$,count:D?1:0,variationIdentifier:H[j].identifier||"",lastAccessed:Date.now()})}return l!==void 0?l:i},ee=()=>{p=!0,w("Closing event stream"),I=me(),H={},clearTimeout(V),g.all.clear(),typeof(_==null?void 0:_.close)=="function"&&_.close()};return{on:Ce,off:Te,variation:z,close:ee,setEvaluations:r=>{if(r.length){let i=!!Object.keys(H).length;r.forEach(ue),i||setTimeout(()=>{g.emit(y.READY,J({},I))},10)}}}}; /** @license * eventsource.js * Available under MIT License (MIT) diff --git a/dist/sdk.client-iife.js b/dist/sdk.client-iife.js index 763d5a52..2dcf7705 100644 --- a/dist/sdk.client-iife.js +++ b/dist/sdk.client-iife.js @@ -1,9 +1,9 @@ -var HarnessFFSDK=(()=>{var tt=Object.create,Oe=Object.defineProperty,nt=Object.getPrototypeOf,rt=Object.prototype.hasOwnProperty,it=Object.getOwnPropertyNames,at=Object.getOwnPropertyDescriptor;var W=Object.assign,ot=n=>Oe(n,"__esModule",{value:!0});var st=(n,i)=>()=>(i||(i={exports:{}},n(i.exports,i)),i.exports),ft=(n,i)=>{for(var v in i)Oe(n,v,{get:i[v],enumerable:!0})},dt=(n,i,v)=>{if(i&&typeof i=="object"||typeof i=="function")for(let c of it(i))!rt.call(n,c)&&c!=="default"&&Oe(n,c,{get:()=>i[c],enumerable:!(v=at(i,c))||v.enumerable});return n},ct=n=>dt(ot(Oe(n!=null?tt(nt(n)):{},"default",n&&n.__esModule&&"default"in n?{get:()=>n.default,enumerable:!0}:{value:n,enumerable:!0})),n);var Ae=(n,i,v)=>new Promise((c,H)=>{var D=$=>{try{L(v.next($))}catch(N){H(N)}},b=$=>{try{L(v.throw($))}catch(N){H(N)}},L=$=>$.done?c($.value):Promise.resolve($.value).then(D,b);L((v=v.apply(n,i)).next())});var Ge=st((Pe,Ne)=>{(function(n){"use strict";var i=n.setTimeout,v=n.clearTimeout,c=n.XMLHttpRequest,H=n.XDomainRequest,D=n.ActiveXObject,b=n.EventSource,L=n.document,$=n.Promise,N=n.fetch,ie=n.Response,ae=n.TextDecoder,ve=n.TextEncoder,w=n.AbortController;if(typeof window!="undefined"&&typeof L!="undefined"&&!("readyState"in L)&&L.body==null&&(L.readyState="loading",window.addEventListener("load",function(e){L.readyState="complete"},!1)),c==null&&D!=null&&(c=function(){return new D("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function r(){}return r.prototype=e,new r}),Date.now||(Date.now=function(){return new Date().getTime()}),w==null){var x=N;N=function(e,r){var a=r.signal;return x(e,{headers:r.headers,credentials:r.credentials,cache:r.cache}).then(function(t){var l=t.body.getReader();return a._reader=l,a._aborted&&a._reader.cancel(),{status:t.status,statusText:t.statusText,headers:t.headers,body:{getReader:function(){return l}}}})},w=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function P(){this.bitsNeeded=0,this.codePoint=0}P.prototype.decode=function(e){function r(E,y,u){if(u===1)return E>=128>>y&&E<=2048>>y&&E<=57344>>y&&E<=65536>>y&&E<>6>15?3:y>31?2:1;if(E===6*2)return y>15?3:2;if(E===6*3)return 3;throw new Error}for(var t=65533,l="",d=this.bitsNeeded,p=this.codePoint,m=0;m191||!r(p<<6|g&63,d-6,a(d,p)))&&(d=0,p=t,l+=String.fromCharCode(p)),d===0?(g>=0&&g<=127?(d=0,p=g):g>=192&&g<=223?(d=6*1,p=g&31):g>=224&&g<=239?(d=6*2,p=g&15):g>=240&&g<=247?(d=6*3,p=g&7):(d=0,p=t),d!==0&&!r(p,d,a(d,p))&&(d=0,p=t)):(d-=6,p=p<<6|g&63),d===0&&(p<=65535?l+=String.fromCharCode(p):(l+=String.fromCharCode(55296+(p-65535-1>>10)),l+=String.fromCharCode(56320+(p-65535-1&1023))))}return this.bitsNeeded=d,this.codePoint=p,l};var R=function(){try{return new ae().decode(new ve().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(ae==null||ve==null||!R())&&(ae=P);var k=function(){};function Q(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=k,this.onload=k,this.onerror=k,this.onreadystatechange=k,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=k}Q.prototype.open=function(e,r){this._abort(!0);var a=this,t=this._xhr,l=1,d=0;this._abort=function(u){a._sendTimeout!==0&&(v(a._sendTimeout),a._sendTimeout=0),(l===1||l===2||l===3)&&(l=4,t.onload=k,t.onerror=k,t.onabort=k,t.onprogress=k,t.onreadystatechange=k,t.abort(),d!==0&&(v(d),d=0),u||(a.readyState=4,a.onabort(null),a.onreadystatechange())),l=0};var p=function(){if(l===1){var u=0,S="",ee=void 0;if("contentType"in t)u=200,S="OK",ee=t.contentType;else try{u=t.status,S=t.statusText,ee=t.getResponseHeader("Content-Type")}catch(me){u=0,S="",ee=void 0}u!==0&&(l=2,a.readyState=2,a.status=u,a.statusText=S,a._contentType=ee,a.onreadystatechange())}},m=function(){if(p(),l===2||l===3){l=3;var u="";try{u=t.responseText}catch(S){}a.readyState=3,a.responseText=u,a.onprogress()}},g=function(u,S){if((S==null||S.preventDefault==null)&&(S={preventDefault:k}),m(),l===1||l===2||l===3){if(l=4,d!==0&&(v(d),d=0),a.readyState=4,u==="load")a.onload(S);else if(u==="error")a.onerror(S);else if(u==="abort")a.onabort(S);else throw new TypeError;a.onreadystatechange()}},E=function(u){t!=null&&(t.readyState===4?(!("onload"in t)||!("onerror"in t)||!("onabort"in t))&&g(t.responseText===""?"error":"load",u):t.readyState===3?"onprogress"in t||m():t.readyState===2&&p())},y=function(){d=i(function(){y()},500),t.readyState===3&&m()};"onload"in t&&(t.onload=function(u){g("load",u)}),"onerror"in t&&(t.onerror=function(u){g("error",u)}),"onabort"in t&&(t.onabort=function(u){g("abort",u)}),"onprogress"in t&&(t.onprogress=m),"onreadystatechange"in t&&(t.onreadystatechange=function(u){E(u)}),("contentType"in t||!("ontimeout"in c.prototype))&&(r+=(r.indexOf("?")===-1?"?":"&")+"padding=true"),t.open(e,r,!0),"readyState"in t&&(d=i(function(){y()},0))},Q.prototype.abort=function(){this._abort(!1)},Q.prototype.getResponseHeader=function(e){return this._contentType},Q.prototype.setRequestHeader=function(e,r){var a=this._xhr;"setRequestHeader"in a&&a.setRequestHeader(e,r)},Q.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},Q.prototype.send=function(){if((!("ontimeout"in c.prototype)||!("sendAsBinary"in c.prototype)&&!("mozAnon"in c.prototype))&&L!=null&&L.readyState!=null&&L.readyState!=="complete"){var e=this;e._sendTimeout=i(function(){e._sendTimeout=0,e.send()},4);return}var r=this._xhr;"withCredentials"in r&&(r.withCredentials=this.withCredentials);try{r.send(void 0)}catch(a){throw a}};function se(e){return e.replace(/[A-Z]/g,function(r){return String.fromCharCode(r.charCodeAt(0)+32)})}function fe(e){for(var r=Object.create(null),a=e.split(`\r -`),t=0;tV,initialize:()=>Je});function Le(n){this.message=n}Le.prototype=new Error,Le.prototype.name="InvalidCharacterError";var Be=typeof window!="undefined"&&window.atob&&window.atob.bind(window)||function(n){var i=String(n).replace(/=+$/,"");if(i.length%4==1)throw new Le("'atob' failed: The string to be decoded is not correctly encoded.");for(var v,c,H=0,D=0,b="";c=i.charAt(D++);~c&&(v=H%4?64*v+c:c,H++%4)?b+=String.fromCharCode(255&v>>(-2*H&6)):0)c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(c);return b};function lt(n){var i=n.replace(/-/g,"+").replace(/_/g,"/");switch(i.length%4){case 0:break;case 2:i+="==";break;case 3:i+="=";break;default:throw"Illegal base64url string!"}try{return function(v){return decodeURIComponent(Be(v).replace(/(.)/g,function(c,H){var D=H.charCodeAt(0).toString(16).toUpperCase();return D.length<2&&(D="0"+D),"%"+D}))}(i)}catch(v){return Be(i)}}function De(n){this.message=n}function ut(n,i){if(typeof n!="string")throw new De("Invalid token specified");var v=(i=i||{}).header===!0?0:1;try{return JSON.parse(lt(n.split(".")[v]))}catch(c){throw new De("Invalid token specified: "+c.message)}}De.prototype=new Error,De.prototype.name="InvalidTokenError";var $e=ut;function Ke(n){return{all:n=n||new Map,on:function(i,v){var c=n.get(i);c&&c.push(v)||n.set(i,[v])},off:function(i,v){var c=n.get(i);c&&c.splice(c.indexOf(v)>>>0,1)},emit:function(i,v){(n.get(i)||[]).slice().map(function(c){c(v)}),(n.get("*")||[]).slice().map(function(c){c(i,v)})}}}var qe=ct(Ge());var V;(function(n){n.READY="ready",n.CONNECTED="connected",n.DISCONNECTED="disconnected",n.CHANGED="changed",n.ERROR="error"})(V||(V={}));var xe=6e4,Xe={debug:!1,baseUrl:"https://config.ff.harness.io/api/1.0",eventUrl:"https://events.ff.harness.io/api/1.0",eventsSyncInterval:xe,streamEnabled:!0,allAttributesPrivate:!1,privateAttributeNames:[]},ue=(n,...i)=>console.error(`[FF-SDK] ${n}`,...i);var ht="1.7.0",pt=500,_e=globalThis.fetch,gt=qe.EventSourcePolyfill,we=!!globalThis.Proxy,Fe=n=>{let{value:i}=n;try{switch(n.kind.toLowerCase()){case"int":case"number":i=Number(i);break;case"boolean":i=i.toString().toLowerCase()==="true";break;case"json":i=JSON.parse(i);break}}catch(v){ue(v)}return i},Je=(n,i,v)=>{let c=!1,H,D,b,L,$,N=!0,ie={},ae=()=>{N=!1},ve=()=>{N=!0},w=[],x=Ke(),P=W(W({},Xe),v);P.eventsSyncInterval{P.debug&&console.debug(`[FF-SDK] ${s}`,...f)},k=s=>{if(N){let f=Date.now();f-s.lastAccessed>pt&&(s.count++,s.lastAccessed=f)}};globalThis.onbeforeunload=()=>{w.length&&globalThis.localStorage&&(ae(),globalThis.localStorage.HARNESS_FF_METRICS=JSON.stringify(w),ve())};let Q=(s,f)=>Ae(void 0,null,function*(){return(yield(yield _e(`${f.baseUrl}/client/auth`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:s,target:W(W({},i),{identifier:String(i.identifier)})})})).json()).authToken}),se=0,fe=()=>{if(w.length){R("Sending metrics...",{metrics:w,evaluations:_});let s={metricsData:w.map(f=>({timestamp:Date.now(),count:f.count,metricsType:"FFMETRICS",attributes:[{key:"featureIdentifier",value:f.featureIdentifier},{key:"featureName",value:f.featureIdentifier},{key:"variationIdentifier",value:f.variationIdentifier},{key:"target",value:i.identifier},{key:"SDK_NAME",value:"JavaScript"},{key:"SDK_LANGUAGE",value:"JavaScript"},{key:"SDK_TYPE",value:"client"},{key:"SDK_VERSION",value:ht}]}))};_e(`${P.eventUrl}/metrics/${H}?cluster=${D}`,{method:"POST",headers:W({"Content-Type":"application/json"},ie),body:JSON.stringify(s)}).then(()=>{w=[],se=0}).catch(f=>{se++&&(w=[],se=0),R(f)}).finally(()=>{$=window.setTimeout(fe,P.eventsSyncInterval)})}else $=window.setTimeout(fe,P.eventsSyncInterval)},_={},he=s=>{R("Sending event for",s.flag),we?x.emit(V.CHANGED,new Proxy(s,{get(f,o){var h;if(f.hasOwnProperty(o)&&o==="value"){let T=f.flag,C=s.value,F=w.find(K=>K.featureIdentifier===T&&K.featureValue===C);F?(k(F),F.variationIdentifier=((h=_[T])==null?void 0:h.identifier)||""):w.push({featureIdentifier:T,featureValue:String(C),variationIdentifier:_[T].identifier||"",count:N?1:0,lastAccessed:Date.now()}),R("Metrics event: Flag",o,"has been read with value via stream update",C)}return o==="value"?Fe(s):s[o]}})):x.emit(V.CHANGED,{deleted:s.deleted,flag:s.flag,value:Fe(s)})},pe=function(){return we?new Proxy({},{get(s,f){var h,T,C;let o=s[f];if(s.hasOwnProperty(f)){let F=s[f],K=w.find(Ce=>Ce.featureIdentifier===f&&F===Ce.featureValue);K?(K.variationIdentifier=((h=_[f])==null?void 0:h.identifier)||"",k(K)):w.push({featureIdentifier:f,featureValue:F,variationIdentifier:((T=_[f])==null?void 0:T.identifier)||"",count:N?1:0,lastAccessed:Date.now()}),R("Metrics event: Flag:",f,"has been read with value:",F,"variationIdentifier:",(C=_[f])==null?void 0:C.identifier)}return o}}):{}},O=pe();Q(n,P).then(s=>{if(c)return;L=s;let f=$e(s);if(ie={Authorization:`Bearer ${L}`,"Harness-AccountID":f.accountID,"Harness-EnvironmentID":f.environmentIdentifier},R("Authenticated",f),globalThis.localStorage&&globalThis.localStorage.HARNESS_FF_METRICS)try{delete globalThis.localStorage.HARNESS_FF_METRICS,R("Picking up metrics from previous session")}catch(o){}$=window.setTimeout(fe,P.eventsSyncInterval),H=f.environment,D=f.clusterIdentifier,ge().then(()=>{R("Fetch all flags ok",O)}).then(()=>{c||be()}).then(()=>{c||(R("Event stream ready",{storage:O}),x.emit(V.READY,W({},O)),we||Object.keys(O).forEach(o=>{var h;w.push({featureIdentifier:o,featureValue:O[o],variationIdentifier:((h=_[o])==null?void 0:h.identifier)||"",count:N?1:0,lastAccessed:Date.now()})}))}).catch(o=>{x.emit(V.ERROR,o)})}).catch(s=>{ue("Authentication error: ",s),x.emit(V.ERROR,s)});let ge=()=>Ae(void 0,null,function*(){try{(yield(yield _e(`${P.baseUrl}/client/env/${H}/target/${i.identifier}/evaluations?cluster=${D}`,{headers:ie})).json()).forEach(o=>{let h=Fe(o),T=O[o.flag];h!==T&&(R("Flag variation has changed for ",o.identifier),O[o.flag]=h,_[o.flag]=W(W({},o),{value:h}),he(o))})}catch(s){return ue("Features fetch operation error: ",s),x.emit(V.ERROR,s),s}}),Z=s=>Ae(void 0,null,function*(){var f;try{let o=yield _e(`${P.baseUrl}/client/env/${H}/target/${i.identifier}/evaluations/${s}?cluster=${D}`,{headers:ie});if(o.ok){let h=yield o.json(),T=Fe(h);if(ae(),O[s]=T,_[s]=W(W({},h),{value:T}),ve(),he(h),!we){let C=h.flag,F=w.find(K=>K.featureIdentifier===C&&K.featureValue===h.value);F?(k(F),F.variationIdentifier=((f=_[C])==null?void 0:f.identifier)||""):w.push({featureIdentifier:C,featureValue:String(h.value),variationIdentifier:_[C].identifier||"",count:N?1:0,lastAccessed:Date.now()})}}else x.emit(V.ERROR,o)}catch(o){ue("Feature fetch operation error: ",o),x.emit(V.ERROR,o)}}),be=()=>{if(!P.streamEnabled){R("Stream is disabled by configuration. Note: Polling is not yet supported");return}b=new gt(`${P.baseUrl}/stream?cluster=${D}`,{headers:W({"API-Key":n},ie)}),b.onopen=o=>{R("Stream connected",o),x.emit(V.CONNECTED)},b.onclose=o=>{R("Stream disconnected"),x.emit(V.DISCONNECTED)},b.onerror=o=>{ue("Stream has issue",o),x.emit(V.ERROR,o)};let s=o=>{switch(o.event){case"create":setTimeout(()=>Z(o.identifier),1e3);break;case"patch":Z(o.identifier);break;case"delete":delete O[o.identifier],x.emit(V.CHANGED,{flag:o.identifier,value:void 0,deleted:!0}),R("Evaluation deleted",{message:o,storage:O});break}},f=o=>{o.event==="patch"&&ge()};b.addEventListener("*",o=>{let h=JSON.parse(o.data);R("Received event from stream: ",h),h.domain==="flag"?s(h):h.domain==="target-segment"&&f(h)})},Ee=(s,f)=>x.on(s,f),Te=(s,f)=>{s?x.off(s,f):Y()},ye=(s,f)=>{var h;let o=O[s];if(!we&&o!==void 0){let T=o,C=s,F=w.find(K=>K.featureIdentifier===C&&K.featureValue===T);F?(k(F),F.variationIdentifier=((h=_[C])==null?void 0:h.identifier)||""):w.push({featureIdentifier:C,featureValue:T,count:N?1:0,variationIdentifier:_[C].identifier||"",lastAccessed:Date.now()})}return o!==void 0?o:f},Y=()=>{c=!0,R("Closing event stream"),O=pe(),_={},clearTimeout($),x.all.clear(),typeof(b==null?void 0:b.close)=="function"&&b.close()};return{on:Ee,off:Te,variation:ye,close:Y}};return vt;})(); +var HarnessFFSDK=(()=>{var tt=Object.create,Ie=Object.defineProperty,nt=Object.getPrototypeOf,rt=Object.prototype.hasOwnProperty,it=Object.getOwnPropertyNames,at=Object.getOwnPropertyDescriptor;var W=Object.assign,ot=t=>Ie(t,"__esModule",{value:!0});var st=(t,o)=>()=>(o||(o={exports:{}},t(o.exports,o)),o.exports),ft=(t,o)=>{for(var v in o)Ie(t,v,{get:o[v],enumerable:!0})},dt=(t,o,v)=>{if(o&&typeof o=="object"||typeof o=="function")for(let d of it(o))!rt.call(t,d)&&d!=="default"&&Ie(t,d,{get:()=>o[d],enumerable:!(v=at(o,d))||v.enumerable});return t},ct=t=>dt(ot(Ie(t!=null?tt(nt(t)):{},"default",t&&t.__esModule&&"default"in t?{get:()=>t.default,enumerable:!0}:{value:t,enumerable:!0})),t);var _e=(t,o,v)=>new Promise((d,F)=>{var A=P=>{try{N(v.next(P))}catch(x){F(x)}},T=P=>{try{N(v.throw(P))}catch(x){F(x)}},N=P=>P.done?d(P.value):Promise.resolve(P.value).then(A,T);N((v=v.apply(t,o)).next())});var Xe=st((Ve,De)=>{(function(t){"use strict";var o=t.setTimeout,v=t.clearTimeout,d=t.XMLHttpRequest,F=t.XDomainRequest,A=t.ActiveXObject,T=t.EventSource,N=t.document,P=t.Promise,x=t.fetch,ae=t.Response,se=t.TextDecoder,Ee=t.TextEncoder,I=t.AbortController;if(typeof window!="undefined"&&typeof N!="undefined"&&!("readyState"in N)&&N.body==null&&(N.readyState="loading",window.addEventListener("load",function(e){N.readyState="complete"},!1)),d==null&&A!=null&&(d=function(){return new A("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function r(){}return r.prototype=e,new r}),Date.now||(Date.now=function(){return new Date().getTime()}),I==null){var E=x;x=function(e,r){var s=r.signal;return E(e,{headers:r.headers,credentials:r.credentials,cache:r.cache}).then(function(n){var c=n.body.getReader();return s._reader=c,s._aborted&&s._reader.cancel(),{status:n.status,statusText:n.statusText,headers:n.headers,body:{getReader:function(){return c}}}})},I=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function H(){this.bitsNeeded=0,this.codePoint=0}H.prototype.decode=function(e){function r(g,y,l){if(l===1)return g>=128>>y&&g<=2048>>y&&g<=57344>>y&&g<=65536>>y&&g<>6>15?3:y>31?2:1;if(g===6*2)return y>15?3:2;if(g===6*3)return 3;throw new Error}for(var n=65533,c="",f=this.bitsNeeded,h=this.codePoint,R=0;R191||!r(h<<6|p&63,f-6,s(f,h)))&&(f=0,h=n,c+=String.fromCharCode(h)),f===0?(p>=0&&p<=127?(f=0,h=p):p>=192&&p<=223?(f=6*1,h=p&31):p>=224&&p<=239?(f=6*2,h=p&15):p>=240&&p<=247?(f=6*3,h=p&7):(f=0,h=n),f!==0&&!r(h,f,s(f,h))&&(f=0,h=n)):(f-=6,h=h<<6|p&63),f===0&&(h<=65535?c+=String.fromCharCode(h):(c+=String.fromCharCode(55296+(h-65535-1>>10)),c+=String.fromCharCode(56320+(h-65535-1&1023))))}return this.bitsNeeded=f,this.codePoint=h,c};var C=function(){try{return new se().decode(new Ee().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(se==null||Ee==null||!C())&&(se=H);var M=function(){};function Q(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=M,this.onload=M,this.onerror=M,this.onreadystatechange=M,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=M}Q.prototype.open=function(e,r){this._abort(!0);var s=this,n=this._xhr,c=1,f=0;this._abort=function(l){s._sendTimeout!==0&&(v(s._sendTimeout),s._sendTimeout=0),(c===1||c===2||c===3)&&(c=4,n.onload=M,n.onerror=M,n.onabort=M,n.onprogress=M,n.onreadystatechange=M,n.abort(),f!==0&&(v(f),f=0),l||(s.readyState=4,s.onabort(null),s.onreadystatechange())),c=0};var h=function(){if(c===1){var l=0,S="",te=void 0;if("contentType"in n)l=200,S="OK",te=n.contentType;else try{l=n.status,S=n.statusText,te=n.getResponseHeader("Content-Type")}catch(Te){l=0,S="",te=void 0}l!==0&&(c=2,s.readyState=2,s.status=l,s.statusText=S,s._contentType=te,s.onreadystatechange())}},R=function(){if(h(),c===2||c===3){c=3;var l="";try{l=n.responseText}catch(S){}s.readyState=3,s.responseText=l,s.onprogress()}},p=function(l,S){if((S==null||S.preventDefault==null)&&(S={preventDefault:M}),R(),c===1||c===2||c===3){if(c=4,f!==0&&(v(f),f=0),s.readyState=4,l==="load")s.onload(S);else if(l==="error")s.onerror(S);else if(l==="abort")s.onabort(S);else throw new TypeError;s.onreadystatechange()}},g=function(l){n!=null&&(n.readyState===4?(!("onload"in n)||!("onerror"in n)||!("onabort"in n))&&p(n.responseText===""?"error":"load",l):n.readyState===3?"onprogress"in n||R():n.readyState===2&&h())},y=function(){f=o(function(){y()},500),n.readyState===3&&R()};"onload"in n&&(n.onload=function(l){p("load",l)}),"onerror"in n&&(n.onerror=function(l){p("error",l)}),"onabort"in n&&(n.onabort=function(l){p("abort",l)}),"onprogress"in n&&(n.onprogress=R),"onreadystatechange"in n&&(n.onreadystatechange=function(l){g(l)}),("contentType"in n||!("ontimeout"in d.prototype))&&(r+=(r.indexOf("?")===-1?"?":"&")+"padding=true"),n.open(e,r,!0),"readyState"in n&&(f=o(function(){y()},0))},Q.prototype.abort=function(){this._abort(!1)},Q.prototype.getResponseHeader=function(e){return this._contentType},Q.prototype.setRequestHeader=function(e,r){var s=this._xhr;"setRequestHeader"in s&&s.setRequestHeader(e,r)},Q.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},Q.prototype.send=function(){if((!("ontimeout"in d.prototype)||!("sendAsBinary"in d.prototype)&&!("mozAnon"in d.prototype))&&N!=null&&N.readyState!=null&&N.readyState!=="complete"){var e=this;e._sendTimeout=o(function(){e._sendTimeout=0,e.send()},4);return}var r=this._xhr;"withCredentials"in r&&(r.withCredentials=this.withCredentials);try{r.send(void 0)}catch(s){throw s}};function ce(e){return e.replace(/[A-Z]/g,function(r){return String.fromCharCode(r.charCodeAt(0)+32)})}function le(e){for(var r=Object.create(null),s=e.split(`\r +`),n=0;nm,initialize:()=>Ye});function Le(t){this.message=t}Le.prototype=new Error,Le.prototype.name="InvalidCharacterError";var Be=typeof window!="undefined"&&window.atob&&window.atob.bind(window)||function(t){var o=String(t).replace(/=+$/,"");if(o.length%4==1)throw new Le("'atob' failed: The string to be decoded is not correctly encoded.");for(var v,d,F=0,A=0,T="";d=o.charAt(A++);~d&&(v=F%4?64*v+d:d,F++%4)?T+=String.fromCharCode(255&v>>(-2*F&6)):0)d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(d);return T};function lt(t){var o=t.replace(/-/g,"+").replace(/_/g,"/");switch(o.length%4){case 0:break;case 2:o+="==";break;case 3:o+="=";break;default:throw"Illegal base64url string!"}try{return function(v){return decodeURIComponent(Be(v).replace(/(.)/g,function(d,F){var A=F.charCodeAt(0).toString(16).toUpperCase();return A.length<2&&(A="0"+A),"%"+A}))}(o)}catch(v){return Be(o)}}function Fe(t){this.message=t}function ut(t,o){if(typeof t!="string")throw new Fe("Invalid token specified");var v=(o=o||{}).header===!0?0:1;try{return JSON.parse(lt(t.split(".")[v]))}catch(d){throw new Fe("Invalid token specified: "+d.message)}}Fe.prototype=new Error,Fe.prototype.name="InvalidTokenError";var $e=ut;function Ke(t){return{all:t=t||new Map,on:function(o,v){var d=t.get(o);d&&d.push(v)||t.set(o,[v])},off:function(o,v){var d=t.get(o);d&&d.splice(d.indexOf(v)>>>0,1)},emit:function(o,v){(t.get(o)||[]).slice().map(function(d){d(v)}),(t.get("*")||[]).slice().map(function(d){d(o,v)})}}}var Je=ct(Xe());var m;(function(t){t.READY="ready",t.CONNECTED="connected",t.DISCONNECTED="disconnected",t.CHANGED="changed",t.ERROR="error",t.ERROR_METRICS="metrics error",t.ERROR_AUTH="auth error",t.ERROR_FETCH_FLAGS="fetch flags error",t.ERROR_FETCH_FLAG="fetch flag error",t.ERROR_STREAM="stream error"})(m||(m={}));var Ne=6e4,qe={debug:!1,baseUrl:"https://config.ff.harness.io/api/1.0",eventUrl:"https://events.ff.harness.io/api/1.0",eventsSyncInterval:Ne,streamEnabled:!0,allAttributesPrivate:!1,privateAttributeNames:[]},oe=(t,...o)=>console.error(`[FF-SDK] ${t}`,...o);var ht="1.8.0",pt=500,xe=globalThis.fetch,gt=Je.EventSourcePolyfill,je=!!globalThis.Proxy,Pe=t=>{let{value:o}=t;try{switch(t.kind.toLowerCase()){case"int":case"number":o=Number(o);break;case"boolean":o=o.toString().toLowerCase()==="true";break;case"json":o=JSON.parse(o);break}}catch(v){oe(v)}return o},Ye=(t,o,v)=>{let d=!1,F,A,T,N,P,x=!0,ae={},se=()=>{x=!1},Ee=()=>{x=!0},I=[],E=Ke(),H=W(W({},qe),v);H.eventsSyncInterval{H.debug&&console.debug(`[FF-SDK] ${i}`,...a)},M=i=>{if(x){let a=Date.now();a-i.lastAccessed>pt&&(i.count++,i.lastAccessed=a)}};globalThis.onbeforeunload=()=>{I.length&&globalThis.localStorage&&(se(),globalThis.localStorage.HARNESS_FF_METRICS=JSON.stringify(I),Ee())};let Q=(i,a)=>_e(void 0,null,function*(){return(yield(yield xe(`${a.baseUrl}/client/auth`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:i,target:W(W({},o),{identifier:String(o.identifier)})})})).json()).authToken}),ce=0,le=()=>{if(I.length){C("Sending metrics...",{metrics:I,evaluations:k});let i={metricsData:I.map(a=>({timestamp:Date.now(),count:a.count,metricsType:"FFMETRICS",attributes:[{key:"featureIdentifier",value:a.featureIdentifier},{key:"featureName",value:a.featureIdentifier},{key:"variationIdentifier",value:a.variationIdentifier},{key:"target",value:o.identifier},{key:"SDK_NAME",value:"JavaScript"},{key:"SDK_LANGUAGE",value:"JavaScript"},{key:"SDK_TYPE",value:"client"},{key:"SDK_VERSION",value:ht}]}))};xe(`${H.eventUrl}/metrics/${F}?cluster=${A}`,{method:"POST",headers:W({"Content-Type":"application/json"},ae),body:JSON.stringify(i)}).then(()=>{I=[],ce=0}).catch(a=>{ce++&&(I=[],ce=0),C(a),E.emit(m.ERROR_METRICS,a)}).finally(()=>{P=window.setTimeout(le,H.eventsSyncInterval)})}else P=window.setTimeout(le,H.eventsSyncInterval)},k={},ye=i=>{C("Sending event for",i.flag),je?E.emit(m.CHANGED,new Proxy(i,{get(a,u){var w;if(a.hasOwnProperty(u)&&u==="value"){let X=a.flag,U=i.value,G=I.find(J=>J.featureIdentifier===X&&J.featureValue===U);G?(M(G),G.variationIdentifier=((w=k[X])==null?void 0:w.identifier)||""):I.push({featureIdentifier:X,featureValue:String(U),variationIdentifier:k[X].identifier||"",count:x?1:0,lastAccessed:Date.now()}),C("Metrics event: Flag",u,"has been read with value via stream update",U)}return u==="value"?Pe(i):i[u]}})):E.emit(m.CHANGED,{deleted:i.deleted,flag:i.flag,value:Pe(i)})},me=function(){return je?new Proxy({},{get(i,a){var w,X,U;let u=i[a];if(i.hasOwnProperty(a)){let G=i[a],J=I.find(ve=>ve.featureIdentifier===a&&G===ve.featureValue);J?(J.variationIdentifier=((w=k[a])==null?void 0:w.identifier)||"",M(J)):I.push({featureIdentifier:a,featureValue:G,variationIdentifier:((X=k[a])==null?void 0:X.identifier)||"",count:x?1:0,lastAccessed:Date.now()}),C("Metrics event: Flag:",a,"has been read with value:",G,"variationIdentifier:",(U=k[a])==null?void 0:U.identifier)}return u}}):{}},D=me();Q(t,H).then(i=>{if(d)return;N=i;let a=$e(i);if(ae={Authorization:`Bearer ${N}`,"Harness-AccountID":a.accountID,"Harness-EnvironmentID":a.environmentIdentifier},C("Authenticated",a),globalThis.localStorage&&globalThis.localStorage.HARNESS_FF_METRICS)try{delete globalThis.localStorage.HARNESS_FF_METRICS,C("Picking up metrics from previous session")}catch(w){}P=window.setTimeout(le,H.eventsSyncInterval),F=a.environment,A=a.clusterIdentifier;let u=!!Object.keys(k).length;Re().then(()=>{C("Fetch all flags ok",D)}).then(()=>{d||Se()}).then(()=>{d||(C("Event stream ready",{storage:D}),u||E.emit(m.READY,W({},D)))}).catch(w=>{E.emit(m.ERROR,w)})}).catch(i=>{oe("Authentication error: ",i),E.emit(m.ERROR_AUTH,i),E.emit(m.ERROR,i)});let Re=()=>_e(void 0,null,function*(){try{let i=yield xe(`${H.baseUrl}/client/env/${F}/target/${o.identifier}/evaluations?cluster=${A}`,{headers:ae});i.ok?(yield i.json()).forEach(ue):(oe("Features fetch operation error: ",i),E.emit(m.ERROR_FETCH_FLAGS,i),E.emit(m.ERROR,i))}catch(i){return oe("Features fetch operation error: ",i),E.emit(m.ERROR_FETCH_FLAGS,i),E.emit(m.ERROR,i),i}}),Z=i=>_e(void 0,null,function*(){try{let a=yield xe(`${H.baseUrl}/client/env/${F}/target/${o.identifier}/evaluations/${i}?cluster=${A}`,{headers:ae});if(a.ok){let u=yield a.json();ue(u),ye(u)}else oe("Feature fetch operation error: ",a),E.emit(m.ERROR_FETCH_FLAG,a),E.emit(m.ERROR,a)}catch(a){oe("Feature fetch operation error: ",a),E.emit(m.ERROR_FETCH_FLAG,a),E.emit(m.ERROR,a)}}),ue=i=>{se();let a=Pe(i);a!==D[i.flag]&&(C("Flag variation has changed for ",i.identifier),D[i.flag]=a,k[i.flag]=W(W({},i),{value:a}),ye(i)),Ee()},Se=()=>{if(!H.streamEnabled){C("Stream is disabled by configuration. Note: Polling is not yet supported");return}T=new gt(`${H.baseUrl}/stream?cluster=${A}`,{headers:W({"API-Key":t},ae)}),T.onopen=u=>{C("Stream connected",u),E.emit(m.CONNECTED)},T.onclose=u=>{C("Stream disconnected"),E.emit(m.DISCONNECTED)},T.onerror=u=>{oe("Stream has issue",u),E.emit(m.ERROR_STREAM,u),E.emit(m.ERROR,u)};let i=u=>{switch(u.event){case"create":setTimeout(()=>Z(u.identifier),1e3);break;case"patch":Z(u.identifier);break;case"delete":delete D[u.identifier],E.emit(m.CHANGED,{flag:u.identifier,value:void 0,deleted:!0}),C("Evaluation deleted",{message:u,storage:D});break}},a=u=>{u.event==="patch"&&Re()};T.addEventListener("*",u=>{let w=JSON.parse(u.data);C("Received event from stream: ",w),w.domain==="flag"?i(w):w.domain==="target-segment"&&a(w)})},Ce=(i,a)=>E.on(i,a),we=(i,a)=>{i?E.off(i,a):ee()},z=(i,a)=>{var w;let u=D[i];if(!je&&u!==void 0){let X=u,U=i,G=I.find(J=>J.featureIdentifier===U&&J.featureValue===X);G?(M(G),G.variationIdentifier=((w=k[U])==null?void 0:w.identifier)||""):I.push({featureIdentifier:U,featureValue:X,count:x?1:0,variationIdentifier:k[U].identifier||"",lastAccessed:Date.now()})}return u!==void 0?u:a},ee=()=>{d=!0,C("Closing event stream"),D=me(),k={},clearTimeout(P),E.all.clear(),typeof(T==null?void 0:T.close)=="function"&&T.close()};return{on:Ce,off:we,variation:z,close:ee,setEvaluations:i=>{if(i.length){let a=!!Object.keys(k).length;i.forEach(ue),a||setTimeout(()=>{E.emit(m.READY,W({},D))},10)}}}};return vt;})(); /** @license * eventsource.js * Available under MIT License (MIT) diff --git a/dist/sdk.client.js b/dist/sdk.client.js index 89f3aad3..820bf1f6 100644 --- a/dist/sdk.client.js +++ b/dist/sdk.client.js @@ -1,9 +1,9 @@ -var et=Object.create,ke=Object.defineProperty,tt=Object.getPrototypeOf,nt=Object.prototype.hasOwnProperty,rt=Object.getOwnPropertyNames,it=Object.getOwnPropertyDescriptor;var W=Object.assign,at=r=>ke(r,"__esModule",{value:!0});var ot=(r,a)=>()=>(a||(a={exports:{}},r(a.exports,a)),a.exports);var st=(r,a,p)=>{if(a&&typeof a=="object"||typeof a=="function")for(let c of rt(a))!nt.call(r,c)&&c!=="default"&&ke(r,c,{get:()=>a[c],enumerable:!(p=it(a,c))||p.enumerable});return r},ft=r=>st(at(ke(r!=null?et(tt(r)):{},"default",r&&r.__esModule&&"default"in r?{get:()=>r.default,enumerable:!0}:{value:r,enumerable:!0})),r);var Oe=(r,a,p)=>new Promise((c,V)=>{var D=$=>{try{L(p.next($))}catch(N){V(N)}},b=$=>{try{L(p.throw($))}catch(N){V(N)}},L=$=>$.done?c($.value):Promise.resolve($.value).then(D,b);L((p=p.apply(r,a)).next())});var Ge=ot((Pe,De)=>{(function(r){"use strict";var a=r.setTimeout,p=r.clearTimeout,c=r.XMLHttpRequest,V=r.XDomainRequest,D=r.ActiveXObject,b=r.EventSource,L=r.document,$=r.Promise,N=r.fetch,ie=r.Response,ae=r.TextDecoder,ve=r.TextEncoder,w=r.AbortController;if(typeof window!="undefined"&&typeof L!="undefined"&&!("readyState"in L)&&L.body==null&&(L.readyState="loading",window.addEventListener("load",function(e){L.readyState="complete"},!1)),c==null&&D!=null&&(c=function(){return new D("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function n(){}return n.prototype=e,new n}),Date.now||(Date.now=function(){return new Date().getTime()}),w==null){var x=N;N=function(e,n){var i=n.signal;return x(e,{headers:n.headers,credentials:n.credentials,cache:n.cache}).then(function(t){var l=t.body.getReader();return i._reader=l,i._aborted&&i._reader.cancel(),{status:t.status,statusText:t.statusText,headers:t.headers,body:{getReader:function(){return l}}}})},w=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function P(){this.bitsNeeded=0,this.codePoint=0}P.prototype.decode=function(e){function n(E,y,u){if(u===1)return E>=128>>y&&E<=2048>>y&&E<=57344>>y&&E<=65536>>y&&E<>6>15?3:y>31?2:1;if(E===6*2)return y>15?3:2;if(E===6*3)return 3;throw new Error}for(var t=65533,l="",d=this.bitsNeeded,h=this.codePoint,m=0;m191||!n(h<<6|g&63,d-6,i(d,h)))&&(d=0,h=t,l+=String.fromCharCode(h)),d===0?(g>=0&&g<=127?(d=0,h=g):g>=192&&g<=223?(d=6*1,h=g&31):g>=224&&g<=239?(d=6*2,h=g&15):g>=240&&g<=247?(d=6*3,h=g&7):(d=0,h=t),d!==0&&!n(h,d,i(d,h))&&(d=0,h=t)):(d-=6,h=h<<6|g&63),d===0&&(h<=65535?l+=String.fromCharCode(h):(l+=String.fromCharCode(55296+(h-65535-1>>10)),l+=String.fromCharCode(56320+(h-65535-1&1023))))}return this.bitsNeeded=d,this.codePoint=h,l};var R=function(){try{return new ae().decode(new ve().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(ae==null||ve==null||!R())&&(ae=P);var H=function(){};function Q(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=H,this.onload=H,this.onerror=H,this.onreadystatechange=H,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=H}Q.prototype.open=function(e,n){this._abort(!0);var i=this,t=this._xhr,l=1,d=0;this._abort=function(u){i._sendTimeout!==0&&(p(i._sendTimeout),i._sendTimeout=0),(l===1||l===2||l===3)&&(l=4,t.onload=H,t.onerror=H,t.onabort=H,t.onprogress=H,t.onreadystatechange=H,t.abort(),d!==0&&(p(d),d=0),u||(i.readyState=4,i.onabort(null),i.onreadystatechange())),l=0};var h=function(){if(l===1){var u=0,S="",ee=void 0;if("contentType"in t)u=200,S="OK",ee=t.contentType;else try{u=t.status,S=t.statusText,ee=t.getResponseHeader("Content-Type")}catch(me){u=0,S="",ee=void 0}u!==0&&(l=2,i.readyState=2,i.status=u,i.statusText=S,i._contentType=ee,i.onreadystatechange())}},m=function(){if(h(),l===2||l===3){l=3;var u="";try{u=t.responseText}catch(S){}i.readyState=3,i.responseText=u,i.onprogress()}},g=function(u,S){if((S==null||S.preventDefault==null)&&(S={preventDefault:H}),m(),l===1||l===2||l===3){if(l=4,d!==0&&(p(d),d=0),i.readyState=4,u==="load")i.onload(S);else if(u==="error")i.onerror(S);else if(u==="abort")i.onabort(S);else throw new TypeError;i.onreadystatechange()}},E=function(u){t!=null&&(t.readyState===4?(!("onload"in t)||!("onerror"in t)||!("onabort"in t))&&g(t.responseText===""?"error":"load",u):t.readyState===3?"onprogress"in t||m():t.readyState===2&&h())},y=function(){d=a(function(){y()},500),t.readyState===3&&m()};"onload"in t&&(t.onload=function(u){g("load",u)}),"onerror"in t&&(t.onerror=function(u){g("error",u)}),"onabort"in t&&(t.onabort=function(u){g("abort",u)}),"onprogress"in t&&(t.onprogress=m),"onreadystatechange"in t&&(t.onreadystatechange=function(u){E(u)}),("contentType"in t||!("ontimeout"in c.prototype))&&(n+=(n.indexOf("?")===-1?"?":"&")+"padding=true"),t.open(e,n,!0),"readyState"in t&&(d=a(function(){y()},0))},Q.prototype.abort=function(){this._abort(!1)},Q.prototype.getResponseHeader=function(e){return this._contentType},Q.prototype.setRequestHeader=function(e,n){var i=this._xhr;"setRequestHeader"in i&&i.setRequestHeader(e,n)},Q.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},Q.prototype.send=function(){if((!("ontimeout"in c.prototype)||!("sendAsBinary"in c.prototype)&&!("mozAnon"in c.prototype))&&L!=null&&L.readyState!=null&&L.readyState!=="complete"){var e=this;e._sendTimeout=a(function(){e._sendTimeout=0,e.send()},4);return}var n=this._xhr;"withCredentials"in n&&(n.withCredentials=this.withCredentials);try{n.send(void 0)}catch(i){throw i}};function se(e){return e.replace(/[A-Z]/g,function(n){return String.fromCharCode(n.charCodeAt(0)+32)})}function fe(e){for(var n=Object.create(null),i=e.split(`\r -`),t=0;t>(-2*V&6)):0)c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(c);return b};function dt(r){var a=r.replace(/-/g,"+").replace(/_/g,"/");switch(a.length%4){case 0:break;case 2:a+="==";break;case 3:a+="=";break;default:throw"Illegal base64url string!"}try{return function(p){return decodeURIComponent(Be(p).replace(/(.)/g,function(c,V){var D=V.charCodeAt(0).toString(16).toUpperCase();return D.length<2&&(D="0"+D),"%"+D}))}(a)}catch(p){return Be(a)}}function Ae(r){this.message=r}function ct(r,a){if(typeof r!="string")throw new Ae("Invalid token specified");var p=(a=a||{}).header===!0?0:1;try{return JSON.parse(dt(r.split(".")[p]))}catch(c){throw new Ae("Invalid token specified: "+c.message)}}Ae.prototype=new Error,Ae.prototype.name="InvalidTokenError";var $e=ct;function Ke(r){return{all:r=r||new Map,on:function(a,p){var c=r.get(a);c&&c.push(p)||r.set(a,[p])},off:function(a,p){var c=r.get(a);c&&c.splice(c.indexOf(p)>>>0,1)},emit:function(a,p){(r.get(a)||[]).slice().map(function(c){c(p)}),(r.get("*")||[]).slice().map(function(c){c(a,p)})}}}var qe=ft(Ge());var k;(function(r){r.READY="ready",r.CONNECTED="connected",r.DISCONNECTED="disconnected",r.CHANGED="changed",r.ERROR="error"})(k||(k={}));var Ne=6e4,Xe={debug:!1,baseUrl:"https://config.ff.harness.io/api/1.0",eventUrl:"https://events.ff.harness.io/api/1.0",eventsSyncInterval:Ne,streamEnabled:!0,allAttributesPrivate:!1,privateAttributeNames:[]},ue=(r,...a)=>console.error(`[FF-SDK] ${r}`,...a);var lt="1.7.0",ut=500,xe=globalThis.fetch,vt=qe.EventSourcePolyfill,we=!!globalThis.Proxy,_e=r=>{let{value:a}=r;try{switch(r.kind.toLowerCase()){case"int":case"number":a=Number(a);break;case"boolean":a=a.toString().toLowerCase()==="true";break;case"json":a=JSON.parse(a);break}}catch(p){ue(p)}return a},ht=(r,a,p)=>{let c=!1,V,D,b,L,$,N=!0,ie={},ae=()=>{N=!1},ve=()=>{N=!0},w=[],x=Ke(),P=W(W({},Xe),p);P.eventsSyncInterval{P.debug&&console.debug(`[FF-SDK] ${s}`,...f)},H=s=>{if(N){let f=Date.now();f-s.lastAccessed>ut&&(s.count++,s.lastAccessed=f)}};globalThis.onbeforeunload=()=>{w.length&&globalThis.localStorage&&(ae(),globalThis.localStorage.HARNESS_FF_METRICS=JSON.stringify(w),ve())};let Q=(s,f)=>Oe(void 0,null,function*(){return(yield(yield xe(`${f.baseUrl}/client/auth`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:s,target:W(W({},a),{identifier:String(a.identifier)})})})).json()).authToken}),se=0,fe=()=>{if(w.length){R("Sending metrics...",{metrics:w,evaluations:_});let s={metricsData:w.map(f=>({timestamp:Date.now(),count:f.count,metricsType:"FFMETRICS",attributes:[{key:"featureIdentifier",value:f.featureIdentifier},{key:"featureName",value:f.featureIdentifier},{key:"variationIdentifier",value:f.variationIdentifier},{key:"target",value:a.identifier},{key:"SDK_NAME",value:"JavaScript"},{key:"SDK_LANGUAGE",value:"JavaScript"},{key:"SDK_TYPE",value:"client"},{key:"SDK_VERSION",value:lt}]}))};xe(`${P.eventUrl}/metrics/${V}?cluster=${D}`,{method:"POST",headers:W({"Content-Type":"application/json"},ie),body:JSON.stringify(s)}).then(()=>{w=[],se=0}).catch(f=>{se++&&(w=[],se=0),R(f)}).finally(()=>{$=window.setTimeout(fe,P.eventsSyncInterval)})}else $=window.setTimeout(fe,P.eventsSyncInterval)},_={},he=s=>{R("Sending event for",s.flag),we?x.emit(k.CHANGED,new Proxy(s,{get(f,o){var v;if(f.hasOwnProperty(o)&&o==="value"){let T=f.flag,C=s.value,F=w.find(K=>K.featureIdentifier===T&&K.featureValue===C);F?(H(F),F.variationIdentifier=((v=_[T])==null?void 0:v.identifier)||""):w.push({featureIdentifier:T,featureValue:String(C),variationIdentifier:_[T].identifier||"",count:N?1:0,lastAccessed:Date.now()}),R("Metrics event: Flag",o,"has been read with value via stream update",C)}return o==="value"?_e(s):s[o]}})):x.emit(k.CHANGED,{deleted:s.deleted,flag:s.flag,value:_e(s)})},pe=function(){return we?new Proxy({},{get(s,f){var v,T,C;let o=s[f];if(s.hasOwnProperty(f)){let F=s[f],K=w.find(Ce=>Ce.featureIdentifier===f&&F===Ce.featureValue);K?(K.variationIdentifier=((v=_[f])==null?void 0:v.identifier)||"",H(K)):w.push({featureIdentifier:f,featureValue:F,variationIdentifier:((T=_[f])==null?void 0:T.identifier)||"",count:N?1:0,lastAccessed:Date.now()}),R("Metrics event: Flag:",f,"has been read with value:",F,"variationIdentifier:",(C=_[f])==null?void 0:C.identifier)}return o}}):{}},O=pe();Q(r,P).then(s=>{if(c)return;L=s;let f=$e(s);if(ie={Authorization:`Bearer ${L}`,"Harness-AccountID":f.accountID,"Harness-EnvironmentID":f.environmentIdentifier},R("Authenticated",f),globalThis.localStorage&&globalThis.localStorage.HARNESS_FF_METRICS)try{delete globalThis.localStorage.HARNESS_FF_METRICS,R("Picking up metrics from previous session")}catch(o){}$=window.setTimeout(fe,P.eventsSyncInterval),V=f.environment,D=f.clusterIdentifier,ge().then(()=>{R("Fetch all flags ok",O)}).then(()=>{c||be()}).then(()=>{c||(R("Event stream ready",{storage:O}),x.emit(k.READY,W({},O)),we||Object.keys(O).forEach(o=>{var v;w.push({featureIdentifier:o,featureValue:O[o],variationIdentifier:((v=_[o])==null?void 0:v.identifier)||"",count:N?1:0,lastAccessed:Date.now()})}))}).catch(o=>{x.emit(k.ERROR,o)})}).catch(s=>{ue("Authentication error: ",s),x.emit(k.ERROR,s)});let ge=()=>Oe(void 0,null,function*(){try{(yield(yield xe(`${P.baseUrl}/client/env/${V}/target/${a.identifier}/evaluations?cluster=${D}`,{headers:ie})).json()).forEach(o=>{let v=_e(o),T=O[o.flag];v!==T&&(R("Flag variation has changed for ",o.identifier),O[o.flag]=v,_[o.flag]=W(W({},o),{value:v}),he(o))})}catch(s){return ue("Features fetch operation error: ",s),x.emit(k.ERROR,s),s}}),Z=s=>Oe(void 0,null,function*(){var f;try{let o=yield xe(`${P.baseUrl}/client/env/${V}/target/${a.identifier}/evaluations/${s}?cluster=${D}`,{headers:ie});if(o.ok){let v=yield o.json(),T=_e(v);if(ae(),O[s]=T,_[s]=W(W({},v),{value:T}),ve(),he(v),!we){let C=v.flag,F=w.find(K=>K.featureIdentifier===C&&K.featureValue===v.value);F?(H(F),F.variationIdentifier=((f=_[C])==null?void 0:f.identifier)||""):w.push({featureIdentifier:C,featureValue:String(v.value),variationIdentifier:_[C].identifier||"",count:N?1:0,lastAccessed:Date.now()})}}else x.emit(k.ERROR,o)}catch(o){ue("Feature fetch operation error: ",o),x.emit(k.ERROR,o)}}),be=()=>{if(!P.streamEnabled){R("Stream is disabled by configuration. Note: Polling is not yet supported");return}b=new vt(`${P.baseUrl}/stream?cluster=${D}`,{headers:W({"API-Key":r},ie)}),b.onopen=o=>{R("Stream connected",o),x.emit(k.CONNECTED)},b.onclose=o=>{R("Stream disconnected"),x.emit(k.DISCONNECTED)},b.onerror=o=>{ue("Stream has issue",o),x.emit(k.ERROR,o)};let s=o=>{switch(o.event){case"create":setTimeout(()=>Z(o.identifier),1e3);break;case"patch":Z(o.identifier);break;case"delete":delete O[o.identifier],x.emit(k.CHANGED,{flag:o.identifier,value:void 0,deleted:!0}),R("Evaluation deleted",{message:o,storage:O});break}},f=o=>{o.event==="patch"&&ge()};b.addEventListener("*",o=>{let v=JSON.parse(o.data);R("Received event from stream: ",v),v.domain==="flag"?s(v):v.domain==="target-segment"&&f(v)})},Ee=(s,f)=>x.on(s,f),Te=(s,f)=>{s?x.off(s,f):Y()},ye=(s,f)=>{var v;let o=O[s];if(!we&&o!==void 0){let T=o,C=s,F=w.find(K=>K.featureIdentifier===C&&K.featureValue===T);F?(H(F),F.variationIdentifier=((v=_[C])==null?void 0:v.identifier)||""):w.push({featureIdentifier:C,featureValue:T,count:N?1:0,variationIdentifier:_[C].identifier||"",lastAccessed:Date.now()})}return o!==void 0?o:f},Y=()=>{c=!0,R("Closing event stream"),O=pe(),_={},clearTimeout($),x.all.clear(),typeof(b==null?void 0:b.close)=="function"&&b.close()};return{on:Ee,off:Te,variation:ye,close:Y}};export{k as Event,ht as initialize}; +var et=Object.create,ke=Object.defineProperty,tt=Object.getPrototypeOf,nt=Object.prototype.hasOwnProperty,rt=Object.getOwnPropertyNames,it=Object.getOwnPropertyDescriptor;var W=Object.assign,at=t=>ke(t,"__esModule",{value:!0});var ot=(t,s)=>()=>(s||(s={exports:{}},t(s.exports,s)),s.exports);var st=(t,s,h)=>{if(s&&typeof s=="object"||typeof s=="function")for(let d of rt(s))!nt.call(t,d)&&d!=="default"&&ke(t,d,{get:()=>s[d],enumerable:!(h=it(s,d))||h.enumerable});return t},ft=t=>st(at(ke(t!=null?et(tt(t)):{},"default",t&&t.__esModule&&"default"in t?{get:()=>t.default,enumerable:!0}:{value:t,enumerable:!0})),t);var Ie=(t,s,h)=>new Promise((d,F)=>{var A=P=>{try{N(h.next(P))}catch(x){F(x)}},T=P=>{try{N(h.throw(P))}catch(x){F(x)}},N=P=>P.done?d(P.value):Promise.resolve(P.value).then(A,T);N((h=h.apply(t,s)).next())});var Xe=ot((Ve,Fe)=>{(function(t){"use strict";var s=t.setTimeout,h=t.clearTimeout,d=t.XMLHttpRequest,F=t.XDomainRequest,A=t.ActiveXObject,T=t.EventSource,N=t.document,P=t.Promise,x=t.fetch,ae=t.Response,se=t.TextDecoder,Ee=t.TextEncoder,I=t.AbortController;if(typeof window!="undefined"&&typeof N!="undefined"&&!("readyState"in N)&&N.body==null&&(N.readyState="loading",window.addEventListener("load",function(e){N.readyState="complete"},!1)),d==null&&A!=null&&(d=function(){return new A("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function r(){}return r.prototype=e,new r}),Date.now||(Date.now=function(){return new Date().getTime()}),I==null){var E=x;x=function(e,r){var o=r.signal;return E(e,{headers:r.headers,credentials:r.credentials,cache:r.cache}).then(function(n){var c=n.body.getReader();return o._reader=c,o._aborted&&o._reader.cancel(),{status:n.status,statusText:n.statusText,headers:n.headers,body:{getReader:function(){return c}}}})},I=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function H(){this.bitsNeeded=0,this.codePoint=0}H.prototype.decode=function(e){function r(g,y,l){if(l===1)return g>=128>>y&&g<=2048>>y&&g<=57344>>y&&g<=65536>>y&&g<>6>15?3:y>31?2:1;if(g===6*2)return y>15?3:2;if(g===6*3)return 3;throw new Error}for(var n=65533,c="",f=this.bitsNeeded,v=this.codePoint,R=0;R191||!r(v<<6|p&63,f-6,o(f,v)))&&(f=0,v=n,c+=String.fromCharCode(v)),f===0?(p>=0&&p<=127?(f=0,v=p):p>=192&&p<=223?(f=6*1,v=p&31):p>=224&&p<=239?(f=6*2,v=p&15):p>=240&&p<=247?(f=6*3,v=p&7):(f=0,v=n),f!==0&&!r(v,f,o(f,v))&&(f=0,v=n)):(f-=6,v=v<<6|p&63),f===0&&(v<=65535?c+=String.fromCharCode(v):(c+=String.fromCharCode(55296+(v-65535-1>>10)),c+=String.fromCharCode(56320+(v-65535-1&1023))))}return this.bitsNeeded=f,this.codePoint=v,c};var C=function(){try{return new se().decode(new Ee().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(se==null||Ee==null||!C())&&(se=H);var M=function(){};function Q(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=M,this.onload=M,this.onerror=M,this.onreadystatechange=M,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=M}Q.prototype.open=function(e,r){this._abort(!0);var o=this,n=this._xhr,c=1,f=0;this._abort=function(l){o._sendTimeout!==0&&(h(o._sendTimeout),o._sendTimeout=0),(c===1||c===2||c===3)&&(c=4,n.onload=M,n.onerror=M,n.onabort=M,n.onprogress=M,n.onreadystatechange=M,n.abort(),f!==0&&(h(f),f=0),l||(o.readyState=4,o.onabort(null),o.onreadystatechange())),c=0};var v=function(){if(c===1){var l=0,S="",te=void 0;if("contentType"in n)l=200,S="OK",te=n.contentType;else try{l=n.status,S=n.statusText,te=n.getResponseHeader("Content-Type")}catch(Te){l=0,S="",te=void 0}l!==0&&(c=2,o.readyState=2,o.status=l,o.statusText=S,o._contentType=te,o.onreadystatechange())}},R=function(){if(v(),c===2||c===3){c=3;var l="";try{l=n.responseText}catch(S){}o.readyState=3,o.responseText=l,o.onprogress()}},p=function(l,S){if((S==null||S.preventDefault==null)&&(S={preventDefault:M}),R(),c===1||c===2||c===3){if(c=4,f!==0&&(h(f),f=0),o.readyState=4,l==="load")o.onload(S);else if(l==="error")o.onerror(S);else if(l==="abort")o.onabort(S);else throw new TypeError;o.onreadystatechange()}},g=function(l){n!=null&&(n.readyState===4?(!("onload"in n)||!("onerror"in n)||!("onabort"in n))&&p(n.responseText===""?"error":"load",l):n.readyState===3?"onprogress"in n||R():n.readyState===2&&v())},y=function(){f=s(function(){y()},500),n.readyState===3&&R()};"onload"in n&&(n.onload=function(l){p("load",l)}),"onerror"in n&&(n.onerror=function(l){p("error",l)}),"onabort"in n&&(n.onabort=function(l){p("abort",l)}),"onprogress"in n&&(n.onprogress=R),"onreadystatechange"in n&&(n.onreadystatechange=function(l){g(l)}),("contentType"in n||!("ontimeout"in d.prototype))&&(r+=(r.indexOf("?")===-1?"?":"&")+"padding=true"),n.open(e,r,!0),"readyState"in n&&(f=s(function(){y()},0))},Q.prototype.abort=function(){this._abort(!1)},Q.prototype.getResponseHeader=function(e){return this._contentType},Q.prototype.setRequestHeader=function(e,r){var o=this._xhr;"setRequestHeader"in o&&o.setRequestHeader(e,r)},Q.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},Q.prototype.send=function(){if((!("ontimeout"in d.prototype)||!("sendAsBinary"in d.prototype)&&!("mozAnon"in d.prototype))&&N!=null&&N.readyState!=null&&N.readyState!=="complete"){var e=this;e._sendTimeout=s(function(){e._sendTimeout=0,e.send()},4);return}var r=this._xhr;"withCredentials"in r&&(r.withCredentials=this.withCredentials);try{r.send(void 0)}catch(o){throw o}};function ce(e){return e.replace(/[A-Z]/g,function(r){return String.fromCharCode(r.charCodeAt(0)+32)})}function le(e){for(var r=Object.create(null),o=e.split(`\r +`),n=0;n>(-2*F&6)):0)d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(d);return T};function dt(t){var s=t.replace(/-/g,"+").replace(/_/g,"/");switch(s.length%4){case 0:break;case 2:s+="==";break;case 3:s+="=";break;default:throw"Illegal base64url string!"}try{return function(h){return decodeURIComponent(Be(h).replace(/(.)/g,function(d,F){var A=F.charCodeAt(0).toString(16).toUpperCase();return A.length<2&&(A="0"+A),"%"+A}))}(s)}catch(h){return Be(s)}}function _e(t){this.message=t}function ct(t,s){if(typeof t!="string")throw new _e("Invalid token specified");var h=(s=s||{}).header===!0?0:1;try{return JSON.parse(dt(t.split(".")[h]))}catch(d){throw new _e("Invalid token specified: "+d.message)}}_e.prototype=new Error,_e.prototype.name="InvalidTokenError";var $e=ct;function Ke(t){return{all:t=t||new Map,on:function(s,h){var d=t.get(s);d&&d.push(h)||t.set(s,[h])},off:function(s,h){var d=t.get(s);d&&d.splice(d.indexOf(h)>>>0,1)},emit:function(s,h){(t.get(s)||[]).slice().map(function(d){d(h)}),(t.get("*")||[]).slice().map(function(d){d(s,h)})}}}var Je=ft(Xe());var m;(function(t){t.READY="ready",t.CONNECTED="connected",t.DISCONNECTED="disconnected",t.CHANGED="changed",t.ERROR="error",t.ERROR_METRICS="metrics error",t.ERROR_AUTH="auth error",t.ERROR_FETCH_FLAGS="fetch flags error",t.ERROR_FETCH_FLAG="fetch flag error",t.ERROR_STREAM="stream error"})(m||(m={}));var De=6e4,qe={debug:!1,baseUrl:"https://config.ff.harness.io/api/1.0",eventUrl:"https://events.ff.harness.io/api/1.0",eventsSyncInterval:De,streamEnabled:!0,allAttributesPrivate:!1,privateAttributeNames:[]},oe=(t,...s)=>console.error(`[FF-SDK] ${t}`,...s);var lt="1.8.0",ut=500,Ne=globalThis.fetch,vt=Je.EventSourcePolyfill,je=!!globalThis.Proxy,Pe=t=>{let{value:s}=t;try{switch(t.kind.toLowerCase()){case"int":case"number":s=Number(s);break;case"boolean":s=s.toString().toLowerCase()==="true";break;case"json":s=JSON.parse(s);break}}catch(h){oe(h)}return s},ht=(t,s,h)=>{let d=!1,F,A,T,N,P,x=!0,ae={},se=()=>{x=!1},Ee=()=>{x=!0},I=[],E=Ke(),H=W(W({},qe),h);H.eventsSyncInterval{H.debug&&console.debug(`[FF-SDK] ${i}`,...a)},M=i=>{if(x){let a=Date.now();a-i.lastAccessed>ut&&(i.count++,i.lastAccessed=a)}};globalThis.onbeforeunload=()=>{I.length&&globalThis.localStorage&&(se(),globalThis.localStorage.HARNESS_FF_METRICS=JSON.stringify(I),Ee())};let Q=(i,a)=>Ie(void 0,null,function*(){return(yield(yield Ne(`${a.baseUrl}/client/auth`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:i,target:W(W({},s),{identifier:String(s.identifier)})})})).json()).authToken}),ce=0,le=()=>{if(I.length){C("Sending metrics...",{metrics:I,evaluations:k});let i={metricsData:I.map(a=>({timestamp:Date.now(),count:a.count,metricsType:"FFMETRICS",attributes:[{key:"featureIdentifier",value:a.featureIdentifier},{key:"featureName",value:a.featureIdentifier},{key:"variationIdentifier",value:a.variationIdentifier},{key:"target",value:s.identifier},{key:"SDK_NAME",value:"JavaScript"},{key:"SDK_LANGUAGE",value:"JavaScript"},{key:"SDK_TYPE",value:"client"},{key:"SDK_VERSION",value:lt}]}))};Ne(`${H.eventUrl}/metrics/${F}?cluster=${A}`,{method:"POST",headers:W({"Content-Type":"application/json"},ae),body:JSON.stringify(i)}).then(()=>{I=[],ce=0}).catch(a=>{ce++&&(I=[],ce=0),C(a),E.emit(m.ERROR_METRICS,a)}).finally(()=>{P=window.setTimeout(le,H.eventsSyncInterval)})}else P=window.setTimeout(le,H.eventsSyncInterval)},k={},ye=i=>{C("Sending event for",i.flag),je?E.emit(m.CHANGED,new Proxy(i,{get(a,u){var w;if(a.hasOwnProperty(u)&&u==="value"){let X=a.flag,U=i.value,G=I.find(J=>J.featureIdentifier===X&&J.featureValue===U);G?(M(G),G.variationIdentifier=((w=k[X])==null?void 0:w.identifier)||""):I.push({featureIdentifier:X,featureValue:String(U),variationIdentifier:k[X].identifier||"",count:x?1:0,lastAccessed:Date.now()}),C("Metrics event: Flag",u,"has been read with value via stream update",U)}return u==="value"?Pe(i):i[u]}})):E.emit(m.CHANGED,{deleted:i.deleted,flag:i.flag,value:Pe(i)})},me=function(){return je?new Proxy({},{get(i,a){var w,X,U;let u=i[a];if(i.hasOwnProperty(a)){let G=i[a],J=I.find(ve=>ve.featureIdentifier===a&&G===ve.featureValue);J?(J.variationIdentifier=((w=k[a])==null?void 0:w.identifier)||"",M(J)):I.push({featureIdentifier:a,featureValue:G,variationIdentifier:((X=k[a])==null?void 0:X.identifier)||"",count:x?1:0,lastAccessed:Date.now()}),C("Metrics event: Flag:",a,"has been read with value:",G,"variationIdentifier:",(U=k[a])==null?void 0:U.identifier)}return u}}):{}},D=me();Q(t,H).then(i=>{if(d)return;N=i;let a=$e(i);if(ae={Authorization:`Bearer ${N}`,"Harness-AccountID":a.accountID,"Harness-EnvironmentID":a.environmentIdentifier},C("Authenticated",a),globalThis.localStorage&&globalThis.localStorage.HARNESS_FF_METRICS)try{delete globalThis.localStorage.HARNESS_FF_METRICS,C("Picking up metrics from previous session")}catch(w){}P=window.setTimeout(le,H.eventsSyncInterval),F=a.environment,A=a.clusterIdentifier;let u=!!Object.keys(k).length;Re().then(()=>{C("Fetch all flags ok",D)}).then(()=>{d||Se()}).then(()=>{d||(C("Event stream ready",{storage:D}),u||E.emit(m.READY,W({},D)))}).catch(w=>{E.emit(m.ERROR,w)})}).catch(i=>{oe("Authentication error: ",i),E.emit(m.ERROR_AUTH,i),E.emit(m.ERROR,i)});let Re=()=>Ie(void 0,null,function*(){try{let i=yield Ne(`${H.baseUrl}/client/env/${F}/target/${s.identifier}/evaluations?cluster=${A}`,{headers:ae});i.ok?(yield i.json()).forEach(ue):(oe("Features fetch operation error: ",i),E.emit(m.ERROR_FETCH_FLAGS,i),E.emit(m.ERROR,i))}catch(i){return oe("Features fetch operation error: ",i),E.emit(m.ERROR_FETCH_FLAGS,i),E.emit(m.ERROR,i),i}}),Z=i=>Ie(void 0,null,function*(){try{let a=yield Ne(`${H.baseUrl}/client/env/${F}/target/${s.identifier}/evaluations/${i}?cluster=${A}`,{headers:ae});if(a.ok){let u=yield a.json();ue(u),ye(u)}else oe("Feature fetch operation error: ",a),E.emit(m.ERROR_FETCH_FLAG,a),E.emit(m.ERROR,a)}catch(a){oe("Feature fetch operation error: ",a),E.emit(m.ERROR_FETCH_FLAG,a),E.emit(m.ERROR,a)}}),ue=i=>{se();let a=Pe(i);a!==D[i.flag]&&(C("Flag variation has changed for ",i.identifier),D[i.flag]=a,k[i.flag]=W(W({},i),{value:a}),ye(i)),Ee()},Se=()=>{if(!H.streamEnabled){C("Stream is disabled by configuration. Note: Polling is not yet supported");return}T=new vt(`${H.baseUrl}/stream?cluster=${A}`,{headers:W({"API-Key":t},ae)}),T.onopen=u=>{C("Stream connected",u),E.emit(m.CONNECTED)},T.onclose=u=>{C("Stream disconnected"),E.emit(m.DISCONNECTED)},T.onerror=u=>{oe("Stream has issue",u),E.emit(m.ERROR_STREAM,u),E.emit(m.ERROR,u)};let i=u=>{switch(u.event){case"create":setTimeout(()=>Z(u.identifier),1e3);break;case"patch":Z(u.identifier);break;case"delete":delete D[u.identifier],E.emit(m.CHANGED,{flag:u.identifier,value:void 0,deleted:!0}),C("Evaluation deleted",{message:u,storage:D});break}},a=u=>{u.event==="patch"&&Re()};T.addEventListener("*",u=>{let w=JSON.parse(u.data);C("Received event from stream: ",w),w.domain==="flag"?i(w):w.domain==="target-segment"&&a(w)})},Ce=(i,a)=>E.on(i,a),we=(i,a)=>{i?E.off(i,a):ee()},z=(i,a)=>{var w;let u=D[i];if(!je&&u!==void 0){let X=u,U=i,G=I.find(J=>J.featureIdentifier===U&&J.featureValue===X);G?(M(G),G.variationIdentifier=((w=k[U])==null?void 0:w.identifier)||""):I.push({featureIdentifier:U,featureValue:X,count:x?1:0,variationIdentifier:k[U].identifier||"",lastAccessed:Date.now()})}return u!==void 0?u:a},ee=()=>{d=!0,C("Closing event stream"),D=me(),k={},clearTimeout(P),E.all.clear(),typeof(T==null?void 0:T.close)=="function"&&T.close()};return{on:Ce,off:we,variation:z,close:ee,setEvaluations:i=>{if(i.length){let a=!!Object.keys(k).length;i.forEach(ue),a||setTimeout(()=>{E.emit(m.READY,W({},D))},10)}}}};export{m as Event,ht as initialize}; /** @license * eventsource.js * Available under MIT License (MIT) diff --git a/dist/sdk.esm.js b/dist/sdk.esm.js index 40c62b21..17295527 100644 --- a/dist/sdk.esm.js +++ b/dist/sdk.esm.js @@ -1,9 +1,9 @@ -var We=Object.create,He=Object.defineProperty,Ye=Object.getPrototypeOf,ze=Object.prototype.hasOwnProperty,Qe=Object.getOwnPropertyNames,Ze=Object.getOwnPropertyDescriptor;var q=Object.assign,et=o=>He(o,"__esModule",{value:!0});var tt=(o,l)=>()=>(l||(l={exports:{}},o(l.exports,l)),l.exports);var nt=(o,l,C)=>{if(l&&typeof l=="object"||typeof l=="function")for(let g of Qe(l))!ze.call(o,g)&&g!=="default"&&He(o,g,{get:()=>l[g],enumerable:!(C=Ze(l,g))||C.enumerable});return o},rt=o=>nt(et(He(o!=null?We(Ye(o)):{},"default",o&&o.__esModule&&"default"in o?{get:()=>o.default,enumerable:!0}:{value:o,enumerable:!0})),o);var Ae=(o,l,C)=>new Promise((g,Y)=>{var z=U=>{try{H(C.next(U))}catch(O){Y(O)}},x=U=>{try{H(C.throw(U))}catch(O){Y(O)}},H=U=>U.done?g(U.value):Promise.resolve(U.value).then(z,x);H((C=C.apply(o,l)).next())});var ke=tt((Le,Oe)=>{(function(o){"use strict";var l=o.setTimeout,C=o.clearTimeout,g=o.XMLHttpRequest,Y=o.XDomainRequest,z=o.ActiveXObject,x=o.EventSource,H=o.document,U=o.Promise,O=o.fetch,ie=o.Response,ae=o.TextDecoder,ve=o.TextEncoder,S=o.AbortController;if(typeof window!="undefined"&&typeof H!="undefined"&&!("readyState"in H)&&H.body==null&&(H.readyState="loading",window.addEventListener("load",function(e){H.readyState="complete"},!1)),g==null&&z!=null&&(g=function(){return new z("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function n(){}return n.prototype=e,new n}),Date.now||(Date.now=function(){return new Date().getTime()}),S==null){var D=O;O=function(e,n){var r=n.signal;return D(e,{headers:n.headers,credentials:n.credentials,cache:n.cache}).then(function(t){var d=t.body.getReader();return r._reader=d,r._aborted&&r._reader.cancel(),{status:t.status,statusText:t.statusText,headers:t.headers,body:{getReader:function(){return d}}}})},S=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function L(){this.bitsNeeded=0,this.codePoint=0}L.prototype.decode=function(e){function n(p,E,c){if(c===1)return p>=128>>E&&p<=2048>>E&&p<=57344>>E&&p<=65536>>E&&p<>6>15?3:E>31?2:1;if(p===6*2)return E>15?3:2;if(p===6*3)return 3;throw new Error}for(var t=65533,d="",f=this.bitsNeeded,v=this.codePoint,y=0;y191||!n(v<<6|h&63,f-6,r(f,v)))&&(f=0,v=t,d+=String.fromCharCode(v)),f===0?(h>=0&&h<=127?(f=0,v=h):h>=192&&h<=223?(f=6*1,v=h&31):h>=224&&h<=239?(f=6*2,v=h&15):h>=240&&h<=247?(f=6*3,v=h&7):(f=0,v=t),f!==0&&!n(v,f,r(f,v))&&(f=0,v=t)):(f-=6,v=v<<6|h&63),f===0&&(v<=65535?d+=String.fromCharCode(v):(d+=String.fromCharCode(55296+(v-65535-1>>10)),d+=String.fromCharCode(56320+(v-65535-1&1023))))}return this.bitsNeeded=f,this.codePoint=v,d};var R=function(){try{return new ae().decode(new ve().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(ae==null||ve==null||!R())&&(ae=L);var M=function(){};function Q(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=M,this.onload=M,this.onerror=M,this.onreadystatechange=M,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=M}Q.prototype.open=function(e,n){this._abort(!0);var r=this,t=this._xhr,d=1,f=0;this._abort=function(c){r._sendTimeout!==0&&(C(r._sendTimeout),r._sendTimeout=0),(d===1||d===2||d===3)&&(d=4,t.onload=M,t.onerror=M,t.onabort=M,t.onprogress=M,t.onreadystatechange=M,t.abort(),f!==0&&(C(f),f=0),c||(r.readyState=4,r.onabort(null),r.onreadystatechange())),d=0};var v=function(){if(d===1){var c=0,m="",ee=void 0;if("contentType"in t)c=200,m="OK",ee=t.contentType;else try{c=t.status,m=t.statusText,ee=t.getResponseHeader("Content-Type")}catch(me){c=0,m="",ee=void 0}c!==0&&(d=2,r.readyState=2,r.status=c,r.statusText=m,r._contentType=ee,r.onreadystatechange())}},y=function(){if(v(),d===2||d===3){d=3;var c="";try{c=t.responseText}catch(m){}r.readyState=3,r.responseText=c,r.onprogress()}},h=function(c,m){if((m==null||m.preventDefault==null)&&(m={preventDefault:M}),y(),d===1||d===2||d===3){if(d=4,f!==0&&(C(f),f=0),r.readyState=4,c==="load")r.onload(m);else if(c==="error")r.onerror(m);else if(c==="abort")r.onabort(m);else throw new TypeError;r.onreadystatechange()}},p=function(c){t!=null&&(t.readyState===4?(!("onload"in t)||!("onerror"in t)||!("onabort"in t))&&h(t.responseText===""?"error":"load",c):t.readyState===3?"onprogress"in t||y():t.readyState===2&&v())},E=function(){f=l(function(){E()},500),t.readyState===3&&y()};"onload"in t&&(t.onload=function(c){h("load",c)}),"onerror"in t&&(t.onerror=function(c){h("error",c)}),"onabort"in t&&(t.onabort=function(c){h("abort",c)}),"onprogress"in t&&(t.onprogress=y),"onreadystatechange"in t&&(t.onreadystatechange=function(c){p(c)}),("contentType"in t||!("ontimeout"in g.prototype))&&(n+=(n.indexOf("?")===-1?"?":"&")+"padding=true"),t.open(e,n,!0),"readyState"in t&&(f=l(function(){E()},0))},Q.prototype.abort=function(){this._abort(!1)},Q.prototype.getResponseHeader=function(e){return this._contentType},Q.prototype.setRequestHeader=function(e,n){var r=this._xhr;"setRequestHeader"in r&&r.setRequestHeader(e,n)},Q.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},Q.prototype.send=function(){if((!("ontimeout"in g.prototype)||!("sendAsBinary"in g.prototype)&&!("mozAnon"in g.prototype))&&H!=null&&H.readyState!=null&&H.readyState!=="complete"){var e=this;e._sendTimeout=l(function(){e._sendTimeout=0,e.send()},4);return}var n=this._xhr;"withCredentials"in n&&(n.withCredentials=this.withCredentials);try{n.send(void 0)}catch(r){throw r}};function se(e){return e.replace(/[A-Z]/g,function(n){return String.fromCharCode(n.charCodeAt(0)+32)})}function fe(e){for(var n=Object.create(null),r=e.split(`\r -`),t=0;tconsole.error(`[FF-SDK] ${o}`,...l);var ot="1.7.0",st=500,Ne=globalThis.fetch,ft=Be.EventSourcePolyfill,be=!!globalThis.Proxy,_e=o=>{let{value:l}=o;try{switch(o.kind.toLowerCase()){case"int":case"number":l=Number(l);break;case"boolean":l=l.toString().toLowerCase()==="true";break;case"json":l=JSON.parse(l);break}}catch(C){ue(C)}return l},dt=(o,l,C)=>{let g=!1,Y,z,x,H,U,O=!0,ie={},ae=()=>{O=!1},ve=()=>{O=!0},S=[],D=at(),L=q(q({},Ue),C);L.eventsSyncInterval{L.debug&&console.debug(`[FF-SDK] ${a}`,...s)},M=a=>{if(O){let s=Date.now();s-a.lastAccessed>st&&(a.count++,a.lastAccessed=s)}};globalThis.onbeforeunload=()=>{S.length&&globalThis.localStorage&&(ae(),globalThis.localStorage.HARNESS_FF_METRICS=JSON.stringify(S),ve())};let Q=(a,s)=>Ae(void 0,null,function*(){return(yield(yield Ne(`${s.baseUrl}/client/auth`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:a,target:q(q({},l),{identifier:String(l.identifier)})})})).json()).authToken}),se=0,fe=()=>{if(S.length){R("Sending metrics...",{metrics:S,evaluations:N});let a={metricsData:S.map(s=>({timestamp:Date.now(),count:s.count,metricsType:"FFMETRICS",attributes:[{key:"featureIdentifier",value:s.featureIdentifier},{key:"featureName",value:s.featureIdentifier},{key:"variationIdentifier",value:s.variationIdentifier},{key:"target",value:l.identifier},{key:"SDK_NAME",value:"JavaScript"},{key:"SDK_LANGUAGE",value:"JavaScript"},{key:"SDK_TYPE",value:"client"},{key:"SDK_VERSION",value:ot}]}))};Ne(`${L.eventUrl}/metrics/${Y}?cluster=${z}`,{method:"POST",headers:q({"Content-Type":"application/json"},ie),body:JSON.stringify(a)}).then(()=>{S=[],se=0}).catch(s=>{se++&&(S=[],se=0),R(s)}).finally(()=>{U=window.setTimeout(fe,L.eventsSyncInterval)})}else U=window.setTimeout(fe,L.eventsSyncInterval)},N={},he=a=>{R("Sending event for",a.flag),be?D.emit(V.CHANGED,new Proxy(a,{get(s,i){var u;if(s.hasOwnProperty(i)&&i==="value"){let b=s.flag,w=a.value,_=S.find(B=>B.featureIdentifier===b&&B.featureValue===w);_?(M(_),_.variationIdentifier=((u=N[b])==null?void 0:u.identifier)||""):S.push({featureIdentifier:b,featureValue:String(w),variationIdentifier:N[b].identifier||"",count:O?1:0,lastAccessed:Date.now()}),R("Metrics event: Flag",i,"has been read with value via stream update",w)}return i==="value"?_e(a):a[i]}})):D.emit(V.CHANGED,{deleted:a.deleted,flag:a.flag,value:_e(a)})},pe=function(){return be?new Proxy({},{get(a,s){var u,b,w;let i=a[s];if(a.hasOwnProperty(s)){let _=a[s],B=S.find(Ce=>Ce.featureIdentifier===s&&_===Ce.featureValue);B?(B.variationIdentifier=((u=N[s])==null?void 0:u.identifier)||"",M(B)):S.push({featureIdentifier:s,featureValue:_,variationIdentifier:((b=N[s])==null?void 0:b.identifier)||"",count:O?1:0,lastAccessed:Date.now()}),R("Metrics event: Flag:",s,"has been read with value:",_,"variationIdentifier:",(w=N[s])==null?void 0:w.identifier)}return i}}):{}},I=pe();Q(o,L).then(a=>{if(g)return;H=a;let s=it(a);if(ie={Authorization:`Bearer ${H}`,"Harness-AccountID":s.accountID,"Harness-EnvironmentID":s.environmentIdentifier},R("Authenticated",s),globalThis.localStorage&&globalThis.localStorage.HARNESS_FF_METRICS)try{delete globalThis.localStorage.HARNESS_FF_METRICS,R("Picking up metrics from previous session")}catch(i){}U=window.setTimeout(fe,L.eventsSyncInterval),Y=s.environment,z=s.clusterIdentifier,ge().then(()=>{R("Fetch all flags ok",I)}).then(()=>{g||we()}).then(()=>{g||(R("Event stream ready",{storage:I}),D.emit(V.READY,q({},I)),be||Object.keys(I).forEach(i=>{var u;S.push({featureIdentifier:i,featureValue:I[i],variationIdentifier:((u=N[i])==null?void 0:u.identifier)||"",count:O?1:0,lastAccessed:Date.now()})}))}).catch(i=>{D.emit(V.ERROR,i)})}).catch(a=>{ue("Authentication error: ",a),D.emit(V.ERROR,a)});let ge=()=>Ae(void 0,null,function*(){try{(yield(yield Ne(`${L.baseUrl}/client/env/${Y}/target/${l.identifier}/evaluations?cluster=${z}`,{headers:ie})).json()).forEach(i=>{let u=_e(i),b=I[i.flag];u!==b&&(R("Flag variation has changed for ",i.identifier),I[i.flag]=u,N[i.flag]=q(q({},i),{value:u}),he(i))})}catch(a){return ue("Features fetch operation error: ",a),D.emit(V.ERROR,a),a}}),Z=a=>Ae(void 0,null,function*(){var s;try{let i=yield Ne(`${L.baseUrl}/client/env/${Y}/target/${l.identifier}/evaluations/${a}?cluster=${z}`,{headers:ie});if(i.ok){let u=yield i.json(),b=_e(u);if(ae(),I[a]=b,N[a]=q(q({},u),{value:b}),ve(),he(u),!be){let w=u.flag,_=S.find(B=>B.featureIdentifier===w&&B.featureValue===u.value);_?(M(_),_.variationIdentifier=((s=N[w])==null?void 0:s.identifier)||""):S.push({featureIdentifier:w,featureValue:String(u.value),variationIdentifier:N[w].identifier||"",count:O?1:0,lastAccessed:Date.now()})}}else D.emit(V.ERROR,i)}catch(i){ue("Feature fetch operation error: ",i),D.emit(V.ERROR,i)}}),we=()=>{if(!L.streamEnabled){R("Stream is disabled by configuration. Note: Polling is not yet supported");return}x=new ft(`${L.baseUrl}/stream?cluster=${z}`,{headers:q({"API-Key":o},ie)}),x.onopen=i=>{R("Stream connected",i),D.emit(V.CONNECTED)},x.onclose=i=>{R("Stream disconnected"),D.emit(V.DISCONNECTED)},x.onerror=i=>{ue("Stream has issue",i),D.emit(V.ERROR,i)};let a=i=>{switch(i.event){case"create":setTimeout(()=>Z(i.identifier),1e3);break;case"patch":Z(i.identifier);break;case"delete":delete I[i.identifier],D.emit(V.CHANGED,{flag:i.identifier,value:void 0,deleted:!0}),R("Evaluation deleted",{message:i,storage:I});break}},s=i=>{i.event==="patch"&&ge()};x.addEventListener("*",i=>{let u=JSON.parse(i.data);R("Received event from stream: ",u),u.domain==="flag"?a(u):u.domain==="target-segment"&&s(u)})},Ee=(a,s)=>D.on(a,s),Te=(a,s)=>{a?D.off(a,s):J()},ye=(a,s)=>{var u;let i=I[a];if(!be&&i!==void 0){let b=i,w=a,_=S.find(B=>B.featureIdentifier===w&&B.featureValue===b);_?(M(_),_.variationIdentifier=((u=N[w])==null?void 0:u.identifier)||""):S.push({featureIdentifier:w,featureValue:b,count:O?1:0,variationIdentifier:N[w].identifier||"",lastAccessed:Date.now()})}return i!==void 0?i:s},J=()=>{g=!0,R("Closing event stream"),I=pe(),N={},clearTimeout(U),D.all.clear(),typeof(x==null?void 0:x.close)=="function"&&x.close()};return{on:Ee,off:Te,variation:ye,close:J}};export{V as Event,dt as initialize}; +var Ye=Object.create,Me=Object.defineProperty,We=Object.getPrototypeOf,ze=Object.prototype.hasOwnProperty,Qe=Object.getOwnPropertyNames,Ze=Object.getOwnPropertyDescriptor;var J=Object.assign,et=a=>Me(a,"__esModule",{value:!0});var tt=(a,l)=>()=>(l||(l={exports:{}},a(l.exports,l)),l.exports);var nt=(a,l,b)=>{if(l&&typeof l=="object"||typeof l=="function")for(let p of Qe(l))!ze.call(a,p)&&p!=="default"&&Me(a,p,{get:()=>l[p],enumerable:!(b=Ze(l,p))||b.enumerable});return a},rt=a=>nt(et(Me(a!=null?Ye(We(a)):{},"default",a&&a.__esModule&&"default"in a?{get:()=>a.default,enumerable:!0}:{value:a,enumerable:!0})),a);var _e=(a,l,b)=>new Promise((p,Y)=>{var W=V=>{try{F(b.next(V))}catch(D){Y(D)}},_=V=>{try{F(b.throw(V))}catch(D){Y(D)}},F=V=>V.done?p(V.value):Promise.resolve(V.value).then(W,_);F((b=b.apply(a,l)).next())});var Ue=tt((ke,Ie)=>{(function(a){"use strict";var l=a.setTimeout,b=a.clearTimeout,p=a.XMLHttpRequest,Y=a.XDomainRequest,W=a.ActiveXObject,_=a.EventSource,F=a.document,V=a.Promise,D=a.fetch,ae=a.Response,se=a.TextDecoder,Ee=a.TextEncoder,O=a.AbortController;if(typeof window!="undefined"&&typeof F!="undefined"&&!("readyState"in F)&&F.body==null&&(F.readyState="loading",window.addEventListener("load",function(e){F.readyState="complete"},!1)),p==null&&W!=null&&(p=function(){return new W("Microsoft.XMLHTTP")}),Object.create==null&&(Object.create=function(e){function n(){}return n.prototype=e,new n}),Date.now||(Date.now=function(){return new Date().getTime()}),O==null){var g=D;D=function(e,n){var o=n.signal;return g(e,{headers:n.headers,credentials:n.credentials,cache:n.cache}).then(function(t){var f=t.body.getReader();return o._reader=f,o._aborted&&o._reader.cancel(),{status:t.status,statusText:t.statusText,headers:t.headers,body:{getReader:function(){return f}}}})},O=function(){this.signal={_reader:null,_aborted:!1},this.abort=function(){this.signal._reader!=null&&this.signal._reader.cancel(),this.signal._aborted=!0}}}function N(){this.bitsNeeded=0,this.codePoint=0}N.prototype.decode=function(e){function n(h,E,d){if(d===1)return h>=128>>E&&h<=2048>>E&&h<=57344>>E&&h<=65536>>E&&h<>6>15?3:E>31?2:1;if(h===6*2)return E>15?3:2;if(h===6*3)return 3;throw new Error}for(var t=65533,f="",s=this.bitsNeeded,u=this.codePoint,m=0;m191||!n(u<<6|v&63,s-6,o(s,u)))&&(s=0,u=t,f+=String.fromCharCode(u)),s===0?(v>=0&&v<=127?(s=0,u=v):v>=192&&v<=223?(s=6*1,u=v&31):v>=224&&v<=239?(s=6*2,u=v&15):v>=240&&v<=247?(s=6*3,u=v&7):(s=0,u=t),s!==0&&!n(u,s,o(s,u))&&(s=0,u=t)):(s-=6,u=u<<6|v&63),s===0&&(u<=65535?f+=String.fromCharCode(u):(f+=String.fromCharCode(55296+(u-65535-1>>10)),f+=String.fromCharCode(56320+(u-65535-1&1023))))}return this.bitsNeeded=s,this.codePoint=u,f};var w=function(){try{return new se().decode(new Ee().encode("test"),{stream:!0})==="test"}catch(e){console.debug("TextDecoder does not support streaming option. Using polyfill instead: "+e)}return!1};(se==null||Ee==null||!w())&&(se=N);var x=function(){};function Q(e){this.withCredentials=!1,this.readyState=0,this.status=0,this.statusText="",this.responseText="",this.onprogress=x,this.onload=x,this.onerror=x,this.onreadystatechange=x,this._contentType="",this._xhr=e,this._sendTimeout=0,this._abort=x}Q.prototype.open=function(e,n){this._abort(!0);var o=this,t=this._xhr,f=1,s=0;this._abort=function(d){o._sendTimeout!==0&&(b(o._sendTimeout),o._sendTimeout=0),(f===1||f===2||f===3)&&(f=4,t.onload=x,t.onerror=x,t.onabort=x,t.onprogress=x,t.onreadystatechange=x,t.abort(),s!==0&&(b(s),s=0),d||(o.readyState=4,o.onabort(null),o.onreadystatechange())),f=0};var u=function(){if(f===1){var d=0,R="",te=void 0;if("contentType"in t)d=200,R="OK",te=t.contentType;else try{d=t.status,R=t.statusText,te=t.getResponseHeader("Content-Type")}catch(be){d=0,R="",te=void 0}d!==0&&(f=2,o.readyState=2,o.status=d,o.statusText=R,o._contentType=te,o.onreadystatechange())}},m=function(){if(u(),f===2||f===3){f=3;var d="";try{d=t.responseText}catch(R){}o.readyState=3,o.responseText=d,o.onprogress()}},v=function(d,R){if((R==null||R.preventDefault==null)&&(R={preventDefault:x}),m(),f===1||f===2||f===3){if(f=4,s!==0&&(b(s),s=0),o.readyState=4,d==="load")o.onload(R);else if(d==="error")o.onerror(R);else if(d==="abort")o.onabort(R);else throw new TypeError;o.onreadystatechange()}},h=function(d){t!=null&&(t.readyState===4?(!("onload"in t)||!("onerror"in t)||!("onabort"in t))&&v(t.responseText===""?"error":"load",d):t.readyState===3?"onprogress"in t||m():t.readyState===2&&u())},E=function(){s=l(function(){E()},500),t.readyState===3&&m()};"onload"in t&&(t.onload=function(d){v("load",d)}),"onerror"in t&&(t.onerror=function(d){v("error",d)}),"onabort"in t&&(t.onabort=function(d){v("abort",d)}),"onprogress"in t&&(t.onprogress=m),"onreadystatechange"in t&&(t.onreadystatechange=function(d){h(d)}),("contentType"in t||!("ontimeout"in p.prototype))&&(n+=(n.indexOf("?")===-1?"?":"&")+"padding=true"),t.open(e,n,!0),"readyState"in t&&(s=l(function(){E()},0))},Q.prototype.abort=function(){this._abort(!1)},Q.prototype.getResponseHeader=function(e){return this._contentType},Q.prototype.setRequestHeader=function(e,n){var o=this._xhr;"setRequestHeader"in o&&o.setRequestHeader(e,n)},Q.prototype.getAllResponseHeaders=function(){return this._xhr.getAllResponseHeaders!=null&&this._xhr.getAllResponseHeaders()||""},Q.prototype.send=function(){if((!("ontimeout"in p.prototype)||!("sendAsBinary"in p.prototype)&&!("mozAnon"in p.prototype))&&F!=null&&F.readyState!=null&&F.readyState!=="complete"){var e=this;e._sendTimeout=l(function(){e._sendTimeout=0,e.send()},4);return}var n=this._xhr;"withCredentials"in n&&(n.withCredentials=this.withCredentials);try{n.send(void 0)}catch(o){throw o}};function ce(e){return e.replace(/[A-Z]/g,function(n){return String.fromCharCode(n.charCodeAt(0)+32)})}function le(e){for(var n=Object.create(null),o=e.split(`\r +`),t=0;tconsole.error(`[FF-SDK] ${a}`,...l);var ot="1.8.0",st=500,De=globalThis.fetch,ft=Be.EventSourcePolyfill,Le=!!globalThis.Proxy,Ve=a=>{let{value:l}=a;try{switch(a.kind.toLowerCase()){case"int":case"number":l=Number(l);break;case"boolean":l=l.toString().toLowerCase()==="true";break;case"json":l=JSON.parse(l);break}}catch(b){oe(b)}return l},dt=(a,l,b)=>{let p=!1,Y,W,_,F,V,D=!0,ae={},se=()=>{D=!1},Ee=()=>{D=!0},O=[],g=at(),N=J(J({},Ge),b);N.eventsSyncInterval{N.debug&&console.debug(`[FF-SDK] ${r}`,...i)},x=r=>{if(D){let i=Date.now();i-r.lastAccessed>st&&(r.count++,r.lastAccessed=i)}};globalThis.onbeforeunload=()=>{O.length&&globalThis.localStorage&&(se(),globalThis.localStorage.HARNESS_FF_METRICS=JSON.stringify(O),Ee())};let Q=(r,i)=>_e(void 0,null,function*(){return(yield(yield De(`${i.baseUrl}/client/auth`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:r,target:J(J({},l),{identifier:String(l.identifier)})})})).json()).authToken}),ce=0,le=()=>{if(O.length){w("Sending metrics...",{metrics:O,evaluations:H});let r={metricsData:O.map(i=>({timestamp:Date.now(),count:i.count,metricsType:"FFMETRICS",attributes:[{key:"featureIdentifier",value:i.featureIdentifier},{key:"featureName",value:i.featureIdentifier},{key:"variationIdentifier",value:i.variationIdentifier},{key:"target",value:l.identifier},{key:"SDK_NAME",value:"JavaScript"},{key:"SDK_LANGUAGE",value:"JavaScript"},{key:"SDK_TYPE",value:"client"},{key:"SDK_VERSION",value:ot}]}))};De(`${N.eventUrl}/metrics/${Y}?cluster=${W}`,{method:"POST",headers:J({"Content-Type":"application/json"},ae),body:JSON.stringify(r)}).then(()=>{O=[],ce=0}).catch(i=>{ce++&&(O=[],ce=0),w(i),g.emit(y.ERROR_METRICS,i)}).finally(()=>{V=window.setTimeout(le,N.eventsSyncInterval)})}else V=window.setTimeout(le,N.eventsSyncInterval)},H={},ye=r=>{w("Sending event for",r.flag),Le?g.emit(y.CHANGED,new Proxy(r,{get(i,c){var S;if(i.hasOwnProperty(c)&&c==="value"){let $=i.flag,j=r.value,P=O.find(X=>X.featureIdentifier===$&&X.featureValue===j);P?(x(P),P.variationIdentifier=((S=H[$])==null?void 0:S.identifier)||""):O.push({featureIdentifier:$,featureValue:String(j),variationIdentifier:H[$].identifier||"",count:D?1:0,lastAccessed:Date.now()}),w("Metrics event: Flag",c,"has been read with value via stream update",j)}return c==="value"?Ve(r):r[c]}})):g.emit(y.CHANGED,{deleted:r.deleted,flag:r.flag,value:Ve(r)})},me=function(){return Le?new Proxy({},{get(r,i){var S,$,j;let c=r[i];if(r.hasOwnProperty(i)){let P=r[i],X=O.find(ve=>ve.featureIdentifier===i&&P===ve.featureValue);X?(X.variationIdentifier=((S=H[i])==null?void 0:S.identifier)||"",x(X)):O.push({featureIdentifier:i,featureValue:P,variationIdentifier:(($=H[i])==null?void 0:$.identifier)||"",count:D?1:0,lastAccessed:Date.now()}),w("Metrics event: Flag:",i,"has been read with value:",P,"variationIdentifier:",(j=H[i])==null?void 0:j.identifier)}return c}}):{}},I=me();Q(a,N).then(r=>{if(p)return;F=r;let i=it(r);if(ae={Authorization:`Bearer ${F}`,"Harness-AccountID":i.accountID,"Harness-EnvironmentID":i.environmentIdentifier},w("Authenticated",i),globalThis.localStorage&&globalThis.localStorage.HARNESS_FF_METRICS)try{delete globalThis.localStorage.HARNESS_FF_METRICS,w("Picking up metrics from previous session")}catch(S){}V=window.setTimeout(le,N.eventsSyncInterval),Y=i.environment,W=i.clusterIdentifier;let c=!!Object.keys(H).length;Re().then(()=>{w("Fetch all flags ok",I)}).then(()=>{p||Se()}).then(()=>{p||(w("Event stream ready",{storage:I}),c||g.emit(y.READY,J({},I)))}).catch(S=>{g.emit(y.ERROR,S)})}).catch(r=>{oe("Authentication error: ",r),g.emit(y.ERROR_AUTH,r),g.emit(y.ERROR,r)});let Re=()=>_e(void 0,null,function*(){try{let r=yield De(`${N.baseUrl}/client/env/${Y}/target/${l.identifier}/evaluations?cluster=${W}`,{headers:ae});r.ok?(yield r.json()).forEach(ue):(oe("Features fetch operation error: ",r),g.emit(y.ERROR_FETCH_FLAGS,r),g.emit(y.ERROR,r))}catch(r){return oe("Features fetch operation error: ",r),g.emit(y.ERROR_FETCH_FLAGS,r),g.emit(y.ERROR,r),r}}),Z=r=>_e(void 0,null,function*(){try{let i=yield De(`${N.baseUrl}/client/env/${Y}/target/${l.identifier}/evaluations/${r}?cluster=${W}`,{headers:ae});if(i.ok){let c=yield i.json();ue(c),ye(c)}else oe("Feature fetch operation error: ",i),g.emit(y.ERROR_FETCH_FLAG,i),g.emit(y.ERROR,i)}catch(i){oe("Feature fetch operation error: ",i),g.emit(y.ERROR_FETCH_FLAG,i),g.emit(y.ERROR,i)}}),ue=r=>{se();let i=Ve(r);i!==I[r.flag]&&(w("Flag variation has changed for ",r.identifier),I[r.flag]=i,H[r.flag]=J(J({},r),{value:i}),ye(r)),Ee()},Se=()=>{if(!N.streamEnabled){w("Stream is disabled by configuration. Note: Polling is not yet supported");return}_=new ft(`${N.baseUrl}/stream?cluster=${W}`,{headers:J({"API-Key":a},ae)}),_.onopen=c=>{w("Stream connected",c),g.emit(y.CONNECTED)},_.onclose=c=>{w("Stream disconnected"),g.emit(y.DISCONNECTED)},_.onerror=c=>{oe("Stream has issue",c),g.emit(y.ERROR_STREAM,c),g.emit(y.ERROR,c)};let r=c=>{switch(c.event){case"create":setTimeout(()=>Z(c.identifier),1e3);break;case"patch":Z(c.identifier);break;case"delete":delete I[c.identifier],g.emit(y.CHANGED,{flag:c.identifier,value:void 0,deleted:!0}),w("Evaluation deleted",{message:c,storage:I});break}},i=c=>{c.event==="patch"&&Re()};_.addEventListener("*",c=>{let S=JSON.parse(c.data);w("Received event from stream: ",S),S.domain==="flag"?r(S):S.domain==="target-segment"&&i(S)})},Ce=(r,i)=>g.on(r,i),Te=(r,i)=>{r?g.off(r,i):ee()},z=(r,i)=>{var S;let c=I[r];if(!Le&&c!==void 0){let $=c,j=r,P=O.find(X=>X.featureIdentifier===j&&X.featureValue===$);P?(x(P),P.variationIdentifier=((S=H[j])==null?void 0:S.identifier)||""):O.push({featureIdentifier:j,featureValue:$,count:D?1:0,variationIdentifier:H[j].identifier||"",lastAccessed:Date.now()})}return c!==void 0?c:i},ee=()=>{p=!0,w("Closing event stream"),I=me(),H={},clearTimeout(V),g.all.clear(),typeof(_==null?void 0:_.close)=="function"&&_.close()};return{on:Ce,off:Te,variation:z,close:ee,setEvaluations:r=>{if(r.length){let i=!!Object.keys(H).length;r.forEach(ue),i||setTimeout(()=>{g.emit(y.READY,J({},I))},10)}}}};export{y as Event,dt as initialize}; /** @license * eventsource.js * Available under MIT License (MIT) diff --git a/dist/types.d.ts b/dist/types.d.ts index 7195e53c..b9f3f68b 100644 --- a/dist/types.d.ts +++ b/dist/types.d.ts @@ -15,7 +15,12 @@ export declare enum Event { CONNECTED = "connected", DISCONNECTED = "disconnected", CHANGED = "changed", - ERROR = "error" + ERROR = "error", + ERROR_METRICS = "metrics error", + ERROR_AUTH = "auth error", + ERROR_FETCH_FLAGS = "fetch flags error", + ERROR_FETCH_FLAG = "fetch flag error", + ERROR_STREAM = "stream error" } export declare type VariationValue = boolean | string | number | object | undefined; export interface Evaluation { @@ -31,6 +36,11 @@ export interface EventCallbackMapping { [Event.DISCONNECTED]: () => void; [Event.CHANGED]: (flag: Evaluation) => void; [Event.ERROR]: (error: unknown) => void; + [Event.ERROR_AUTH]: (error: unknown) => void; + [Event.ERROR_FETCH_FLAGS]: (error: unknown) => void; + [Event.ERROR_FETCH_FLAG]: (error: unknown) => void; + [Event.ERROR_STREAM]: (error: unknown) => void; + [Event.ERROR_METRICS]: (error: unknown) => void; } export declare type EventOnBinding = (event: K, callback: EventCallbackMapping[K]) => void; export declare type EventOffBinding = (event?: K, callback?: EventCallbackMapping[K]) => void; @@ -39,6 +49,7 @@ export interface Result { off: EventOffBinding; variation: (identifier: string, defaultValue: any) => VariationValue; close: () => void; + setEvaluations: (evaluations: Evaluation[]) => void; } export interface Options { baseUrl?: string; diff --git a/examples/preact/package-lock.json b/examples/preact/package-lock.json index 6e72c9cf..29e0b3bc 100644 --- a/examples/preact/package-lock.json +++ b/examples/preact/package-lock.json @@ -18,7 +18,7 @@ }, "../..": { "name": "@harnessio/ff-javascript-client-sdk", - "version": "1.7.0", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { "jwt-decode": "^3.1.2", diff --git a/examples/react-redux/package-lock.json b/examples/react-redux/package-lock.json index b3046e3d..ec47a2ef 100644 --- a/examples/react-redux/package-lock.json +++ b/examples/react-redux/package-lock.json @@ -27,7 +27,7 @@ }, "../..": { "name": "@harnessio/ff-javascript-client-sdk", - "version": "1.7.0", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { "jwt-decode": "^3.1.2", diff --git a/examples/react/package-lock.json b/examples/react/package-lock.json index 0afecb85..a1249af2 100644 --- a/examples/react/package-lock.json +++ b/examples/react/package-lock.json @@ -15,7 +15,7 @@ }, "../..": { "name": "@harnessio/ff-javascript-client-sdk", - "version": "1.7.0", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { "jwt-decode": "^3.1.2", diff --git a/package-lock.json b/package-lock.json index e0f8c7e5..01840d29 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@harnessio/ff-javascript-client-sdk", - "version": "1.7.0", + "version": "1.8.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@harnessio/ff-javascript-client-sdk", - "version": "1.7.0", + "version": "1.8.0", "license": "Apache-2.0", "dependencies": { "jwt-decode": "^3.1.2", diff --git a/package.json b/package.json index 91b9adb5..3351df18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@harnessio/ff-javascript-client-sdk", - "version": "1.7.0", + "version": "1.8.0", "author": "Harness", "license": "Apache-2.0", "main": "dist/sdk.cjs.js", @@ -16,6 +16,7 @@ "build:esm": "esbuild ./src/index.ts --minify --bundle --target=es2016 --format=esm --external:jwt-decode --external:mitt --external:event-source-polyfill --outfile=./dist/sdk.esm.js", "build:cjs": "esbuild ./src/index.ts --minify --bundle --target=es2016 --platform=node --format=cjs --external:jwt-decode --external:mitt --external:event-source-polyfill --outfile=./dist/sdk.cjs.js", "build": "npm run clean; npm run type; npm run build:esm; npm run build:cjs; npm run build:client; npm run build:client-esm", + "install-examples": "cd examples/preact; rm -rf node_modules; npm i; cd -; cd examples/react; rm -rf node_modules; npm i; cd -; cd examples/react-redux; rm -rf node_modules; npm i; cd -", "format": "prettier --write \"src/**/*.ts\"", "lint": "tslint -p tsconfig.json", "type": "tsc ./src/*.ts --declaration --emitDeclarationOnly --outDir dist --lib ES2015,DOM", diff --git a/src/index.ts b/src/index.ts index a3378eb0..9765e303 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,7 +15,7 @@ import type { import { Event } from './types' import { defaultOptions, logError, MIN_EVENTS_SYNC_INTERVAL } from './utils' -const SDK_VERSION = '1.7.0' +const SDK_VERSION = '1.8.0' const METRICS_VALID_COUNT_INTERVAL = 500 const fetch = globalThis.fetch const EventSource = EventSourcePolyfill @@ -172,6 +172,7 @@ const initialize = (apiKey: string, target: Target, options?: Options): Result = failedMetricsCallCount = 0 } logDebug(error) + eventBus.emit(Event.ERROR_METRICS, error) }) .finally(() => { metricsSchedulerId = window.setTimeout(scheduleSendingMetrics, configurations.eventsSyncInterval) @@ -307,6 +308,8 @@ const initialize = (apiKey: string, target: Target, options?: Options): Result = environment = decoded.environment clusterIdentifier = decoded.clusterIdentifier + const hasExistingFlags = !!Object.keys(evaluations).length + // When authentication is done, fetch all flags fetchFlags() .then(() => { @@ -321,18 +324,10 @@ const initialize = (apiKey: string, target: Target, options?: Options): Result = if (closed) return logDebug('Event stream ready', { storage }) - eventBus.emit(Event.READY, { ...storage }) - if (!hasProxy) { - Object.keys(storage).forEach(key => { - metrics.push({ - featureIdentifier: key, - featureValue: storage[key], - variationIdentifier: evaluations[key]?.identifier || '', - count: metricsCollectorEnabled ? 1 : 0, - lastAccessed: Date.now() - }) - }) + // emit the ready event only if flags weren't already set using setEvaluations + if (!hasExistingFlags) { + eventBus.emit(Event.READY, { ...storage }) } }) .catch(err => { @@ -341,6 +336,7 @@ const initialize = (apiKey: string, target: Target, options?: Options): Result = }) .catch(error => { logError('Authentication error: ', error) + eventBus.emit(Event.ERROR_AUTH, error) eventBus.emit(Event.ERROR, error) }) @@ -352,22 +348,18 @@ const initialize = (apiKey: string, target: Target, options?: Options): Result = headers: standardHeaders } ) - const data = await res.json() - - data.forEach((_evaluation: Evaluation) => { - const _value = convertValue(_evaluation) - - // Update the flag if the values are different - const _oldValue = storage[_evaluation.flag] - if (_value !== _oldValue) { - logDebug('Flag variation has changed for ', _evaluation.identifier) - storage[_evaluation.flag] = _value - evaluations[_evaluation.flag] = { ..._evaluation, value: _value } - sendEvent(_evaluation) - } - }) + + if (res.ok) { + const data = await res.json() + data.forEach(registerEvaluation) + } else { + logError('Features fetch operation error: ', res) + eventBus.emit(Event.ERROR_FETCH_FLAGS, res) + eventBus.emit(Event.ERROR, res) + } } catch (error) { logError('Features fetch operation error: ', error) + eventBus.emit(Event.ERROR_FETCH_FLAGS, error) eventBus.emit(Event.ERROR, error) return error } @@ -384,43 +376,35 @@ const initialize = (apiKey: string, target: Target, options?: Options): Result = if (result.ok) { const flagInfo: Evaluation = await result.json() - const _value = convertValue(flagInfo) - - stopMetricsCollector() - storage[identifier] = _value - evaluations[identifier] = { ...flagInfo, value: _value } - startMetricsCollector() + registerEvaluation(flagInfo) sendEvent(flagInfo) - - if (!hasProxy) { - const featureIdentifier = flagInfo.flag - const entry = metrics.find( - _entry => _entry.featureIdentifier === featureIdentifier && _entry.featureValue === flagInfo.value - ) - - if (entry) { - updateMetrics(entry) - entry.variationIdentifier = evaluations[featureIdentifier as string]?.identifier || '' - } else { - metrics.push({ - featureIdentifier: featureIdentifier as string, - featureValue: String(flagInfo.value), - variationIdentifier: evaluations[featureIdentifier].identifier || '', - count: metricsCollectorEnabled ? 1 : 0, - lastAccessed: Date.now() - }) - } - } } else { + logError('Feature fetch operation error: ', result) + eventBus.emit(Event.ERROR_FETCH_FLAG, result) eventBus.emit(Event.ERROR, result) } } catch (error) { logError('Feature fetch operation error: ', error) + eventBus.emit(Event.ERROR_FETCH_FLAG, error) eventBus.emit(Event.ERROR, error) } } + const registerEvaluation = (evaluation: Evaluation) => { + stopMetricsCollector() + const value = convertValue(evaluation) + + // Update the flag if the values are different + if (value !== storage[evaluation.flag]) { + logDebug('Flag variation has changed for ', evaluation.identifier) + storage[evaluation.flag] = value + evaluations[evaluation.flag] = { ...evaluation, value: value } + sendEvent(evaluation) + } + startMetricsCollector() + } + const startStream = () => { // TODO: Implement polling when stream is disabled if (!configurations.streamEnabled) { @@ -446,6 +430,7 @@ const initialize = (apiKey: string, target: Target, options?: Options): Result = eventSource.onerror = (event: any) => { logError('Stream has issue', event) + eventBus.emit(Event.ERROR_STREAM, event) eventBus.emit(Event.ERROR, event) } @@ -537,7 +522,22 @@ const initialize = (apiKey: string, target: Target, options?: Options): Result = } } - return { on, off, variation, close } + const setEvaluations = (evals: Evaluation[]): void => { + if (evals.length) { + const hasExistingFlags = !!Object.keys(evaluations).length + + evals.forEach(registerEvaluation) + + if (!hasExistingFlags) { + // defer for 10ms to allow ready handlers to be registered + setTimeout(() => { + eventBus.emit(Event.READY, { ...storage }) + }, 10) + } + } + } + + return { on, off, variation, close, setEvaluations } } export { diff --git a/src/types.ts b/src/types.ts index 7f0ae92a..a7ce0230 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,7 +17,12 @@ export enum Event { CONNECTED = 'connected', DISCONNECTED = 'disconnected', CHANGED = 'changed', - ERROR = 'error' + ERROR = 'error', + ERROR_METRICS = 'metrics error', + ERROR_AUTH = 'auth error', + ERROR_FETCH_FLAGS = 'fetch flags error', + ERROR_FETCH_FLAG = 'fetch flag error', + ERROR_STREAM = 'stream error' } export type VariationValue = boolean | string | number | object | undefined @@ -36,6 +41,11 @@ export interface EventCallbackMapping { [Event.DISCONNECTED]: () => void [Event.CHANGED]: (flag: Evaluation) => void [Event.ERROR]: (error: unknown) => void + [Event.ERROR_AUTH]: (error: unknown) => void + [Event.ERROR_FETCH_FLAGS]: (error: unknown) => void + [Event.ERROR_FETCH_FLAG]: (error: unknown) => void + [Event.ERROR_STREAM]: (error: unknown) => void + [Event.ERROR_METRICS]: (error: unknown) => void } export type EventOnBinding = (event: K, callback: EventCallbackMapping[K]) => void @@ -49,6 +59,7 @@ export interface Result { off: EventOffBinding variation: (identifier: string, defaultValue: any) => VariationValue close: () => void + setEvaluations: (evaluations: Evaluation[]) => void } export interface Options {