@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 +1 -1
- package/dist/index.d.cts +2 -55
- package/dist/index.d.ts +2 -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 $={};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,
|
|
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,
|
|
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};
|
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 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.
|
|
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"
|