@splitlab/js-client 0.5.0 → 0.7.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 +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +1 -1
- package/dist/splitlab.min.js +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var g=Object.defineProperty;var P=Object.getOwnPropertyDescriptor;var x=Object.getOwnPropertyNames;var $=Object.prototype.hasOwnProperty;var D=(n,e)=>{for(var t in e)g(n,t,{get:e[t],enumerable:!0})},L=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of x(e))!$.call(n,s)&&s!==t&&g(n,s,{get:()=>e[s],enumerable:!(i=P(e,s))||i.enumerable});return n};var O=n=>L(g({},"__esModule",{value:!0}),n);var B={};D(B,{SplitLabClient:()=>b,hashToFloat:()=>f.hashToFloat,murmurhash3:()=>f.murmurhash3});module.exports=O(B);var f=require("@splitlab/core"),m=require("@splitlab/core");var S=require("@splitlab/core");var o=typeof globalThis<"u"&&typeof globalThis.document<"u";function w(){if(!o)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,r=(0,S.parseUserAgent)(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 j=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],y="__ot_utm";function _(){if(!o)return{};try{let i=globalThis.sessionStorage?.getItem(y);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of j){let s=n.get(i);s&&(e[i]=s,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(y,JSON.stringify(e))}catch{}return e}var c="_sl_did",d="_sl_sid",R=63072e3,C="__ot_did";function p(n){if(!o)return null;try{let e=globalThis.document?.cookie?.match(new RegExp("(?:^|; )"+n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}catch{return null}}function u(n,e,t){if(o)try{globalThis.document.cookie=`${n}=${encodeURIComponent(e)}; path=/; max-age=${t}; SameSite=Lax`}catch{}}function E(n){if(!o)return h();let e=Math.floor(n/1e3),t=p(d);if(t)return u(d,t,e),t;let i=h();return u(d,i,e),i}function T(){if(!o)return h();let n=p(c);if(n)return n;let e=null;try{let t=globalThis.localStorage;e=t?.getItem(C)||null,e&&t?.removeItem(C)}catch{}return e||(e=h()),u(c,e,R),e}function U(n){!o||p(c)===n||u(c,n,R)}var I="__ot_ref";function k(){if(!o)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 h(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var v=typeof globalThis<"u"&&typeof globalThis.document<"u",b=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.trackedExposures=new Set;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??v,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=_()),this.config.captureContext&&(this.initialReferrer=k()),e.bootstrap?.deviceId?(this.deviceId=e.bootstrap.deviceId,U(e.bootstrap.deviceId)):this.deviceId=T()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){let e=this.initialized;if(!e)if(this.config.enableLocalEvaluation)try{let{config:t,etag:i}=await this.fetchConfig();this.serverConfig=t,this.lastEtag=i,this.evalResult=(0,m.localEvaluate)(this.serverConfig,this.bucketingId,this.config.attributes)}catch(t){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",t),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let t={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(t.attributes=this.config.attributes);let i=await this.request("POST","/api/sdk/evaluate",t);this.evalResult={experiments:i.experiments,flags:i.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(),v&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&v&&this.setupPageviewTracking(),this.initialized=!0,e&&this.refresh().catch(()=>{})}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");let t=this.evalResult.experiments[e]??null;return t!==null&&!this.trackedExposures.has(e)&&(this.trackedExposures.add(e),this.track("$exposure",{experiment_key:e,variant_key:t}).catch(()=>{})),t}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}hydrateFromBootstrap(e){this.serverConfig=e.serverConfig,this.lastEtag=e.etag,this.evalResult=e.evalResult,this.initialized=!0}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,m.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=w();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=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=(...l)=>{s(...l),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...l)=>{r(...l),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=s,t.replaceState=r,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=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 a=await i.text().catch(()=>"");throw new Error(`SplitLab API error ${i.status}: ${a}`)}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,a=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(!a.ok){let l=await a.text().catch(()=>"");throw new Error(`SplitLab API error ${a.status}: ${l}`)}return a.json()}};0&&(module.exports={SplitLabClient,hashToFloat,murmurhash3});
|
|
1
|
+
"use strict";var v=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var D=Object.getOwnPropertyNames;var z=Object.prototype.hasOwnProperty;var j=(n,e)=>{for(var t in e)v(n,t,{get:e[t],enumerable:!0})},A=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of D(e))!z.call(n,s)&&s!==t&&v(n,s,{get:()=>e[s],enumerable:!(i=O(e,s))||i.enumerable});return n};var B=n=>A(v({},"__esModule",{value:!0}),n);var Q={};j(Q,{SplitLabClient:()=>w,hashToFloat:()=>g.hashToFloat,murmurhash3:()=>g.murmurhash3});module.exports=B(Q);var g=require("@splitlab/core"),C=require("@splitlab/core");var U=require("@splitlab/core");var c=typeof globalThis<"u"&&typeof globalThis.document<"u";function R(){if(!c)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,r=(0,U.parseUserAgent)(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:F()}}function F(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var H=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],I="__ot_utm";function T(){if(!c)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 H){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 u="_sl_did",m="_sl_sid",k=63072e3,E="__ot_did";function b(n){if(!c)return null;try{let e=globalThis.document?.cookie?.match(new RegExp("(?:^|; )"+n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}catch{return null}}function h(n,e,t){if(c)try{globalThis.document.cookie=`${n}=${encodeURIComponent(e)}; path=/; max-age=${t}; SameSite=Lax`}catch{}}function x(n){if(!c)return f();let e=Math.floor(n/1e3),t=b(m);if(t)return h(m,t,e),t;let i=f();return h(m,i,e),i}function P(){if(!c)return f();let n=b(u);if(n)return n;let e=null;try{let t=globalThis.localStorage;e=t?.getItem(E)||null,e&&t?.removeItem(E)}catch{}return e||(e=f()),h(u,e,k),e}function $(n){!c||b(u)===n||h(u,n,k)}var _="__ot_ref";function L(){if(!c)return null;try{let n=globalThis.sessionStorage,e=n?.getItem(_);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return n?.setItem(_,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 y=typeof globalThis<"u"&&typeof globalThis.document<"u",w=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.initialized=!1;this.visibilityHandler=null;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.trackedExposures=new Set;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??8e3,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,realtimeUpdates:e.realtimeUpdates??!1,onConfigUpdate:e.onConfigUpdate??null,captureContext:e.captureContext??y,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=T()),this.config.captureContext&&(this.initialReferrer=L()),e.bootstrap?.deviceId?(this.deviceId=e.bootstrap.deviceId,$(e.bootstrap.deviceId)):this.deviceId=P()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){let e=this.initialized;if(!e)if(this.config.enableLocalEvaluation)try{let{config:t,etag:i}=await this.fetchConfig();this.serverConfig=t,this.lastEtag=i,this.evalResult=(0,C.localEvaluate)(this.serverConfig,this.bucketingId,this.config.attributes)}catch(t){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",t),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let t={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(t.attributes=this.config.attributes);let i=await this.request("POST","/api/sdk/evaluate",t);this.evalResult={experiments:i.experiments,flags:i.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),y&&(this.visibilityHandler=()=>{globalThis.document?.visibilityState==="hidden"?this.flushSync():this.config.enableLocalEvaluation&&this.refresh().catch(()=>{})},globalThis.document?.addEventListener("visibilitychange",this.visibilityHandler),this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&y&&this.setupPageviewTracking(),this.initialized=!0,e&&this.refresh().catch(()=>{})}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");let t=this.evalResult.experiments[e]??null;return t!==null&&!this.trackedExposures.has(e)&&(this.trackedExposures.add(e),this.track("$exposure",{experiment_key:e,variant_key:t}).catch(()=>{})),t}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}hydrateFromBootstrap(e){this.serverConfig=e.serverConfig,this.lastEtag=e.etag,this.evalResult=e.evalResult,this.initialized=!0}async track(e,t){this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:this.mergeUserProperties(t),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{let t=JSON.stringify({context:this.buildContext(),events:e}),i=await K(t),s=JSON.stringify({_e:i}),r=`${this.config.ingestUrl}/ingest/batch?key=${encodeURIComponent(this.config.apiKey)}`,o=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:s});if(!o.ok)throw new Error(`${o.status}`)}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,C.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.eventSource&&(this.eventSource.close(),this.eventSource=null),this.visibilityHandler&&(globalThis.document?.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=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}buildContext(){let e={};if(this.config.captureContext){let t=R();if(t){for(let[i,s]of Object.entries(t))s!==null&&s!==""&&s!==void 0&&(e[i]=s);this.initialReferrer&&(e.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[t,i]of Object.entries(this.utmParams))i&&(e[t]=i);this.config.trackSessions&&(e.session_id=x(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(e.device_id=this.deviceId);for(let[t,i]of Object.entries(this.config.superProperties))i!==void 0&&(e[t]=i);return e}mergeUserProperties(e){if(!e)return;let t={};for(let[i,s]of Object.entries(e))s!==void 0&&(t[i]=s);return Object.keys(t).length>0?t:void 0}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,s=t.pushState.bind(t);t.pushState=(...l)=>{s(...l),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...l)=>{r(...l),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({context:this.buildContext(),events:e}),i=typeof btoa=="function"?btoa(unescape(encodeURIComponent(t))):t,r=typeof btoa=="function"?JSON.stringify({_e:i}):t,o=`${this.config.ingestUrl}/ingest/batch?key=${encodeURIComponent(this.config.apiKey)}`;if(typeof globalThis.navigator?.sendBeacon=="function"){let l=new Blob([r],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(o,l))return}catch{}}if(typeof fetch=="function")try{fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:r,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i){let s=await fetch(`${this.config.baseUrl}${t}`,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:i?JSON.stringify(i):void 0});if(!s.ok){let r=await s.text().catch(()=>"");throw new Error(`SplitLab API error ${s.status}: ${r}`)}return s.json()}};async function K(n){let e=new TextEncoder().encode(n);if(typeof CompressionStream<"u")try{let t=new CompressionStream("gzip"),i=t.writable.getWriter();i.write(e),i.close();let s=t.readable.getReader(),r=[];for(;;){let{done:a,value:p}=await s.read();if(a)break;r.push(p)}let o=r.reduce((a,p)=>a+p.length,0),l=new Uint8Array(o),d=0;for(let a of r)l.set(a,d),d+=a.length;let S="";for(let a=0;a<l.length;a++)S+=String.fromCharCode(l[a]);return btoa(S)}catch{}return btoa(unescape(encodeURIComponent(n)))}0&&(module.exports={SplitLabClient,hashToFloat,murmurhash3});
|
package/dist/index.d.cts
CHANGED
|
@@ -9,12 +9,11 @@ interface SplitLabConfig {
|
|
|
9
9
|
/** Unique user identifier. If omitted, the SDK auto-generates a persistent device ID. */
|
|
10
10
|
distinctId?: string;
|
|
11
11
|
attributes?: Record<string, any>;
|
|
12
|
+
/** Event flush interval in ms. Default: 8000 (8s). */
|
|
12
13
|
autoFlushInterval?: number;
|
|
13
14
|
autoFlushSize?: number;
|
|
14
15
|
/** Enable local evaluation of experiments/flags (no /evaluate network call). Default: true. */
|
|
15
16
|
enableLocalEvaluation?: boolean;
|
|
16
|
-
/** Config polling interval in ms. Default: 30000 (30s). */
|
|
17
|
-
configRefreshInterval?: number;
|
|
18
17
|
/** Enable SSE for instant config push from server. Default: false. */
|
|
19
18
|
realtimeUpdates?: boolean;
|
|
20
19
|
/** Callback when config changes (after refresh). */
|
|
@@ -68,8 +67,8 @@ declare class SplitLabClient {
|
|
|
68
67
|
private serverConfig;
|
|
69
68
|
private eventQueue;
|
|
70
69
|
private flushTimer;
|
|
71
|
-
private configRefreshTimer;
|
|
72
70
|
private initialized;
|
|
71
|
+
private visibilityHandler;
|
|
73
72
|
private beforeUnloadHandler;
|
|
74
73
|
private lastEtag;
|
|
75
74
|
private eventSource;
|
|
@@ -108,7 +107,10 @@ declare class SplitLabClient {
|
|
|
108
107
|
getDeviceId(): string;
|
|
109
108
|
setAttributes(attributes: Record<string, any>): void;
|
|
110
109
|
destroy(): Promise<void>;
|
|
111
|
-
|
|
110
|
+
/** Shared context sent once per batch (not duplicated per-event). */
|
|
111
|
+
private buildContext;
|
|
112
|
+
/** Per-event properties: only user-supplied props (no auto-captured context). */
|
|
113
|
+
private mergeUserProperties;
|
|
112
114
|
private setupPageviewTracking;
|
|
113
115
|
private onUrlChange;
|
|
114
116
|
private fetchConfig;
|
package/dist/index.d.ts
CHANGED
|
@@ -9,12 +9,11 @@ interface SplitLabConfig {
|
|
|
9
9
|
/** Unique user identifier. If omitted, the SDK auto-generates a persistent device ID. */
|
|
10
10
|
distinctId?: string;
|
|
11
11
|
attributes?: Record<string, any>;
|
|
12
|
+
/** Event flush interval in ms. Default: 8000 (8s). */
|
|
12
13
|
autoFlushInterval?: number;
|
|
13
14
|
autoFlushSize?: number;
|
|
14
15
|
/** Enable local evaluation of experiments/flags (no /evaluate network call). Default: true. */
|
|
15
16
|
enableLocalEvaluation?: boolean;
|
|
16
|
-
/** Config polling interval in ms. Default: 30000 (30s). */
|
|
17
|
-
configRefreshInterval?: number;
|
|
18
17
|
/** Enable SSE for instant config push from server. Default: false. */
|
|
19
18
|
realtimeUpdates?: boolean;
|
|
20
19
|
/** Callback when config changes (after refresh). */
|
|
@@ -68,8 +67,8 @@ declare class SplitLabClient {
|
|
|
68
67
|
private serverConfig;
|
|
69
68
|
private eventQueue;
|
|
70
69
|
private flushTimer;
|
|
71
|
-
private configRefreshTimer;
|
|
72
70
|
private initialized;
|
|
71
|
+
private visibilityHandler;
|
|
73
72
|
private beforeUnloadHandler;
|
|
74
73
|
private lastEtag;
|
|
75
74
|
private eventSource;
|
|
@@ -108,7 +107,10 @@ declare class SplitLabClient {
|
|
|
108
107
|
getDeviceId(): string;
|
|
109
108
|
setAttributes(attributes: Record<string, any>): void;
|
|
110
109
|
destroy(): Promise<void>;
|
|
111
|
-
|
|
110
|
+
/** Shared context sent once per batch (not duplicated per-event). */
|
|
111
|
+
private buildContext;
|
|
112
|
+
/** Per-event properties: only user-supplied props (no auto-captured context). */
|
|
113
|
+
private mergeUserProperties;
|
|
112
114
|
private setupPageviewTracking;
|
|
113
115
|
private onUrlChange;
|
|
114
116
|
private fetchConfig;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{murmurhash3 as z,hashToFloat as j}from"@splitlab/core";import{localEvaluate as R}from"@splitlab/core";import{parseUserAgent as T}from"@splitlab/core";var o=typeof globalThis<"u"&&typeof globalThis.document<"u";function b(){if(!o)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,r=T(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:U()}}function U(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var k=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],p="__ot_utm";function y(){if(!o)return{};try{let i=globalThis.sessionStorage?.getItem(p);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(p,JSON.stringify(e))}catch{}return e}var c="_sl_did",f="_sl_sid",C=63072e3,v="__ot_did";function g(n){if(!o)return null;try{let e=globalThis.document?.cookie?.match(new RegExp("(?:^|; )"+n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}catch{return null}}function u(n,e,t){if(o)try{globalThis.document.cookie=`${n}=${encodeURIComponent(e)}; path=/; max-age=${t}; SameSite=Lax`}catch{}}function I(n){if(!o)return h();let e=Math.floor(n/1e3),t=g(f);if(t)return u(f,t,e),t;let i=h();return u(f,i,e),i}function S(){if(!o)return h();let n=g(c);if(n)return n;let e=null;try{let t=globalThis.localStorage;e=t?.getItem(v)||null,e&&t?.removeItem(v)}catch{}return e||(e=h()),u(c,e,C),e}function w(n){!o||g(c)===n||u(c,n,C)}var m="__ot_ref";function _(){if(!o)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 h(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var d=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.trackedExposures=new Set;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??d,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=_()),e.bootstrap?.deviceId?(this.deviceId=e.bootstrap.deviceId,w(e.bootstrap.deviceId)):this.deviceId=S()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){let e=this.initialized;if(!e)if(this.config.enableLocalEvaluation)try{let{config:t,etag:i}=await this.fetchConfig();this.serverConfig=t,this.lastEtag=i,this.evalResult=R(this.serverConfig,this.bucketingId,this.config.attributes)}catch(t){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",t),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let t={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(t.attributes=this.config.attributes);let i=await this.request("POST","/api/sdk/evaluate",t);this.evalResult={experiments:i.experiments,flags:i.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(),d&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&d&&this.setupPageviewTracking(),this.initialized=!0,e&&this.refresh().catch(()=>{})}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");let t=this.evalResult.experiments[e]??null;return t!==null&&!this.trackedExposures.has(e)&&(this.trackedExposures.add(e),this.track("$exposure",{experiment_key:e,variant_key:t}).catch(()=>{})),t}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}hydrateFromBootstrap(e){this.serverConfig=e.serverConfig,this.lastEtag=e.etag,this.evalResult=e.evalResult,this.initialized=!0}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=R(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=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=(...l)=>{s(...l),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...l)=>{r(...l),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=s,t.replaceState=r,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=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 a=await i.text().catch(()=>"");throw new Error(`SplitLab API error ${i.status}: ${a}`)}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,a=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(!a.ok){let l=await a.text().catch(()=>"");throw new Error(`SplitLab API error ${a.status}: ${l}`)}return a.json()}};export{E as SplitLabClient,j as hashToFloat,z as murmurhash3};
|
|
1
|
+
import{murmurhash3 as H,hashToFloat as K}from"@splitlab/core";import{localEvaluate as k}from"@splitlab/core";import{parseUserAgent as P}from"@splitlab/core";var c=typeof globalThis<"u"&&typeof globalThis.document<"u";function S(){if(!c)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,s=n.screen,r=P(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:$()}}function $(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var L=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],y="__ot_utm";function I(){if(!c)return{};try{let i=globalThis.sessionStorage?.getItem(y);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of L){let s=n.get(i);s&&(e[i]=s,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(y,JSON.stringify(e))}catch{}return e}var u="_sl_did",p="_sl_sid",E=63072e3,C="__ot_did";function v(n){if(!c)return null;try{let e=globalThis.document?.cookie?.match(new RegExp("(?:^|; )"+n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}catch{return null}}function h(n,e,t){if(c)try{globalThis.document.cookie=`${n}=${encodeURIComponent(e)}; path=/; max-age=${t}; SameSite=Lax`}catch{}}function _(n){if(!c)return f();let e=Math.floor(n/1e3),t=v(p);if(t)return h(p,t,e),t;let i=f();return h(p,i,e),i}function U(){if(!c)return f();let n=v(u);if(n)return n;let e=null;try{let t=globalThis.localStorage;e=t?.getItem(C)||null,e&&t?.removeItem(C)}catch{}return e||(e=f()),h(u,e,E),e}function R(n){!c||v(u)===n||h(u,n,E)}var w="__ot_ref";function T(){if(!c)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 f(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var m=typeof globalThis<"u"&&typeof globalThis.document<"u",x=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.initialized=!1;this.visibilityHandler=null;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.trackedExposures=new Set;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??8e3,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,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??{},environment:e.environment??"production"},this.config.captureUtm&&(this.utmParams=I()),this.config.captureContext&&(this.initialReferrer=T()),e.bootstrap?.deviceId?(this.deviceId=e.bootstrap.deviceId,R(e.bootstrap.deviceId)):this.deviceId=U()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){let e=this.initialized;if(!e)if(this.config.enableLocalEvaluation)try{let{config:t,etag:i}=await this.fetchConfig();this.serverConfig=t,this.lastEtag=i,this.evalResult=k(this.serverConfig,this.bucketingId,this.config.attributes)}catch(t){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",t),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let t={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(t.attributes=this.config.attributes);let i=await this.request("POST","/api/sdk/evaluate",t);this.evalResult={experiments:i.experiments,flags:i.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),m&&(this.visibilityHandler=()=>{globalThis.document?.visibilityState==="hidden"?this.flushSync():this.config.enableLocalEvaluation&&this.refresh().catch(()=>{})},globalThis.document?.addEventListener("visibilitychange",this.visibilityHandler),this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&m&&this.setupPageviewTracking(),this.initialized=!0,e&&this.refresh().catch(()=>{})}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");let t=this.evalResult.experiments[e]??null;return t!==null&&!this.trackedExposures.has(e)&&(this.trackedExposures.add(e),this.track("$exposure",{experiment_key:e,variant_key:t}).catch(()=>{})),t}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}hydrateFromBootstrap(e){this.serverConfig=e.serverConfig,this.lastEtag=e.etag,this.evalResult=e.evalResult,this.initialized=!0}async track(e,t){this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:this.mergeUserProperties(t),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{let t=JSON.stringify({context:this.buildContext(),events:e}),i=await O(t),s=JSON.stringify({_e:i}),r=`${this.config.ingestUrl}/ingest/batch?key=${encodeURIComponent(this.config.apiKey)}`,o=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:s});if(!o.ok)throw new Error(`${o.status}`)}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=k(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.eventSource&&(this.eventSource.close(),this.eventSource=null),this.visibilityHandler&&(globalThis.document?.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=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}buildContext(){let e={};if(this.config.captureContext){let t=S();if(t){for(let[i,s]of Object.entries(t))s!==null&&s!==""&&s!==void 0&&(e[i]=s);this.initialReferrer&&(e.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[t,i]of Object.entries(this.utmParams))i&&(e[t]=i);this.config.trackSessions&&(e.session_id=_(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(e.device_id=this.deviceId);for(let[t,i]of Object.entries(this.config.superProperties))i!==void 0&&(e[t]=i);return e}mergeUserProperties(e){if(!e)return;let t={};for(let[i,s]of Object.entries(e))s!==void 0&&(t[i]=s);return Object.keys(t).length>0?t:void 0}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,s=t.pushState.bind(t);t.pushState=(...l)=>{s(...l),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...l)=>{r(...l),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({context:this.buildContext(),events:e}),i=typeof btoa=="function"?btoa(unescape(encodeURIComponent(t))):t,r=typeof btoa=="function"?JSON.stringify({_e:i}):t,o=`${this.config.ingestUrl}/ingest/batch?key=${encodeURIComponent(this.config.apiKey)}`;if(typeof globalThis.navigator?.sendBeacon=="function"){let l=new Blob([r],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(o,l))return}catch{}}if(typeof fetch=="function")try{fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:r,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i){let s=await fetch(`${this.config.baseUrl}${t}`,{method:e,headers:{"Content-Type":"application/json","X-API-Key":this.config.apiKey},body:i?JSON.stringify(i):void 0});if(!s.ok){let r=await s.text().catch(()=>"");throw new Error(`SplitLab API error ${s.status}: ${r}`)}return s.json()}};async function O(n){let e=new TextEncoder().encode(n);if(typeof CompressionStream<"u")try{let t=new CompressionStream("gzip"),i=t.writable.getWriter();i.write(e),i.close();let s=t.readable.getReader(),r=[];for(;;){let{done:a,value:d}=await s.read();if(a)break;r.push(d)}let o=r.reduce((a,d)=>a+d.length,0),l=new Uint8Array(o),g=0;for(let a of r)l.set(a,g),g+=a.length;let b="";for(let a=0;a<l.length;a++)b+=String.fromCharCode(l[a]);return btoa(b)}catch{}return btoa(unescape(encodeURIComponent(n)))}export{x as SplitLabClient,K as hashToFloat,H as murmurhash3};
|
package/dist/splitlab.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var SplitLab=(()=>{var y=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var K=Object.prototype.hasOwnProperty;var Q=(n,e)=>{for(var t in e)y(n,t,{get:e[t],enumerable:!0})},H=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of N(e))!K.call(n,a)&&a!==t&&y(n,a,{get:()=>e[a],enumerable:!(i=j(e,a))||i.enumerable});return n};var G=n=>H(y({},"__esModule",{value:!0}),n);var X={};Q(X,{SplitLabClient:()=>I,hashToFloat:()=>E,murmurhash3:()=>g});function g(n,e=0){let t=e>>>0,i=n.length,a=i>>2,r=3432918353,o=461845907;for(let l=0;l<a;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=a*4,s=0;switch(i&3){case 3:s^=(n.charCodeAt(u+2)&255)<<16;case 2:s^=(n.charCodeAt(u+1)&255)<<8;case 1:s^=n.charCodeAt(u)&255,s=Math.imul(s,r),s=s<<15|s>>>17,s=Math.imul(s,o),t^=s}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 E(n,e){return g(n,e)/4294967295}function S(n,e,t={}){let i={},a={},r=n.segments||[],o=n.exclusion_groups||[],u=new Map;for(let s of o)for(let[l,c]of Object.entries(s.experiments))u.set(l,{layer_key:s.layer_key,...c});for(let s of n.experiments){if(s.targeting_rules&&!v(s.targeting_rules,t,r)){i[s.key]=null;continue}let l=u.get(s.key);if(l){let f=g(l.layer_key+":"+e)%1e4;if(f<l.bucket_start||f>=l.bucket_end){i[s.key]=null;continue}}let c=g(s.key+":"+e);if(c%1e4/100>=s.traffic_percentage){i[s.key]=null;continue}let z=s.variants.reduce((f,F)=>f+F.weight,0),B=c%z,k=0,d=null;for(let f of s.variants)if(k+=f.weight,B<k){d=f.key;break}d||(d=s.variants[s.variants.length-1].key),i[s.key]=d}for(let s of n.flags){if(s.rules&&!v(s.rules,t,r)){a[s.key]=!1;continue}let l=g(s.key+":"+e)%100;a[s.key]=l<s.rollout_percentage}return{experiments:i,flags:a}}function v(n,e,t=[]){if(!n.groups||n.groups.length===0)return!0;let i=r=>{if(!r.attribute)return!0;if(r.attribute==="$segment"){if(!Array.isArray(r.value))return!1;let u=r.value.map(String);return r.operator==="in"?u.some(s=>{let l=t.find(c=>c.id===s||c.key===s);return l?v(l.rules,e,t):!1}):r.operator==="not_in"?!u.some(s=>{let l=t.find(c=>c.id===s||c.key===s);return l?v(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}},a=r=>r.conditions.every(i);return n.match==="any"?n.groups.some(a):n.groups.every(a)}var V=[[/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"]],Y=[[/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(n){if(!n)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(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,u]of V){let s=n.match(o);if(s){e=u,t=s[1]||null;break}}for(let[o,u]of Y){let s=n.match(o);if(s){i=u,a=s[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:a,device_type:r}}var h=typeof globalThis<"u"&&typeof globalThis.document<"u";function P(){if(!h)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,a=n.screen,r=R(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:n.innerWidth??null,viewport_height:n.innerHeight??null,language:e?.language??null,timezone:q()}}function q(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var J=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],T="__ot_utm";function O(){if(!h)return{};try{let i=globalThis.sessionStorage?.getItem(T);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of J){let a=n.get(i);a&&(e[i]=a,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(T,JSON.stringify(e))}catch{}return e}var p="_sl_did",C="_sl_sid",A=63072e3,x="__ot_did";function w(n){if(!h)return null;try{let e=globalThis.document?.cookie?.match(new RegExp("(?:^|; )"+n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}catch{return null}}function m(n,e,t){if(h)try{globalThis.document.cookie=`${n}=${encodeURIComponent(e)}; path=/; max-age=${t}; SameSite=Lax`}catch{}}function L(n){if(!h)return b();let e=Math.floor(n/1e3),t=w(C);if(t)return m(C,t,e),t;let i=b();return m(C,i,e),i}function $(){if(!h)return b();let n=w(p);if(n)return n;let e=null;try{let t=globalThis.localStorage;e=t?.getItem(x)||null,e&&t?.removeItem(x)}catch{}return e||(e=b()),m(p,e,A),e}function D(n){!h||w(p)===n||m(p,n,A)}var U="__ot_ref";function M(){if(!h)return null;try{let n=globalThis.sessionStorage,e=n?.getItem(U);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return n?.setItem(U,t),t||null}catch{return globalThis.document?.referrer||null}}function b(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var _=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.trackedExposures=new Set;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??_,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=O()),this.config.captureContext&&(this.initialReferrer=M()),e.bootstrap?.deviceId?(this.deviceId=e.bootstrap.deviceId,D(e.bootstrap.deviceId)):this.deviceId=$()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){let e=this.initialized;if(!e)if(this.config.enableLocalEvaluation)try{let{config:t,etag:i}=await this.fetchConfig();this.serverConfig=t,this.lastEtag=i,this.evalResult=S(this.serverConfig,this.bucketingId,this.config.attributes)}catch(t){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",t),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let t={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(t.attributes=this.config.attributes);let i=await this.request("POST","/api/sdk/evaluate",t);this.evalResult={experiments:i.experiments,flags:i.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(),_&&(this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&_&&this.setupPageviewTracking(),this.initialized=!0,e&&this.refresh().catch(()=>{})}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");let t=this.evalResult.experiments[e]??null;return t!==null&&!this.trackedExposures.has(e)&&(this.trackedExposures.add(e),this.track("$exposure",{experiment_key:e,variant_key:t}).catch(()=>{})),t}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}hydrateFromBootstrap(e){this.serverConfig=e.serverConfig,this.lastEtag=e.etag,this.evalResult=e.evalResult,this.initialized=!0}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=S(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=P();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=L(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=(...u)=>{a(...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=a,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 a=i.headers.get("etag");return{config:await i.json(),etag:a}}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 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,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 G(X);})();
|
|
1
|
+
"use strict";var SplitLab=(()=>{var S=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var N=Object.getOwnPropertyNames;var H=Object.prototype.hasOwnProperty;var K=(n,e)=>{for(var t in e)S(n,t,{get:e[t],enumerable:!0})},Q=(n,e,t,i)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of N(e))!H.call(n,a)&&a!==t&&S(n,a,{get:()=>e[a],enumerable:!(i=j(e,a))||i.enumerable});return n};var J=n=>Q(S({},"__esModule",{value:!0}),n);var X={};K(X,{SplitLabClient:()=>E,hashToFloat:()=>U,murmurhash3:()=>g});function g(n,e=0){let t=e>>>0,i=n.length,a=i>>2,r=3432918353,o=461845907;for(let u=0;u<a;u++){let l=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;l=Math.imul(l,r),l=l<<15|l>>>17,l=Math.imul(l,o),t^=l,t=t<<13|t>>>19,t=Math.imul(t,5)+3864292196}let c=a*4,s=0;switch(i&3){case 3:s^=(n.charCodeAt(c+2)&255)<<16;case 2:s^=(n.charCodeAt(c+1)&255)<<8;case 1:s^=n.charCodeAt(c)&255,s=Math.imul(s,r),s=s<<15|s>>>17,s=Math.imul(s,o),t^=s}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(n,e){return g(n,e)/4294967295}function C(n,e,t={}){let i={},a={},r=n.segments||[],o=n.exclusion_groups||[],c=new Map;for(let s of o)for(let[u,l]of Object.entries(s.experiments))c.set(u,{layer_key:s.layer_key,...l});for(let s of n.experiments){if(s.targeting_rules&&!v(s.targeting_rules,t,r)){i[s.key]=null;continue}let u=c.get(s.key);if(u){let f=g(u.layer_key+":"+e)%1e4;if(f<u.bucket_start||f>=u.bucket_end){i[s.key]=null;continue}}let l=g(s.key+":"+e);if(l%1e4/100>=s.traffic_percentage){i[s.key]=null;continue}let d=s.variants.reduce((f,F)=>f+F.weight,0),B=l%d,I=0,p=null;for(let f of s.variants)if(I+=f.weight,B<I){p=f.key;break}p||(p=s.variants[s.variants.length-1].key),i[s.key]=p}for(let s of n.flags){if(s.rules&&!v(s.rules,t,r)){a[s.key]=!1;continue}let u=g(s.key+":"+e)%100;a[s.key]=u<s.rollout_percentage}return{experiments:i,flags:a}}function v(n,e,t=[]){if(!n.groups||n.groups.length===0)return!0;let i=r=>{if(!r.attribute)return!0;if(r.attribute==="$segment"){if(!Array.isArray(r.value))return!1;let c=r.value.map(String);return r.operator==="in"?c.some(s=>{let u=t.find(l=>l.id===s||l.key===s);return u?v(u.rules,e,t):!1}):r.operator==="not_in"?!c.some(s=>{let u=t.find(l=>l.id===s||l.key===s);return u?v(u.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}},a=r=>r.conditions.every(i);return n.match==="any"?n.groups.some(a):n.groups.every(a)}var G=[[/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"]],V=[[/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 x(n){if(!n)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(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,c]of G){let s=n.match(o);if(s){e=c,t=s[1]||null;break}}for(let[o,c]of V){let s=n.match(o);if(s){i=c,a=s[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:a,device_type:r}}var h=typeof globalThis<"u"&&typeof globalThis.document<"u";function O(){if(!h)return null;let n=globalThis,e=n.navigator,t=n.location,i=n.document,a=n.screen,r=x(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:n.innerWidth??null,viewport_height:n.innerHeight??null,language:e?.language??null,timezone:Y()}}function Y(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}}var W=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],R="__ot_utm";function A(){if(!h)return{};try{let i=globalThis.sessionStorage?.getItem(R);if(i)return JSON.parse(i)}catch{}let n=new URLSearchParams(globalThis.location?.search||""),e={},t=!1;for(let i of W){let a=n.get(i);a&&(e[i]=a,t=!0)}if(t)try{globalThis.sessionStorage?.setItem(R,JSON.stringify(e))}catch{}return e}var m="_sl_did",w="_sl_sid",L=63072e3,T="__ot_did";function _(n){if(!h)return null;try{let e=globalThis.document?.cookie?.match(new RegExp("(?:^|; )"+n.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")+"=([^;]*)"));return e?decodeURIComponent(e[1]):null}catch{return null}}function y(n,e,t){if(h)try{globalThis.document.cookie=`${n}=${encodeURIComponent(e)}; path=/; max-age=${t}; SameSite=Lax`}catch{}}function $(n){if(!h)return b();let e=Math.floor(n/1e3),t=_(w);if(t)return y(w,t,e),t;let i=b();return y(w,i,e),i}function D(){if(!h)return b();let n=_(m);if(n)return n;let e=null;try{let t=globalThis.localStorage;e=t?.getItem(T)||null,e&&t?.removeItem(T)}catch{}return e||(e=b()),y(m,e,L),e}function z(n){!h||_(m)===n||y(m,n,L)}var P="__ot_ref";function M(){if(!h)return null;try{let n=globalThis.sessionStorage,e=n?.getItem(P);if(e!==null)return e||null;let t=globalThis.document?.referrer||"";return n?.setItem(P,t),t||null}catch{return globalThis.document?.referrer||null}}function b(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}var k=typeof globalThis<"u"&&typeof globalThis.document<"u",E=class{constructor(e){this.evalResult=null;this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.initialized=!1;this.visibilityHandler=null;this.beforeUnloadHandler=null;this.lastEtag=null;this.eventSource=null;this.deviceId=null;this.utmParams={};this.initialReferrer=null;this.pageviewCleanup=null;this.trackedExposures=new Set;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??8e3,autoFlushSize:e.autoFlushSize??20,enableLocalEvaluation:e.enableLocalEvaluation??!0,realtimeUpdates:e.realtimeUpdates??!1,onConfigUpdate:e.onConfigUpdate??null,captureContext:e.captureContext??k,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=A()),this.config.captureContext&&(this.initialReferrer=M()),e.bootstrap?.deviceId?(this.deviceId=e.bootstrap.deviceId,z(e.bootstrap.deviceId)):this.deviceId=D()}get effectiveDistinctId(){return this.config.distinctId||this.deviceId}get bucketingId(){return this.deviceId}async initialize(){let e=this.initialized;if(!e)if(this.config.enableLocalEvaluation)try{let{config:t,etag:i}=await this.fetchConfig();this.serverConfig=t,this.lastEtag=i,this.evalResult=C(this.serverConfig,this.bucketingId,this.config.attributes)}catch(t){this.serverConfig||(console.warn("SplitLab: config fetch failed, using safe defaults",t),this.serverConfig={experiments:[],flags:[]},this.evalResult={experiments:{},flags:{}})}else{let t={distinct_id:this.effectiveDistinctId};Object.keys(this.config.attributes).length>0&&(t.attributes=this.config.attributes);let i=await this.request("POST","/api/sdk/evaluate",t);this.evalResult={experiments:i.experiments,flags:i.flags}}this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.config.autoFlushInterval),this.config.realtimeUpdates&&this.config.enableLocalEvaluation&&this.connectConfigStream(),k&&(this.visibilityHandler=()=>{globalThis.document?.visibilityState==="hidden"?this.flushSync():this.config.enableLocalEvaluation&&this.refresh().catch(()=>{})},globalThis.document?.addEventListener("visibilitychange",this.visibilityHandler),this.beforeUnloadHandler=()=>{this.flushSync()},globalThis.addEventListener("beforeunload",this.beforeUnloadHandler)),this.config.trackPageviews&&k&&this.setupPageviewTracking(),this.initialized=!0,e&&this.refresh().catch(()=>{})}getVariant(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");let t=this.evalResult.experiments[e]??null;return t!==null&&!this.trackedExposures.has(e)&&(this.trackedExposures.add(e),this.track("$exposure",{experiment_key:e,variant_key:t}).catch(()=>{})),t}isFeatureEnabled(e){if(!this.initialized||!this.evalResult)throw new Error("SplitLabClient not initialized. Call initialize() first.");return this.evalResult.flags[e]??!1}hydrateFromBootstrap(e){this.serverConfig=e.serverConfig,this.lastEtag=e.etag,this.evalResult=e.evalResult,this.initialized=!0}async track(e,t){this.eventQueue.push({distinct_id:this.effectiveDistinctId,event_name:e,properties:this.mergeUserProperties(t),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{let t=JSON.stringify({context:this.buildContext(),events:e}),i=await q(t),a=JSON.stringify({_e:i}),r=`${this.config.ingestUrl}/ingest/batch?key=${encodeURIComponent(this.config.apiKey)}`,o=await fetch(r,{method:"POST",headers:{"Content-Type":"application/json"},body:a});if(!o.ok)throw new Error(`${o.status}`)}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.eventSource&&(this.eventSource.close(),this.eventSource=null),this.visibilityHandler&&(globalThis.document?.removeEventListener("visibilitychange",this.visibilityHandler),this.visibilityHandler=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}buildContext(){let e={};if(this.config.captureContext){let t=O();if(t){for(let[i,a]of Object.entries(t))a!==null&&a!==""&&a!==void 0&&(e[i]=a);this.initialReferrer&&(e.referrer=this.initialReferrer)}}if(this.config.captureUtm)for(let[t,i]of Object.entries(this.utmParams))i&&(e[t]=i);this.config.trackSessions&&(e.session_id=$(this.config.sessionTimeout)),this.config.persistDeviceId&&this.deviceId&&(e.device_id=this.deviceId);for(let[t,i]of Object.entries(this.config.superProperties))i!==void 0&&(e[t]=i);return e}mergeUserProperties(e){if(!e)return;let t={};for(let[i,a]of Object.entries(e))a!==void 0&&(t[i]=a);return Object.keys(t).length>0?t:void 0}setupPageviewTracking(){let e=globalThis,t=e.history;if(!t?.pushState)return;let i=e.location?.href,a=t.pushState.bind(t);t.pushState=(...c)=>{a(...c),this.onUrlChange(i),i=e.location?.href};let r=t.replaceState.bind(t);t.replaceState=(...c)=>{r(...c),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=a,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 a=i.headers.get("etag");return{config:await i.json(),etag:a}}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({context:this.buildContext(),events:e}),i=typeof btoa=="function"?btoa(unescape(encodeURIComponent(t))):t,r=typeof btoa=="function"?JSON.stringify({_e:i}):t,o=`${this.config.ingestUrl}/ingest/batch?key=${encodeURIComponent(this.config.apiKey)}`;if(typeof globalThis.navigator?.sendBeacon=="function"){let c=new Blob([r],{type:"application/json"});try{if(globalThis.navigator.sendBeacon(o,c))return}catch{}}if(typeof fetch=="function")try{fetch(o,{method:"POST",headers:{"Content-Type":"application/json"},body:r,keepalive:!0}).catch(()=>{})}catch{}}async request(e,t,i){let a=await fetch(`${this.config.baseUrl}${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 r=await a.text().catch(()=>"");throw new Error(`SplitLab API error ${a.status}: ${r}`)}return a.json()}};async function q(n){let e=new TextEncoder().encode(n);if(typeof CompressionStream<"u")try{let t=new CompressionStream("gzip"),i=t.writable.getWriter();i.write(e),i.close();let a=t.readable.getReader(),r=[];for(;;){let{done:l,value:d}=await a.read();if(l)break;r.push(d)}let o=r.reduce((l,d)=>l+d.length,0),c=new Uint8Array(o),s=0;for(let l of r)c.set(l,s),s+=l.length;let u="";for(let l=0;l<c.length;l++)u+=String.fromCharCode(c[l]);return btoa(u)}catch{}return btoa(unescape(encodeURIComponent(n)))}return J(X);})();
|