@splitlab/js-client 0.1.3 → 0.3.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 +1 -1
- package/dist/index.d.cts +4 -55
- package/dist/index.d.ts +4 -55
- package/dist/index.js +1 -1
- package/dist/splitlab.min.js +1 -1
- package/package.json +4 -1
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 F={};x(F,{SplitLabClient:()=>m,hashToFloat:()=>h.hashToFloat,murmurhash3:()=>h.murmurhash3});module.exports=O(F);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"]],$=[[/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 D(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 $){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=D(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:z()}}function z(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var A=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],b="__ot_utm";function C(){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 A){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 I(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??{},environment:e.environment??"production"},this.config.captureUtm&&(this.utmParams=C()),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=I(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=this.config.environment!=="production"?`&env=${encodeURIComponent(this.config.environment)}`:"",i=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}${t}`,{method:"GET",headers:e});if(i.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!i.ok){let o=await i.text().catch(()=>"");throw new Error(`SplitLab API error ${i.status}: ${o}`)}let s=i.headers.get("etag");return{config:await i.json(),etag:s}}connectConfigStream(){if(typeof EventSource>"u")return;let e=this.config.environment!=="production"?`&env=${encodeURIComponent(this.config.environment)}`:"",t=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}${e}`;this.eventSource=new EventSource(t),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, ExclusionGroupConfig, ExperimentConfig, FlagConfig, SegmentConfig, ServerConfig, TargetingCondition, TargetingGroup, TargetingRules, TrackEvent, Variant, hashToFloat, murmurhash3 } from '@splitlab/core';
|
|
11
2
|
|
|
12
3
|
interface SplitLabConfig {
|
|
13
4
|
apiKey: string;
|
|
@@ -40,48 +31,8 @@ interface SplitLabConfig {
|
|
|
40
31
|
trackPageviews?: boolean;
|
|
41
32
|
/** Properties attached to every tracked event. */
|
|
42
33
|
superProperties?: Record<string, any>;
|
|
43
|
-
|
|
44
|
-
|
|
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;
|
|
34
|
+
/** Environment to fetch config for (e.g. 'development', 'staging', 'production'). Default: 'production'. */
|
|
35
|
+
environment?: string;
|
|
85
36
|
}
|
|
86
37
|
|
|
87
38
|
interface BrowserContext {
|
|
@@ -149,12 +100,10 @@ declare class SplitLabClient {
|
|
|
149
100
|
private enrichProperties;
|
|
150
101
|
private setupPageviewTracking;
|
|
151
102
|
private onUrlChange;
|
|
152
|
-
private localEvaluate;
|
|
153
|
-
private evaluateRules;
|
|
154
103
|
private fetchConfig;
|
|
155
104
|
private connectConfigStream;
|
|
156
105
|
private flushSync;
|
|
157
106
|
private request;
|
|
158
107
|
}
|
|
159
108
|
|
|
160
|
-
export { type BrowserContext,
|
|
109
|
+
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, ExclusionGroupConfig, ExperimentConfig, FlagConfig, SegmentConfig, ServerConfig, TargetingCondition, TargetingGroup, TargetingRules, TrackEvent, Variant, hashToFloat, murmurhash3 } from '@splitlab/core';
|
|
11
2
|
|
|
12
3
|
interface SplitLabConfig {
|
|
13
4
|
apiKey: string;
|
|
@@ -40,48 +31,8 @@ interface SplitLabConfig {
|
|
|
40
31
|
trackPageviews?: boolean;
|
|
41
32
|
/** Properties attached to every tracked event. */
|
|
42
33
|
superProperties?: Record<string, any>;
|
|
43
|
-
|
|
44
|
-
|
|
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;
|
|
34
|
+
/** Environment to fetch config for (e.g. 'development', 'staging', 'production'). Default: 'production'. */
|
|
35
|
+
environment?: string;
|
|
85
36
|
}
|
|
86
37
|
|
|
87
38
|
interface BrowserContext {
|
|
@@ -149,12 +100,10 @@ declare class SplitLabClient {
|
|
|
149
100
|
private enrichProperties;
|
|
150
101
|
private setupPageviewTracking;
|
|
151
102
|
private onUrlChange;
|
|
152
|
-
private localEvaluate;
|
|
153
|
-
private evaluateRules;
|
|
154
103
|
private fetchConfig;
|
|
155
104
|
private connectConfigStream;
|
|
156
105
|
private flushSync;
|
|
157
106
|
private request;
|
|
158
107
|
}
|
|
159
108
|
|
|
160
|
-
export { type BrowserContext,
|
|
109
|
+
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 D,hashToFloat as z}from"@splitlab/core";import{localEvaluate as C}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(s){if(!s)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(s)&&!/Chrome|CriOS|Chromium|Edg|OPR|SamsungBrowser|UCBrowser/.test(s)){let a=s.match(/Version\/(\S+)/);e="Safari",t=a?a[1]:null}else for(let[a,h]of R){let c=s.match(a);if(c){e=h,t=c[1]||null;break}}for(let[a,h]of T){let c=s.match(a);if(c){i=h,n=c[1]?.replace(/_/g,".")||null;break}}let o="desktop";return/Mobi|Android.*Mobile|iPhone/.test(s)?o="mobile":/iPad|Android(?!.*Mobile)|Tablet/.test(s)&&(o="tablet"),{browser:e,browser_version:t,os:i,os_version:n,device_type:o}}function b(){if(!u)return null;let s=globalThis,e=s.navigator,t=s.location,i=s.document,n=s.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:n?.width??null,screen_height:n?.height??null,viewport_width:s.innerWidth??null,viewport_height:s.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 s=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of U){let n=s.get(i);n&&(e[i]=n,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(d,JSON.stringify(e))}catch{}return e}var p="__ot_sid",f="__ot_sts";function S(s){if(!u)return l();let e=globalThis.sessionStorage;if(!e)return l();try{let t=Date.now(),i=e.getItem(p),n=Number(e.getItem(f)||"0");if(i&&t-n<s)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 s=globalThis.localStorage;if(!s)return l();let e=s.getItem(v);if(e)return e;let t=l();return s.setItem(v,t),t}catch{return l()}}var m="__ot_ref";function _(){if(!u)return null;try{let s=globalThis.sessionStorage,e=s?.getItem(m);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return s?.setItem(m,t),t||null}catch{return globalThis.document?.referrer||null}}function l(){let s=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${s}-${e}`}var g=typeof globalThis<"u"&&typeof globalThis.document<"u",I=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??{},environment:e.environment??"production"},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=C(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=C(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[n,r]of Object.entries(i))r!==null&&r!==""&&r!==void 0&&(t[n]=r);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=S(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=(...a)=>{n(...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=n,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=this.config.environment!=="production"?`&env=${encodeURIComponent(this.config.environment)}`:"",i=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}${t}`,{method:"GET",headers:e});if(i.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!i.ok){let o=await i.text().catch(()=>"");throw new Error(`SplitLab API error ${i.status}: ${o}`)}let n=i.headers.get("etag");return{config:await i.json(),etag:n}}connectConfigStream(){if(typeof EventSource>"u")return;let e=this.config.environment!=="production"?`&env=${encodeURIComponent(this.config.environment)}`:"",t=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}${e}`;this.eventSource=new EventSource(t),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 r=n?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{I as SplitLabClient,z as hashToFloat,D as murmurhash3};
|
package/dist/splitlab.min.js
CHANGED
|
@@ -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 m=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var M=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var F=(n,e)=>{for(var t in e)m(n,t,{get:e[t],enumerable:!0})},N=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of M(e))!z.call(n,s)&&s!==t&&m(n,s,{get:()=>e[s],enumerable:!(i=D(e,s))||i.enumerable});return n};var j=n=>N(m({},"__esModule",{value:!0}),n);var q={};F(q,{SplitLabClient:()=>w,hashToFloat:()=>C,murmurhash3:()=>h});function h(n,e=0){let t=e>>>0,i=n.length,s=i>>2,r=3432918353,o=461845907;for(let l=0;l<s;l++){let c=n.charCodeAt(l*4)&255|(n.charCodeAt(l*4+1)&255)<<8|(n.charCodeAt(l*4+2)&255)<<16|(n.charCodeAt(l*4+3)&255)<<24;c=Math.imul(c,r),c=c<<15|c>>>17,c=Math.imul(c,o),t^=c,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196}let u=s*4,a=0;switch(i&3){case 3:a^=(n.charCodeAt(u+2)&255)<<16;case 2:a^=(n.charCodeAt(u+1)&255)<<8;case 1:a^=n.charCodeAt(u)&255,a=Math.imul(a,r),a=a<<15|a>>>17,a=Math.imul(a,o),t^=a}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 C(n,e){return h(n,e)/4294967295}function b(n,e,t={}){let i={},s={},r=n.segments||[],o=n.exclusion_groups||[],u=new Map;for(let a of o)for(let[l,c]of Object.entries(a.experiments))u.set(l,{layer_key:a.layer_key,...c});for(let a of n.experiments){if(a.targeting_rules&&!p(a.targeting_rules,t,r)){i[a.key]=null;continue}let l=u.get(a.key);if(l){let g=h(l.layer_key+":"+e)%1e4;if(g<l.bucket_start||g>=l.bucket_end){i[a.key]=null;continue}}let c=h(a.key+":"+e);if(c%1e4/100>=a.traffic_percentage){i[a.key]=null;continue}let A=a.variants.reduce((g,$)=>g+$.weight,0),L=c%A,_=0,v=null;for(let g of a.variants)if(_+=g.weight,L<_){v=g.key;break}v||(v=a.variants[a.variants.length-1].key),i[a.key]=v}for(let a of n.flags){if(a.rules&&!p(a.rules,t,r)){s[a.key]=!1;continue}let l=h(a.key+":"+e)%100;s[a.key]=l<a.rollout_percentage}return{experiments:i,flags:s}}function p(n,e,t=[]){if(!n.groups||n.groups.length===0)return!0;let i=r=>{if(r.attribute==="$segment"){if(!Array.isArray(r.value))return!1;let u=r.value.map(String);return r.operator==="in"?u.some(a=>{let l=t.find(c=>c.id===a||c.key===a);return l?p(l.rules,e,t):!1}):r.operator==="not_in"?!u.some(a=>{let l=t.find(c=>c.id===a||c.key===a);return l?p(l.rules,e,t):!1}):!1}let o=e[r.attribute];if(o==null)return!1;switch(r.operator){case"is":return String(o)===String(r.value);case"is_not":return String(o)!==String(r.value);case"contains":return String(o).toLowerCase().includes(String(r.value).toLowerCase());case"not_contains":return!String(o).toLowerCase().includes(String(r.value).toLowerCase());case"gt":return Number(o)>Number(r.value);case"lt":return Number(o)<Number(r.value);case"gte":return Number(o)>=Number(r.value);case"lte":return Number(o)<=Number(r.value);case"in":return Array.isArray(r.value)&&r.value.map(String).includes(String(o));case"not_in":return Array.isArray(r.value)&&!r.value.map(String).includes(String(o));default:return!1}},s=r=>r.conditions.every(i);return n.match==="any"?n.groups.some(s):n.groups.every(s)}var d=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"]],B=[[/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 Q(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 u=n.match(/Version\/(\S+)/);e="Safari",t=u?u[1]:null}else for(let[u,a]of K){let l=n.match(u);if(l){e=a,t=l[1]||null;break}}for(let[u,a]of B){let l=n.match(u);if(l){i=a,s=l[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 E(){if(!d)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,r=Q(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:H()}}function H(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var Y=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],I="__ot_utm";function P(){if(!d)return{};try{let i=globalThis.sessionStorage?.getItem(I);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of Y){let s=n.get(i);s&&(e[i]=s,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(I,JSON.stringify(e))}catch{}return e}var R="__ot_sid",y="__ot_sts";function U(n){if(!d)return f();let e=globalThis.sessionStorage;if(!e)return f();try{let t=Date.now(),i=e.getItem(R),s=Number(e.getItem(y)||"0");if(i&&t-s<n)return e.setItem(y,String(t)),i;let r=f();return e.setItem(R,r),e.setItem(y,String(t)),r}catch{return f()}}var T="__ot_did";function x(){if(!d)return f();try{let n=globalThis.localStorage;if(!n)return f();let e=n.getItem(T);if(e)return e;let t=f();return n.setItem(T,t),t}catch{return f()}}var k="__ot_ref";function O(){if(!d)return null;try{let n=globalThis.sessionStorage,e=n?.getItem(k);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return n?.setItem(k,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 S=typeof globalThis<"u"&&typeof globalThis.document<"u",w=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??S,captureUtm:e.captureUtm??!0,trackSessions:e.trackSessions??!0,sessionTimeout:e.sessionTimeout??30*6e4,persistDeviceId:e.persistDeviceId??!0,trackPageviews:e.trackPageviews??!1,superProperties:e.superProperties??{},environment:e.environment??"production"},this.config.captureUtm&&(this.utmParams=P()),this.config.captureContext&&(this.initialReferrer=O()),this.deviceId=x()}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=b(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(),S&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&S&&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=b(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=E();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=U(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=(...u)=>{s(...u),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...u)=>{r(...u),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=this.config.environment!=="production"?`&env=${encodeURIComponent(this.config.environment)}`:"",i=await fetch(`${this.config.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.config.apiKey)}${t}`,{method:"GET",headers:e});if(i.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!i.ok){let o=await i.text().catch(()=>"");throw new Error(`SplitLab API error ${i.status}: ${o}`)}let s=i.headers.get("etag");return{config:await i.json(),etag:s}}connectConfigStream(){if(typeof EventSource>"u")return;let e=this.config.environment!=="production"?`&env=${encodeURIComponent(this.config.environment)}`:"",t=`${this.config.baseUrl}/api/sdk/config/stream?key=${encodeURIComponent(this.config.apiKey)}${e}`;this.eventSource=new EventSource(t),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 u=await o.text().catch(()=>"");throw new Error(`SplitLab API error ${o.status}: ${u}`)}return o.json()}};return j(q);})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitlab/js-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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.2.0"
|
|
34
|
+
},
|
|
32
35
|
"devDependencies": {
|
|
33
36
|
"tsup": "^8.0.0",
|
|
34
37
|
"typescript": "^5.4.0"
|