unleash-proxy-client
Advanced tools
Comparing version 2.4.3 to 2.4.4-beta.0
@@ -172,2 +172,3 @@ "use strict"; | ||
headerName: headerName, | ||
customHeaders: customHeaders, | ||
}); | ||
@@ -174,0 +175,0 @@ return _this; |
@@ -1,1 +0,1 @@ | ||
(function(a,b){"object"==typeof exports&&"undefined"!=typeof module?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a=a||self,b(a.unleash={}))})(this,function(a){"use strict";function b(a,c){function b(){this.constructor=a}if("function"!=typeof c&&null!==c)throw new TypeError("Class extends value "+(c+"")+" is not a constructor or null");l(a,c),a.prototype=null===c?Object.create(c):(b.prototype=c.prototype,new b)}function c(a,b,c,d){function e(a){return a instanceof c?a:new c(function(b){b(a)})}return new(c||(c=Promise))(function(c,f){function g(a){try{i(d.next(a))}catch(a){f(a)}}function h(a){try{i(d["throw"](a))}catch(a){f(a)}}function i(a){a.done?c(a.value):e(a.value).then(g,h)}i((d=d.apply(a,b||[])).next())})}function d(a,b){function c(a){return function(b){return d([a,b])}}function d(c){if(e)throw new TypeError("Generator is already executing.");for(;k;)try{if(e=1,h&&(i=2&c[0]?h["return"]:c[0]?h["throw"]||((i=h["return"])&&i.call(h),0):h.next)&&!(i=i.call(h,c[1])).done)return i;switch((h=0,i)&&(c=[2&c[0],i.value]),c[0]){case 0:case 1:i=c;break;case 4:return k.label++,{value:c[1],done:!1};case 5:k.label++,h=c[1],c=[0];continue;case 7:c=k.ops.pop(),k.trys.pop();continue;default:if((i=k.trys,!(i=0<i.length&&i[i.length-1]))&&(6===c[0]||2===c[0])){k=0;continue}if(3===c[0]&&(!i||c[1]>i[0]&&c[1]<i[3])){k.label=c[1];break}if(6===c[0]&&k.label<i[1]){k.label=i[1],i=c;break}if(i&&k.label<i[2]){k.label=i[2],k.ops.push(c);break}i[2]&&k.ops.pop(),k.trys.pop();continue;}c=b.call(a,k)}catch(a){c=[6,a],h=0}finally{e=i=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}var e,h,i,j,k={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return j={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(j[Symbol.iterator]=function(){return this}),j}function e(a,b,c){if(c||2===arguments.length)for(var d,e=0,f=b.length;e<f;e++)(d||!(e in b))&&(d||(d=Array.prototype.slice.call(b,0,e)),d[e]=b[e]);return a.concat(d||Array.prototype.slice.call(b))}function f(){}function g(){if(!o&&(o="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto),!o))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return o(s)}function h(a){return"string"==typeof a&&t.test(a)}function j(a){var b=1<arguments.length&&arguments[1]!==void 0?arguments[1]:0,c=(u[a[b+0]]+u[a[b+1]]+u[a[b+2]]+u[a[b+3]]+"-"+u[a[b+4]]+u[a[b+5]]+"-"+u[a[b+6]]+u[a[b+7]]+"-"+u[a[b+8]]+u[a[b+9]]+"-"+u[a[b+10]]+u[a[b+11]]+u[a[b+12]]+u[a[b+13]]+u[a[b+14]]+u[a[b+15]]).toLowerCase();if(!h(c))throw TypeError("Stringified UUID is invalid");return c}function k(a,b,c){a=a||{};var d=a.random||(a.rng||g)();if(d[6]=64|15&d[6],d[8]=128|63&d[8],b){c=c||0;for(var e=0;16>e;++e)b[c+e]=d[e];return b}return j(d)}var l=function(a,c){return l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,c){a.__proto__=c}||function(a,c){for(var b in c)Object.prototype.hasOwnProperty.call(c,b)&&(a[b]=c[b])},l(a,c)},m=function(){return m=Object.assign||function(a){for(var b,c=1,d=arguments.length;c<d;c++)for(var e in b=arguments[c],b)Object.prototype.hasOwnProperty.call(b,e)&&(a[e]=b[e]);return a},m.apply(this,arguments)};f.prototype={on:function(a,b,c){var d=this.e||(this.e={});return(d[a]||(d[a]=[])).push({fn:b,ctx:c}),this},once:function(a,b,c){function d(){e.off(a,d),b.apply(c,arguments)}var e=this;return d._=b,this.on(a,d,c)},emit:function(a){var b=[].slice.call(arguments,1),c=((this.e||(this.e={}))[a]||[]).slice(),d=0,e=c.length;for(d;d<e;d++)c[d].fn.apply(c[d].ctx,b);return this},off:function(a,b){var c=this.e||(this.e={}),d=c[a],e=[];if(d&&b)for(var f=0,g=d.length;f<g;f++)d[f].fn!==b&&d[f].fn._!==b&&e.push(d[f]);return e.length?c[a]=e:delete c[a],this}};var n=f;f.TinyEmitter=n;for(var o,p=function(){function a(a){var b=a.onError,c=a.appName,d=a.metricsInterval,e=a.disableMetrics,f=a.url,g=a.clientKey,h=a.fetch,i=a.headerName;this.onError=b,this.disabled=void 0!==e&&e,this.metricsInterval=1e3*d,this.appName=c,this.url=f instanceof URL?f:new URL(f),this.clientKey=g,this.bucket=this.createEmptyBucket(),this.fetch=h,this.headerName=i}return a.prototype.start=function(){var a=this;return!this.disabled&&void("number"==typeof this.metricsInterval&&0<this.metricsInterval&&setTimeout(function(){a.startTimer(),a.sendMetrics()},2e3))},a.prototype.stop=function(){this.timer&&(clearTimeout(this.timer),delete this.timer)},a.prototype.createEmptyBucket=function(){return{start:new Date,stop:null,toggles:{}}},a.prototype.sendMetrics=function(){return c(this,void 0,void 0,function(){var a,b,c,e;return d(this,function(d){switch(d.label){case 0:if(a="".concat(this.url,"/client/metrics"),b=this.getPayload(),this.bucketIsEmpty(b))return[2];d.label=1;case 1:return d.trys.push([1,3,,4]),[4,this.fetch(a,{cache:"no-cache",method:"POST",headers:(e={},e[this.headerName]=this.clientKey,e.Accept="application/json",e["Content-Type"]="application/json",e),body:JSON.stringify(b)})];case 2:return d.sent(),[3,4];case 3:return c=d.sent(),console.error("Unleash: unable to send feature metrics",c),this.onError(c),[3,4];case 4:return[2];}})})},a.prototype.count=function(a,b){return!this.disabled&&this.bucket&&(this.assertBucket(a),this.bucket.toggles[a][b?"yes":"no"]++,!0)},a.prototype.assertBucket=function(a){return!this.disabled&&this.bucket&&void(!this.bucket.toggles[a]&&(this.bucket.toggles[a]={yes:0,no:0}))},a.prototype.startTimer=function(){var a=this;this.timer=setInterval(function(){a.sendMetrics()},this.metricsInterval)},a.prototype.bucketIsEmpty=function(a){return 0===Object.keys(a.bucket.toggles).length},a.prototype.getPayload=function(){var a=m(m({},this.bucket),{stop:new Date});return this.bucket=this.createEmptyBucket(),{bucket:a,appName:this.appName,instanceId:"browser"}},a}(),q=function(){function a(){this.store=new Map}return a.prototype.save=function(a,b){return c(this,void 0,void 0,function(){return d(this,function(){return this.store.set(a,b),[2]})})},a.prototype.get=function(a){return c(this,void 0,void 0,function(){return d(this,function(){return[2,this.store.get(a)]})})},a}(),r=function(){function a(){this.prefix="unleash:repository"}return a.prototype.save=function(a,b){return c(this,void 0,void 0,function(){var c,e;return d(this,function(){c=JSON.stringify(b),e="".concat(this.prefix,":").concat(a);try{window.localStorage.setItem(e,c)}catch(a){console.error(a)}return[2]})})},a.prototype.get=function(a){try{var b="".concat(this.prefix,":").concat(a),c=window.localStorage.getItem(b);return c?JSON.parse(c):void 0}catch(a){console.error(a)}},a}(),s=new Uint8Array(16),t=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,u=[],v=0;256>v;++v)u.push((v+256).toString(16).substr(1));var w=function(){function a(){}return a.prototype.generateEventId=function(){return k()},a.prototype.createImpressionEvent=function(a,b,c,d,e,f){var g=this.createBaseEvent(a,b,c,d,e);return f?m(m({},g),{variant:f}):g},a.prototype.createBaseEvent=function(a,b,c,d,e){return{eventType:d,eventId:this.generateEventId(),context:a,enabled:b,featureName:c,impressionData:e}},a}(),x=function(a){var b=a[1];return b!==void 0&&null!==b},y=function(a,b){var c=new URL(a.toString());return Object.entries(b).filter(x).forEach(function(a){var b=a[0],d=a[1];"properties"===b&&d?Object.entries(d).filter(x).forEach(function(a){var b=a[0],d=a[1];return c.searchParams.append("properties[".concat(b,"]"),d)}):c.searchParams.append(b,d)}),c},z=["userId","sessionId","remoteAddress"],A={INIT:"initialized",ERROR:"error",READY:"ready",UPDATE:"update",IMPRESSION:"impression"},B={IS_ENABLED:"isEnabled",GET_VARIANT:"getVariant"},C={name:"disabled",enabled:!1},D="repo",E=function(){try{if("undefined"!=typeof window&&"fetch"in window)return fetch.bind(window);if("fetch"in globalThis)return fetch.bind(globalThis)}catch(a){console.error("Unleash failed to resolve \"fetch\"",a)}},F=function(a){function f(b){var c=b.storageProvider,d=b.url,e=b.clientKey,f=b.disableRefresh,g=b.refreshInterval,h=void 0===g?30:g,i=b.metricsInterval,j=void 0===i?30:i,k=b.disableMetrics,l=b.appName,n=b.environment,o=void 0===n?"default":n,s=b.context,t=b.fetch,u=void 0===t?E():t,v=b.bootstrap,x=b.bootstrapOverride,y=b.headerName,z=void 0===y?"Authorization":y,B=b.customHeaders,C=void 0===B?{}:B,D=b.impressionDataAll,F=b.usePOSTrequests,G=a.call(this)||this;if(G.toggles=[],G.etag="",G.readyEventEmitted=!1,G.usePOSTrequests=!1,G.started=!1,!d)throw new Error("url is required");if(!e)throw new Error("clientKey is required");if(!l)throw new Error("appName is required.");return G.eventsHandler=new w,G.impressionDataAll=void 0!==D&&D,G.toggles=v&&0<v.length?v:[],G.url=d instanceof URL?d:new URL(d),G.clientKey=e,G.headerName=z,G.customHeaders=C,G.storage=c||("undefined"==typeof window?new q:new r),G.refreshInterval=void 0!==f&&f?0:1e3*h,G.context=m({appName:l,environment:o},s),G.usePOSTrequests=void 0!==F&&F,G.ready=new Promise(function(a){G.init().then(a).catch(function(b){console.error(b),G.emit(A.ERROR,b),a()})}),u||console.error("Unleash: You must either provide your own \"fetch\" implementation or run in an environment where \"fetch\" is available."),G.fetch=u,G.bootstrap=v&&0<v.length?v:void 0,G.bootstrapOverride=void 0===x||x,G.metrics=new p({onError:G.emit.bind(G,A.ERROR),appName:l,metricsInterval:j,disableMetrics:void 0!==k&&k,url:G.url,clientKey:e,fetch:u,headerName:z}),G}return b(f,a),f.prototype.getAllToggles=function(){return e([],this.toggles,!0)},f.prototype.isEnabled=function(a){var b,c=this.toggles.find(function(b){return b.name===a}),d=!!c&&c.enabled;if(this.metrics.count(a,d),(null===c||void 0===c?void 0:c.impressionData)||this.impressionDataAll){var e=this.eventsHandler.createImpressionEvent(this.context,d,a,B.IS_ENABLED,null!==(b=null===c||void 0===c?void 0:c.impressionData)&&void 0!==b?b:void 0);this.emit(A.IMPRESSION,e)}return d},f.prototype.getVariant=function(a){var b,c=this.toggles.find(function(b){return b.name===a}),d=(null===c||void 0===c?void 0:c.enabled)||!1,e=c?c.variant:C;if(this.metrics.count(a,d),(null===c||void 0===c?void 0:c.impressionData)||this.impressionDataAll){var f=this.eventsHandler.createImpressionEvent(this.context,d,a,B.GET_VARIANT,null!==(b=null===c||void 0===c?void 0:c.impressionData)&&void 0!==b?b:void 0,e.name);this.emit(A.IMPRESSION,f)}return e},f.prototype.updateContext=function(a){return c(this,void 0,void 0,function(){var b,c=this;return d(this,function(d){switch(d.label){case 0:return(a.appName||a.environment)&&console.warn("appName and environment are static. They can't be updated with updateContext."),b={environment:this.context.environment,appName:this.context.appName,sessionId:this.context.sessionId},this.context=m(m({},b),a),this.timerRef||this.readyEventEmitted?[4,this.fetchToggles()]:[3,2];case 1:return d.sent(),[3,4];case 2:return this.started?[4,new Promise(function(a){var b=function(){c.fetchToggles().then(function(){c.off(A.READY,b),a()})};c.once(A.READY,b)})]:[3,4];case 3:d.sent(),d.label=4;case 4:return[2];}})})},f.prototype.getContext=function(){return m({},this.context)},f.prototype.setContextField=function(a,b){var c,d;if(z.includes(a))this.context=m(m({},this.context),(c={},c[a]=b,c));else{var e=m(m({},this.context.properties),(d={},d[a]=b,d));this.context=m(m({},this.context),{properties:e})}this.timerRef&&this.fetchToggles()},f.prototype.init=function(){return c(this,void 0,void 0,function(){var a,b;return d(this,function(c){switch(c.label){case 0:return[4,this.resolveSessionId()];case 1:return a=c.sent(),this.context=m({sessionId:a},this.context),b=this,[4,this.storage.get(D)];case 2:return b.toggles=c.sent()||[],this.bootstrap&&(this.bootstrapOverride||0===this.toggles.length)?[4,this.storage.save(D,this.bootstrap)]:[3,4];case 3:c.sent(),this.toggles=this.bootstrap,this.emit(A.READY),c.label=4;case 4:return this.emit(A.INIT),[2];}})})},f.prototype.start=function(){return c(this,void 0,void 0,function(){var a,b=this;return d(this,function(c){switch(c.label){case 0:return this.started=!0,this.timerRef?(console.error("Unleash SDK has already started, if you want to restart the SDK you should call client.stop() before starting again."),[2]):[4,this.ready];case 1:return c.sent(),this.metrics.start(),a=this.refreshInterval,[4,this.fetchToggles()];case 2:return c.sent(),0<a&&(this.timerRef=setInterval(function(){return b.fetchToggles()},a)),[2];}})})},f.prototype.stop=function(){this.timerRef&&(clearInterval(this.timerRef),this.timerRef=void 0),this.metrics.stop()},f.prototype.resolveSessionId=function(){return c(this,void 0,void 0,function(){var a;return d(this,function(b){var c=Math.floor;switch(b.label){case 0:return this.context.sessionId?[2,this.context.sessionId]:[3,1];case 1:return[4,this.storage.get("sessionId")];case 2:return(a=b.sent(),!!a)?[3,4]:(a=c(1e9*Math.random()),[4,this.storage.save("sessionId",a)]);case 3:b.sent(),b.label=4;case 4:return[2,a];}})})},f.prototype.getHeaders=function(){var a,b=(a={},a[this.headerName]=this.clientKey,a.Accept="application/json",a["Content-Type"]="application/json",a["If-None-Match"]=this.etag,a);return Object.entries(this.customHeaders).filter(x).forEach(function(a){var c=a[0],d=a[1];return b[c]=d}),b},f.prototype.storeToggles=function(a){return c(this,void 0,void 0,function(){return d(this,function(b){switch(b.label){case 0:return this.toggles=a,this.emit(A.UPDATE),[4,this.storage.save(D,a)];case 1:return b.sent(),[2];}})})},f.prototype.fetchToggles=function(){return c(this,void 0,void 0,function(){var a,b,c,e,f,g,h;return d(this,function(d){switch(d.label){case 0:if(!this.fetch)return[3,8];d.label=1;case 1:return d.trys.push([1,7,,8]),a=this.usePOSTrequests,b=a?this.url:y(this.url,this.context),c=a?"POST":"GET",e=a?JSON.stringify({context:this.context}):void 0,[4,this.fetch(b.toString(),{method:c,cache:"no-cache",headers:this.getHeaders(),body:e})];case 2:return(f=d.sent(),!(f.ok&&304!==f.status))?[3,5]:(this.etag=f.headers.get("ETag")||"",[4,f.json()]);case 3:return g=d.sent(),[4,this.storeToggles(g.toggles)];case 4:return d.sent(),this.bootstrap||this.readyEventEmitted||(this.emit(A.READY),this.readyEventEmitted=!0),[3,6];case 5:f.ok||304===f.status||(console.error("Unleash: Fetching feature toggles did not have an ok response"),this.emit(A.ERROR,{type:"HttpError",code:f.status})),d.label=6;case 6:return[3,8];case 7:return h=d.sent(),console.error("Unleash: unable to fetch feature toggles",h),this.emit(A.ERROR,h),[3,8];case 8:return[2];}})})},f}(n);a.EVENTS=A,a.InMemoryStorageProvider=q,a.LocalStorageProvider=r,a.UnleashClient=F,a.resolveFetch=E,Object.defineProperty(a,"__esModule",{value:!0})}); | ||
(function(a,b){"object"==typeof exports&&"undefined"!=typeof module?b(exports):"function"==typeof define&&define.amd?define(["exports"],b):(a=a||self,b(a.unleash={}))})(this,function(a){"use strict";function b(a,c){function b(){this.constructor=a}if("function"!=typeof c&&null!==c)throw new TypeError("Class extends value "+(c+"")+" is not a constructor or null");l(a,c),a.prototype=null===c?Object.create(c):(b.prototype=c.prototype,new b)}function c(a,b,c,d){function e(a){return a instanceof c?a:new c(function(b){b(a)})}return new(c||(c=Promise))(function(c,f){function g(a){try{i(d.next(a))}catch(a){f(a)}}function h(a){try{i(d["throw"](a))}catch(a){f(a)}}function i(a){a.done?c(a.value):e(a.value).then(g,h)}i((d=d.apply(a,b||[])).next())})}function d(a,b){function c(a){return function(b){return d([a,b])}}function d(c){if(e)throw new TypeError("Generator is already executing.");for(;k;)try{if(e=1,h&&(i=2&c[0]?h["return"]:c[0]?h["throw"]||((i=h["return"])&&i.call(h),0):h.next)&&!(i=i.call(h,c[1])).done)return i;switch((h=0,i)&&(c=[2&c[0],i.value]),c[0]){case 0:case 1:i=c;break;case 4:return k.label++,{value:c[1],done:!1};case 5:k.label++,h=c[1],c=[0];continue;case 7:c=k.ops.pop(),k.trys.pop();continue;default:if((i=k.trys,!(i=0<i.length&&i[i.length-1]))&&(6===c[0]||2===c[0])){k=0;continue}if(3===c[0]&&(!i||c[1]>i[0]&&c[1]<i[3])){k.label=c[1];break}if(6===c[0]&&k.label<i[1]){k.label=i[1],i=c;break}if(i&&k.label<i[2]){k.label=i[2],k.ops.push(c);break}i[2]&&k.ops.pop(),k.trys.pop();continue;}c=b.call(a,k)}catch(a){c=[6,a],h=0}finally{e=i=0}if(5&c[0])throw c[1];return{value:c[0]?c[1]:void 0,done:!0}}var e,h,i,j,k={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return j={next:c(0),throw:c(1),return:c(2)},"function"==typeof Symbol&&(j[Symbol.iterator]=function(){return this}),j}function e(a,b,c){if(c||2===arguments.length)for(var d,e=0,f=b.length;e<f;e++)(d||!(e in b))&&(d||(d=Array.prototype.slice.call(b,0,e)),d[e]=b[e]);return a.concat(d||Array.prototype.slice.call(b))}function f(){}function g(){if(!o&&(o="undefined"!=typeof crypto&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto)||"undefined"!=typeof msCrypto&&"function"==typeof msCrypto.getRandomValues&&msCrypto.getRandomValues.bind(msCrypto),!o))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return o(u)}function h(a){return"string"==typeof a&&v.test(a)}function j(a){var b=1<arguments.length&&arguments[1]!==void 0?arguments[1]:0,c=(w[a[b+0]]+w[a[b+1]]+w[a[b+2]]+w[a[b+3]]+"-"+w[a[b+4]]+w[a[b+5]]+"-"+w[a[b+6]]+w[a[b+7]]+"-"+w[a[b+8]]+w[a[b+9]]+"-"+w[a[b+10]]+w[a[b+11]]+w[a[b+12]]+w[a[b+13]]+w[a[b+14]]+w[a[b+15]]).toLowerCase();if(!h(c))throw TypeError("Stringified UUID is invalid");return c}function k(a,b,c){a=a||{};var d=a.random||(a.rng||g)();if(d[6]=64|15&d[6],d[8]=128|63&d[8],b){c=c||0;for(var e=0;16>e;++e)b[c+e]=d[e];return b}return j(d)}var l=function(a,c){return l=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(a,c){a.__proto__=c}||function(a,c){for(var b in c)Object.prototype.hasOwnProperty.call(c,b)&&(a[b]=c[b])},l(a,c)},m=function(){return m=Object.assign||function(a){for(var b,c=1,d=arguments.length;c<d;c++)for(var e in b=arguments[c],b)Object.prototype.hasOwnProperty.call(b,e)&&(a[e]=b[e]);return a},m.apply(this,arguments)};f.prototype={on:function(a,b,c){var d=this.e||(this.e={});return(d[a]||(d[a]=[])).push({fn:b,ctx:c}),this},once:function(a,b,c){function d(){e.off(a,d),b.apply(c,arguments)}var e=this;return d._=b,this.on(a,d,c)},emit:function(a){var b=[].slice.call(arguments,1),c=((this.e||(this.e={}))[a]||[]).slice(),d=0,e=c.length;for(d;d<e;d++)c[d].fn.apply(c[d].ctx,b);return this},off:function(a,b){var c=this.e||(this.e={}),d=c[a],e=[];if(d&&b)for(var f=0,g=d.length;f<g;f++)d[f].fn!==b&&d[f].fn._!==b&&e.push(d[f]);return e.length?c[a]=e:delete c[a],this}};var n=f;f.TinyEmitter=n;for(var o,p=function(a){var b=a[1];return b!==void 0&&null!==b},q=function(a,b){var c=new URL(a.toString());return Object.entries(b).filter(p).forEach(function(a){var b=a[0],d=a[1];"properties"===b&&d?Object.entries(d).filter(p).forEach(function(a){var b=a[0],d=a[1];return c.searchParams.append("properties[".concat(b,"]"),d)}):c.searchParams.append(b,d)}),c},r=function(){function a(a){var b=a.onError,c=a.appName,d=a.metricsInterval,e=a.disableMetrics,f=a.url,g=a.clientKey,h=a.fetch,i=a.headerName,j=a.customHeaders,k=void 0===j?{}:j;this.onError=b,this.disabled=void 0!==e&&e,this.metricsInterval=1e3*d,this.appName=c,this.url=f instanceof URL?f:new URL(f),this.clientKey=g,this.bucket=this.createEmptyBucket(),this.fetch=h,this.headerName=i,this.customHeaders=k}return a.prototype.start=function(){var a=this;return!this.disabled&&void("number"==typeof this.metricsInterval&&0<this.metricsInterval&&setTimeout(function(){a.startTimer(),a.sendMetrics()},2e3))},a.prototype.stop=function(){this.timer&&(clearTimeout(this.timer),delete this.timer)},a.prototype.createEmptyBucket=function(){return{start:new Date,stop:null,toggles:{}}},a.prototype.getHeaders=function(){var a,b=(a={},a[this.headerName]=this.clientKey,a.Accept="application/json",a["Content-Type"]="application/json",a);return Object.entries(this.customHeaders).filter(p).forEach(function(a){var c=a[0],d=a[1];return b[c]=d}),b},a.prototype.sendMetrics=function(){return c(this,void 0,void 0,function(){var a,b,c;return d(this,function(d){switch(d.label){case 0:if(a="".concat(this.url,"/client/metrics"),b=this.getPayload(),this.bucketIsEmpty(b))return[2];d.label=1;case 1:return d.trys.push([1,3,,4]),[4,this.fetch(a,{cache:"no-cache",method:"POST",headers:this.getHeaders(),body:JSON.stringify(b)})];case 2:return d.sent(),[3,4];case 3:return c=d.sent(),console.error("Unleash: unable to send feature metrics",c),this.onError(c),[3,4];case 4:return[2];}})})},a.prototype.count=function(a,b){return!this.disabled&&this.bucket&&(this.assertBucket(a),this.bucket.toggles[a][b?"yes":"no"]++,!0)},a.prototype.assertBucket=function(a){return!this.disabled&&this.bucket&&void(!this.bucket.toggles[a]&&(this.bucket.toggles[a]={yes:0,no:0}))},a.prototype.startTimer=function(){var a=this;this.timer=setInterval(function(){a.sendMetrics()},this.metricsInterval)},a.prototype.bucketIsEmpty=function(a){return 0===Object.keys(a.bucket.toggles).length},a.prototype.getPayload=function(){var a=m(m({},this.bucket),{stop:new Date});return this.bucket=this.createEmptyBucket(),{bucket:a,appName:this.appName,instanceId:"browser"}},a}(),s=function(){function a(){this.store=new Map}return a.prototype.save=function(a,b){return c(this,void 0,void 0,function(){return d(this,function(){return this.store.set(a,b),[2]})})},a.prototype.get=function(a){return c(this,void 0,void 0,function(){return d(this,function(){return[2,this.store.get(a)]})})},a}(),t=function(){function a(){this.prefix="unleash:repository"}return a.prototype.save=function(a,b){return c(this,void 0,void 0,function(){var c,e;return d(this,function(){c=JSON.stringify(b),e="".concat(this.prefix,":").concat(a);try{window.localStorage.setItem(e,c)}catch(a){console.error(a)}return[2]})})},a.prototype.get=function(a){try{var b="".concat(this.prefix,":").concat(a),c=window.localStorage.getItem(b);return c?JSON.parse(c):void 0}catch(a){console.error(a)}},a}(),u=new Uint8Array(16),v=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i,w=[],x=0;256>x;++x)w.push((x+256).toString(16).substr(1));var y=function(){function a(){}return a.prototype.generateEventId=function(){return k()},a.prototype.createImpressionEvent=function(a,b,c,d,e,f){var g=this.createBaseEvent(a,b,c,d,e);return f?m(m({},g),{variant:f}):g},a.prototype.createBaseEvent=function(a,b,c,d,e){return{eventType:d,eventId:this.generateEventId(),context:a,enabled:b,featureName:c,impressionData:e}},a}(),z=["userId","sessionId","remoteAddress"],A={INIT:"initialized",ERROR:"error",READY:"ready",UPDATE:"update",IMPRESSION:"impression"},B={IS_ENABLED:"isEnabled",GET_VARIANT:"getVariant"},C={name:"disabled",enabled:!1},D="repo",E=function(){try{if("undefined"!=typeof window&&"fetch"in window)return fetch.bind(window);if("fetch"in globalThis)return fetch.bind(globalThis)}catch(a){console.error("Unleash failed to resolve \"fetch\"",a)}},F=function(a){function f(b){var c=b.storageProvider,d=b.url,e=b.clientKey,f=b.disableRefresh,g=b.refreshInterval,h=void 0===g?30:g,i=b.metricsInterval,j=void 0===i?30:i,k=b.disableMetrics,l=b.appName,n=b.environment,o=void 0===n?"default":n,p=b.context,q=b.fetch,u=void 0===q?E():q,v=b.bootstrap,w=b.bootstrapOverride,x=b.headerName,z=void 0===x?"Authorization":x,B=b.customHeaders,C=void 0===B?{}:B,D=b.impressionDataAll,F=b.usePOSTrequests,G=a.call(this)||this;if(G.toggles=[],G.etag="",G.readyEventEmitted=!1,G.usePOSTrequests=!1,G.started=!1,!d)throw new Error("url is required");if(!e)throw new Error("clientKey is required");if(!l)throw new Error("appName is required.");return G.eventsHandler=new y,G.impressionDataAll=void 0!==D&&D,G.toggles=v&&0<v.length?v:[],G.url=d instanceof URL?d:new URL(d),G.clientKey=e,G.headerName=z,G.customHeaders=C,G.storage=c||("undefined"==typeof window?new s:new t),G.refreshInterval=void 0!==f&&f?0:1e3*h,G.context=m({appName:l,environment:o},p),G.usePOSTrequests=void 0!==F&&F,G.ready=new Promise(function(a){G.init().then(a).catch(function(b){console.error(b),G.emit(A.ERROR,b),a()})}),u||console.error("Unleash: You must either provide your own \"fetch\" implementation or run in an environment where \"fetch\" is available."),G.fetch=u,G.bootstrap=v&&0<v.length?v:void 0,G.bootstrapOverride=void 0===w||w,G.metrics=new r({onError:G.emit.bind(G,A.ERROR),appName:l,metricsInterval:j,disableMetrics:void 0!==k&&k,url:G.url,clientKey:e,fetch:u,headerName:z,customHeaders:C}),G}return b(f,a),f.prototype.getAllToggles=function(){return e([],this.toggles,!0)},f.prototype.isEnabled=function(a){var b,c=this.toggles.find(function(b){return b.name===a}),d=!!c&&c.enabled;if(this.metrics.count(a,d),(null===c||void 0===c?void 0:c.impressionData)||this.impressionDataAll){var e=this.eventsHandler.createImpressionEvent(this.context,d,a,B.IS_ENABLED,null!==(b=null===c||void 0===c?void 0:c.impressionData)&&void 0!==b?b:void 0);this.emit(A.IMPRESSION,e)}return d},f.prototype.getVariant=function(a){var b,c=this.toggles.find(function(b){return b.name===a}),d=(null===c||void 0===c?void 0:c.enabled)||!1,e=c?c.variant:C;if(this.metrics.count(a,d),(null===c||void 0===c?void 0:c.impressionData)||this.impressionDataAll){var f=this.eventsHandler.createImpressionEvent(this.context,d,a,B.GET_VARIANT,null!==(b=null===c||void 0===c?void 0:c.impressionData)&&void 0!==b?b:void 0,e.name);this.emit(A.IMPRESSION,f)}return e},f.prototype.updateContext=function(a){return c(this,void 0,void 0,function(){var b,c=this;return d(this,function(d){switch(d.label){case 0:return(a.appName||a.environment)&&console.warn("appName and environment are static. They can't be updated with updateContext."),b={environment:this.context.environment,appName:this.context.appName,sessionId:this.context.sessionId},this.context=m(m({},b),a),this.timerRef||this.readyEventEmitted?[4,this.fetchToggles()]:[3,2];case 1:return d.sent(),[3,4];case 2:return this.started?[4,new Promise(function(a){var b=function(){c.fetchToggles().then(function(){c.off(A.READY,b),a()})};c.once(A.READY,b)})]:[3,4];case 3:d.sent(),d.label=4;case 4:return[2];}})})},f.prototype.getContext=function(){return m({},this.context)},f.prototype.setContextField=function(a,b){var c,d;if(z.includes(a))this.context=m(m({},this.context),(c={},c[a]=b,c));else{var e=m(m({},this.context.properties),(d={},d[a]=b,d));this.context=m(m({},this.context),{properties:e})}this.timerRef&&this.fetchToggles()},f.prototype.init=function(){return c(this,void 0,void 0,function(){var a,b;return d(this,function(c){switch(c.label){case 0:return[4,this.resolveSessionId()];case 1:return a=c.sent(),this.context=m({sessionId:a},this.context),b=this,[4,this.storage.get(D)];case 2:return b.toggles=c.sent()||[],this.bootstrap&&(this.bootstrapOverride||0===this.toggles.length)?[4,this.storage.save(D,this.bootstrap)]:[3,4];case 3:c.sent(),this.toggles=this.bootstrap,this.emit(A.READY),c.label=4;case 4:return this.emit(A.INIT),[2];}})})},f.prototype.start=function(){return c(this,void 0,void 0,function(){var a,b=this;return d(this,function(c){switch(c.label){case 0:return this.started=!0,this.timerRef?(console.error("Unleash SDK has already started, if you want to restart the SDK you should call client.stop() before starting again."),[2]):[4,this.ready];case 1:return c.sent(),this.metrics.start(),a=this.refreshInterval,[4,this.fetchToggles()];case 2:return c.sent(),0<a&&(this.timerRef=setInterval(function(){return b.fetchToggles()},a)),[2];}})})},f.prototype.stop=function(){this.timerRef&&(clearInterval(this.timerRef),this.timerRef=void 0),this.metrics.stop()},f.prototype.resolveSessionId=function(){return c(this,void 0,void 0,function(){var a;return d(this,function(b){var c=Math.floor;switch(b.label){case 0:return this.context.sessionId?[2,this.context.sessionId]:[3,1];case 1:return[4,this.storage.get("sessionId")];case 2:return(a=b.sent(),!!a)?[3,4]:(a=c(1e9*Math.random()),[4,this.storage.save("sessionId",a)]);case 3:b.sent(),b.label=4;case 4:return[2,a];}})})},f.prototype.getHeaders=function(){var a,b=(a={},a[this.headerName]=this.clientKey,a.Accept="application/json",a["Content-Type"]="application/json",a["If-None-Match"]=this.etag,a);return Object.entries(this.customHeaders).filter(p).forEach(function(a){var c=a[0],d=a[1];return b[c]=d}),b},f.prototype.storeToggles=function(a){return c(this,void 0,void 0,function(){return d(this,function(b){switch(b.label){case 0:return this.toggles=a,this.emit(A.UPDATE),[4,this.storage.save(D,a)];case 1:return b.sent(),[2];}})})},f.prototype.fetchToggles=function(){return c(this,void 0,void 0,function(){var a,b,c,e,f,g,h;return d(this,function(d){switch(d.label){case 0:if(!this.fetch)return[3,8];d.label=1;case 1:return d.trys.push([1,7,,8]),a=this.usePOSTrequests,b=a?this.url:q(this.url,this.context),c=a?"POST":"GET",e=a?JSON.stringify({context:this.context}):void 0,[4,this.fetch(b.toString(),{method:c,cache:"no-cache",headers:this.getHeaders(),body:e})];case 2:return(f=d.sent(),!(f.ok&&304!==f.status))?[3,5]:(this.etag=f.headers.get("ETag")||"",[4,f.json()]);case 3:return g=d.sent(),[4,this.storeToggles(g.toggles)];case 4:return d.sent(),this.bootstrap||this.readyEventEmitted||(this.emit(A.READY),this.readyEventEmitted=!0),[3,6];case 5:f.ok||304===f.status||(console.error("Unleash: Fetching feature toggles did not have an ok response"),this.emit(A.ERROR,{type:"HttpError",code:f.status})),d.label=6;case 6:return[3,8];case 7:return h=d.sent(),console.error("Unleash: unable to fetch feature toggles",h),this.emit(A.ERROR,h),[3,8];case 8:return[2];}})})},f}(n);a.EVENTS=A,a.InMemoryStorageProvider=s,a.LocalStorageProvider=t,a.UnleashClient=F,a.resolveFetch=E,Object.defineProperty(a,"__esModule",{value:!0})}); |
@@ -10,2 +10,3 @@ export interface MetricsOptions { | ||
headerName: string; | ||
customHeaders?: Record<string, string>; | ||
} | ||
@@ -34,6 +35,8 @@ interface Bucket { | ||
private headerName; | ||
constructor({ onError, appName, metricsInterval, disableMetrics, url, clientKey, fetch, headerName, }: MetricsOptions); | ||
private customHeaders; | ||
constructor({ onError, appName, metricsInterval, disableMetrics, url, clientKey, fetch, headerName, customHeaders, }: MetricsOptions); | ||
start(): false | undefined; | ||
stop(): void; | ||
createEmptyBucket(): Bucket; | ||
private getHeaders; | ||
sendMetrics(): Promise<void>; | ||
@@ -40,0 +43,0 @@ count(name: string, enabled: boolean): boolean; |
@@ -51,5 +51,6 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var util_1 = require("./util"); | ||
var Metrics = /** @class */ (function () { | ||
function Metrics(_a) { | ||
var onError = _a.onError, appName = _a.appName, metricsInterval = _a.metricsInterval, _b = _a.disableMetrics, disableMetrics = _b === void 0 ? false : _b, url = _a.url, clientKey = _a.clientKey, fetch = _a.fetch, headerName = _a.headerName; | ||
var onError = _a.onError, appName = _a.appName, metricsInterval = _a.metricsInterval, _b = _a.disableMetrics, disableMetrics = _b === void 0 ? false : _b, url = _a.url, clientKey = _a.clientKey, fetch = _a.fetch, headerName = _a.headerName, _c = _a.customHeaders, customHeaders = _c === void 0 ? {} : _c; | ||
this.onError = onError; | ||
@@ -64,2 +65,3 @@ this.disabled = disableMetrics; | ||
this.headerName = headerName; | ||
this.customHeaders = customHeaders; | ||
} | ||
@@ -93,8 +95,22 @@ Metrics.prototype.start = function () { | ||
}; | ||
Metrics.prototype.getHeaders = function () { | ||
var _a; | ||
var headers = (_a = {}, | ||
_a[this.headerName] = this.clientKey, | ||
_a.Accept = 'application/json', | ||
_a['Content-Type'] = 'application/json', | ||
_a); | ||
Object.entries(this.customHeaders) | ||
.filter(util_1.notNullOrUndefined) | ||
.forEach(function (_a) { | ||
var name = _a[0], value = _a[1]; | ||
return (headers[name] = value); | ||
}); | ||
return headers; | ||
}; | ||
Metrics.prototype.sendMetrics = function () { | ||
return __awaiter(this, void 0, void 0, function () { | ||
var url, payload, e_1; | ||
var _a; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
return __generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
@@ -106,20 +122,16 @@ url = "".concat(this.url, "/client/metrics"); | ||
} | ||
_b.label = 1; | ||
_a.label = 1; | ||
case 1: | ||
_b.trys.push([1, 3, , 4]); | ||
_a.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, this.fetch(url, { | ||
cache: 'no-cache', | ||
method: 'POST', | ||
headers: (_a = {}, | ||
_a[this.headerName] = this.clientKey, | ||
_a.Accept = 'application/json', | ||
_a['Content-Type'] = 'application/json', | ||
_a), | ||
headers: this.getHeaders(), | ||
body: JSON.stringify(payload), | ||
})]; | ||
case 2: | ||
_b.sent(); | ||
_a.sent(); | ||
return [3 /*break*/, 4]; | ||
case 3: | ||
e_1 = _b.sent(); | ||
e_1 = _a.sent(); | ||
console.error('Unleash: unable to send feature metrics', e_1); | ||
@@ -126,0 +138,0 @@ this.onError(e_1); |
{ | ||
"name": "unleash-proxy-client", | ||
"version": "2.4.3", | ||
"version": "2.4.4-beta.0", | ||
"description": "A browser client that can be used together with the unleash-proxy.", | ||
@@ -5,0 +5,0 @@ "main": "./build/index.js", |
@@ -120,9 +120,9 @@ # Unleash Proxy Client for the browser (JS) | ||
| storageProvider | no | `LocalStorageProvider` in browser, `InMemoryStorageProvider` otherwise | Allows you to inject a custom storeProvider | | ||
| environment | no | `default` | Identify the current environment. Will be part of the Unleash Context | | ||
| fetch | no | `window.fetch` or global `fetch` | Allows you to override the fetch implementation to use. Useful in Node.js environments where you can inject `node-fetch` | | ||
| bootstrap | no | `[]` | Allows you to bootstrap the cached feature toggle configuration. | | ||
| bootstrapOverride | no| `true` | Should the bootstrap automatically override cached data in the local-storage. Will only be used if bootstrap is not an empty array. | | ||
| headerName | no| `Authorization` | Provides possiblity to specify custom header that is passed to Unleash / Unleash Proxy with the `clientKey` | | ||
| customHeaders | no| `{}` | Additional headers to use when making HTTP requests to the Unleash proxy. In case of name collisions with the default headers, the `customHeaders` value will be used. | | ||
| impressionDataAll | no| `false` | Allows you to trigger "impression" events for **all** `getToggle` and `getVariant` invocations. This is particularly useful for "disabled" feature toggles that are not visible to frontend SDKs. | | ||
| headerName | no| `Authorization` | Which header the SDK should use to authorize with Unleash / Unleash Proxy. The header will be given the `clientKey` as its value. | | ||
| customHeaders | no| `{}` | Additional headers to use when making HTTP requests to the Unleash proxy. In case of name collisions with the default headers, the `customHeaders` value will be used if it is not `null` or `undefined`. `customHeaders` values that are `null` or `undefined` will be ignored. | | ||
| impressionDataAll | no| `false` | Allows you to trigger "impression" events for **all** `getToggle` and `getVariant` invocations. This is particularly useful for "disabled" feature toggles that are not visible to frontend SDKs. | | ||
| environment | no | `default` | Sets the `environment` option of the [Unleash context](https://docs.getunleash.io/reference/unleash-context). This does **not** affect the SDK's [Unleash environment](https://docs.getunleash.io/reference/environments). | | ||
@@ -129,0 +129,0 @@ ### Listen for updates via the EventEmitter |
@@ -74,3 +74,2 @@ import { FetchMock } from 'jest-fetch-mock'; | ||
test('Should have correct variant', async () => { | ||
@@ -568,20 +567,22 @@ fetchMock.mockResponseOnce(JSON.stringify(data)); | ||
test.each([400, 401, 403, 404, 429, 500, 502, 503])('Should publish error when fetch receives a %d error', async (errorCode) => { | ||
expect.assertions(1); | ||
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn()); | ||
fetchMock.mockResponseOnce("{}", { status: errorCode}); | ||
test.each([400, 401, 403, 404, 429, 500, 502, 503])( | ||
'Should publish error when fetch receives a %d error', | ||
async (errorCode) => { | ||
expect.assertions(1); | ||
jest.spyOn(global.console, 'error').mockImplementation(() => jest.fn()); | ||
fetchMock.mockResponseOnce('{}', { status: errorCode }); | ||
const config: IConfig = { | ||
url: 'http://localhost/test', | ||
clientKey: '12', | ||
appName: 'web', | ||
}; | ||
const client = new UnleashClient(config); | ||
client.on(EVENTS.ERROR, (e: any) => { | ||
expect(e).toStrictEqual({ type: 'HttpError', code: errorCode}); | ||
const config: IConfig = { | ||
url: 'http://localhost/test', | ||
clientKey: '12', | ||
appName: 'web', | ||
}; | ||
const client = new UnleashClient(config); | ||
client.on(EVENTS.ERROR, (e: any) => { | ||
expect(e).toStrictEqual({ type: 'HttpError', code: errorCode }); | ||
}); | ||
await client.start(); | ||
} | ||
); | ||
}); | ||
await client.start(); | ||
}) | ||
test('Should publish update when state changes after refreshInterval', async () => { | ||
@@ -857,3 +858,2 @@ expect.assertions(1); | ||
test('Should update context fields on request', async () => { | ||
@@ -911,3 +911,3 @@ fetchMock.mockResponses( | ||
await client.updateContext({ | ||
userId: '123' | ||
userId: '123', | ||
}); | ||
@@ -945,3 +945,5 @@ | ||
expect(url.searchParams.get('sessionId')).toEqual(context.sessionId?.toString()); | ||
expect(url.searchParams.get('sessionId')).toEqual( | ||
context.sessionId?.toString() | ||
); | ||
}); | ||
@@ -1178,7 +1180,18 @@ | ||
const request = getTypeSafeRequest(fetchMock); | ||
const featureRequest = getTypeSafeRequest(fetchMock, 0); | ||
expect(request.headers).toMatchObject({ | ||
expect(featureRequest.headers).toMatchObject({ | ||
customheader1: 'header1val', | ||
customheader2: 'header2val', | ||
}); | ||
const _ = client.isEnabled('count-metrics'); | ||
jest.advanceTimersByTime(2001); | ||
const metricsRequest = getTypeSafeRequest(fetchMock, 1); | ||
expect(metricsRequest.headers).toMatchObject({ | ||
customheader1: 'header1val', | ||
customheader2: 'header2val', | ||
}); | ||
}); | ||
@@ -1432,1 +1445,23 @@ | ||
}); | ||
test.each([null, undefined])( | ||
'Setting a context field to %s should clear it from the context', | ||
async () => { | ||
fetchMock.mockResponse(JSON.stringify(data)); | ||
const config: IConfig = { | ||
url: 'http://localhost/test', | ||
clientKey: '12', | ||
appName: 'web', | ||
}; | ||
const client = new UnleashClient(config); | ||
await client.start(); | ||
await client.updateContext({ userId: '123' }); | ||
expect(client.getContext().userId).toEqual('123'); | ||
const userId = undefined; | ||
await client.updateContext({ userId }); | ||
expect(client.getContext().userId).toBeUndefined(); | ||
} | ||
); |
@@ -188,2 +188,3 @@ import { TinyEmitter } from 'tiny-emitter'; | ||
headerName, | ||
customHeaders, | ||
}); | ||
@@ -190,0 +191,0 @@ } |
@@ -65,3 +65,3 @@ import { FetchMock } from 'jest-fetch-mock'; | ||
test('should send metrics under custom header', async () => { | ||
test('should send metrics with custom auth header', async () => { | ||
const metrics = new Metrics({ | ||
@@ -149,1 +149,66 @@ onError: console.error, | ||
}); | ||
describe('Custom headers for metrics', () => { | ||
const runMetrics = async (customHeaders: Record<string, string>) => { | ||
const metrics = new Metrics({ | ||
onError: console.error, | ||
appName: 'test', | ||
metricsInterval: 5, | ||
disableMetrics: false, | ||
url: 'http://localhost:3000', | ||
clientKey: '123', | ||
fetch: fetchMock, | ||
headerName: 'Authorization', | ||
customHeaders, | ||
}); | ||
metrics.count('foo', true); | ||
await metrics.sendMetrics(); | ||
return getTypeSafeRequest(fetchMock); | ||
}; | ||
test('Should apply any custom headers to the metrics request', async () => { | ||
const customHeaders = { | ||
'x-custom-header': '123', | ||
}; | ||
const requestBody = await runMetrics(customHeaders); | ||
expect(requestBody.headers).toMatchObject(customHeaders); | ||
}); | ||
test('Custom headers should override preset headers', async () => { | ||
const customHeaders = { | ||
Authorization: 'definitely-not-the-client-key', | ||
}; | ||
const requestBody = await runMetrics(customHeaders); | ||
expect(requestBody.headers).toMatchObject(customHeaders); | ||
}); | ||
test('Empty custom headers do not override preset headers on collision', async () => { | ||
const customHeaders = { | ||
Authorization: null, | ||
}; | ||
// @ts-expect-error this shouldn't be allowed in TS, but there's | ||
// nothing stopping you from doing it in JS. | ||
const requestBody = await runMetrics(customHeaders); | ||
expect(requestBody.headers).not.toMatchObject(customHeaders); | ||
}); | ||
test.each([null, undefined])( | ||
'Custom headers that are "%s" should not be sent', | ||
async (emptyValue) => { | ||
const customHeaders = { | ||
'invalid-header': emptyValue, | ||
}; | ||
// @ts-expect-error this shouldn't be allowed in TS, but there's | ||
// nothing stopping you from doing it in JS. | ||
const requestBody = await runMetrics(customHeaders); | ||
expect(requestBody.headers).not.toMatchObject(customHeaders); | ||
} | ||
); | ||
}); |
// Simplified version of: https://github.com/Unleash/unleash-client-node/blob/main/src/metrics.ts | ||
import { notNullOrUndefined } from './util'; | ||
export interface MetricsOptions { | ||
@@ -12,2 +14,3 @@ onError: OnError; | ||
headerName: string; | ||
customHeaders?: Record<string, string>; | ||
} | ||
@@ -40,2 +43,3 @@ | ||
private headerName: string; | ||
private customHeaders: Record<string, string>; | ||
@@ -51,2 +55,3 @@ constructor({ | ||
headerName, | ||
customHeaders = {}, | ||
}: MetricsOptions) { | ||
@@ -62,2 +67,3 @@ this.onError = onError; | ||
this.headerName = headerName; | ||
this.customHeaders = customHeaders; | ||
} | ||
@@ -97,2 +103,16 @@ | ||
private getHeaders() { | ||
const headers = { | ||
[this.headerName]: this.clientKey, | ||
Accept: 'application/json', | ||
'Content-Type': 'application/json', | ||
}; | ||
Object.entries(this.customHeaders) | ||
.filter(notNullOrUndefined) | ||
.forEach(([name, value]) => (headers[name] = value)); | ||
return headers; | ||
} | ||
public async sendMetrics(): Promise<void> { | ||
@@ -112,7 +132,3 @@ /* istanbul ignore next if */ | ||
method: 'POST', | ||
headers: { | ||
[this.headerName]: this.clientKey, | ||
Accept: 'application/json', | ||
'Content-Type': 'application/json', | ||
}, | ||
headers: this.getHeaders(), | ||
body: JSON.stringify(payload), | ||
@@ -119,0 +135,0 @@ }); |
@@ -1,7 +0,7 @@ | ||
import {urlWithContextAsQuery} from './util'; | ||
import { urlWithContextAsQuery } from './util'; | ||
test('should not add paramters to URL', async () => { | ||
const someUrl = new URL("https://test.com"); | ||
const someUrl = new URL('https://test.com'); | ||
//@ts-ignore on purpose for testing! | ||
//@ts-expect-error on purpose for testing! | ||
const result = urlWithContextAsQuery(someUrl, {}); | ||
@@ -12,18 +12,50 @@ | ||
test('should add context as query params', async () => { | ||
const someUrl = new URL('https://test.com'); | ||
test('should not add context as query params', async () => { | ||
const someUrl = new URL("https://test.com"); | ||
const result = urlWithContextAsQuery(someUrl, { | ||
appName: 'test', | ||
userId: '1234A', | ||
}); | ||
const result = urlWithContextAsQuery(someUrl, {appName: 'test', userId: '1234A'}); | ||
expect(result.toString()).toBe( | ||
'https://test.com/?appName=test&userId=1234A' | ||
); | ||
}); | ||
expect(result.toString()).toBe('https://test.com/?appName=test&userId=1234A'); | ||
test('should add context properties as query params', async () => { | ||
const someUrl = new URL('https://test.com'); | ||
const result = urlWithContextAsQuery(someUrl, { | ||
appName: 'test', | ||
userId: '1234A', | ||
properties: { custom1: 'test', custom2: 'test2' }, | ||
}); | ||
expect(result.toString()).toBe( | ||
'https://test.com/?appName=test&userId=1234A&properties%5Bcustom1%5D=test&properties%5Bcustom2%5D=test2' | ||
); | ||
}); | ||
test('should exclude context properties that are null or undefined', async () => { | ||
const someUrl = new URL('https://test.com'); | ||
test('should not add context properties as query params', async () => { | ||
const someUrl = new URL("https://test.com"); | ||
const result = urlWithContextAsQuery(someUrl, { | ||
appName: 'test', | ||
userId: undefined, | ||
properties: { | ||
custom1: 'test', | ||
custom2: 'test2', | ||
//@ts-expect-error this shouldn't be allowed if you're using TS, but | ||
//you could probably force it | ||
custom3: null, | ||
//@ts-expect-error same as the null property above | ||
custom4: undefined, | ||
}, | ||
}); | ||
const result = urlWithContextAsQuery(someUrl, {appName: 'test', userId: '1234A', properties: { custom1: 'test', custom2: "test2"}}); | ||
expect(result.toString()).toBe( | ||
'https://test.com/?appName=test&properties%5Bcustom1%5D=test&properties%5Bcustom2%5D=test2' | ||
); | ||
}); | ||
expect(result.toString()).toBe('https://test.com/?appName=test&userId=1234A&properties%5Bcustom1%5D=test&properties%5Bcustom2%5D=test2'); | ||
}); |
@@ -32,2 +32,2 @@ import { IContext } from '.'; | ||
return urlWithQuery; | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
190029
3374
1