@featurevisor/sdk 2.17.0 → 2.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/coverage/clover.xml +328 -312
- package/coverage/coverage-final.json +9 -9
- package/coverage/lcov-report/bucketer.ts.html +16 -16
- package/coverage/lcov-report/child.ts.html +1 -1
- package/coverage/lcov-report/conditions.ts.html +7 -7
- package/coverage/lcov-report/datafileReader.ts.html +55 -55
- package/coverage/lcov-report/emitter.ts.html +2 -2
- package/coverage/lcov-report/evaluate.ts.html +247 -85
- package/coverage/lcov-report/events.ts.html +1 -1
- package/coverage/lcov-report/helpers.ts.html +4 -4
- package/coverage/lcov-report/hooks.ts.html +6 -6
- package/coverage/lcov-report/index.html +25 -25
- package/coverage/lcov-report/instance.ts.html +21 -21
- package/coverage/lcov-report/logger.ts.html +14 -14
- package/coverage/lcov.info +586 -554
- package/dist/evaluate.d.ts +3 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.gz +0 -0
- package/dist/index.mjs.map +1 -1
- package/lib/evaluate.d.ts +3 -1
- package/package.json +3 -3
- package/src/evaluate.ts +78 -24
- package/src/instance.spec.ts +131 -0
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=/^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i,t=t=>{if("string"!=typeof t)throw new TypeError("Invalid argument expected string");const r=t.match(e);if(!r)throw new Error(`Invalid argument not valid semver ('${t}' received)`);return r.shift(),r},r=e=>"*"===e||"x"===e||"X"===e,a=e=>{const t=parseInt(e,10);return isNaN(t)?e:t},i=(e,t)=>{if(r(e)||r(t))return 0;const[i,n]=((e,t)=>typeof e!=typeof t?[String(e),String(t)]:[e,t])(a(e),a(t));return i>n?1:i<n?-1:0},n=(e,t)=>{for(let r=0;r<Math.max(e.length,t.length);r++){const a=i(e[r]||"0",t[r]||"0");if(0!==a)return a}return 0},s=(e,r)=>{const a=t(e),i=t(r),s=a.pop(),o=i.pop(),l=n(a,i);return 0!==l?l:s&&o?n(s.split("."),o.split(".")):s||o?s?-1:1:0};function o(e,t){return-1===t.indexOf(".")?e[t]:t.split(".").reduce(((e,t)=>e?e[t]:void 0),e)}function l(e,t,r){const{attribute:a,operator:i,value:n,regexFlags:l}=e,c=o(t,a);if("equals"===i)return c===n;if("notEquals"===i)return c!==n;if("before"===i||"after"===i){const e=c instanceof Date?c:new Date(c),t=n instanceof Date?n:new Date(n);return"before"===i?e<t:e>t}if(!Array.isArray(n)||-1===["string","number"].indexOf(typeof c)&&null!==c)if("string"==typeof c&&"string"==typeof n){const e=c;if("contains"===i)return-1!==e.indexOf(n);if("notContains"===i)return-1===e.indexOf(n);if("startsWith"===i)return e.startsWith(n);if("endsWith"===i)return e.endsWith(n);if("semverEquals"===i)return 0===s(e,n);if("semverNotEquals"===i)return 0!==s(e,n);if("semverGreaterThan"===i)return 1===s(e,n);if("semverGreaterThanOrEquals"===i)return s(e,n)>=0;if("semverLessThan"===i)return-1===s(e,n);if("semverLessThanOrEquals"===i)return s(e,n)<=0;if("matches"===i)return r(n,l||"").test(e);if("notMatches"===i)return!r(n,l||"").test(e)}else if("number"==typeof c&&"number"==typeof n){if("greaterThan"===i)return c>n;if("greaterThanOrEquals"===i)return c>=n;if("lessThan"===i)return c<n;if("lessThanOrEquals"===i)return c<=n}else{if("exists"===i)return void 0!==c;if("notExists"===i)return void 0===c;if(Array.isArray(c)&&"string"==typeof n){const e=c;if("includes"===i)return e.indexOf(n)>-1;if("notIncludes"===i)return-1===e.indexOf(n)}}else{const e=c;if("in"===i)return-1!==n.indexOf(e);if("notIn"===i)return-1===n.indexOf(e)}return!1}const c=Math.pow(2,32),u=1e5;function f(e){const t=function(e,t){var r;let a,i,n,s,o,l,c,u;for("string"==typeof e&&(r=e,e=(new TextEncoder).encode(r)),a=3&e.length,i=e.length-a,n=t,o=3432918353,l=461845907,u=0;u<i;)c=255&e[u]|(255&e[++u])<<8|(255&e[++u])<<16|(255&e[++u])<<24,++u,c=(65535&c)*o+(((c>>>16)*o&65535)<<16)&4294967295,c=c<<15|c>>>17,c=(65535&c)*l+(((c>>>16)*l&65535)<<16)&4294967295,n^=c,n=n<<13|n>>>19,s=5*(65535&n)+((5*(n>>>16)&65535)<<16)&4294967295,n=27492+(65535&s)+((58964+(s>>>16)&65535)<<16);switch(c=0,a){case 3:c^=(255&e[u+2])<<16;case 2:c^=(255&e[u+1])<<8;case 1:c^=255&e[u],c=(65535&c)*o+(((c>>>16)*o&65535)<<16)&4294967295,c=c<<15|c>>>17,c=(65535&c)*l+(((c>>>16)*l&65535)<<16)&4294967295,n^=c}return n^=e.length,n^=n>>>16,n=2246822507*(65535&n)+((2246822507*(n>>>16)&65535)<<16)&4294967295,n^=n>>>13,n=3266489909*(65535&n)+((3266489909*(n>>>16)&65535)<<16)&4294967295,n^=n>>>16,n>>>0}(e,1)/c;return Math.floor(t*u)}function g(e){const{featureKey:t,bucketBy:r,context:a,logger:i}=e;let n,s;if("string"==typeof r)n="plain",s=[r];else if(Array.isArray(r))n="and",s=r;else{if("object"!=typeof r||!Array.isArray(r.or))throw i.error("invalid bucketBy",{featureKey:t,bucketBy:r}),new Error("invalid bucketBy");n="or",s=r.or}const l=[];return s.forEach((e=>{const t=o(a,e);void 0!==t&&("plain"===n||"and"===n||0===l.length)&&l.push(t)})),l.push(t),l.join(".")}const d="[Featurevisor]",h=function(e,t,r={}){let a="log";"info"===e?a="info":"warn"===e?a="warn":"error"===e&&(a="error"),console[a](d,t,r)};class b{constructor(e){this.level=e.level||b.defaultLevel,this.handle=e.handler||h}setLevel(e){this.level=e}log(e,t,r){b.allLevels.indexOf(this.level)>=b.allLevels.indexOf(e)&&this.handle(e,t,r)}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}}function y(e={}){return new b(e)}b.allLevels=["fatal","error","warn","info","debug"],b.defaultLevel="info";class v{constructor(e){this.hooks=[],this.logger=e.logger,e.hooks&&e.hooks.forEach((e=>{this.add(e)}))}add(e){if(!this.hooks.some((t=>t.name===e.name)))return this.hooks.push(e),()=>{this.remove(e.name)};this.logger.error(`Hook with name "${e.name}" already exists.`,{name:e.name,hook:e})}remove(e){this.hooks=this.hooks.filter((t=>t.name!==e))}getAll(){return this.hooks}}class p{constructor(){this.listeners={}}on(e,t){this.listeners[e]||(this.listeners[e]=[]);const r=this.listeners[e];r.push(t);let a=!0;return function(){if(!a)return;a=!1;const e=r.indexOf(t);-1!==e&&r.splice(e,1)}}trigger(e,t={}){const r=this.listeners[e];r&&r.forEach((function(e){try{e(t)}catch(e){console.error(e)}}))}clearAll(){this.listeners={}}}class O{constructor(e){const{datafile:t,logger:r}=e;this.logger=r,this.schemaVersion=t.schemaVersion,this.revision=t.revision,this.segments=t.segments,this.features=t.features,this.regexCache={}}getRevision(){return this.revision}getSchemaVersion(){return this.schemaVersion}getSegment(e){const t=this.segments[e];if(t)return t.conditions=this.parseConditionsIfStringified(t.conditions),t}getFeatureKeys(){return Object.keys(this.features)}getFeature(e){return this.features[e]}getVariableKeys(e){const t=this.getFeature(e);return t?Object.keys(t.variablesSchema||{}):[]}hasVariations(e){const t=this.getFeature(e);return!!t&&Array.isArray(t.variations)&&t.variations.length>0}getRegex(e,t){const r=t||"",a=`${e}-${r}`;if(this.regexCache[a])return this.regexCache[a];const i=new RegExp(e,r);return this.regexCache[a]=i,i}allConditionsAreMatched(e,t){if("string"==typeof e)return"*"===e;const r=(e,t)=>this.getRegex(e,t);if("attribute"in e)try{return l(e,t,r)}catch(r){return this.logger.warn(r.message,{error:r,details:{condition:e,context:t}}),!1}return"and"in e&&Array.isArray(e.and)?e.and.every((e=>this.allConditionsAreMatched(e,t))):"or"in e&&Array.isArray(e.or)?e.or.some((e=>this.allConditionsAreMatched(e,t))):"not"in e&&Array.isArray(e.not)?e.not.every((()=>!1===this.allConditionsAreMatched({and:e.not},t))):!!Array.isArray(e)&&e.every((e=>this.allConditionsAreMatched(e,t)))}segmentIsMatched(e,t){return this.allConditionsAreMatched(e.conditions,t)}allSegmentsAreMatched(e,t){if("*"===e)return!0;if("string"==typeof e){const r=this.getSegment(e);return!!r&&this.segmentIsMatched(r,t)}if("object"==typeof e){if("and"in e&&Array.isArray(e.and))return e.and.every((e=>this.allSegmentsAreMatched(e,t)));if("or"in e&&Array.isArray(e.or))return e.or.some((e=>this.allSegmentsAreMatched(e,t)));if("not"in e&&Array.isArray(e.not))return e.not.every((e=>!1===this.allSegmentsAreMatched(e,t)))}return!!Array.isArray(e)&&e.every((e=>this.allSegmentsAreMatched(e,t)))}getMatchedTraffic(e,t){return e.find((e=>!!this.allSegmentsAreMatched(this.parseSegmentsIfStringified(e.segments),t)))}getMatchedAllocation(e,t){if(e.allocation)for(const r of e.allocation){const[e,a]=r.range;if(r.range&&e<=t&&a>=t)return r}}getMatchedForce(e,t){const r={force:void 0,forceIndex:void 0},a="string"==typeof e?this.getFeature(e):e;if(!a||!a.force)return r;for(let e=0;e<a.force.length;e++){const i=a.force[e];if(i.conditions&&this.allConditionsAreMatched(this.parseConditionsIfStringified(i.conditions),t)){r.force=i,r.forceIndex=e;break}if(i.segments&&this.allSegmentsAreMatched(this.parseSegmentsIfStringified(i.segments),t)){r.force=i,r.forceIndex=e;break}}return r}parseConditionsIfStringified(e){if("string"!=typeof e)return e;if("*"===e)return e;try{return JSON.parse(e)}catch(t){return this.logger.error("Error parsing conditions",{error:t,details:{conditions:e}}),e}}parseSegmentsIfStringified(e){return"string"==typeof e&&(e.startsWith("{")||e.startsWith("["))?JSON.parse(e):e}}var k;function V(e){try{const{hooksManager:t}=e,r=t.getAll();let a=e;for(const e of t.getAll())e.before&&(a=e.before(a));let i=A(a);void 0!==a.defaultVariationValue&&"variation"===i.type&&void 0===i.variationValue&&(i.variationValue=a.defaultVariationValue),void 0!==a.defaultVariableValue&&"variable"===i.type&&void 0===i.variableValue&&(i.variableValue=a.defaultVariableValue);for(const e of r)e.after&&(i=e.after(i,a));return i}catch(t){const{type:r,featureKey:a,variableKey:i,logger:n}=e,s={type:r,featureKey:a,variableKey:i,reason:k.ERROR,error:t};return n.error("error during evaluation",s),s}}function A(e){const{type:t,featureKey:r,variableKey:a,context:i,logger:n,datafileReader:s,sticky:o,hooksManager:l}=e,c=l.getAll();let u;try{let l;if("flag"!==t&&(l=A(Object.assign(Object.assign({},e),{type:"flag"})),!1===l.enabled)){u={type:t,featureKey:r,reason:k.DISABLED};const e=s.getFeature(r);if("variable"===t&&e&&a&&e.variablesSchema&&e.variablesSchema[a]){const i=e.variablesSchema[a];void 0!==i.disabledValue?u={type:t,featureKey:r,reason:k.VARIABLE_DISABLED,variableKey:a,variableValue:i.disabledValue,variableSchema:i,enabled:!1}:i.useDefaultWhenDisabled&&(u={type:t,featureKey:r,reason:k.VARIABLE_DEFAULT,variableKey:a,variableValue:i.defaultValue,variableSchema:i,enabled:!1})}return"variation"===t&&e&&e.disabledVariationValue&&(u={type:t,featureKey:r,reason:k.VARIATION_DISABLED,variationValue:e.disabledVariationValue,enabled:!1}),n.debug("feature is disabled",u),u}if(o&&o[r]){if("flag"===t&&void 0!==o[r].enabled)return u={type:t,featureKey:r,reason:k.STICKY,sticky:o[r],enabled:o[r].enabled},n.debug("using sticky enabled",u),u;if("variation"===t){const e=o[r].variation;if(void 0!==e)return u={type:t,featureKey:r,reason:k.STICKY,variationValue:e},n.debug("using sticky variation",u),u}if(a){const e=o[r].variables;if(e){const i=e[a];if(void 0!==i)return u={type:t,featureKey:r,reason:k.STICKY,variableKey:a,variableValue:i},n.debug("using sticky variable",u),u}}}const d="string"==typeof r?s.getFeature(r):r;if(!d)return u={type:t,featureKey:r,reason:k.FEATURE_NOT_FOUND},n.warn("feature not found",u),u;let h;if("flag"===t&&d.deprecated&&n.warn("feature is deprecated",{featureKey:r}),a){if(d.variablesSchema&&d.variablesSchema[a]&&(h=d.variablesSchema[a]),!h)return u={type:t,featureKey:r,reason:k.VARIABLE_NOT_FOUND,variableKey:a},n.warn("variable schema not found",u),u;h.deprecated&&n.warn("variable is deprecated",{featureKey:r,variableKey:a})}if("variation"===t&&(!d.variations||0===d.variations.length))return u={type:t,featureKey:r,reason:k.NO_VARIATIONS},n.warn("no variations",u),u;const{force:b,forceIndex:y}=s.getMatchedForce(d,i);if(b){if("flag"===t&&void 0!==b.enabled)return u={type:t,featureKey:r,reason:k.FORCED,forceIndex:y,force:b,enabled:b.enabled},n.debug("forced enabled found",u),u;if("variation"===t&&b.variation&&d.variations){const e=d.variations.find((e=>e.value===b.variation));if(e)return u={type:t,featureKey:r,reason:k.FORCED,forceIndex:y,force:b,variation:e},n.debug("forced variation found",u),u}if(a&&b.variables&&void 0!==b.variables[a])return u={type:t,featureKey:r,reason:k.FORCED,forceIndex:y,force:b,variableKey:a,variableSchema:h,variableValue:b.variables[a]},n.debug("forced variable",u),u}if("flag"===t&&d.required&&d.required.length>0){const a=d.required.every((t=>{let r,a;if("string"==typeof t?r=t:(r=t.key,a=t.variation),!A(Object.assign(Object.assign({},e),{type:"flag",featureKey:r})).enabled)return!1;if(void 0!==a){const t=A(Object.assign(Object.assign({},e),{type:"variation",featureKey:r}));let i;return t.variationValue?i=t.variationValue:t.variation&&(i=t.variation.value),i===a}return!0}));if(!a)return u={type:t,featureKey:r,reason:k.REQUIRED,required:d.required,enabled:a},n.debug("required features not enabled",u),u}let v=g({featureKey:r,bucketBy:d.bucketBy,context:i,logger:n});for(const e of c)e.bucketKey&&(v=e.bucketKey({featureKey:r,context:i,bucketBy:d.bucketBy,bucketKey:v}));let p,O,V=f(v);for(const e of c)e.bucketValue&&(V=e.bucketValue({featureKey:r,bucketKey:v,context:i,bucketValue:V}));if("flag"!==t?(p=s.getMatchedTraffic(d.traffic,i),p&&(O=s.getMatchedAllocation(p,V))):p=s.getMatchedTraffic(d.traffic,i),p){if(0===p.percentage)return u={type:t,featureKey:r,reason:k.RULE,bucketKey:v,bucketValue:V,ruleKey:p.key,traffic:p,enabled:!1},n.debug("matched rule with 0 percentage",u),u;if("flag"===t){if(d.ranges&&d.ranges.length>0)return d.ranges.find((e=>V>=e[0]&&V<e[1]))?(u={type:t,featureKey:r,reason:k.ALLOCATED,bucketKey:v,bucketValue:V,ruleKey:p.key,traffic:p,enabled:void 0===p.enabled||p.enabled},n.debug("matched",u),u):(u={type:t,featureKey:r,reason:k.OUT_OF_RANGE,bucketKey:v,bucketValue:V,enabled:!1},n.debug("not matched",u),u);if(void 0!==p.enabled)return u={type:t,featureKey:r,reason:k.RULE,bucketKey:v,bucketValue:V,ruleKey:p.key,traffic:p,enabled:p.enabled},n.debug("override from rule",u),u;if(V<=p.percentage)return u={type:t,featureKey:r,reason:k.RULE,bucketKey:v,bucketValue:V,ruleKey:p.key,traffic:p,enabled:!0},n.debug("matched traffic",u),u}if("variation"===t&&d.variations){if(p.variation){const e=d.variations.find((e=>e.value===p.variation));if(e)return u={type:t,featureKey:r,reason:k.RULE,bucketKey:v,bucketValue:V,ruleKey:p.key,traffic:p,variation:e},n.debug("override from rule",u),u}if(O&&O.variation){const e=d.variations.find((e=>e.value===O.variation));if(e)return u={type:t,featureKey:r,reason:k.ALLOCATED,bucketKey:v,bucketValue:V,ruleKey:p.key,traffic:p,variation:e},n.debug("allocated variation",u),u}}}if("variable"===t&&a){if(p&&p.variables&&void 0!==p.variables[a])return u={type:t,featureKey:r,reason:k.RULE,bucketKey:v,bucketValue:V,ruleKey:p.key,traffic:p,variableKey:a,variableSchema:h,variableValue:p.variables[a]},n.debug("override from rule",u),u;let e;if(b&&b.variation?e=b.variation:p&&p.variation?e=p.variation:O&&O.variation&&(e=O.variation),e&&Array.isArray(d.variations)){const o=d.variations.find((t=>t.value===e));if(o&&o.variableOverrides&&o.variableOverrides[a]){const e=o.variableOverrides[a].find((e=>e.conditions?s.allConditionsAreMatched("string"==typeof e.conditions&&"*"!==e.conditions?JSON.parse(e.conditions):e.conditions,i):!!e.segments&&s.allSegmentsAreMatched(s.parseSegmentsIfStringified(e.segments),i)));if(e)return u={type:t,featureKey:r,reason:k.VARIABLE_OVERRIDE,bucketKey:v,bucketValue:V,ruleKey:null==p?void 0:p.key,traffic:p,variableKey:a,variableSchema:h,variableValue:e.value},n.debug("variable override",u),u}if(o&&o.variables&&void 0!==o.variables[a])return u={type:t,featureKey:r,reason:k.ALLOCATED,bucketKey:v,bucketValue:V,ruleKey:null==p?void 0:p.key,traffic:p,variableKey:a,variableSchema:h,variableValue:o.variables[a]},n.debug("allocated variable",u),u}}return"variation"===t?(u={type:t,featureKey:r,reason:k.NO_MATCH,bucketKey:v,bucketValue:V},n.debug("no matched variation",u),u):"variable"===t?h?(u={type:t,featureKey:r,reason:k.VARIABLE_DEFAULT,bucketKey:v,bucketValue:V,variableKey:a,variableSchema:h,variableValue:h.defaultValue},n.debug("using default value",u),u):(u={type:t,featureKey:r,reason:k.VARIABLE_NOT_FOUND,variableKey:a,bucketKey:v,bucketValue:V},n.debug("variable not found",u),u):(u={type:t,featureKey:r,reason:k.NO_MATCH,bucketKey:v,bucketValue:V,enabled:!1},n.debug("nothing matched",u),u)}catch(e){return u={type:t,featureKey:r,variableKey:a,reason:k.ERROR,error:e},n.error("error",u),u}}function K(e={},t={},r){const a=[...Object.keys(e),...Object.keys(t)];return{features:a.filter(((e,t)=>a.indexOf(e)===t)),replaced:r}}!function(e){e.FEATURE_NOT_FOUND="feature_not_found",e.DISABLED="disabled",e.REQUIRED="required",e.OUT_OF_RANGE="out_of_range",e.NO_VARIATIONS="no_variations",e.VARIATION_DISABLED="variation_disabled",e.VARIABLE_NOT_FOUND="variable_not_found",e.VARIABLE_DEFAULT="variable_default",e.VARIABLE_DISABLED="variable_disabled",e.VARIABLE_OVERRIDE="variable_override",e.NO_MATCH="no_match",e.FORCED="forced",e.STICKY="sticky",e.RULE="rule",e.ALLOCATED="allocated",e.ERROR="error"}(k||(k={}));class m{constructor(e){this.parent=e.parent,this.context=e.context,this.sticky=e.sticky||{},this.emitter=new p}on(e,t){return"context_set"===e||"sticky_set"===e?this.emitter.on(e,t):this.parent.on(e,t)}close(){this.emitter.clearAll()}setContext(e,t=!1){this.context=t?e:Object.assign(Object.assign({},this.context),e),this.emitter.trigger("context_set",{context:this.context,replaced:t})}getContext(e){return this.parent.getContext(Object.assign(Object.assign({},this.context),e))}setSticky(e,t=!1){const r=this.sticky||{};this.sticky=t?Object.assign({},e):Object.assign(Object.assign({},this.sticky),e);const a=K(r,this.sticky,t);this.emitter.trigger("sticky_set",a)}isEnabled(e,t={},r={}){return this.parent.isEnabled(e,Object.assign(Object.assign({},this.context),t),Object.assign({sticky:this.sticky},r))}getVariation(e,t={},r={}){return this.parent.getVariation(e,Object.assign(Object.assign({},this.context),t),Object.assign({sticky:this.sticky},r))}getVariable(e,t,r={},a={}){return this.parent.getVariable(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableBoolean(e,t,r={},a={}){return this.parent.getVariableBoolean(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableString(e,t,r={},a={}){return this.parent.getVariableString(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableInteger(e,t,r={},a={}){return this.parent.getVariableInteger(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableDouble(e,t,r={},a={}){return this.parent.getVariableDouble(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableArray(e,t,r={},a={}){return this.parent.getVariableArray(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableObject(e,t,r={},a={}){return this.parent.getVariableObject(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableJSON(e,t,r={},a={}){return this.parent.getVariableJSON(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getAllEvaluations(e={},t=[],r={}){return this.parent.getAllEvaluations(Object.assign(Object.assign({},this.context),e),t,Object.assign({sticky:this.sticky},r))}}function x(e,t){if(void 0===e)return null;switch(t){case"string":return"string"==typeof e?e:null;case"integer":return parseInt(e,10);case"double":return parseFloat(e);case"boolean":return!0===e;case"array":return Array.isArray(e)?e:null;case"object":return"object"==typeof e?e:null;default:return e}}const E={schemaVersion:"2",revision:"unknown",segments:{},features:{}};class j{constructor(e){this.context={},this.context=e.context||{},this.logger=e.logger||y({level:e.logLevel||b.defaultLevel}),this.hooksManager=new v({hooks:e.hooks||[],logger:this.logger}),this.emitter=new p,this.sticky=e.sticky,this.datafileReader=new O({datafile:E,logger:this.logger}),e.datafile&&(this.datafileReader=new O({datafile:"string"==typeof e.datafile?JSON.parse(e.datafile):e.datafile,logger:this.logger})),this.logger.info("Featurevisor SDK initialized")}setLogLevel(e){this.logger.setLevel(e)}setDatafile(e){try{const t=new O({datafile:"string"==typeof e?JSON.parse(e):e,logger:this.logger}),r=function(e,t){const r=e.getRevision(),a=e.getFeatureKeys(),i=t.getRevision(),n=t.getFeatureKeys(),s=[],o=[],l=[];for(const r of a){if(-1===n.indexOf(r)){s.push(r);continue}const a=e.getFeature(r),i=t.getFeature(r);(null==a?void 0:a.hash)!==(null==i?void 0:i.hash)&&o.push(r)}for(const e of n)-1===a.indexOf(e)&&l.push(e);return{revision:i,previousRevision:r,revisionChanged:r!==i,features:[...s,...o,...l].filter(((e,t,r)=>r.indexOf(e)===t))}}(this.datafileReader,t);this.datafileReader=t,this.logger.info("datafile set",r),this.emitter.trigger("datafile_set",r)}catch(e){this.logger.error("could not parse datafile",{error:e})}}setSticky(e,t=!1){const r=this.sticky||{};this.sticky=t?Object.assign({},e):Object.assign(Object.assign({},this.sticky),e);const a=K(r,this.sticky,t);this.logger.info("sticky features set",a),this.emitter.trigger("sticky_set",a)}getRevision(){return this.datafileReader.getRevision()}getFeature(e){return this.datafileReader.getFeature(e)}addHook(e){return this.hooksManager.add(e)}on(e,t){return this.emitter.on(e,t)}close(){this.emitter.clearAll()}setContext(e,t=!1){this.context=t?e:Object.assign(Object.assign({},this.context),e),this.emitter.trigger("context_set",{context:this.context,replaced:t}),this.logger.debug(t?"context replaced":"context updated",{context:this.context,replaced:t})}getContext(e){return e?Object.assign(Object.assign({},this.context),e):this.context}spawn(e={},t={}){return new m({parent:this,context:this.getContext(e),sticky:t.sticky})}getEvaluationDependencies(e,t={}){return{context:this.getContext(e),logger:this.logger,hooksManager:this.hooksManager,datafileReader:this.datafileReader,sticky:t.sticky?Object.assign(Object.assign({},this.sticky),t.sticky):this.sticky,defaultVariationValue:t.defaultVariationValue,defaultVariableValue:t.defaultVariableValue}}evaluateFlag(e,t={},r={}){return V(Object.assign(Object.assign({},this.getEvaluationDependencies(t,r)),{type:"flag",featureKey:e}))}isEnabled(e,t={},r={}){try{return!0===this.evaluateFlag(e,t,r).enabled}catch(t){return this.logger.error("isEnabled",{featureKey:e,error:t}),!1}}evaluateVariation(e,t={},r={}){return V(Object.assign(Object.assign({},this.getEvaluationDependencies(t,r)),{type:"variation",featureKey:e}))}getVariation(e,t={},r={}){try{const a=this.evaluateVariation(e,t,r);return void 0!==a.variationValue?a.variationValue:a.variation?a.variation.value:null}catch(t){return this.logger.error("getVariation",{featureKey:e,error:t}),null}}evaluateVariable(e,t,r={},a={}){return V(Object.assign(Object.assign({},this.getEvaluationDependencies(r,a)),{type:"variable",featureKey:e,variableKey:t}))}getVariable(e,t,r={},a={}){try{const i=this.evaluateVariable(e,t,r,a);return void 0!==i.variableValue?i.variableSchema&&"json"===i.variableSchema.type&&"string"==typeof i.variableValue?JSON.parse(i.variableValue):i.variableValue:null}catch(r){return this.logger.error("getVariable",{featureKey:e,variableKey:t,error:r}),null}}getVariableBoolean(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"boolean")}getVariableString(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"string")}getVariableInteger(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"integer")}getVariableDouble(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"double")}getVariableArray(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"array")}getVariableObject(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"object")}getVariableJSON(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"json")}getAllEvaluations(e={},t=[],r={}){const a={},i=t.length>0?t:this.datafileReader.getFeatureKeys();for(const t of i){const i={enabled:this.isEnabled(t,e,r)};if(this.datafileReader.hasVariations(t)){const a=this.getVariation(t,e,r);a&&(i.variation=a)}const n=this.datafileReader.getVariableKeys(t);if(n.length>0){i.variables={};for(const a of n)i.variables[a]=this.getVariable(t,a,e,r)}a[t]=i}return a}}function S(e={}){return new j(e)}export{O as DatafileReader,k as EvaluationReason,m as FeaturevisorChildInstance,j as FeaturevisorInstance,b as Logger,u as MAX_BUCKETED_NUMBER,l as conditionIsMatched,S as createInstance,y as createLogger,h as defaultLogHandler,A as evaluate,V as evaluateWithHooks,g as getBucketKey,f as getBucketedNumber,o as getValueFromContext,d as loggerPrefix};
|
|
1
|
+
const e=/^[v^~<>=]*?(\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+)(?:\.([x*]|\d+))?(?:-([\da-z\-]+(?:\.[\da-z\-]+)*))?(?:\+[\da-z\-]+(?:\.[\da-z\-]+)*)?)?)?$/i,t=t=>{if("string"!=typeof t)throw new TypeError("Invalid argument expected string");const r=t.match(e);if(!r)throw new Error(`Invalid argument not valid semver ('${t}' received)`);return r.shift(),r},r=e=>"*"===e||"x"===e||"X"===e,a=e=>{const t=parseInt(e,10);return isNaN(t)?e:t},i=(e,t)=>{if(r(e)||r(t))return 0;const[i,n]=((e,t)=>typeof e!=typeof t?[String(e),String(t)]:[e,t])(a(e),a(t));return i>n?1:i<n?-1:0},n=(e,t)=>{for(let r=0;r<Math.max(e.length,t.length);r++){const a=i(e[r]||"0",t[r]||"0");if(0!==a)return a}return 0},s=(e,r)=>{const a=t(e),i=t(r),s=a.pop(),o=i.pop(),l=n(a,i);return 0!==l?l:s&&o?n(s.split("."),o.split(".")):s||o?s?-1:1:0};function o(e,t){return-1===t.indexOf(".")?e[t]:t.split(".").reduce(((e,t)=>e?e[t]:void 0),e)}function l(e,t,r){const{attribute:a,operator:i,value:n,regexFlags:l}=e,c=o(t,a);if("equals"===i)return c===n;if("notEquals"===i)return c!==n;if("before"===i||"after"===i){const e=c instanceof Date?c:new Date(c),t=n instanceof Date?n:new Date(n);return"before"===i?e<t:e>t}if(!Array.isArray(n)||-1===["string","number"].indexOf(typeof c)&&null!==c)if("string"==typeof c&&"string"==typeof n){const e=c;if("contains"===i)return-1!==e.indexOf(n);if("notContains"===i)return-1===e.indexOf(n);if("startsWith"===i)return e.startsWith(n);if("endsWith"===i)return e.endsWith(n);if("semverEquals"===i)return 0===s(e,n);if("semverNotEquals"===i)return 0!==s(e,n);if("semverGreaterThan"===i)return 1===s(e,n);if("semverGreaterThanOrEquals"===i)return s(e,n)>=0;if("semverLessThan"===i)return-1===s(e,n);if("semverLessThanOrEquals"===i)return s(e,n)<=0;if("matches"===i)return r(n,l||"").test(e);if("notMatches"===i)return!r(n,l||"").test(e)}else if("number"==typeof c&&"number"==typeof n){if("greaterThan"===i)return c>n;if("greaterThanOrEquals"===i)return c>=n;if("lessThan"===i)return c<n;if("lessThanOrEquals"===i)return c<=n}else{if("exists"===i)return void 0!==c;if("notExists"===i)return void 0===c;if(Array.isArray(c)&&"string"==typeof n){const e=c;if("includes"===i)return e.indexOf(n)>-1;if("notIncludes"===i)return-1===e.indexOf(n)}}else{const e=c;if("in"===i)return-1!==n.indexOf(e);if("notIn"===i)return-1===n.indexOf(e)}return!1}const c=Math.pow(2,32),u=1e5;function f(e){const t=function(e,t){var r;let a,i,n,s,o,l,c,u;for("string"==typeof e&&(r=e,e=(new TextEncoder).encode(r)),a=3&e.length,i=e.length-a,n=t,o=3432918353,l=461845907,u=0;u<i;)c=255&e[u]|(255&e[++u])<<8|(255&e[++u])<<16|(255&e[++u])<<24,++u,c=(65535&c)*o+(((c>>>16)*o&65535)<<16)&4294967295,c=c<<15|c>>>17,c=(65535&c)*l+(((c>>>16)*l&65535)<<16)&4294967295,n^=c,n=n<<13|n>>>19,s=5*(65535&n)+((5*(n>>>16)&65535)<<16)&4294967295,n=27492+(65535&s)+((58964+(s>>>16)&65535)<<16);switch(c=0,a){case 3:c^=(255&e[u+2])<<16;case 2:c^=(255&e[u+1])<<8;case 1:c^=255&e[u],c=(65535&c)*o+(((c>>>16)*o&65535)<<16)&4294967295,c=c<<15|c>>>17,c=(65535&c)*l+(((c>>>16)*l&65535)<<16)&4294967295,n^=c}return n^=e.length,n^=n>>>16,n=2246822507*(65535&n)+((2246822507*(n>>>16)&65535)<<16)&4294967295,n^=n>>>13,n=3266489909*(65535&n)+((3266489909*(n>>>16)&65535)<<16)&4294967295,n^=n>>>16,n>>>0}(e,1)/c;return Math.floor(t*u)}function g(e){const{featureKey:t,bucketBy:r,context:a,logger:i}=e;let n,s;if("string"==typeof r)n="plain",s=[r];else if(Array.isArray(r))n="and",s=r;else{if("object"!=typeof r||!Array.isArray(r.or))throw i.error("invalid bucketBy",{featureKey:t,bucketBy:r}),new Error("invalid bucketBy");n="or",s=r.or}const l=[];return s.forEach((e=>{const t=o(a,e);void 0!==t&&("plain"===n||"and"===n||0===l.length)&&l.push(t)})),l.push(t),l.join(".")}const d="[Featurevisor]",b=function(e,t,r={}){let a="log";"info"===e?a="info":"warn"===e?a="warn":"error"===e&&(a="error"),console[a](d,t,r)};class h{constructor(e){this.level=e.level||h.defaultLevel,this.handle=e.handler||b}setLevel(e){this.level=e}log(e,t,r){h.allLevels.indexOf(this.level)>=h.allLevels.indexOf(e)&&this.handle(e,t,r)}debug(e,t){this.log("debug",e,t)}info(e,t){this.log("info",e,t)}warn(e,t){this.log("warn",e,t)}error(e,t){this.log("error",e,t)}}function y(e={}){return new h(e)}h.allLevels=["fatal","error","warn","info","debug"],h.defaultLevel="info";class v{constructor(e){this.hooks=[],this.logger=e.logger,e.hooks&&e.hooks.forEach((e=>{this.add(e)}))}add(e){if(!this.hooks.some((t=>t.name===e.name)))return this.hooks.push(e),()=>{this.remove(e.name)};this.logger.error(`Hook with name "${e.name}" already exists.`,{name:e.name,hook:e})}remove(e){this.hooks=this.hooks.filter((t=>t.name!==e))}getAll(){return this.hooks}}class p{constructor(){this.listeners={}}on(e,t){this.listeners[e]||(this.listeners[e]=[]);const r=this.listeners[e];r.push(t);let a=!0;return function(){if(!a)return;a=!1;const e=r.indexOf(t);-1!==e&&r.splice(e,1)}}trigger(e,t={}){const r=this.listeners[e];r&&r.forEach((function(e){try{e(t)}catch(e){console.error(e)}}))}clearAll(){this.listeners={}}}class O{constructor(e){const{datafile:t,logger:r}=e;this.logger=r,this.schemaVersion=t.schemaVersion,this.revision=t.revision,this.segments=t.segments,this.features=t.features,this.regexCache={}}getRevision(){return this.revision}getSchemaVersion(){return this.schemaVersion}getSegment(e){const t=this.segments[e];if(t)return t.conditions=this.parseConditionsIfStringified(t.conditions),t}getFeatureKeys(){return Object.keys(this.features)}getFeature(e){return this.features[e]}getVariableKeys(e){const t=this.getFeature(e);return t?Object.keys(t.variablesSchema||{}):[]}hasVariations(e){const t=this.getFeature(e);return!!t&&Array.isArray(t.variations)&&t.variations.length>0}getRegex(e,t){const r=t||"",a=`${e}-${r}`;if(this.regexCache[a])return this.regexCache[a];const i=new RegExp(e,r);return this.regexCache[a]=i,i}allConditionsAreMatched(e,t){if("string"==typeof e)return"*"===e;const r=(e,t)=>this.getRegex(e,t);if("attribute"in e)try{return l(e,t,r)}catch(r){return this.logger.warn(r.message,{error:r,details:{condition:e,context:t}}),!1}return"and"in e&&Array.isArray(e.and)?e.and.every((e=>this.allConditionsAreMatched(e,t))):"or"in e&&Array.isArray(e.or)?e.or.some((e=>this.allConditionsAreMatched(e,t))):"not"in e&&Array.isArray(e.not)?e.not.every((()=>!1===this.allConditionsAreMatched({and:e.not},t))):!!Array.isArray(e)&&e.every((e=>this.allConditionsAreMatched(e,t)))}segmentIsMatched(e,t){return this.allConditionsAreMatched(e.conditions,t)}allSegmentsAreMatched(e,t){if("*"===e)return!0;if("string"==typeof e){const r=this.getSegment(e);return!!r&&this.segmentIsMatched(r,t)}if("object"==typeof e){if("and"in e&&Array.isArray(e.and))return e.and.every((e=>this.allSegmentsAreMatched(e,t)));if("or"in e&&Array.isArray(e.or))return e.or.some((e=>this.allSegmentsAreMatched(e,t)));if("not"in e&&Array.isArray(e.not))return e.not.every((e=>!1===this.allSegmentsAreMatched(e,t)))}return!!Array.isArray(e)&&e.every((e=>this.allSegmentsAreMatched(e,t)))}getMatchedTraffic(e,t){return e.find((e=>!!this.allSegmentsAreMatched(this.parseSegmentsIfStringified(e.segments),t)))}getMatchedAllocation(e,t){if(e.allocation)for(const r of e.allocation){const[e,a]=r.range;if(r.range&&e<=t&&a>=t)return r}}getMatchedForce(e,t){const r={force:void 0,forceIndex:void 0},a="string"==typeof e?this.getFeature(e):e;if(!a||!a.force)return r;for(let e=0;e<a.force.length;e++){const i=a.force[e];if(i.conditions&&this.allConditionsAreMatched(this.parseConditionsIfStringified(i.conditions),t)){r.force=i,r.forceIndex=e;break}if(i.segments&&this.allSegmentsAreMatched(this.parseSegmentsIfStringified(i.segments),t)){r.force=i,r.forceIndex=e;break}}return r}parseConditionsIfStringified(e){if("string"!=typeof e)return e;if("*"===e)return e;try{return JSON.parse(e)}catch(t){return this.logger.error("Error parsing conditions",{error:t,details:{conditions:e}}),e}}parseSegmentsIfStringified(e){return"string"==typeof e&&(e.startsWith("{")||e.startsWith("["))?JSON.parse(e):e}}var V;function k(e){try{const{hooksManager:t}=e,r=t.getAll();let a=e;for(const e of t.getAll())e.before&&(a=e.before(a));let i=A(a);void 0!==a.defaultVariationValue&&"variation"===i.type&&void 0===i.variationValue&&(i.variationValue=a.defaultVariationValue),void 0!==a.defaultVariableValue&&"variable"===i.type&&void 0===i.variableValue&&(i.variableValue=a.defaultVariableValue);for(const e of r)e.after&&(i=e.after(i,a));return i}catch(t){const{type:r,featureKey:a,variableKey:i,logger:n}=e,s={type:r,featureKey:a,variableKey:i,reason:V.ERROR,error:t};return n.error("error during evaluation",s),s}}function A(e){const{type:t,featureKey:r,variableKey:a,context:i,logger:n,datafileReader:s,sticky:o,hooksManager:l}=e,c=l.getAll();let u;try{let l;if("flag"!==t&&(l=A(Object.assign(Object.assign({},e),{type:"flag"})),!1===l.enabled)){u={type:t,featureKey:r,reason:V.DISABLED};const e=s.getFeature(r);if("variable"===t&&e&&a&&e.variablesSchema&&e.variablesSchema[a]){const i=e.variablesSchema[a];void 0!==i.disabledValue?u={type:t,featureKey:r,reason:V.VARIABLE_DISABLED,variableKey:a,variableValue:i.disabledValue,variableSchema:i,enabled:!1}:i.useDefaultWhenDisabled&&(u={type:t,featureKey:r,reason:V.VARIABLE_DEFAULT,variableKey:a,variableValue:i.defaultValue,variableSchema:i,enabled:!1})}return"variation"===t&&e&&e.disabledVariationValue&&(u={type:t,featureKey:r,reason:V.VARIATION_DISABLED,variationValue:e.disabledVariationValue,enabled:!1}),n.debug("feature is disabled",u),u}if(o&&o[r]){if("flag"===t&&void 0!==o[r].enabled)return u={type:t,featureKey:r,reason:V.STICKY,sticky:o[r],enabled:o[r].enabled},n.debug("using sticky enabled",u),u;if("variation"===t){const e=o[r].variation;if(void 0!==e)return u={type:t,featureKey:r,reason:V.STICKY,variationValue:e},n.debug("using sticky variation",u),u}if(a){const e=o[r].variables;if(e){const i=e[a];if(void 0!==i)return u={type:t,featureKey:r,reason:V.STICKY,variableKey:a,variableValue:i},n.debug("using sticky variable",u),u}}}const d="string"==typeof r?s.getFeature(r):r;if(!d)return u={type:t,featureKey:r,reason:V.FEATURE_NOT_FOUND},n.warn("feature not found",u),u;let b;if("flag"===t&&d.deprecated&&n.warn("feature is deprecated",{featureKey:r}),a){if(d.variablesSchema&&d.variablesSchema[a]&&(b=d.variablesSchema[a]),!b)return u={type:t,featureKey:r,reason:V.VARIABLE_NOT_FOUND,variableKey:a},n.warn("variable schema not found",u),u;b.deprecated&&n.warn("variable is deprecated",{featureKey:r,variableKey:a})}if("variation"===t&&(!d.variations||0===d.variations.length))return u={type:t,featureKey:r,reason:V.NO_VARIATIONS},n.warn("no variations",u),u;const{force:h,forceIndex:y}=s.getMatchedForce(d,i);if(h){if("flag"===t&&void 0!==h.enabled)return u={type:t,featureKey:r,reason:V.FORCED,forceIndex:y,force:h,enabled:h.enabled},n.debug("forced enabled found",u),u;if("variation"===t&&h.variation&&d.variations){const e=d.variations.find((e=>e.value===h.variation));if(e)return u={type:t,featureKey:r,reason:V.FORCED,forceIndex:y,force:h,variation:e},n.debug("forced variation found",u),u}if(a&&h.variables&&void 0!==h.variables[a])return u={type:t,featureKey:r,reason:V.FORCED,forceIndex:y,force:h,variableKey:a,variableSchema:b,variableValue:h.variables[a]},n.debug("forced variable",u),u}if("flag"===t&&d.required&&d.required.length>0){const a=d.required.every((t=>{let r,a;if("string"==typeof t?r=t:(r=t.key,a=t.variation),!A(Object.assign(Object.assign({},e),{type:"flag",featureKey:r})).enabled)return!1;if(void 0!==a){const t=A(Object.assign(Object.assign({},e),{type:"variation",featureKey:r}));let i;return t.variationValue?i=t.variationValue:t.variation&&(i=t.variation.value),i===a}return!0}));if(!a)return u={type:t,featureKey:r,reason:V.REQUIRED,required:d.required,enabled:a},n.debug("required features not enabled",u),u}let v=g({featureKey:r,bucketBy:d.bucketBy,context:i,logger:n});for(const e of c)e.bucketKey&&(v=e.bucketKey({featureKey:r,context:i,bucketBy:d.bucketBy,bucketKey:v}));let p,O,k=f(v);for(const e of c)e.bucketValue&&(k=e.bucketValue({featureKey:r,bucketKey:v,context:i,bucketValue:k}));if("flag"!==t?(p=s.getMatchedTraffic(d.traffic,i),p&&(O=s.getMatchedAllocation(p,k))):p=s.getMatchedTraffic(d.traffic,i),p){if(0===p.percentage)return u={type:t,featureKey:r,reason:V.RULE,bucketKey:v,bucketValue:k,ruleKey:p.key,traffic:p,enabled:!1},n.debug("matched rule with 0 percentage",u),u;if("flag"===t){if(d.ranges&&d.ranges.length>0)return d.ranges.find((e=>k>=e[0]&&k<e[1]))?(u={type:t,featureKey:r,reason:V.ALLOCATED,bucketKey:v,bucketValue:k,ruleKey:p.key,traffic:p,enabled:void 0===p.enabled||p.enabled},n.debug("matched",u),u):(u={type:t,featureKey:r,reason:V.OUT_OF_RANGE,bucketKey:v,bucketValue:k,enabled:!1},n.debug("not matched",u),u);if(void 0!==p.enabled)return u={type:t,featureKey:r,reason:V.RULE,bucketKey:v,bucketValue:k,ruleKey:p.key,traffic:p,enabled:p.enabled},n.debug("override from rule",u),u;if(k<=p.percentage)return u={type:t,featureKey:r,reason:V.RULE,bucketKey:v,bucketValue:k,ruleKey:p.key,traffic:p,enabled:!0},n.debug("matched traffic",u),u}if("variation"===t&&d.variations){if(p.variation){const e=d.variations.find((e=>e.value===p.variation));if(e)return u={type:t,featureKey:r,reason:V.RULE,bucketKey:v,bucketValue:k,ruleKey:p.key,traffic:p,variation:e},n.debug("override from rule",u),u}if(O&&O.variation){const e=d.variations.find((e=>e.value===O.variation));if(e)return u={type:t,featureKey:r,reason:V.ALLOCATED,bucketKey:v,bucketValue:k,ruleKey:p.key,traffic:p,variation:e},n.debug("allocated variation",u),u}}}if("variable"===t&&a){if(p){if(p.variableOverrides&&p.variableOverrides[a]){const e=p.variableOverrides[a],o=e.findIndex((e=>e.conditions?s.allConditionsAreMatched("string"==typeof e.conditions&&"*"!==e.conditions?JSON.parse(e.conditions):e.conditions,i):!!e.segments&&s.allSegmentsAreMatched(s.parseSegmentsIfStringified(e.segments),i)));if(-1!==o){const i=e[o];return u={type:t,featureKey:r,reason:V.VARIABLE_OVERRIDE_RULE,bucketKey:v,bucketValue:k,ruleKey:null==p?void 0:p.key,traffic:p,variableKey:a,variableSchema:b,variableValue:i.value,variableOverrideIndex:o},n.debug("variable override from rule",u),u}}if(p.variables&&void 0!==p.variables[a])return u={type:t,featureKey:r,reason:V.RULE,bucketKey:v,bucketValue:k,ruleKey:p.key,traffic:p,variableKey:a,variableSchema:b,variableValue:p.variables[a]},n.debug("override from rule",u),u}let e;if(h&&h.variation?e=h.variation:p&&p.variation?e=p.variation:O&&O.variation&&(e=O.variation),e&&Array.isArray(d.variations)){const o=d.variations.find((t=>t.value===e));if(o&&o.variableOverrides&&o.variableOverrides[a]){const e=o.variableOverrides[a],l=e.findIndex((e=>e.conditions?s.allConditionsAreMatched("string"==typeof e.conditions&&"*"!==e.conditions?JSON.parse(e.conditions):e.conditions,i):!!e.segments&&s.allSegmentsAreMatched(s.parseSegmentsIfStringified(e.segments),i)));if(-1!==l){const i=e[l];return u={type:t,featureKey:r,reason:V.VARIABLE_OVERRIDE_VARIATION,bucketKey:v,bucketValue:k,ruleKey:null==p?void 0:p.key,traffic:p,variableKey:a,variableSchema:b,variableValue:i.value,variableOverrideIndex:l},n.debug("variable override from variation",u),u}}if(o&&o.variables&&void 0!==o.variables[a])return u={type:t,featureKey:r,reason:V.ALLOCATED,bucketKey:v,bucketValue:k,ruleKey:null==p?void 0:p.key,traffic:p,variableKey:a,variableSchema:b,variableValue:o.variables[a]},n.debug("allocated variable",u),u}}return"variation"===t?(u={type:t,featureKey:r,reason:V.NO_MATCH,bucketKey:v,bucketValue:k},n.debug("no matched variation",u),u):"variable"===t?b?(u={type:t,featureKey:r,reason:V.VARIABLE_DEFAULT,bucketKey:v,bucketValue:k,variableKey:a,variableSchema:b,variableValue:b.defaultValue},n.debug("using default value",u),u):(u={type:t,featureKey:r,reason:V.VARIABLE_NOT_FOUND,variableKey:a,bucketKey:v,bucketValue:k},n.debug("variable not found",u),u):(u={type:t,featureKey:r,reason:V.NO_MATCH,bucketKey:v,bucketValue:k,enabled:!1},n.debug("nothing matched",u),u)}catch(e){return u={type:t,featureKey:r,variableKey:a,reason:V.ERROR,error:e},n.error("error",u),u}}function K(e={},t={},r){const a=[...Object.keys(e),...Object.keys(t)];return{features:a.filter(((e,t)=>a.indexOf(e)===t)),replaced:r}}!function(e){e.FEATURE_NOT_FOUND="feature_not_found",e.DISABLED="disabled",e.REQUIRED="required",e.OUT_OF_RANGE="out_of_range",e.NO_VARIATIONS="no_variations",e.VARIATION_DISABLED="variation_disabled",e.VARIABLE_NOT_FOUND="variable_not_found",e.VARIABLE_DEFAULT="variable_default",e.VARIABLE_DISABLED="variable_disabled",e.VARIABLE_OVERRIDE_VARIATION="variable_override_variation",e.VARIABLE_OVERRIDE_RULE="variable_override_rule",e.NO_MATCH="no_match",e.FORCED="forced",e.STICKY="sticky",e.RULE="rule",e.ALLOCATED="allocated",e.ERROR="error"}(V||(V={}));class m{constructor(e){this.parent=e.parent,this.context=e.context,this.sticky=e.sticky||{},this.emitter=new p}on(e,t){return"context_set"===e||"sticky_set"===e?this.emitter.on(e,t):this.parent.on(e,t)}close(){this.emitter.clearAll()}setContext(e,t=!1){this.context=t?e:Object.assign(Object.assign({},this.context),e),this.emitter.trigger("context_set",{context:this.context,replaced:t})}getContext(e){return this.parent.getContext(Object.assign(Object.assign({},this.context),e))}setSticky(e,t=!1){const r=this.sticky||{};this.sticky=t?Object.assign({},e):Object.assign(Object.assign({},this.sticky),e);const a=K(r,this.sticky,t);this.emitter.trigger("sticky_set",a)}isEnabled(e,t={},r={}){return this.parent.isEnabled(e,Object.assign(Object.assign({},this.context),t),Object.assign({sticky:this.sticky},r))}getVariation(e,t={},r={}){return this.parent.getVariation(e,Object.assign(Object.assign({},this.context),t),Object.assign({sticky:this.sticky},r))}getVariable(e,t,r={},a={}){return this.parent.getVariable(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableBoolean(e,t,r={},a={}){return this.parent.getVariableBoolean(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableString(e,t,r={},a={}){return this.parent.getVariableString(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableInteger(e,t,r={},a={}){return this.parent.getVariableInteger(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableDouble(e,t,r={},a={}){return this.parent.getVariableDouble(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableArray(e,t,r={},a={}){return this.parent.getVariableArray(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableObject(e,t,r={},a={}){return this.parent.getVariableObject(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getVariableJSON(e,t,r={},a={}){return this.parent.getVariableJSON(e,t,Object.assign(Object.assign({},this.context),r),Object.assign({sticky:this.sticky},a))}getAllEvaluations(e={},t=[],r={}){return this.parent.getAllEvaluations(Object.assign(Object.assign({},this.context),e),t,Object.assign({sticky:this.sticky},r))}}function x(e,t){if(void 0===e)return null;switch(t){case"string":return"string"==typeof e?e:null;case"integer":return parseInt(e,10);case"double":return parseFloat(e);case"boolean":return!0===e;case"array":return Array.isArray(e)?e:null;case"object":return"object"==typeof e?e:null;default:return e}}const E={schemaVersion:"2",revision:"unknown",segments:{},features:{}};class j{constructor(e){this.context={},this.context=e.context||{},this.logger=e.logger||y({level:e.logLevel||h.defaultLevel}),this.hooksManager=new v({hooks:e.hooks||[],logger:this.logger}),this.emitter=new p,this.sticky=e.sticky,this.datafileReader=new O({datafile:E,logger:this.logger}),e.datafile&&(this.datafileReader=new O({datafile:"string"==typeof e.datafile?JSON.parse(e.datafile):e.datafile,logger:this.logger})),this.logger.info("Featurevisor SDK initialized")}setLogLevel(e){this.logger.setLevel(e)}setDatafile(e){try{const t=new O({datafile:"string"==typeof e?JSON.parse(e):e,logger:this.logger}),r=function(e,t){const r=e.getRevision(),a=e.getFeatureKeys(),i=t.getRevision(),n=t.getFeatureKeys(),s=[],o=[],l=[];for(const r of a){if(-1===n.indexOf(r)){s.push(r);continue}const a=e.getFeature(r),i=t.getFeature(r);(null==a?void 0:a.hash)!==(null==i?void 0:i.hash)&&o.push(r)}for(const e of n)-1===a.indexOf(e)&&l.push(e);return{revision:i,previousRevision:r,revisionChanged:r!==i,features:[...s,...o,...l].filter(((e,t,r)=>r.indexOf(e)===t))}}(this.datafileReader,t);this.datafileReader=t,this.logger.info("datafile set",r),this.emitter.trigger("datafile_set",r)}catch(e){this.logger.error("could not parse datafile",{error:e})}}setSticky(e,t=!1){const r=this.sticky||{};this.sticky=t?Object.assign({},e):Object.assign(Object.assign({},this.sticky),e);const a=K(r,this.sticky,t);this.logger.info("sticky features set",a),this.emitter.trigger("sticky_set",a)}getRevision(){return this.datafileReader.getRevision()}getFeature(e){return this.datafileReader.getFeature(e)}addHook(e){return this.hooksManager.add(e)}on(e,t){return this.emitter.on(e,t)}close(){this.emitter.clearAll()}setContext(e,t=!1){this.context=t?e:Object.assign(Object.assign({},this.context),e),this.emitter.trigger("context_set",{context:this.context,replaced:t}),this.logger.debug(t?"context replaced":"context updated",{context:this.context,replaced:t})}getContext(e){return e?Object.assign(Object.assign({},this.context),e):this.context}spawn(e={},t={}){return new m({parent:this,context:this.getContext(e),sticky:t.sticky})}getEvaluationDependencies(e,t={}){return{context:this.getContext(e),logger:this.logger,hooksManager:this.hooksManager,datafileReader:this.datafileReader,sticky:t.sticky?Object.assign(Object.assign({},this.sticky),t.sticky):this.sticky,defaultVariationValue:t.defaultVariationValue,defaultVariableValue:t.defaultVariableValue}}evaluateFlag(e,t={},r={}){return k(Object.assign(Object.assign({},this.getEvaluationDependencies(t,r)),{type:"flag",featureKey:e}))}isEnabled(e,t={},r={}){try{return!0===this.evaluateFlag(e,t,r).enabled}catch(t){return this.logger.error("isEnabled",{featureKey:e,error:t}),!1}}evaluateVariation(e,t={},r={}){return k(Object.assign(Object.assign({},this.getEvaluationDependencies(t,r)),{type:"variation",featureKey:e}))}getVariation(e,t={},r={}){try{const a=this.evaluateVariation(e,t,r);return void 0!==a.variationValue?a.variationValue:a.variation?a.variation.value:null}catch(t){return this.logger.error("getVariation",{featureKey:e,error:t}),null}}evaluateVariable(e,t,r={},a={}){return k(Object.assign(Object.assign({},this.getEvaluationDependencies(r,a)),{type:"variable",featureKey:e,variableKey:t}))}getVariable(e,t,r={},a={}){try{const i=this.evaluateVariable(e,t,r,a);return void 0!==i.variableValue?i.variableSchema&&"json"===i.variableSchema.type&&"string"==typeof i.variableValue?JSON.parse(i.variableValue):i.variableValue:null}catch(r){return this.logger.error("getVariable",{featureKey:e,variableKey:t,error:r}),null}}getVariableBoolean(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"boolean")}getVariableString(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"string")}getVariableInteger(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"integer")}getVariableDouble(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"double")}getVariableArray(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"array")}getVariableObject(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"object")}getVariableJSON(e,t,r={},a={}){return x(this.getVariable(e,t,r,a),"json")}getAllEvaluations(e={},t=[],r={}){const a={},i=t.length>0?t:this.datafileReader.getFeatureKeys();for(const t of i){const i={enabled:this.isEnabled(t,e,r)};if(this.datafileReader.hasVariations(t)){const a=this.getVariation(t,e,r);a&&(i.variation=a)}const n=this.datafileReader.getVariableKeys(t);if(n.length>0){i.variables={};for(const a of n)i.variables[a]=this.getVariable(t,a,e,r)}a[t]=i}return a}}function R(e={}){return new j(e)}export{O as DatafileReader,V as EvaluationReason,m as FeaturevisorChildInstance,j as FeaturevisorInstance,h as Logger,u as MAX_BUCKETED_NUMBER,l as conditionIsMatched,R as createInstance,y as createLogger,b as defaultLogHandler,A as evaluate,k as evaluateWithHooks,g as getBucketKey,f as getBucketedNumber,o as getValueFromContext,d as loggerPrefix};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.gz
CHANGED
|
Binary file
|