@splitlab/js-client 0.1.3 → 0.2.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/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var p=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var D=(n,e)=>{for(var t in e)p(n,t,{get:e[t],enumerable:!0})},z=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of A(e))!L.call(n,s)&&s!==t&&p(n,s,{get:()=>e[s],enumerable:!(i=O(e,s))||i.enumerable});return n};var M=n=>z(p({},"__esModule",{value:!0}),n);var j={};D(j,{SplitLabClient:()=>y,hashToFloat:()=>w,murmurhash3:()=>g});module.exports=M(j);function g(n,e=0){let t=e>>>0,i=n.length,s=i>>2,a=3432918353,r=461845907;for(let u=0;u<s;u++){let c=n.charCodeAt(u*4)&255|(n.charCodeAt(u*4+1)&255)<<8|(n.charCodeAt(u*4+2)&255)<<16|(n.charCodeAt(u*4+3)&255)<<24;c=Math.imul(c,a),c=c<<15|c>>>17,c=Math.imul(c,r),t^=c,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196}let o=s*4,l=0;switch(i&3){case 3:l^=(n.charCodeAt(o+2)&255)<<16;case 2:l^=(n.charCodeAt(o+1)&255)<<8;case 1:l^=n.charCodeAt(o)&255,l=Math.imul(l,a),l=l<<15|l>>>17,l=Math.imul(l,r),t^=l}return t^=i,t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,t>>>0}function w(n,e){return g(n,e)/4294967295}var h=typeof globalThis<"u"&&typeof globalThis.document<"u",B=[[/Edg(?:e|A|iOS)?\/(\S+)/,"Edge"],[/OPR\/(\S+)/,"Opera"],[/SamsungBrowser\/(\S+)/,"Samsung Internet"],[/UCBrowser\/(\S+)/,"UC Browser"],[/Firefox\/(\S+)/,"Firefox"],[/CriOS\/(\S+)/,"Chrome"],[/FxiOS\/(\S+)/,"Firefox"],[/Chrome\/(\S+)/,"Chrome"],[/Safari\/(\S+)/,"Safari"]],F=[[/Windows NT (\d+\.\d+)/,"Windows"],[/Mac OS X ([\d_]+)/,"macOS"],[/Android ([\d.]+)/,"Android"],[/iPhone OS ([\d_]+)/,"iOS"],[/iPad.*OS ([\d_]+)/,"iPadOS"],[/CrOS/,"ChromeOS"],[/Linux/,"Linux"]];function N(n){if(!n)return{browser:null,browser_version:null,os:null,os_version:null,device_type:null};let e=null,t=null,i=null,s=null;if(/Safari/.test(n)&&!/Chrome|CriOS|Chromium|Edg|OPR|SamsungBrowser|UCBrowser/.test(n)){let o=n.match(/Version\/(\S+)/);e="Safari",t=o?o[1]:null}else for(let[o,l]of B){let u=n.match(o);if(u){e=l,t=u[1]||null;break}}for(let[o,l]of F){let u=n.match(o);if(u){i=l,s=u[1]?.replace(/_/g,".")||null;break}}let r="desktop";return/Mobi|Android.*Mobile|iPhone/.test(n)?r="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(n)&&(r="tablet"),{browser:e,browser_version:t,os:i,os_version:s,device_type:r}}function T(){if(!h)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,a=N(e?.userAgent);return{pathname:t?.pathname??null,hostname:t?.hostname??null,referrer:i?.referrer||null,search_params:t?.search||null,page_title:i?.title||null,hash:t?.hash||null,user_agent:e?.userAgent??null,browser:a.browser,browser_version:a.browser_version,os:a.os,os_version:a.os_version,device_type:a.device_type,screen_width:s?.width??null,screen_height:s?.height??null,viewport_width:n.innerWidth??null,viewport_height:n.innerHeight??null,language:e?.language??null,timezone:$()}}function $(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var K=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],C="__ot_utm";function x(){if(!h)return{};try{let i=globalThis.sessionStorage?.getItem(C);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of K){let s=n.get(i);s&&(e[i]=s,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(C,JSON.stringify(e))}catch{}return e}var _="__ot_sid",m="__ot_sts";function E(n){if(!h)return f();let e=globalThis.sessionStorage;if(!e)return f();try{let t=Date.now(),i=e.getItem(_),s=Number(e.getItem(m)||"0");if(i&&t-s<n)return e.setItem(m,String(t)),i;let a=f();return e.setItem(_,a),e.setItem(m,String(t)),a}catch{return f()}}var R="__ot_did";function P(){if(!h)return f();try{let n=globalThis.localStorage;if(!n)return f();let e=n.getItem(R);if(e)return e;let t=f();return n.setItem(R,t),t}catch{return f()}}var I="__ot_ref";function U(){if(!h)return null;try{let n=globalThis.sessionStorage,e=n?.getItem(I);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return n?.setItem(I,t),t||null}catch{return globalThis.document?.referrer||null}}function f(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var b=typeof globalThis<"u"&&typeof globalThis.document<"u",y=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.configRefreshTimer=null;this.initialized=!1;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.config={apiKey:e.apiKey,baseUrl:e.baseUrl.replace(/\/$/,""),ingestUrl:(e.ingestUrl||e.baseUrl).replace(/\/$/,""),distinctId:e.distinctId??null,attributes:e.attributes||{},autoFlushInterval:e.autoFlushInterval??3e4,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,configRefreshInterval:e.configRefreshInterval??3e4,realtimeUpdates:e.realtimeUpdates??!1,onConfigUpdate:e.onConfigUpdate??null,captureContext:e.captureContext??b,captureUtm:e.captureUtm??!0,trackSessions:e.trackSessions??!0,sessionTimeout:e.sessionTimeout??30*6e4,persistDeviceId:e.persistDeviceId??!0,trackPageviews:e.trackPageviews??!1,superProperties:e.superProperties??{}},this.config.captureUtm&&(this.utmParams=x()),this.config.captureContext&&(this.initialReferrer=U()),this.deviceId=P()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t}=await this.fetchConfig();this.serverConfig=e,this.lastEtag=t,this.evalResult=this.localEvaluate(this.serverConfig,this.bucketingId)}catch(e){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",e),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.enableLocalEvaluation&&(this.configRefreshTimer=setInterval(()=>{this.refresh().catch(()=>{})},this.config.configRefreshInterval)),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),b&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&b&&this.setupPageviewTracking(),this.initialized=!0}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.experiments[e]??null}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}async track(e,t){let i=this.enrichProperties(t);this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:i,timestamp:new Date().toISOString()}),this.eventQueue.length>=this.config.autoFlushSize&&await this.flush()}async trackPageview(e){await this.track("$pageview",e)}setSuperProperties(e){Object.assign(this.config.superProperties,e)}unsetSuperProperty(e){delete this.config.superProperties[e]}async flush(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];try{await this.request("POST","/ingest/batch",{events:e},!0)}catch{this.eventQueue=e.concat(this.eventQueue)}}async identify(e,t){let i=this.effectiveDistinctId;this.config.distinctId=e,i!==e&&await this.track("$identify",{...t,previous_distinct_id:i}),this.config.enableLocalEvaluation||(this.evalResult=null,this.initialized=!1,await this.initialize())}async refresh(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t,notModified:i}=await this.fetchConfig();if(i)return;this.serverConfig=e,this.lastEtag=t,this.evalResult=this.localEvaluate(this.serverConfig,this.bucketingId),this.config.onConfigUpdate&&this.config.onConfigUpdate()}catch{}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}}getDistinctId(){return this.effectiveDistinctId}getDeviceId(){return this.deviceId}setAttributes(e){this.config.attributes={...this.config.attributes,...e}}async destroy(){await this.flush(),this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),this.configRefreshTimer!==null&&(clearInterval(this.configRefreshTimer),this.configRefreshTimer=null),this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.beforeUnloadHandler&&typeof globalThis.removeEventListener=="function"&&(globalThis.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.pageviewCleanup&&(this.pageviewCleanup(),this.pageviewCleanup=null),this.initialized=!1}enrichProperties(e){let t={};if(this.config.captureContext){let i=T();if(i){for(let[s,a]of Object.entries(i))a!==null&&a!==""&&a!==void 0&&(t[s]=a);this.initialReferrer&&(t.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[i,s]of Object.entries(this.utmParams))s&&(t[i]=s);this.config.trackSessions&&(t.session_id=E(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(t.device_id=this.deviceId);for(let[i,s]of Object.entries(this.config.superProperties))s!==void 0&&(t[i]=s);if(e)for(let[i,s]of Object.entries(e))s!==void 0&&(t[i]=s);return t}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,s=t.pushState.bind(t);t.pushState=(...o)=>{s(...o),this.onUrlChange(i),i=e.location?.href};let a=t.replaceState.bind(t);t.replaceState=(...o)=>{a(...o),this.onUrlChange(i),i=e.location?.href};let r=()=>{this.onUrlChange(i),i=e.location?.href};e.addEventListener("popstate",r),this.trackPageview().catch(()=>{}),this.pageviewCleanup=()=>{t.pushState=s,t.replaceState=a,e.removeEventListener("popstate",r)}}onUrlChange(e){globalThis.location?.href!==e&&setTimeout(()=>{this.trackPageview({previous_url:e||null}).catch(()=>{})},0)}localEvaluate(e,t){let i={},s={},a=this.config.attributes;for(let r of e.experiments){if(r.targeting_rules&&!this.evaluateRules(r.targeting_rules,a)){i[r.key]=null;continue}let o=g(r.key+":"+t);if(o%1e4/100>=r.traffic_percentage){i[r.key]=null;continue}let u=r.variants.reduce((v,k)=>v+k.weight,0),c=o%u,S=0,d=null;for(let v of r.variants)if(S+=v.weight,c<S){d=v.key;break}d||(d=r.variants[r.variants.length-1].key),i[r.key]=d}for(let r of e.flags){if(r.rules&&!this.evaluateRules(r.rules,a)){s[r.key]=!1;continue}let l=g(r.key+":"+t)%100;s[r.key]=l<r.rollout_percentage}return{experiments:i,flags:s}}evaluateRules(e,t){if(!e.groups||e.groups.length===0)return!0;let i=a=>{let r=t[a.attribute];if(r==null)return!1;switch(a.operator){case"is":return String(r)===String(a.value);case"is_not":return String(r)!==String(a.value);case"contains":return String(r).toLowerCase().includes(String(a.value).toLowerCase());case"not_contains":return!String(r).toLowerCase().includes(String(a.value).toLowerCase());case"gt":return Number(r)>Number(a.value);case"lt":return Number(r)<Number(a.value);case"gte":return Number(r)>=Number(a.value);case"lte":return Number(r)<=Number(a.value);case"in":return Array.isArray(a.value)&&a.value.map(String).includes(String(r));case"not_in":return Array.isArray(a.value)&&!a.value.map(String).includes(String(r));default:return!1}},s=a=>a.conditions.every(i);return e.match==="any"?e.groups.some(s):e.groups.every(s)}async fetchConfig(){let e={};this.lastEtag&&(e["If-None-Match"]=this.lastEtag);let t=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}`,{method:"GET",headers:e});if(t.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!t.ok){let a=await t.text().catch(()=>"");throw new Error(`SplitLab API error ${t.status}: ${a}`)}let i=t.headers.get("etag");return{config:await t.json(),etag:i}}connectConfigStream(){if(typeof EventSource>"u")return;let e=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}`;this.eventSource=new EventSource(e),this.eventSource.addEventListener("config_updated",()=>{this.refresh().catch(()=>{})}),this.eventSource.onerror=()=>{}}flushSync(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];let t=JSON.stringify({events:e}),i=`${this.config.ingestUrl}/ingest/batch`;if(typeof globalThis.navigator?.sendBeacon=="function"){let s=new Blob([t],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(i,s))return}catch{}}if(typeof fetch=="function")try{fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:t,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i,s){let a=s?this.config.ingestUrl:this.config.baseUrl,r=await fetch(`${a}${t}`,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:i?JSON.stringify(i):void 0});if(!r.ok){let o=await r.text().catch(()=>"");throw new Error(`SplitLab API error ${r.status}: ${o}`)}return r.json()}};0&&(module.exports={SplitLabClient,hashToFloat,murmurhash3});
1
+ "use strict";var g=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var P=Object.getOwnPropertyNames;var U=Object.prototype.hasOwnProperty;var x=(n,e)=>{for(var t in e)g(n,t,{get:e[t],enumerable:!0})},k=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of P(e))!U.call(n,s)&&s!==t&&g(n,s,{get:()=>e[s],enumerable:!(i=E(e,s))||i.enumerable});return n};var O=n=>k(g({},"__esModule",{value:!0}),n);var $={};x($,{SplitLabClient:()=>m,hashToFloat:()=>h.hashToFloat,murmurhash3:()=>h.murmurhash3});module.exports=O($);var h=require("@splitlab/core"),v=require("@splitlab/core");var u=typeof globalThis<"u"&&typeof globalThis.document<"u",L=[[/Edg(?:e|A|iOS)?\/(\S+)/,"Edge"],[/OPR\/(\S+)/,"Opera"],[/SamsungBrowser\/(\S+)/,"Samsung Internet"],[/UCBrowser\/(\S+)/,"UC Browser"],[/Firefox\/(\S+)/,"Firefox"],[/CriOS\/(\S+)/,"Chrome"],[/FxiOS\/(\S+)/,"Firefox"],[/Chrome\/(\S+)/,"Chrome"],[/Safari\/(\S+)/,"Safari"]],D=[[/Windows NT (\d+\.\d+)/,"Windows"],[/Mac OS X ([\d_]+)/,"macOS"],[/Android ([\d.]+)/,"Android"],[/iPhone OS ([\d_]+)/,"iOS"],[/iPad.*OS ([\d_]+)/,"iPadOS"],[/CrOS/,"ChromeOS"],[/Linux/,"Linux"]];function z(n){if(!n)return{browser:null,browser_version:null,os:null,os_version:null,device_type:null};let e=null,t=null,i=null,s=null;if(/Safari/.test(n)&&!/Chrome|CriOS|Chromium|Edg|OPR|SamsungBrowser|UCBrowser/.test(n)){let a=n.match(/Version\/(\S+)/);e="Safari",t=a?a[1]:null}else for(let[a,f]of L){let c=n.match(a);if(c){e=f,t=c[1]||null;break}}for(let[a,f]of D){let c=n.match(a);if(c){i=f,s=c[1]?.replace(/_/g,".")||null;break}}let o="desktop";return/Mobi|Android.*Mobile|iPhone/.test(n)?o="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(n)&&(o="tablet"),{browser:e,browser_version:t,os:i,os_version:s,device_type:o}}function _(){if(!u)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,r=z(e?.userAgent);return{pathname:t?.pathname??null,hostname:t?.hostname??null,referrer:i?.referrer||null,search_params:t?.search||null,page_title:i?.title||null,hash:t?.hash||null,user_agent:e?.userAgent??null,browser:r.browser,browser_version:r.browser_version,os:r.os,os_version:r.os_version,device_type:r.device_type,screen_width:s?.width??null,screen_height:s?.height??null,viewport_width:n.innerWidth??null,viewport_height:n.innerHeight??null,language:e?.language??null,timezone:A()}}function A(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var F=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],b="__ot_utm";function I(){if(!u)return{};try{let i=globalThis.sessionStorage?.getItem(b);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of F){let s=n.get(i);s&&(e[i]=s,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(b,JSON.stringify(e))}catch{}return e}var y="__ot_sid",d="__ot_sts";function C(n){if(!u)return l();let e=globalThis.sessionStorage;if(!e)return l();try{let t=Date.now(),i=e.getItem(y),s=Number(e.getItem(d)||"0");if(i&&t-s<n)return e.setItem(d,String(t)),i;let r=l();return e.setItem(y,r),e.setItem(d,String(t)),r}catch{return l()}}var S="__ot_did";function R(){if(!u)return l();try{let n=globalThis.localStorage;if(!n)return l();let e=n.getItem(S);if(e)return e;let t=l();return n.setItem(S,t),t}catch{return l()}}var w="__ot_ref";function T(){if(!u)return null;try{let n=globalThis.sessionStorage,e=n?.getItem(w);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return n?.setItem(w,t),t||null}catch{return globalThis.document?.referrer||null}}function l(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var p=typeof globalThis<"u"&&typeof globalThis.document<"u",m=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.configRefreshTimer=null;this.initialized=!1;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.config={apiKey:e.apiKey,baseUrl:e.baseUrl.replace(/\/$/,""),ingestUrl:(e.ingestUrl||e.baseUrl).replace(/\/$/,""),distinctId:e.distinctId??null,attributes:e.attributes||{},autoFlushInterval:e.autoFlushInterval??3e4,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,configRefreshInterval:e.configRefreshInterval??3e4,realtimeUpdates:e.realtimeUpdates??!1,onConfigUpdate:e.onConfigUpdate??null,captureContext:e.captureContext??p,captureUtm:e.captureUtm??!0,trackSessions:e.trackSessions??!0,sessionTimeout:e.sessionTimeout??30*6e4,persistDeviceId:e.persistDeviceId??!0,trackPageviews:e.trackPageviews??!1,superProperties:e.superProperties??{}},this.config.captureUtm&&(this.utmParams=I()),this.config.captureContext&&(this.initialReferrer=T()),this.deviceId=R()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t}=await this.fetchConfig();this.serverConfig=e,this.lastEtag=t,this.evalResult=(0,v.localEvaluate)(this.serverConfig,this.bucketingId,this.config.attributes)}catch(e){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",e),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.enableLocalEvaluation&&(this.configRefreshTimer=setInterval(()=>{this.refresh().catch(()=>{})},this.config.configRefreshInterval)),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),p&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&p&&this.setupPageviewTracking(),this.initialized=!0}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.experiments[e]??null}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}async track(e,t){let i=this.enrichProperties(t);this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:i,timestamp:new Date().toISOString()}),this.eventQueue.length>=this.config.autoFlushSize&&await this.flush()}async trackPageview(e){await this.track("$pageview",e)}setSuperProperties(e){Object.assign(this.config.superProperties,e)}unsetSuperProperty(e){delete this.config.superProperties[e]}async flush(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];try{await this.request("POST","/ingest/batch",{events:e},!0)}catch{this.eventQueue=e.concat(this.eventQueue)}}async identify(e,t){let i=this.effectiveDistinctId;this.config.distinctId=e,i!==e&&await this.track("$identify",{...t,previous_distinct_id:i}),this.config.enableLocalEvaluation||(this.evalResult=null,this.initialized=!1,await this.initialize())}async refresh(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t,notModified:i}=await this.fetchConfig();if(i)return;this.serverConfig=e,this.lastEtag=t,this.evalResult=(0,v.localEvaluate)(this.serverConfig,this.bucketingId,this.config.attributes),this.config.onConfigUpdate&&this.config.onConfigUpdate()}catch{}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}}getDistinctId(){return this.effectiveDistinctId}getDeviceId(){return this.deviceId}setAttributes(e){this.config.attributes={...this.config.attributes,...e}}async destroy(){await this.flush(),this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),this.configRefreshTimer!==null&&(clearInterval(this.configRefreshTimer),this.configRefreshTimer=null),this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.beforeUnloadHandler&&typeof globalThis.removeEventListener=="function"&&(globalThis.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.pageviewCleanup&&(this.pageviewCleanup(),this.pageviewCleanup=null),this.initialized=!1}enrichProperties(e){let t={};if(this.config.captureContext){let i=_();if(i){for(let[s,r]of Object.entries(i))r!==null&&r!==""&&r!==void 0&&(t[s]=r);this.initialReferrer&&(t.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[i,s]of Object.entries(this.utmParams))s&&(t[i]=s);this.config.trackSessions&&(t.session_id=C(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(t.device_id=this.deviceId);for(let[i,s]of Object.entries(this.config.superProperties))s!==void 0&&(t[i]=s);if(e)for(let[i,s]of Object.entries(e))s!==void 0&&(t[i]=s);return t}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,s=t.pushState.bind(t);t.pushState=(...a)=>{s(...a),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...a)=>{r(...a),this.onUrlChange(i),i=e.location?.href};let o=()=>{this.onUrlChange(i),i=e.location?.href};e.addEventListener("popstate",o),this.trackPageview().catch(()=>{}),this.pageviewCleanup=()=>{t.pushState=s,t.replaceState=r,e.removeEventListener("popstate",o)}}onUrlChange(e){globalThis.location?.href!==e&&setTimeout(()=>{this.trackPageview({previous_url:e||null}).catch(()=>{})},0)}async fetchConfig(){let e={};this.lastEtag&&(e["If-None-Match"]=this.lastEtag);let t=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}`,{method:"GET",headers:e});if(t.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!t.ok){let r=await t.text().catch(()=>"");throw new Error(`SplitLab API error ${t.status}: ${r}`)}let i=t.headers.get("etag");return{config:await t.json(),etag:i}}connectConfigStream(){if(typeof EventSource>"u")return;let e=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}`;this.eventSource=new EventSource(e),this.eventSource.addEventListener("config_updated",()=>{this.refresh().catch(()=>{})}),this.eventSource.onerror=()=>{}}flushSync(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];let t=JSON.stringify({events:e}),i=`${this.config.ingestUrl}/ingest/batch`;if(typeof globalThis.navigator?.sendBeacon=="function"){let s=new Blob([t],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(i,s))return}catch{}}if(typeof fetch=="function")try{fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:t,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i,s){let r=s?this.config.ingestUrl:this.config.baseUrl,o=await fetch(`${r}${t}`,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:i?JSON.stringify(i):void 0});if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`SplitLab API error ${o.status}: ${a}`)}return o.json()}};0&&(module.exports={SplitLabClient,hashToFloat,murmurhash3});
package/dist/index.d.cts CHANGED
@@ -1,13 +1,4 @@
1
- /**
2
- * MurmurHash3_x86_32 — pure TypeScript implementation.
3
- * Identical to api/src/utils/hash.ts — the SDK and server
4
- * MUST produce the same output for local evaluation to work.
5
- */
6
- declare function murmurhash3(key: string, seed?: number): number;
7
- /**
8
- * Hash a key to a float in [0, 1).
9
- */
10
- declare function hashToFloat(key: string, seed?: number): number;
1
+ export { EvalResult, ExperimentConfig, FlagConfig, ServerConfig, TargetingCondition, TargetingGroup, TargetingRules, TrackEvent, Variant, hashToFloat, murmurhash3 } from '@splitlab/core';
11
2
 
12
3
  interface SplitLabConfig {
13
4
  apiKey: string;
@@ -41,48 +32,6 @@ interface SplitLabConfig {
41
32
  /** Properties attached to every tracked event. */
42
33
  superProperties?: Record<string, any>;
43
34
  }
44
- interface Variant {
45
- key: string;
46
- weight: number;
47
- }
48
- interface TargetingCondition {
49
- attribute: string;
50
- operator: 'is' | 'is_not' | 'contains' | 'not_contains' | 'gt' | 'lt' | 'gte' | 'lte' | 'in' | 'not_in';
51
- value: string | number | string[];
52
- }
53
- interface TargetingGroup {
54
- conditions: TargetingCondition[];
55
- }
56
- interface TargetingRules {
57
- match: 'all' | 'any';
58
- groups: TargetingGroup[];
59
- }
60
- interface ExperimentConfig {
61
- key: string;
62
- traffic_percentage: number;
63
- variants: Variant[];
64
- targeting_rules?: TargetingRules | null;
65
- }
66
- interface FlagConfig {
67
- key: string;
68
- enabled: boolean;
69
- rollout_percentage: number;
70
- rules?: TargetingRules | null;
71
- }
72
- interface ServerConfig {
73
- experiments: ExperimentConfig[];
74
- flags: FlagConfig[];
75
- }
76
- interface EvalResult {
77
- experiments: Record<string, string | null>;
78
- flags: Record<string, boolean>;
79
- }
80
- interface TrackEvent {
81
- distinct_id: string;
82
- event_name: string;
83
- properties?: Record<string, any>;
84
- timestamp?: string;
85
- }
86
35
 
87
36
  interface BrowserContext {
88
37
  pathname: string | null;
@@ -149,12 +98,10 @@ declare class SplitLabClient {
149
98
  private enrichProperties;
150
99
  private setupPageviewTracking;
151
100
  private onUrlChange;
152
- private localEvaluate;
153
- private evaluateRules;
154
101
  private fetchConfig;
155
102
  private connectConfigStream;
156
103
  private flushSync;
157
104
  private request;
158
105
  }
159
106
 
160
- export { type BrowserContext, type EvalResult, type ExperimentConfig, type FlagConfig, type ServerConfig, SplitLabClient, type SplitLabConfig, type TargetingCondition, type TargetingGroup, type TargetingRules, type TrackEvent, type UtmParams, type Variant, hashToFloat, murmurhash3 };
107
+ export { type BrowserContext, SplitLabClient, type SplitLabConfig, type UtmParams };
package/dist/index.d.ts CHANGED
@@ -1,13 +1,4 @@
1
- /**
2
- * MurmurHash3_x86_32 — pure TypeScript implementation.
3
- * Identical to api/src/utils/hash.ts — the SDK and server
4
- * MUST produce the same output for local evaluation to work.
5
- */
6
- declare function murmurhash3(key: string, seed?: number): number;
7
- /**
8
- * Hash a key to a float in [0, 1).
9
- */
10
- declare function hashToFloat(key: string, seed?: number): number;
1
+ export { EvalResult, ExperimentConfig, FlagConfig, ServerConfig, TargetingCondition, TargetingGroup, TargetingRules, TrackEvent, Variant, hashToFloat, murmurhash3 } from '@splitlab/core';
11
2
 
12
3
  interface SplitLabConfig {
13
4
  apiKey: string;
@@ -41,48 +32,6 @@ interface SplitLabConfig {
41
32
  /** Properties attached to every tracked event. */
42
33
  superProperties?: Record<string, any>;
43
34
  }
44
- interface Variant {
45
- key: string;
46
- weight: number;
47
- }
48
- interface TargetingCondition {
49
- attribute: string;
50
- operator: 'is' | 'is_not' | 'contains' | 'not_contains' | 'gt' | 'lt' | 'gte' | 'lte' | 'in' | 'not_in';
51
- value: string | number | string[];
52
- }
53
- interface TargetingGroup {
54
- conditions: TargetingCondition[];
55
- }
56
- interface TargetingRules {
57
- match: 'all' | 'any';
58
- groups: TargetingGroup[];
59
- }
60
- interface ExperimentConfig {
61
- key: string;
62
- traffic_percentage: number;
63
- variants: Variant[];
64
- targeting_rules?: TargetingRules | null;
65
- }
66
- interface FlagConfig {
67
- key: string;
68
- enabled: boolean;
69
- rollout_percentage: number;
70
- rules?: TargetingRules | null;
71
- }
72
- interface ServerConfig {
73
- experiments: ExperimentConfig[];
74
- flags: FlagConfig[];
75
- }
76
- interface EvalResult {
77
- experiments: Record<string, string | null>;
78
- flags: Record<string, boolean>;
79
- }
80
- interface TrackEvent {
81
- distinct_id: string;
82
- event_name: string;
83
- properties?: Record<string, any>;
84
- timestamp?: string;
85
- }
86
35
 
87
36
  interface BrowserContext {
88
37
  pathname: string | null;
@@ -149,12 +98,10 @@ declare class SplitLabClient {
149
98
  private enrichProperties;
150
99
  private setupPageviewTracking;
151
100
  private onUrlChange;
152
- private localEvaluate;
153
- private evaluateRules;
154
101
  private fetchConfig;
155
102
  private connectConfigStream;
156
103
  private flushSync;
157
104
  private request;
158
105
  }
159
106
 
160
- export { type BrowserContext, type EvalResult, type ExperimentConfig, type FlagConfig, type ServerConfig, SplitLabClient, type SplitLabConfig, type TargetingCondition, type TargetingGroup, type TargetingRules, type TrackEvent, type UtmParams, type Variant, hashToFloat, murmurhash3 };
107
+ export { type BrowserContext, SplitLabClient, type SplitLabConfig, type UtmParams };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- function g(s,e=0){let t=e>>>0,i=s.length,a=i>>2,r=3432918353,n=461845907;for(let u=0;u<a;u++){let c=s.charCodeAt(u*4)&255|(s.charCodeAt(u*4+1)&255)<<8|(s.charCodeAt(u*4+2)&255)<<16|(s.charCodeAt(u*4+3)&255)<<24;c=Math.imul(c,r),c=c<<15|c>>>17,c=Math.imul(c,n),t^=c,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196}let o=a*4,l=0;switch(i&3){case 3:l^=(s.charCodeAt(o+2)&255)<<16;case 2:l^=(s.charCodeAt(o+1)&255)<<8;case 1:l^=s.charCodeAt(o)&255,l=Math.imul(l,r),l=l<<15|l>>>17,l=Math.imul(l,n),t^=l}return t^=i,t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,t>>>0}function U(s,e){return g(s,e)/4294967295}var h=typeof globalThis<"u"&&typeof globalThis.document<"u",k=[[/Edg(?:e|A|iOS)?\/(\S+)/,"Edge"],[/OPR\/(\S+)/,"Opera"],[/SamsungBrowser\/(\S+)/,"Samsung Internet"],[/UCBrowser\/(\S+)/,"UC Browser"],[/Firefox\/(\S+)/,"Firefox"],[/CriOS\/(\S+)/,"Chrome"],[/FxiOS\/(\S+)/,"Firefox"],[/Chrome\/(\S+)/,"Chrome"],[/Safari\/(\S+)/,"Safari"]],O=[[/Windows NT (\d+\.\d+)/,"Windows"],[/Mac OS X ([\d_]+)/,"macOS"],[/Android ([\d.]+)/,"Android"],[/iPhone OS ([\d_]+)/,"iOS"],[/iPad.*OS ([\d_]+)/,"iPadOS"],[/CrOS/,"ChromeOS"],[/Linux/,"Linux"]];function A(s){if(!s)return{browser:null,browser_version:null,os:null,os_version:null,device_type:null};let e=null,t=null,i=null,a=null;if(/Safari/.test(s)&&!/Chrome|CriOS|Chromium|Edg|OPR|SamsungBrowser|UCBrowser/.test(s)){let o=s.match(/Version\/(\S+)/);e="Safari",t=o?o[1]:null}else for(let[o,l]of k){let u=s.match(o);if(u){e=l,t=u[1]||null;break}}for(let[o,l]of O){let u=s.match(o);if(u){i=l,a=u[1]?.replace(/_/g,".")||null;break}}let n="desktop";return/Mobi|Android.*Mobile|iPhone/.test(s)?n="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(s)&&(n="tablet"),{browser:e,browser_version:t,os:i,os_version:a,device_type:n}}function _(){if(!h)return null;let s=globalThis,e=s.navigator,t=s.location,i=s.document,a=s.screen,r=A(e?.userAgent);return{pathname:t?.pathname??null,hostname:t?.hostname??null,referrer:i?.referrer||null,search_params:t?.search||null,page_title:i?.title||null,hash:t?.hash||null,user_agent:e?.userAgent??null,browser:r.browser,browser_version:r.browser_version,os:r.os,os_version:r.os_version,device_type:r.device_type,screen_width:a?.width??null,screen_height:a?.height??null,viewport_width:s.innerWidth??null,viewport_height:s.innerHeight??null,language:e?.language??null,timezone:L()}}function L(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var D=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],y="__ot_utm";function R(){if(!h)return{};try{let i=globalThis.sessionStorage?.getItem(y);if(i)return JSON.parse(i)}catch{}let s=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of D){let a=s.get(i);a&&(e[i]=a,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(y,JSON.stringify(e))}catch{}return e}var S="__ot_sid",p="__ot_sts";function I(s){if(!h)return f();let e=globalThis.sessionStorage;if(!e)return f();try{let t=Date.now(),i=e.getItem(S),a=Number(e.getItem(p)||"0");if(i&&t-a<s)return e.setItem(p,String(t)),i;let r=f();return e.setItem(S,r),e.setItem(p,String(t)),r}catch{return f()}}var w="__ot_did";function T(){if(!h)return f();try{let s=globalThis.localStorage;if(!s)return f();let e=s.getItem(w);if(e)return e;let t=f();return s.setItem(w,t),t}catch{return f()}}var C="__ot_ref";function x(){if(!h)return null;try{let s=globalThis.sessionStorage,e=s?.getItem(C);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return s?.setItem(C,t),t||null}catch{return globalThis.document?.referrer||null}}function f(){let s=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${s}-${e}`}var m=typeof globalThis<"u"&&typeof globalThis.document<"u",E=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.configRefreshTimer=null;this.initialized=!1;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.config={apiKey:e.apiKey,baseUrl:e.baseUrl.replace(/\/$/,""),ingestUrl:(e.ingestUrl||e.baseUrl).replace(/\/$/,""),distinctId:e.distinctId??null,attributes:e.attributes||{},autoFlushInterval:e.autoFlushInterval??3e4,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,configRefreshInterval:e.configRefreshInterval??3e4,realtimeUpdates:e.realtimeUpdates??!1,onConfigUpdate:e.onConfigUpdate??null,captureContext:e.captureContext??m,captureUtm:e.captureUtm??!0,trackSessions:e.trackSessions??!0,sessionTimeout:e.sessionTimeout??30*6e4,persistDeviceId:e.persistDeviceId??!0,trackPageviews:e.trackPageviews??!1,superProperties:e.superProperties??{}},this.config.captureUtm&&(this.utmParams=R()),this.config.captureContext&&(this.initialReferrer=x()),this.deviceId=T()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t}=await this.fetchConfig();this.serverConfig=e,this.lastEtag=t,this.evalResult=this.localEvaluate(this.serverConfig,this.bucketingId)}catch(e){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",e),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.enableLocalEvaluation&&(this.configRefreshTimer=setInterval(()=>{this.refresh().catch(()=>{})},this.config.configRefreshInterval)),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),m&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&m&&this.setupPageviewTracking(),this.initialized=!0}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.experiments[e]??null}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}async track(e,t){let i=this.enrichProperties(t);this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:i,timestamp:new Date().toISOString()}),this.eventQueue.length>=this.config.autoFlushSize&&await this.flush()}async trackPageview(e){await this.track("$pageview",e)}setSuperProperties(e){Object.assign(this.config.superProperties,e)}unsetSuperProperty(e){delete this.config.superProperties[e]}async flush(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];try{await this.request("POST","/ingest/batch",{events:e},!0)}catch{this.eventQueue=e.concat(this.eventQueue)}}async identify(e,t){let i=this.effectiveDistinctId;this.config.distinctId=e,i!==e&&await this.track("$identify",{...t,previous_distinct_id:i}),this.config.enableLocalEvaluation||(this.evalResult=null,this.initialized=!1,await this.initialize())}async refresh(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t,notModified:i}=await this.fetchConfig();if(i)return;this.serverConfig=e,this.lastEtag=t,this.evalResult=this.localEvaluate(this.serverConfig,this.bucketingId),this.config.onConfigUpdate&&this.config.onConfigUpdate()}catch{}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}}getDistinctId(){return this.effectiveDistinctId}getDeviceId(){return this.deviceId}setAttributes(e){this.config.attributes={...this.config.attributes,...e}}async destroy(){await this.flush(),this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),this.configRefreshTimer!==null&&(clearInterval(this.configRefreshTimer),this.configRefreshTimer=null),this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.beforeUnloadHandler&&typeof globalThis.removeEventListener=="function"&&(globalThis.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.pageviewCleanup&&(this.pageviewCleanup(),this.pageviewCleanup=null),this.initialized=!1}enrichProperties(e){let t={};if(this.config.captureContext){let i=_();if(i){for(let[a,r]of Object.entries(i))r!==null&&r!==""&&r!==void 0&&(t[a]=r);this.initialReferrer&&(t.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[i,a]of Object.entries(this.utmParams))a&&(t[i]=a);this.config.trackSessions&&(t.session_id=I(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(t.device_id=this.deviceId);for(let[i,a]of Object.entries(this.config.superProperties))a!==void 0&&(t[i]=a);if(e)for(let[i,a]of Object.entries(e))a!==void 0&&(t[i]=a);return t}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,a=t.pushState.bind(t);t.pushState=(...o)=>{a(...o),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...o)=>{r(...o),this.onUrlChange(i),i=e.location?.href};let n=()=>{this.onUrlChange(i),i=e.location?.href};e.addEventListener("popstate",n),this.trackPageview().catch(()=>{}),this.pageviewCleanup=()=>{t.pushState=a,t.replaceState=r,e.removeEventListener("popstate",n)}}onUrlChange(e){globalThis.location?.href!==e&&setTimeout(()=>{this.trackPageview({previous_url:e||null}).catch(()=>{})},0)}localEvaluate(e,t){let i={},a={},r=this.config.attributes;for(let n of e.experiments){if(n.targeting_rules&&!this.evaluateRules(n.targeting_rules,r)){i[n.key]=null;continue}let o=g(n.key+":"+t);if(o%1e4/100>=n.traffic_percentage){i[n.key]=null;continue}let u=n.variants.reduce((v,P)=>v+P.weight,0),c=o%u,b=0,d=null;for(let v of n.variants)if(b+=v.weight,c<b){d=v.key;break}d||(d=n.variants[n.variants.length-1].key),i[n.key]=d}for(let n of e.flags){if(n.rules&&!this.evaluateRules(n.rules,r)){a[n.key]=!1;continue}let l=g(n.key+":"+t)%100;a[n.key]=l<n.rollout_percentage}return{experiments:i,flags:a}}evaluateRules(e,t){if(!e.groups||e.groups.length===0)return!0;let i=r=>{let n=t[r.attribute];if(n==null)return!1;switch(r.operator){case"is":return String(n)===String(r.value);case"is_not":return String(n)!==String(r.value);case"contains":return String(n).toLowerCase().includes(String(r.value).toLowerCase());case"not_contains":return!String(n).toLowerCase().includes(String(r.value).toLowerCase());case"gt":return Number(n)>Number(r.value);case"lt":return Number(n)<Number(r.value);case"gte":return Number(n)>=Number(r.value);case"lte":return Number(n)<=Number(r.value);case"in":return Array.isArray(r.value)&&r.value.map(String).includes(String(n));case"not_in":return Array.isArray(r.value)&&!r.value.map(String).includes(String(n));default:return!1}},a=r=>r.conditions.every(i);return e.match==="any"?e.groups.some(a):e.groups.every(a)}async fetchConfig(){let e={};this.lastEtag&&(e["If-None-Match"]=this.lastEtag);let t=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}`,{method:"GET",headers:e});if(t.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!t.ok){let r=await t.text().catch(()=>"");throw new Error(`SplitLab API error ${t.status}: ${r}`)}let i=t.headers.get("etag");return{config:await t.json(),etag:i}}connectConfigStream(){if(typeof EventSource>"u")return;let e=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}`;this.eventSource=new EventSource(e),this.eventSource.addEventListener("config_updated",()=>{this.refresh().catch(()=>{})}),this.eventSource.onerror=()=>{}}flushSync(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];let t=JSON.stringify({events:e}),i=`${this.config.ingestUrl}/ingest/batch`;if(typeof globalThis.navigator?.sendBeacon=="function"){let a=new Blob([t],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(i,a))return}catch{}}if(typeof fetch=="function")try{fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:t,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i,a){let r=a?this.config.ingestUrl:this.config.baseUrl,n=await fetch(`${r}${t}`,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:i?JSON.stringify(i):void 0});if(!n.ok){let o=await n.text().catch(()=>"");throw new Error(`SplitLab API error ${n.status}: ${o}`)}return n.json()}};export{E as SplitLabClient,U as hashToFloat,g as murmurhash3};
1
+ import{murmurhash3 as z,hashToFloat as A}from"@splitlab/core";import{localEvaluate as I}from"@splitlab/core";var u=typeof globalThis<"u"&&typeof globalThis.document<"u",R=[[/Edg(?:e|A|iOS)?\/(\S+)/,"Edge"],[/OPR\/(\S+)/,"Opera"],[/SamsungBrowser\/(\S+)/,"Samsung Internet"],[/UCBrowser\/(\S+)/,"UC Browser"],[/Firefox\/(\S+)/,"Firefox"],[/CriOS\/(\S+)/,"Chrome"],[/FxiOS\/(\S+)/,"Firefox"],[/Chrome\/(\S+)/,"Chrome"],[/Safari\/(\S+)/,"Safari"]],T=[[/Windows NT (\d+\.\d+)/,"Windows"],[/Mac OS X ([\d_]+)/,"macOS"],[/Android ([\d.]+)/,"Android"],[/iPhone OS ([\d_]+)/,"iOS"],[/iPad.*OS ([\d_]+)/,"iPadOS"],[/CrOS/,"ChromeOS"],[/Linux/,"Linux"]];function E(n){if(!n)return{browser:null,browser_version:null,os:null,os_version:null,device_type:null};let e=null,t=null,i=null,s=null;if(/Safari/.test(n)&&!/Chrome|CriOS|Chromium|Edg|OPR|SamsungBrowser|UCBrowser/.test(n)){let a=n.match(/Version\/(\S+)/);e="Safari",t=a?a[1]:null}else for(let[a,h]of R){let c=n.match(a);if(c){e=h,t=c[1]||null;break}}for(let[a,h]of T){let c=n.match(a);if(c){i=h,s=c[1]?.replace(/_/g,".")||null;break}}let o="desktop";return/Mobi|Android.*Mobile|iPhone/.test(n)?o="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(n)&&(o="tablet"),{browser:e,browser_version:t,os:i,os_version:s,device_type:o}}function b(){if(!u)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,r=E(e?.userAgent);return{pathname:t?.pathname??null,hostname:t?.hostname??null,referrer:i?.referrer||null,search_params:t?.search||null,page_title:i?.title||null,hash:t?.hash||null,user_agent:e?.userAgent??null,browser:r.browser,browser_version:r.browser_version,os:r.os,os_version:r.os_version,device_type:r.device_type,screen_width:s?.width??null,screen_height:s?.height??null,viewport_width:n.innerWidth??null,viewport_height:n.innerHeight??null,language:e?.language??null,timezone:P()}}function P(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var U=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],d="__ot_utm";function y(){if(!u)return{};try{let i=globalThis.sessionStorage?.getItem(d);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of U){let s=n.get(i);s&&(e[i]=s,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(d,JSON.stringify(e))}catch{}return e}var p="__ot_sid",f="__ot_sts";function S(n){if(!u)return l();let e=globalThis.sessionStorage;if(!e)return l();try{let t=Date.now(),i=e.getItem(p),s=Number(e.getItem(f)||"0");if(i&&t-s<n)return e.setItem(f,String(t)),i;let r=l();return e.setItem(p,r),e.setItem(f,String(t)),r}catch{return l()}}var v="__ot_did";function w(){if(!u)return l();try{let n=globalThis.localStorage;if(!n)return l();let e=n.getItem(v);if(e)return e;let t=l();return n.setItem(v,t),t}catch{return l()}}var m="__ot_ref";function _(){if(!u)return null;try{let n=globalThis.sessionStorage,e=n?.getItem(m);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return n?.setItem(m,t),t||null}catch{return globalThis.document?.referrer||null}}function l(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var g=typeof globalThis<"u"&&typeof globalThis.document<"u",C=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.configRefreshTimer=null;this.initialized=!1;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.config={apiKey:e.apiKey,baseUrl:e.baseUrl.replace(/\/$/,""),ingestUrl:(e.ingestUrl||e.baseUrl).replace(/\/$/,""),distinctId:e.distinctId??null,attributes:e.attributes||{},autoFlushInterval:e.autoFlushInterval??3e4,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,configRefreshInterval:e.configRefreshInterval??3e4,realtimeUpdates:e.realtimeUpdates??!1,onConfigUpdate:e.onConfigUpdate??null,captureContext:e.captureContext??g,captureUtm:e.captureUtm??!0,trackSessions:e.trackSessions??!0,sessionTimeout:e.sessionTimeout??30*6e4,persistDeviceId:e.persistDeviceId??!0,trackPageviews:e.trackPageviews??!1,superProperties:e.superProperties??{}},this.config.captureUtm&&(this.utmParams=y()),this.config.captureContext&&(this.initialReferrer=_()),this.deviceId=w()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t}=await this.fetchConfig();this.serverConfig=e,this.lastEtag=t,this.evalResult=I(this.serverConfig,this.bucketingId,this.config.attributes)}catch(e){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",e),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.enableLocalEvaluation&&(this.configRefreshTimer=setInterval(()=>{this.refresh().catch(()=>{})},this.config.configRefreshInterval)),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),g&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&g&&this.setupPageviewTracking(),this.initialized=!0}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.experiments[e]??null}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}async track(e,t){let i=this.enrichProperties(t);this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:i,timestamp:new Date().toISOString()}),this.eventQueue.length>=this.config.autoFlushSize&&await this.flush()}async trackPageview(e){await this.track("$pageview",e)}setSuperProperties(e){Object.assign(this.config.superProperties,e)}unsetSuperProperty(e){delete this.config.superProperties[e]}async flush(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];try{await this.request("POST","/ingest/batch",{events:e},!0)}catch{this.eventQueue=e.concat(this.eventQueue)}}async identify(e,t){let i=this.effectiveDistinctId;this.config.distinctId=e,i!==e&&await this.track("$identify",{...t,previous_distinct_id:i}),this.config.enableLocalEvaluation||(this.evalResult=null,this.initialized=!1,await this.initialize())}async refresh(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t,notModified:i}=await this.fetchConfig();if(i)return;this.serverConfig=e,this.lastEtag=t,this.evalResult=I(this.serverConfig,this.bucketingId,this.config.attributes),this.config.onConfigUpdate&&this.config.onConfigUpdate()}catch{}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}}getDistinctId(){return this.effectiveDistinctId}getDeviceId(){return this.deviceId}setAttributes(e){this.config.attributes={...this.config.attributes,...e}}async destroy(){await this.flush(),this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),this.configRefreshTimer!==null&&(clearInterval(this.configRefreshTimer),this.configRefreshTimer=null),this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.beforeUnloadHandler&&typeof globalThis.removeEventListener=="function"&&(globalThis.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.pageviewCleanup&&(this.pageviewCleanup(),this.pageviewCleanup=null),this.initialized=!1}enrichProperties(e){let t={};if(this.config.captureContext){let i=b();if(i){for(let[s,r]of Object.entries(i))r!==null&&r!==""&&r!==void 0&&(t[s]=r);this.initialReferrer&&(t.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[i,s]of Object.entries(this.utmParams))s&&(t[i]=s);this.config.trackSessions&&(t.session_id=S(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(t.device_id=this.deviceId);for(let[i,s]of Object.entries(this.config.superProperties))s!==void 0&&(t[i]=s);if(e)for(let[i,s]of Object.entries(e))s!==void 0&&(t[i]=s);return t}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,s=t.pushState.bind(t);t.pushState=(...a)=>{s(...a),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...a)=>{r(...a),this.onUrlChange(i),i=e.location?.href};let o=()=>{this.onUrlChange(i),i=e.location?.href};e.addEventListener("popstate",o),this.trackPageview().catch(()=>{}),this.pageviewCleanup=()=>{t.pushState=s,t.replaceState=r,e.removeEventListener("popstate",o)}}onUrlChange(e){globalThis.location?.href!==e&&setTimeout(()=>{this.trackPageview({previous_url:e||null}).catch(()=>{})},0)}async fetchConfig(){let e={};this.lastEtag&&(e["If-None-Match"]=this.lastEtag);let t=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}`,{method:"GET",headers:e});if(t.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!t.ok){let r=await t.text().catch(()=>"");throw new Error(`SplitLab API error ${t.status}: ${r}`)}let i=t.headers.get("etag");return{config:await t.json(),etag:i}}connectConfigStream(){if(typeof EventSource>"u")return;let e=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}`;this.eventSource=new EventSource(e),this.eventSource.addEventListener("config_updated",()=>{this.refresh().catch(()=>{})}),this.eventSource.onerror=()=>{}}flushSync(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];let t=JSON.stringify({events:e}),i=`${this.config.ingestUrl}/ingest/batch`;if(typeof globalThis.navigator?.sendBeacon=="function"){let s=new Blob([t],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(i,s))return}catch{}}if(typeof fetch=="function")try{fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:t,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i,s){let r=s?this.config.ingestUrl:this.config.baseUrl,o=await fetch(`${r}${t}`,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:i?JSON.stringify(i):void 0});if(!o.ok){let a=await o.text().catch(()=>"");throw new Error(`SplitLab API error ${o.status}: ${a}`)}return o.json()}};export{C as SplitLabClient,A as hashToFloat,z as murmurhash3};
@@ -1 +1 @@
1
- "use strict";var SplitLab=(()=>{var p=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var D=(n,e)=>{for(var t in e)p(n,t,{get:e[t],enumerable:!0})},z=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of A(e))!L.call(n,s)&&s!==t&&p(n,s,{get:()=>e[s],enumerable:!(i=O(e,s))||i.enumerable});return n};var M=n=>z(p({},"__esModule",{value:!0}),n);var j={};D(j,{SplitLabClient:()=>y,hashToFloat:()=>w,murmurhash3:()=>g});function g(n,e=0){let t=e>>>0,i=n.length,s=i>>2,a=3432918353,r=461845907;for(let u=0;u<s;u++){let c=n.charCodeAt(u*4)&255|(n.charCodeAt(u*4+1)&255)<<8|(n.charCodeAt(u*4+2)&255)<<16|(n.charCodeAt(u*4+3)&255)<<24;c=Math.imul(c,a),c=c<<15|c>>>17,c=Math.imul(c,r),t^=c,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196}let o=s*4,l=0;switch(i&3){case 3:l^=(n.charCodeAt(o+2)&255)<<16;case 2:l^=(n.charCodeAt(o+1)&255)<<8;case 1:l^=n.charCodeAt(o)&255,l=Math.imul(l,a),l=l<<15|l>>>17,l=Math.imul(l,r),t^=l}return t^=i,t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,t>>>0}function w(n,e){return g(n,e)/4294967295}var h=typeof globalThis<"u"&&typeof globalThis.document<"u",B=[[/Edg(?:e|A|iOS)?\/(\S+)/,"Edge"],[/OPR\/(\S+)/,"Opera"],[/SamsungBrowser\/(\S+)/,"Samsung Internet"],[/UCBrowser\/(\S+)/,"UC Browser"],[/Firefox\/(\S+)/,"Firefox"],[/CriOS\/(\S+)/,"Chrome"],[/FxiOS\/(\S+)/,"Firefox"],[/Chrome\/(\S+)/,"Chrome"],[/Safari\/(\S+)/,"Safari"]],F=[[/Windows NT (\d+\.\d+)/,"Windows"],[/Mac OS X ([\d_]+)/,"macOS"],[/Android ([\d.]+)/,"Android"],[/iPhone OS ([\d_]+)/,"iOS"],[/iPad.*OS ([\d_]+)/,"iPadOS"],[/CrOS/,"ChromeOS"],[/Linux/,"Linux"]];function N(n){if(!n)return{browser:null,browser_version:null,os:null,os_version:null,device_type:null};let e=null,t=null,i=null,s=null;if(/Safari/.test(n)&&!/Chrome|CriOS|Chromium|Edg|OPR|SamsungBrowser|UCBrowser/.test(n)){let o=n.match(/Version\/(\S+)/);e="Safari",t=o?o[1]:null}else for(let[o,l]of B){let u=n.match(o);if(u){e=l,t=u[1]||null;break}}for(let[o,l]of F){let u=n.match(o);if(u){i=l,s=u[1]?.replace(/_/g,".")||null;break}}let r="desktop";return/Mobi|Android.*Mobile|iPhone/.test(n)?r="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(n)&&(r="tablet"),{browser:e,browser_version:t,os:i,os_version:s,device_type:r}}function T(){if(!h)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,a=N(e?.userAgent);return{pathname:t?.pathname??null,hostname:t?.hostname??null,referrer:i?.referrer||null,search_params:t?.search||null,page_title:i?.title||null,hash:t?.hash||null,user_agent:e?.userAgent??null,browser:a.browser,browser_version:a.browser_version,os:a.os,os_version:a.os_version,device_type:a.device_type,screen_width:s?.width??null,screen_height:s?.height??null,viewport_width:n.innerWidth??null,viewport_height:n.innerHeight??null,language:e?.language??null,timezone:$()}}function $(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var K=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],C="__ot_utm";function x(){if(!h)return{};try{let i=globalThis.sessionStorage?.getItem(C);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of K){let s=n.get(i);s&&(e[i]=s,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(C,JSON.stringify(e))}catch{}return e}var _="__ot_sid",m="__ot_sts";function E(n){if(!h)return f();let e=globalThis.sessionStorage;if(!e)return f();try{let t=Date.now(),i=e.getItem(_),s=Number(e.getItem(m)||"0");if(i&&t-s<n)return e.setItem(m,String(t)),i;let a=f();return e.setItem(_,a),e.setItem(m,String(t)),a}catch{return f()}}var R="__ot_did";function P(){if(!h)return f();try{let n=globalThis.localStorage;if(!n)return f();let e=n.getItem(R);if(e)return e;let t=f();return n.setItem(R,t),t}catch{return f()}}var I="__ot_ref";function U(){if(!h)return null;try{let n=globalThis.sessionStorage,e=n?.getItem(I);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return n?.setItem(I,t),t||null}catch{return globalThis.document?.referrer||null}}function f(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var b=typeof globalThis<"u"&&typeof globalThis.document<"u",y=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.configRefreshTimer=null;this.initialized=!1;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.config={apiKey:e.apiKey,baseUrl:e.baseUrl.replace(/\/$/,""),ingestUrl:(e.ingestUrl||e.baseUrl).replace(/\/$/,""),distinctId:e.distinctId??null,attributes:e.attributes||{},autoFlushInterval:e.autoFlushInterval??3e4,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,configRefreshInterval:e.configRefreshInterval??3e4,realtimeUpdates:e.realtimeUpdates??!1,onConfigUpdate:e.onConfigUpdate??null,captureContext:e.captureContext??b,captureUtm:e.captureUtm??!0,trackSessions:e.trackSessions??!0,sessionTimeout:e.sessionTimeout??30*6e4,persistDeviceId:e.persistDeviceId??!0,trackPageviews:e.trackPageviews??!1,superProperties:e.superProperties??{}},this.config.captureUtm&&(this.utmParams=x()),this.config.captureContext&&(this.initialReferrer=U()),this.deviceId=P()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t}=await this.fetchConfig();this.serverConfig=e,this.lastEtag=t,this.evalResult=this.localEvaluate(this.serverConfig,this.bucketingId)}catch(e){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",e),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.enableLocalEvaluation&&(this.configRefreshTimer=setInterval(()=>{this.refresh().catch(()=>{})},this.config.configRefreshInterval)),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),b&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&b&&this.setupPageviewTracking(),this.initialized=!0}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.experiments[e]??null}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}async track(e,t){let i=this.enrichProperties(t);this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:i,timestamp:new Date().toISOString()}),this.eventQueue.length>=this.config.autoFlushSize&&await this.flush()}async trackPageview(e){await this.track("$pageview",e)}setSuperProperties(e){Object.assign(this.config.superProperties,e)}unsetSuperProperty(e){delete this.config.superProperties[e]}async flush(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];try{await this.request("POST","/ingest/batch",{events:e},!0)}catch{this.eventQueue=e.concat(this.eventQueue)}}async identify(e,t){let i=this.effectiveDistinctId;this.config.distinctId=e,i!==e&&await this.track("$identify",{...t,previous_distinct_id:i}),this.config.enableLocalEvaluation||(this.evalResult=null,this.initialized=!1,await this.initialize())}async refresh(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t,notModified:i}=await this.fetchConfig();if(i)return;this.serverConfig=e,this.lastEtag=t,this.evalResult=this.localEvaluate(this.serverConfig,this.bucketingId),this.config.onConfigUpdate&&this.config.onConfigUpdate()}catch{}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}}getDistinctId(){return this.effectiveDistinctId}getDeviceId(){return this.deviceId}setAttributes(e){this.config.attributes={...this.config.attributes,...e}}async destroy(){await this.flush(),this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),this.configRefreshTimer!==null&&(clearInterval(this.configRefreshTimer),this.configRefreshTimer=null),this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.beforeUnloadHandler&&typeof globalThis.removeEventListener=="function"&&(globalThis.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.pageviewCleanup&&(this.pageviewCleanup(),this.pageviewCleanup=null),this.initialized=!1}enrichProperties(e){let t={};if(this.config.captureContext){let i=T();if(i){for(let[s,a]of Object.entries(i))a!==null&&a!==""&&a!==void 0&&(t[s]=a);this.initialReferrer&&(t.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[i,s]of Object.entries(this.utmParams))s&&(t[i]=s);this.config.trackSessions&&(t.session_id=E(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(t.device_id=this.deviceId);for(let[i,s]of Object.entries(this.config.superProperties))s!==void 0&&(t[i]=s);if(e)for(let[i,s]of Object.entries(e))s!==void 0&&(t[i]=s);return t}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,s=t.pushState.bind(t);t.pushState=(...o)=>{s(...o),this.onUrlChange(i),i=e.location?.href};let a=t.replaceState.bind(t);t.replaceState=(...o)=>{a(...o),this.onUrlChange(i),i=e.location?.href};let r=()=>{this.onUrlChange(i),i=e.location?.href};e.addEventListener("popstate",r),this.trackPageview().catch(()=>{}),this.pageviewCleanup=()=>{t.pushState=s,t.replaceState=a,e.removeEventListener("popstate",r)}}onUrlChange(e){globalThis.location?.href!==e&&setTimeout(()=>{this.trackPageview({previous_url:e||null}).catch(()=>{})},0)}localEvaluate(e,t){let i={},s={},a=this.config.attributes;for(let r of e.experiments){if(r.targeting_rules&&!this.evaluateRules(r.targeting_rules,a)){i[r.key]=null;continue}let o=g(r.key+":"+t);if(o%1e4/100>=r.traffic_percentage){i[r.key]=null;continue}let u=r.variants.reduce((v,k)=>v+k.weight,0),c=o%u,S=0,d=null;for(let v of r.variants)if(S+=v.weight,c<S){d=v.key;break}d||(d=r.variants[r.variants.length-1].key),i[r.key]=d}for(let r of e.flags){if(r.rules&&!this.evaluateRules(r.rules,a)){s[r.key]=!1;continue}let l=g(r.key+":"+t)%100;s[r.key]=l<r.rollout_percentage}return{experiments:i,flags:s}}evaluateRules(e,t){if(!e.groups||e.groups.length===0)return!0;let i=a=>{let r=t[a.attribute];if(r==null)return!1;switch(a.operator){case"is":return String(r)===String(a.value);case"is_not":return String(r)!==String(a.value);case"contains":return String(r).toLowerCase().includes(String(a.value).toLowerCase());case"not_contains":return!String(r).toLowerCase().includes(String(a.value).toLowerCase());case"gt":return Number(r)>Number(a.value);case"lt":return Number(r)<Number(a.value);case"gte":return Number(r)>=Number(a.value);case"lte":return Number(r)<=Number(a.value);case"in":return Array.isArray(a.value)&&a.value.map(String).includes(String(r));case"not_in":return Array.isArray(a.value)&&!a.value.map(String).includes(String(r));default:return!1}},s=a=>a.conditions.every(i);return e.match==="any"?e.groups.some(s):e.groups.every(s)}async fetchConfig(){let e={};this.lastEtag&&(e["If-None-Match"]=this.lastEtag);let t=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}`,{method:"GET",headers:e});if(t.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!t.ok){let a=await t.text().catch(()=>"");throw new Error(`SplitLab API error ${t.status}: ${a}`)}let i=t.headers.get("etag");return{config:await t.json(),etag:i}}connectConfigStream(){if(typeof EventSource>"u")return;let e=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}`;this.eventSource=new EventSource(e),this.eventSource.addEventListener("config_updated",()=>{this.refresh().catch(()=>{})}),this.eventSource.onerror=()=>{}}flushSync(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];let t=JSON.stringify({events:e}),i=`${this.config.ingestUrl}/ingest/batch`;if(typeof globalThis.navigator?.sendBeacon=="function"){let s=new Blob([t],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(i,s))return}catch{}}if(typeof fetch=="function")try{fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:t,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i,s){let a=s?this.config.ingestUrl:this.config.baseUrl,r=await fetch(`${a}${t}`,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:i?JSON.stringify(i):void 0});if(!r.ok){let o=await r.text().catch(()=>"");throw new Error(`SplitLab API error ${r.status}: ${o}`)}return r.json()}};return M(j);})();
1
+ "use strict";var SplitLab=(()=>{var p=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var L=Object.prototype.hasOwnProperty;var D=(r,e)=>{for(var t in e)p(r,t,{get:e[t],enumerable:!0})},z=(r,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of A(e))!L.call(r,n)&&n!==t&&p(r,n,{get:()=>e[n],enumerable:!(i=O(e,n))||i.enumerable});return r};var M=r=>z(p({},"__esModule",{value:!0}),r);var B={};D(B,{SplitLabClient:()=>y,hashToFloat:()=>w,murmurhash3:()=>g});function g(r,e=0){let t=e>>>0,i=r.length,n=i>>2,s=3432918353,a=461845907;for(let u=0;u<n;u++){let c=r.charCodeAt(u*4)&255|(r.charCodeAt(u*4+1)&255)<<8|(r.charCodeAt(u*4+2)&255)<<16|(r.charCodeAt(u*4+3)&255)<<24;c=Math.imul(c,s),c=c<<15|c>>>17,c=Math.imul(c,a),t^=c,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196}let o=n*4,l=0;switch(i&3){case 3:l^=(r.charCodeAt(o+2)&255)<<16;case 2:l^=(r.charCodeAt(o+1)&255)<<8;case 1:l^=r.charCodeAt(o)&255,l=Math.imul(l,s),l=l<<15|l>>>17,l=Math.imul(l,a),t^=l}return t^=i,t^=t>>>16,t=Math.imul(t,2246822507),t^=t>>>13,t=Math.imul(t,3266489909),t^=t>>>16,t>>>0}function w(r,e){return g(r,e)/4294967295}function v(r,e,t={}){let i={},n={};for(let s of r.experiments){if(s.targeting_rules&&!S(s.targeting_rules,t)){i[s.key]=null;continue}let a=g(s.key+":"+e);if(a%1e4/100>=s.traffic_percentage){i[s.key]=null;continue}let o=s.variants.reduce((d,x)=>d+x.weight,0),l=a%o,u=0,c=null;for(let d of s.variants)if(u+=d.weight,l<u){c=d.key;break}c||(c=s.variants[s.variants.length-1].key),i[s.key]=c}for(let s of r.flags){if(s.rules&&!S(s.rules,t)){n[s.key]=!1;continue}let a=g(s.key+":"+e)%100;n[s.key]=a<s.rollout_percentage}return{experiments:i,flags:n}}function S(r,e){if(!r.groups||r.groups.length===0)return!0;let t=n=>{let s=e[n.attribute];if(s==null)return!1;switch(n.operator){case"is":return String(s)===String(n.value);case"is_not":return String(s)!==String(n.value);case"contains":return String(s).toLowerCase().includes(String(n.value).toLowerCase());case"not_contains":return!String(s).toLowerCase().includes(String(n.value).toLowerCase());case"gt":return Number(s)>Number(n.value);case"lt":return Number(s)<Number(n.value);case"gte":return Number(s)>=Number(n.value);case"lte":return Number(s)<=Number(n.value);case"in":return Array.isArray(n.value)&&n.value.map(String).includes(String(s));case"not_in":return Array.isArray(n.value)&&!n.value.map(String).includes(String(s));default:return!1}},i=n=>n.conditions.every(t);return r.match==="any"?r.groups.some(i):r.groups.every(i)}var f=typeof globalThis<"u"&&typeof globalThis.document<"u",F=[[/Edg(?:e|A|iOS)?\/(\S+)/,"Edge"],[/OPR\/(\S+)/,"Opera"],[/SamsungBrowser\/(\S+)/,"Samsung Internet"],[/UCBrowser\/(\S+)/,"UC Browser"],[/Firefox\/(\S+)/,"Firefox"],[/CriOS\/(\S+)/,"Chrome"],[/FxiOS\/(\S+)/,"Firefox"],[/Chrome\/(\S+)/,"Chrome"],[/Safari\/(\S+)/,"Safari"]],N=[[/Windows NT (\d+\.\d+)/,"Windows"],[/Mac OS X ([\d_]+)/,"macOS"],[/Android ([\d.]+)/,"Android"],[/iPhone OS ([\d_]+)/,"iOS"],[/iPad.*OS ([\d_]+)/,"iPadOS"],[/CrOS/,"ChromeOS"],[/Linux/,"Linux"]];function $(r){if(!r)return{browser:null,browser_version:null,os:null,os_version:null,device_type:null};let e=null,t=null,i=null,n=null;if(/Safari/.test(r)&&!/Chrome|CriOS|Chromium|Edg|OPR|SamsungBrowser|UCBrowser/.test(r)){let o=r.match(/Version\/(\S+)/);e="Safari",t=o?o[1]:null}else for(let[o,l]of F){let u=r.match(o);if(u){e=l,t=u[1]||null;break}}for(let[o,l]of N){let u=r.match(o);if(u){i=l,n=u[1]?.replace(/_/g,".")||null;break}}let a="desktop";return/Mobi|Android.*Mobile|iPhone/.test(r)?a="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(r)&&(a="tablet"),{browser:e,browser_version:t,os:i,os_version:n,device_type:a}}function T(){if(!f)return null;let r=globalThis,e=r.navigator,t=r.location,i=r.document,n=r.screen,s=$(e?.userAgent);return{pathname:t?.pathname??null,hostname:t?.hostname??null,referrer:i?.referrer||null,search_params:t?.search||null,page_title:i?.title||null,hash:t?.hash||null,user_agent:e?.userAgent??null,browser:s.browser,browser_version:s.browser_version,os:s.os,os_version:s.os_version,device_type:s.device_type,screen_width:n?.width??null,screen_height:n?.height??null,viewport_width:r.innerWidth??null,viewport_height:r.innerHeight??null,language:e?.language??null,timezone:K()}}function K(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var j=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],_="__ot_utm";function E(){if(!f)return{};try{let i=globalThis.sessionStorage?.getItem(_);if(i)return JSON.parse(i)}catch{}let r=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of j){let n=r.get(i);n&&(e[i]=n,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(_,JSON.stringify(e))}catch{}return e}var C="__ot_sid",m="__ot_sts";function P(r){if(!f)return h();let e=globalThis.sessionStorage;if(!e)return h();try{let t=Date.now(),i=e.getItem(C),n=Number(e.getItem(m)||"0");if(i&&t-n<r)return e.setItem(m,String(t)),i;let s=h();return e.setItem(C,s),e.setItem(m,String(t)),s}catch{return h()}}var I="__ot_did";function U(){if(!f)return h();try{let r=globalThis.localStorage;if(!r)return h();let e=r.getItem(I);if(e)return e;let t=h();return r.setItem(I,t),t}catch{return h()}}var R="__ot_ref";function k(){if(!f)return null;try{let r=globalThis.sessionStorage,e=r?.getItem(R);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return r?.setItem(R,t),t||null}catch{return globalThis.document?.referrer||null}}function h(){let r=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${r}-${e}`}var b=typeof globalThis<"u"&&typeof globalThis.document<"u",y=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.configRefreshTimer=null;this.initialized=!1;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.config={apiKey:e.apiKey,baseUrl:e.baseUrl.replace(/\/$/,""),ingestUrl:(e.ingestUrl||e.baseUrl).replace(/\/$/,""),distinctId:e.distinctId??null,attributes:e.attributes||{},autoFlushInterval:e.autoFlushInterval??3e4,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,configRefreshInterval:e.configRefreshInterval??3e4,realtimeUpdates:e.realtimeUpdates??!1,onConfigUpdate:e.onConfigUpdate??null,captureContext:e.captureContext??b,captureUtm:e.captureUtm??!0,trackSessions:e.trackSessions??!0,sessionTimeout:e.sessionTimeout??30*6e4,persistDeviceId:e.persistDeviceId??!0,trackPageviews:e.trackPageviews??!1,superProperties:e.superProperties??{}},this.config.captureUtm&&(this.utmParams=E()),this.config.captureContext&&(this.initialReferrer=k()),this.deviceId=U()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t}=await this.fetchConfig();this.serverConfig=e,this.lastEtag=t,this.evalResult=v(this.serverConfig,this.bucketingId,this.config.attributes)}catch(e){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",e),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.enableLocalEvaluation&&(this.configRefreshTimer=setInterval(()=>{this.refresh().catch(()=>{})},this.config.configRefreshInterval)),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),b&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&b&&this.setupPageviewTracking(),this.initialized=!0}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.experiments[e]??null}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}async track(e,t){let i=this.enrichProperties(t);this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:i,timestamp:new Date().toISOString()}),this.eventQueue.length>=this.config.autoFlushSize&&await this.flush()}async trackPageview(e){await this.track("$pageview",e)}setSuperProperties(e){Object.assign(this.config.superProperties,e)}unsetSuperProperty(e){delete this.config.superProperties[e]}async flush(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];try{await this.request("POST","/ingest/batch",{events:e},!0)}catch{this.eventQueue=e.concat(this.eventQueue)}}async identify(e,t){let i=this.effectiveDistinctId;this.config.distinctId=e,i!==e&&await this.track("$identify",{...t,previous_distinct_id:i}),this.config.enableLocalEvaluation||(this.evalResult=null,this.initialized=!1,await this.initialize())}async refresh(){if(this.config.enableLocalEvaluation)try{let{config:e,etag:t,notModified:i}=await this.fetchConfig();if(i)return;this.serverConfig=e,this.lastEtag=t,this.evalResult=v(this.serverConfig,this.bucketingId,this.config.attributes),this.config.onConfigUpdate&&this.config.onConfigUpdate()}catch{}else{let e={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(e.attributes=this.config.attributes);let t=await this.request("POST","/api/sdk/evaluate",e);this.evalResult={experiments:t.experiments,flags:t.flags}}}getDistinctId(){return this.effectiveDistinctId}getDeviceId(){return this.deviceId}setAttributes(e){this.config.attributes={...this.config.attributes,...e}}async destroy(){await this.flush(),this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),this.configRefreshTimer!==null&&(clearInterval(this.configRefreshTimer),this.configRefreshTimer=null),this.eventSource&&(this.eventSource.close(),this.eventSource=null),this.beforeUnloadHandler&&typeof globalThis.removeEventListener=="function"&&(globalThis.removeEventListener("beforeunload",this.beforeUnloadHandler),this.beforeUnloadHandler=null),this.pageviewCleanup&&(this.pageviewCleanup(),this.pageviewCleanup=null),this.initialized=!1}enrichProperties(e){let t={};if(this.config.captureContext){let i=T();if(i){for(let[n,s]of Object.entries(i))s!==null&&s!==""&&s!==void 0&&(t[n]=s);this.initialReferrer&&(t.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[i,n]of Object.entries(this.utmParams))n&&(t[i]=n);this.config.trackSessions&&(t.session_id=P(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(t.device_id=this.deviceId);for(let[i,n]of Object.entries(this.config.superProperties))n!==void 0&&(t[i]=n);if(e)for(let[i,n]of Object.entries(e))n!==void 0&&(t[i]=n);return t}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,n=t.pushState.bind(t);t.pushState=(...o)=>{n(...o),this.onUrlChange(i),i=e.location?.href};let s=t.replaceState.bind(t);t.replaceState=(...o)=>{s(...o),this.onUrlChange(i),i=e.location?.href};let a=()=>{this.onUrlChange(i),i=e.location?.href};e.addEventListener("popstate",a),this.trackPageview().catch(()=>{}),this.pageviewCleanup=()=>{t.pushState=n,t.replaceState=s,e.removeEventListener("popstate",a)}}onUrlChange(e){globalThis.location?.href!==e&&setTimeout(()=>{this.trackPageview({previous_url:e||null}).catch(()=>{})},0)}async fetchConfig(){let e={};this.lastEtag&&(e["If-None-Match"]=this.lastEtag);let t=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}`,{method:"GET",headers:e});if(t.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!t.ok){let s=await t.text().catch(()=>"");throw new Error(`SplitLab API error ${t.status}: ${s}`)}let i=t.headers.get("etag");return{config:await t.json(),etag:i}}connectConfigStream(){if(typeof EventSource>"u")return;let e=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}`;this.eventSource=new EventSource(e),this.eventSource.addEventListener("config_updated",()=>{this.refresh().catch(()=>{})}),this.eventSource.onerror=()=>{}}flushSync(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];let t=JSON.stringify({events:e}),i=`${this.config.ingestUrl}/ingest/batch`;if(typeof globalThis.navigator?.sendBeacon=="function"){let n=new Blob([t],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(i,n))return}catch{}}if(typeof fetch=="function")try{fetch(i,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:t,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i,n){let s=n?this.config.ingestUrl:this.config.baseUrl,a=await fetch(`${s}${t}`,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:i?JSON.stringify(i):void 0});if(!a.ok){let o=await a.text().catch(()=>"");throw new Error(`SplitLab API error ${a.status}: ${o}`)}return a.json()}};return M(B);})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitlab/js-client",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "description": "Lightweight JavaScript client SDK for SplitLab A/B testing and analytics",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -29,6 +29,9 @@
29
29
  "build": "tsup",
30
30
  "dev": "tsup --watch"
31
31
  },
32
+ "dependencies": {
33
+ "@splitlab/core": "^0.1.0"
34
+ },
32
35
  "devDependencies": {
33
36
  "tsup": "^8.0.0",
34
37
  "typescript": "^5.4.0"