@splitlab/node 0.5.0 → 0.6.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 +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var m=Object.defineProperty;var
|
|
1
|
+
"use strict";var m=Object.defineProperty;var E=Object.getOwnPropertyDescriptor;var k=Object.getOwnPropertyNames;var S=Object.prototype.hasOwnProperty;var b=(n,e)=>{for(var s in e)m(n,s,{get:e[s],enumerable:!0})},x=(n,e,s,t)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of k(e))!S.call(n,r)&&r!==s&&m(n,r,{get:()=>e[r],enumerable:!(t=E(e,r))||t.enumerable});return n};var w=n=>x(m({},"__esModule",{value:!0}),n);var O={};b(O,{RequestContext:()=>p,SplitLabServer:()=>_,hashToFloat:()=>d.hashToFloat,murmurhash3:()=>d.murmurhash3});module.exports=w(O);var d=require("@splitlab/core"),l=require("@splitlab/core"),v=class v{constructor(e){this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.configRefreshTimer=null;this.lastEtag=null;this.ready=!1;this.trackedExposures=new Set;this.apiKey=e.apiKey,this.baseUrl=e.baseUrl.replace(/\/$/,""),this.ingestUrl=(e.ingestUrl||e.baseUrl).replace(/\/$/,""),this.configRefreshInterval=e.configRefreshInterval??3e4,this.flushInterval=e.flushInterval??1e4,this.flushSize=e.flushSize??100,this.onConfigUpdate=e.onConfigUpdate??null,this.environment=e.environment??"production"}async initialize(){let{config:e,etag:s}=await this.fetchConfig();this.serverConfig=e,this.lastEtag=s,this.configRefreshTimer=setInterval(()=>{this.refresh().catch(()=>{})},this.configRefreshInterval),this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.flushInterval),this.ready=!0}isReady(){return this.ready}async destroy(){this.configRefreshTimer!==null&&(clearInterval(this.configRefreshTimer),this.configRefreshTimer=null),this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),await this.flush(),this.ready=!1}getVariant(e,s,t){if(!this.serverConfig)return null;let r=this.serverConfig.experiments.find(o=>o.key===e);if(!r)return null;let i=this.serverConfig.segments||[];if(r.targeting_rules&&!(0,l.evaluateRules)(r.targeting_rules,t||{},i))return null;let a=(0,l.murmurhash3)(r.key+":"+s);if(a%1e4/100>=r.traffic_percentage)return null;let h=r.variants.reduce((o,R)=>o+R.weight,0),f=a%h,c=0;for(let o of r.variants)if(c+=o.weight,f<c)return this.trackExposure(e,o.key,s),o.key;let g=r.variants[r.variants.length-1].key;return this.trackExposure(e,g,s),g}isFeatureEnabled(e,s,t){if(!this.serverConfig)return!1;let r=this.serverConfig.flags.find(h=>h.key===e);if(!r)return!1;let i=this.serverConfig.segments||[];return r.rules&&!(0,l.evaluateRules)(r.rules,t||{},i)?!1:(0,l.murmurhash3)(r.key+":"+s)%100<r.rollout_percentage}evaluateAll(e,s){return this.serverConfig?(0,l.localEvaluate)(this.serverConfig,e,s||{}):{experiments:{},flags:{}}}getConfigSnapshot(){return{serverConfig:this.serverConfig,lastEtag:this.lastEtag}}withContext(e){return new p(this,e)}remapCustomDimensions(e){if(!e||!this.serverConfig?.custom_dimensions?.length)return e;let s={...e};for(let t of this.serverConfig.custom_dimensions)t.property_key in s&&(s[`dim_${t.index}`]=s[t.property_key],delete s[t.property_key]);return s}track(e,s,t){this.eventQueue.push({distinct_id:e,event_name:s,properties:this.remapCustomDimensions(t),timestamp:new Date().toISOString()}),this.eventQueue.length>=this.flushSize&&this.flush().catch(()=>{})}trackExposure(e,s,t){let r=`${e}:${t}`;this.trackedExposures.has(r)||(this.trackedExposures.size>=v.MAX_EXPOSURE_CACHE&&this.trackedExposures.clear(),this.trackedExposures.add(r),this.track(t,"$exposure",{experiment_key:e,variant_key:s}))}async flush(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];try{(await fetch(`${this.ingestUrl}/ingest/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e})})).ok||(this.eventQueue=e.concat(this.eventQueue))}catch{this.eventQueue=e.concat(this.eventQueue)}}async refresh(){try{let{config:e,etag:s,notModified:t}=await this.fetchConfig();if(t)return;this.serverConfig=e,this.lastEtag=s,this.onConfigUpdate&&this.onConfigUpdate()}catch{}}async fetchConfig(){let e={"Content-Type":"application/json"};this.lastEtag&&(e["If-None-Match"]=this.lastEtag);let s=this.environment!=="production"?`&env=${encodeURIComponent(this.environment)}`:"",t=await fetch(`${this.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.apiKey)}${s}`,{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 r=t.headers.get("etag");return{config:await t.json(),etag:r}}};v.MAX_EXPOSURE_CACHE=5e4;var _=v,y="_sl_did",C="_sl_sid",U=63072e3,T=1800,$=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],p=class{constructor(e,s){this.server=e;let t=P(s.headers),r=s.cookies||A(t.cookie||""),i=(0,l.parseUserAgent)(t["user-agent"]||void 0),a=D(s.url,t.host),u=r[y]||null;this._deviceId=u||I(),this._newDeviceId=!u;let h=r[C]||null;this._sessionId=h||I(),this._newSessionId=!h,this.props={},i.browser&&(this.props.browser=i.browser),i.browser_version&&(this.props.browser_version=i.browser_version),i.os&&(this.props.os=i.os),i.os_version&&(this.props.os_version=i.os_version),i.device_type&&(this.props.device_type=i.device_type),t["user-agent"]&&(this.props.user_agent=t["user-agent"]),a.pathname&&(this.props.pathname=a.pathname),a.hostname&&(this.props.hostname=a.hostname);let f=t.referer||t.referrer||null;f&&(this.props.referrer=f);let c=t["accept-language"];if(c&&(this.props.language=c.split(",")[0].split(";")[0].trim()),a.searchParams)for(let g of $){let o=a.searchParams.get(g);o&&(this.props[g]=o)}this.props.device_id=this._deviceId,this._sessionId&&(this.props.session_id=this._sessionId)}get deviceId(){return this._deviceId}get sessionId(){return this._sessionId}getVariant(e,s){return this.server.getVariant(e,s||this._deviceId)}isFeatureEnabled(e,s,t){return this.server.isFeatureEnabled(e,s||this._deviceId,t)}track(e,s,t){let r={...this.props,...s};this.server.track(t||this._deviceId,e,r)}getResponseCookies(){let e=[];return this._newDeviceId&&e.push(`${y}=${encodeURIComponent(this._deviceId)}; Path=/; Max-Age=${U}; SameSite=Lax`),this._sessionId&&e.push(`${C}=${encodeURIComponent(this._sessionId)}; Path=/; Max-Age=${T}; SameSite=Lax`),e}getBootstrapData(e,s){let t=e||this._deviceId,r=this.server.evaluateAll(t,s),{serverConfig:i,lastEtag:a}=this.server.getConfigSnapshot();return{evalResult:r,serverConfig:i||{experiments:[],flags:[]},deviceId:this._deviceId,sessionId:this._sessionId,etag:a,generatedAt:new Date().toISOString()}}};function I(){let n=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${n}-${e}`}function P(n){let e={};if(n instanceof Headers)n.forEach((s,t)=>{e[t]=s});else for(let[s,t]of Object.entries(n))t!==void 0&&(e[s.toLowerCase()]=Array.isArray(t)?t[0]:t);return e}function A(n){let e={};if(!n)return e;for(let s of n.split(";")){let t=s.indexOf("=");if(t===-1)continue;let r=s.substring(0,t).trim(),i=s.substring(t+1).trim();try{e[r]=decodeURIComponent(i)}catch{e[r]=i}}return e}function D(n,e){if(!n)return{pathname:null,hostname:null,searchParams:null};try{let s=e?`http://${e}`:"http://localhost",t=new URL(n,s);return{pathname:t.pathname,hostname:t.hostname!=="localhost"?t.hostname:e?.split(":")[0]||null,searchParams:t.searchParams}}catch{return{pathname:n.split("?")[0]||null,hostname:null,searchParams:null}}}0&&(module.exports={RequestContext,SplitLabServer,hashToFloat,murmurhash3});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EvalResult, ServerConfig, BootstrapData } from '@splitlab/core';
|
|
2
|
-
export { BootstrapData, EvalResult, ExclusionGroupConfig, ExperimentConfig, FlagConfig, SegmentConfig, ServerConfig, TargetingCondition, TargetingGroup, TargetingRules, TrackEvent, UAResult, Variant, hashToFloat, murmurhash3 } from '@splitlab/core';
|
|
2
|
+
export { BootstrapData, CustomDimensionMapping, EvalResult, ExclusionGroupConfig, ExperimentConfig, FlagConfig, SegmentConfig, ServerConfig, TargetingCondition, TargetingGroup, TargetingRules, TrackEvent, UAResult, Variant, hashToFloat, murmurhash3 } from '@splitlab/core';
|
|
3
3
|
|
|
4
4
|
interface SplitLabServerConfig {
|
|
5
5
|
apiKey: string;
|
|
@@ -53,6 +53,8 @@ declare class SplitLabServer {
|
|
|
53
53
|
* to get Set-Cookie headers for the response.
|
|
54
54
|
*/
|
|
55
55
|
withContext(req: IncomingRequest): RequestContext;
|
|
56
|
+
/** Remap user property keys to dim_N slots based on custom dimension config. */
|
|
57
|
+
private remapCustomDimensions;
|
|
56
58
|
track(distinctId: string, eventName: string, properties?: Record<string, any>): void;
|
|
57
59
|
private trackExposure;
|
|
58
60
|
flush(): Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { EvalResult, ServerConfig, BootstrapData } from '@splitlab/core';
|
|
2
|
-
export { BootstrapData, EvalResult, ExclusionGroupConfig, ExperimentConfig, FlagConfig, SegmentConfig, ServerConfig, TargetingCondition, TargetingGroup, TargetingRules, TrackEvent, UAResult, Variant, hashToFloat, murmurhash3 } from '@splitlab/core';
|
|
2
|
+
export { BootstrapData, CustomDimensionMapping, EvalResult, ExclusionGroupConfig, ExperimentConfig, FlagConfig, SegmentConfig, ServerConfig, TargetingCondition, TargetingGroup, TargetingRules, TrackEvent, UAResult, Variant, hashToFloat, murmurhash3 } from '@splitlab/core';
|
|
3
3
|
|
|
4
4
|
interface SplitLabServerConfig {
|
|
5
5
|
apiKey: string;
|
|
@@ -53,6 +53,8 @@ declare class SplitLabServer {
|
|
|
53
53
|
* to get Set-Cookie headers for the response.
|
|
54
54
|
*/
|
|
55
55
|
withContext(req: IncomingRequest): RequestContext;
|
|
56
|
+
/** Remap user property keys to dim_N slots based on custom dimension config. */
|
|
57
|
+
private remapCustomDimensions;
|
|
56
58
|
track(distinctId: string, eventName: string, properties?: Record<string, any>): void;
|
|
57
59
|
private trackExposure;
|
|
58
60
|
flush(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{murmurhash3 as A,hashToFloat as
|
|
1
|
+
import{murmurhash3 as A,hashToFloat as D}from"@splitlab/core";import{murmurhash3 as v,localEvaluate as R,evaluateRules as d,parseUserAgent as E}from"@splitlab/core";var f=class f{constructor(e){this.serverConfig=null;this.eventQueue=[];this.flushTimer=null;this.configRefreshTimer=null;this.lastEtag=null;this.ready=!1;this.trackedExposures=new Set;this.apiKey=e.apiKey,this.baseUrl=e.baseUrl.replace(/\/$/,""),this.ingestUrl=(e.ingestUrl||e.baseUrl).replace(/\/$/,""),this.configRefreshInterval=e.configRefreshInterval??3e4,this.flushInterval=e.flushInterval??1e4,this.flushSize=e.flushSize??100,this.onConfigUpdate=e.onConfigUpdate??null,this.environment=e.environment??"production"}async initialize(){let{config:e,etag:s}=await this.fetchConfig();this.serverConfig=e,this.lastEtag=s,this.configRefreshTimer=setInterval(()=>{this.refresh().catch(()=>{})},this.configRefreshInterval),this.flushTimer=setInterval(()=>{this.flush().catch(()=>{})},this.flushInterval),this.ready=!0}isReady(){return this.ready}async destroy(){this.configRefreshTimer!==null&&(clearInterval(this.configRefreshTimer),this.configRefreshTimer=null),this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),await this.flush(),this.ready=!1}getVariant(e,s,t){if(!this.serverConfig)return null;let r=this.serverConfig.experiments.find(o=>o.key===e);if(!r)return null;let n=this.serverConfig.segments||[];if(r.targeting_rules&&!d(r.targeting_rules,t||{},n))return null;let i=v(r.key+":"+s);if(i%1e4/100>=r.traffic_percentage)return null;let l=r.variants.reduce((o,I)=>o+I.weight,0),u=i%l,h=0;for(let o of r.variants)if(h+=o.weight,u<h)return this.trackExposure(e,o.key,s),o.key;let c=r.variants[r.variants.length-1].key;return this.trackExposure(e,c,s),c}isFeatureEnabled(e,s,t){if(!this.serverConfig)return!1;let r=this.serverConfig.flags.find(l=>l.key===e);if(!r)return!1;let n=this.serverConfig.segments||[];return r.rules&&!d(r.rules,t||{},n)?!1:v(r.key+":"+s)%100<r.rollout_percentage}evaluateAll(e,s){return this.serverConfig?R(this.serverConfig,e,s||{}):{experiments:{},flags:{}}}getConfigSnapshot(){return{serverConfig:this.serverConfig,lastEtag:this.lastEtag}}withContext(e){return new p(this,e)}remapCustomDimensions(e){if(!e||!this.serverConfig?.custom_dimensions?.length)return e;let s={...e};for(let t of this.serverConfig.custom_dimensions)t.property_key in s&&(s[`dim_${t.index}`]=s[t.property_key],delete s[t.property_key]);return s}track(e,s,t){this.eventQueue.push({distinct_id:e,event_name:s,properties:this.remapCustomDimensions(t),timestamp:new Date().toISOString()}),this.eventQueue.length>=this.flushSize&&this.flush().catch(()=>{})}trackExposure(e,s,t){let r=`${e}:${t}`;this.trackedExposures.has(r)||(this.trackedExposures.size>=f.MAX_EXPOSURE_CACHE&&this.trackedExposures.clear(),this.trackedExposures.add(r),this.track(t,"$exposure",{experiment_key:e,variant_key:s}))}async flush(){if(this.eventQueue.length===0)return;let e=this.eventQueue;this.eventQueue=[];try{(await fetch(`${this.ingestUrl}/ingest/batch`,{method:"POST",headers:{"Content-Type":"application/json","X-API-Key":this.apiKey},body:JSON.stringify({events:e})})).ok||(this.eventQueue=e.concat(this.eventQueue))}catch{this.eventQueue=e.concat(this.eventQueue)}}async refresh(){try{let{config:e,etag:s,notModified:t}=await this.fetchConfig();if(t)return;this.serverConfig=e,this.lastEtag=s,this.onConfigUpdate&&this.onConfigUpdate()}catch{}}async fetchConfig(){let e={"Content-Type":"application/json"};this.lastEtag&&(e["If-None-Match"]=this.lastEtag);let s=this.environment!=="production"?`&env=${encodeURIComponent(this.environment)}`:"",t=await fetch(`${this.baseUrl}/api/sdk/config?key=${encodeURIComponent(this.apiKey)}${s}`,{method:"GET",headers:e});if(t.status===304)return{config:this.serverConfig,etag:this.lastEtag,notModified:!0};if(!t.ok){let i=await t.text().catch(()=>"");throw new Error(`SplitLab API error ${t.status}: ${i}`)}let r=t.headers.get("etag");return{config:await t.json(),etag:r}}};f.MAX_EXPOSURE_CACHE=5e4;var m=f,_="_sl_did",y="_sl_sid",k=63072e3,S=1800,b=["utm_source","utm_medium","utm_campaign","utm_term","utm_content"],p=class{constructor(e,s){this.server=e;let t=x(s.headers),r=s.cookies||w(t.cookie||""),n=E(t["user-agent"]||void 0),i=U(s.url,t.host),g=r[_]||null;this._deviceId=g||C(),this._newDeviceId=!g;let l=r[y]||null;this._sessionId=l||C(),this._newSessionId=!l,this.props={},n.browser&&(this.props.browser=n.browser),n.browser_version&&(this.props.browser_version=n.browser_version),n.os&&(this.props.os=n.os),n.os_version&&(this.props.os_version=n.os_version),n.device_type&&(this.props.device_type=n.device_type),t["user-agent"]&&(this.props.user_agent=t["user-agent"]),i.pathname&&(this.props.pathname=i.pathname),i.hostname&&(this.props.hostname=i.hostname);let u=t.referer||t.referrer||null;u&&(this.props.referrer=u);let h=t["accept-language"];if(h&&(this.props.language=h.split(",")[0].split(";")[0].trim()),i.searchParams)for(let c of b){let o=i.searchParams.get(c);o&&(this.props[c]=o)}this.props.device_id=this._deviceId,this._sessionId&&(this.props.session_id=this._sessionId)}get deviceId(){return this._deviceId}get sessionId(){return this._sessionId}getVariant(e,s){return this.server.getVariant(e,s||this._deviceId)}isFeatureEnabled(e,s,t){return this.server.isFeatureEnabled(e,s||this._deviceId,t)}track(e,s,t){let r={...this.props,...s};this.server.track(t||this._deviceId,e,r)}getResponseCookies(){let e=[];return this._newDeviceId&&e.push(`${_}=${encodeURIComponent(this._deviceId)}; Path=/; Max-Age=${k}; SameSite=Lax`),this._sessionId&&e.push(`${y}=${encodeURIComponent(this._sessionId)}; Path=/; Max-Age=${S}; SameSite=Lax`),e}getBootstrapData(e,s){let t=e||this._deviceId,r=this.server.evaluateAll(t,s),{serverConfig:n,lastEtag:i}=this.server.getConfigSnapshot();return{evalResult:r,serverConfig:n||{experiments:[],flags:[]},deviceId:this._deviceId,sessionId:this._sessionId,etag:i,generatedAt:new Date().toISOString()}}};function C(){let a=Date.now().toString(36),e=Math.random().toString(36).substring(2,10);return`${a}-${e}`}function x(a){let e={};if(a instanceof Headers)a.forEach((s,t)=>{e[t]=s});else for(let[s,t]of Object.entries(a))t!==void 0&&(e[s.toLowerCase()]=Array.isArray(t)?t[0]:t);return e}function w(a){let e={};if(!a)return e;for(let s of a.split(";")){let t=s.indexOf("=");if(t===-1)continue;let r=s.substring(0,t).trim(),n=s.substring(t+1).trim();try{e[r]=decodeURIComponent(n)}catch{e[r]=n}}return e}function U(a,e){if(!a)return{pathname:null,hostname:null,searchParams:null};try{let s=e?`http://${e}`:"http://localhost",t=new URL(a,s);return{pathname:t.pathname,hostname:t.hostname!=="localhost"?t.hostname:e?.split(":")[0]||null,searchParams:t.searchParams}}catch{return{pathname:a.split("?")[0]||null,hostname:null,searchParams:null}}}export{p as RequestContext,m as SplitLabServer,D as hashToFloat,A as murmurhash3};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splitlab/node",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Node.js server SDK for SplitLab A/B testing and feature flags",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dev": "tsup --watch"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@splitlab/core": "^0.
|
|
36
|
+
"@splitlab/core": "^0.4.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"tsup": "^8.0.0",
|