@obelism/improve-sdk 0.3.6 → 0.5.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/README.md CHANGED
@@ -24,6 +24,7 @@ constructor({
24
24
  environment: 'develop' | 'staging' | 'production'
25
25
  config?: Configuration
26
26
  fetchTimeout?: number
27
+ dataLayer?: boolean
27
28
  }) => void
28
29
  ```
29
30
 
@@ -31,6 +32,7 @@ constructor({
31
32
  - **environment** - Application environment, can be one of three values
32
33
  - **config** - (optional) Configuration file, this can be either fetched or provided on initialization
33
34
  - **fetchTimeout** - (optional) When fetching the config after what amount of ms should it abort, default; 3000ms
35
+ - **dataLayer** - (optional) Mirror analytics onto the GTM `window.dataLayer` (with `improve: { test, variant, visitorId }` dimensions) so they can drive Google Tag Manager / Google Ads conversions. Default; `true`, set to `false` to opt out
34
36
 
35
37
  ### fetchConfig
36
38
 
package/dist/client.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0});var i=require("ua-parser-js"),t=i&&i.__esModule?i:{default:i};const e={device:["wearable","mobile","tablet","console","smarttv","embedded","desktop"],browser:["chrome","safari","firefox","edge","ie","samsung internet","social","other"],os:["mac os","ios","android","windows","unix"]},s=i=>i&&e.device.includes(i)?i:"desktop",r=["tiktok","wechat","weibo","snapchat","klarna","Line","instagram","facebook","alipay","Baidu"],o=(i="")=>{if(!i)return"other";let t=i.toLowerCase();return e.browser.find(i=>t.includes(i))||(r.includes(t)?"social":"other")},n=["wearable","mobile","tablet"],l=i=>n.includes(i)?"coarse":"fine",a=(i="")=>{if(!i)return"unix";let t=i.toLowerCase();return e.os.find(i=>t.includes(i))||"unix"},h=i=>{if(!i||"string"!=typeof i)return null;let e=new t.default(i).getResult(),r=s(e.device.type);return{pointer:l(r),device:r,browser:o(e.browser.name),os:a(e.os.name)}},u=(i,t)=>!i||Object.entries(i).every(([i,e])=>t[i]===e),c=i=>{if(0===i.length)return null;if(1===i.length)return i[0].slug;let t=Math.random()*i.reduce((i,{split:t})=>i+t,0);return(i.find(({split:i})=>(t-=i)<=0)||i[0]).slug},f="visi",d="https://improve.obelism.studio",g="/api/log",v="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",p=i=>new Promise(t=>setTimeout(t,i)),m=async(i=1e3,t)=>(await p(i),t?.abort(),null),w=(i=3e3,t,e)=>{let s=new AbortController;return Promise.race([fetch(t,{...e,signal:s.signal}),m(i,s)])};class b{#i;constructor({organizationId:i,environment:t,state:e,config:s,fetchTimeout:r,baseUrl:o}){this.environment="develop",this.#i=null,this.config=null,this._fetchConfig=async i=>{if(this.config)return;if(!this.#i)throw Error("No config fetch setup provided");let t=await w(this.#i.timeout,this.#i.url,i);if(!t||!t.ok)throw Error("Configuration fetch timed-out");return this.config=await t.json(),this.config},this.loadConfig=i=>{this.config=i},this.generateVisitorId=()=>[f,(function(i=5){return i&&"number"==typeof i?Array(i).fill("").reduce(i=>i+=v.charAt(Math.floor(Math.random()*v.length)),""):""})(26).toUpperCase()].join("_"),this.getVisitorCookieName=()=>"visitorId",this.validateTestValue=(i,t)=>{if(!this.config)throw Error("Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup");let e=this.config.tests[i];if(!e)throw Error(`No config found for ${i}`);return!!e.options.find(i=>i.slug===t)},this.validateVisitorId=i=>{let t=i.split("_");if(2!==t.length)return!1;let[e,s]=t;return e===f&&26===s.length},this.organizationId=i,this.environment=t,this.state=e,this._baseUrl=o||d,s?this.config=s:this.#i={url:[`${this._baseUrl}/config`,this.organizationId,this.environment,this.state||"active"].join("/"),timeout:r||3e3}}}const y=i=>{if(!i)return!1;let t=document.cookie.split("; ").find(t=>{let[e]=t.split("=");return i===e});return!!t&&t.split("=")[1]},I=(i,t)=>{let e=new Date;e.setMonth(e.getMonth()+1),document.cookie=`${i}=${t};path=/;expires=${e.toUTCString()}`},V=()=>{let i=window.innerWidth;return i<=768?"small":i<=1024?"medium":i<=1200?"large":"huge"};exports.ImproveClientSDK=class extends b{#t;#e;#s;#r;#o;constructor(i){super(i),this.#e=!1,this.#s="",this.#r={},this.#o=`${d}${g}`,this.fetchConfig=this._fetchConfig,this.setupVisitor=(i=window.navigator.userAgent)=>{let t=y(this.getVisitorCookieName()),e=t&&this.validateVisitorId(t);this.#e=e,this.#s=e?t:this.generateVisitorId();let s=h(i);return s?(this.#t=s,I(this.getVisitorCookieName(),this.#s),this.#s):null},this.getFlagValue=i=>{if(!this.config)return null;let t=this.config.flags[i];if(!t||!t.options[0])return null;if(this.#t||this.setupVisitor(),!this.#s||!this.#t)return t.options[0].slug;if(this.#t?.[i])return this.#t[i];if(!u(this.config.audience[t.audience],this.#t))return t.options[0].slug;let e=y(i)||c(t.options);return e?(this.#t[i]=e,I(i,e),e):null},this.getTestValue=i=>{if(!this.config)return null;let t=this.config.tests[i];if(!t)return null;if(this.#t||this.setupVisitor(),!this.#s||!this.#t)return t.defaultValue;if(this.#t?.[i])return this.#t[i];if(!u(this.config.audience[t.audience],this.#t))return t.defaultValue;if(t.allocation<100&&100*Math.random()>t.allocation)return this.#t[i]=t.defaultValue,this.#t?.[i];let e=y(i),s=e&&this.validateTestValue(i,e)?e:c(t.options);return s?(this.#t[i]=s,I(i,s),s):null},this.setAnalyticsUrls=i=>{this.#o=i},this.postAnalytic=(i,t,e)=>{if(!this.config)return null;let s=this.config.tests[i];if(this.#t||this.setupVisitor(),!s||!this.#t||this.#r?.[i]?.[t])return null;let r=this.#r[i]||{};r[t]=!0,this.#r[i]=r;let o=this.#t?.[i]||null;if(o||(o=this.getTestValue(i)||null),!o)return;let n={organizationId:this.organizationId,environment:this.environment,testId:s.id,testValue:o,visitorId:this.#s,pointer:this.#t.pointer,device:this.#t.device,screen:V(),browser:this.#t.browser,os:this.#t.os,visitor:this.#e?"recurring":"new",event:t,message:e||""};return fetch(this.#o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})},this.#o=`${this._baseUrl}${g}`}};
1
+ Object.defineProperty(exports,"__esModule",{value:!0});var t=require("ua-parser-js"),i=t&&t.__esModule?t:{default:t};let e=["wearable","mobile","tablet","console","smarttv","embedded","desktop"],s=["chrome","safari","firefox","edge","ie","samsung internet","social","other"],r=["mac os","ios","android","windows","unix"],o=["tiktok","wechat","weibo","snapchat","klarna","Line","instagram","facebook","alipay","Baidu"],n=["wearable","mobile","tablet"],a=(t,i)=>!t||Object.entries(t).every(([t,e])=>i[t]===e),l=t=>{if(0===t.length)return null;if(1===t.length)return t[0].slug;let i=Math.random()*t.reduce((t,{split:i})=>t+i,0);return(t.find(({split:t})=>(i-=t)<=0)||t[0]).slug},h="visi",u="https://improve.obelism.studio",d="/api/log",c="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",f=async(t=1e3,i)=>(await new Promise(i=>setTimeout(i,t)),i?.abort(),null);class g{#t;constructor({organizationId:t,environment:i,state:e,config:s,fetchTimeout:r,baseUrl:o}){this.environment="develop",this.#t=null,this.config=null,this._fetchConfig=async t=>{if(this.config)return;if(!this.#t)throw Error("No config fetch setup provided");let i=await ((t=3e3,i,e)=>{let s=new AbortController;return Promise.race([fetch(i,{...e,signal:s.signal}),f(t,s)])})(this.#t.timeout,this.#t.url,t);if(!i||!i.ok)throw Error("Configuration fetch timed-out");return this.config=await i.json(),this.config},this.loadConfig=t=>{this.config=t},this.generateVisitorId=()=>[h,(function(t=5){return t&&"number"==typeof t?Array(t).fill("").reduce(t=>t+=c.charAt(Math.floor(Math.random()*c.length)),""):""})(26).toUpperCase()].join("_"),this.getVisitorCookieName=()=>"visitorId",this.validateTestValue=(t,i)=>{if(!this.config)throw Error("Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup");let e=this.config.tests[t];if(!e)throw Error(`No config found for ${t}`);return!!e.options.find(t=>t.slug===i)},this.validateVisitorId=t=>{let i=t.split("_");if(2!==i.length)return!1;let[e,s]=i;return e===h&&26===s.length},this.organizationId=t,this.environment=i,this.state=e,this._baseUrl=o||u,s?this.config=s:this.#t={url:[`${this._baseUrl}/config`,this.organizationId,this.environment,this.state||"active"].join("/"),timeout:r||3e3}}}let v=t=>{if(!t)return!1;let i=document.cookie.split("; ").find(i=>{let[e]=i.split("=");return t===e});return!!i&&i.split("=")[1]},p=(t,i)=>{let e=new Date;e.setDate(e.getDate()+30),document.cookie=`${t}=${i};path=/;expires=${e.toUTCString()};SameSite=Lax;Secure`};exports.ImproveClientSDK=class extends g{#i;#e;#s;#r;#o;#n;constructor(t){super(t),this.#e=!1,this.#s="",this.#r={},this.#o=`${u}${d}`,this.#n=!0,this.fetchConfig=this._fetchConfig,this.setupVisitor=(t=window.navigator.userAgent)=>{let a=v(this.getVisitorCookieName()),l=a&&this.validateVisitorId(a);this.#e=l,this.#s=l?a:this.generateVisitorId();let h=(t=>{var a;if(!t||"string"!=typeof t)return null;let l=new i.default(t).getResult(),h=(a=l.device.type)&&e.includes(a)?a:"desktop";return{pointer:n.includes(h)?"coarse":"fine",device:h,browser:((t="")=>{if(!t)return"other";let i=t.toLowerCase(),e=s.find(t=>i.includes(t));return e||(o.includes(i)?"social":"other")})(l.browser.name),os:((t="")=>{if(!t)return"unix";let i=t.toLowerCase(),e=r.find(t=>i.includes(t));return e||"unix"})(l.os.name)}})(t);return h?(this.#i=h,p(this.getVisitorCookieName(),this.#s),this.#s):null},this.getFlagValue=t=>{if(!this.config)return null;let i=this.config.flags[t];if(!i||!i.options[0])return null;if(this.#i||this.setupVisitor(),!this.#s||!this.#i)return i.options[0].slug;if(this.#i?.[t])return this.#i[t];if(!a(this.config.audience[i.audience],this.#i))return i.options[0].slug;let e=v(t)||l(i.options);return e?(this.#i[t]=e,p(t,e),e):null},this.getTestValue=t=>{if(!this.config)return null;let i=this.config.tests[t];if(!i)return null;if(this.#i||this.setupVisitor(),!this.#s||!this.#i)return i.defaultValue;if(this.#i?.[t])return this.#i[t];if(!a(this.config.audience[i.audience],this.#i))return i.defaultValue;if(i.allocation<100&&100*Math.random()>i.allocation)return this.#i[t]=i.defaultValue,this.#i?.[t];let e=v(t),s=e&&this.validateTestValue(t,e)?e:l(i.options);return s?(this.#i[t]=s,p(t,s),s):null},this.setAnalyticsUrls=t=>{this.#o=t},this.postAnalytic=(t,i,e)=>{var s;let r;if(!this.config)return null;let o=this.config.tests[t];if(this.#i||this.setupVisitor(),!o||!this.#i||this.#r?.[t]?.[i])return null;let n=this.#r[t]||{};n[i]=!0,this.#r[t]=n;let a=this.#i?.[t]||null;if(a||(a=this.getTestValue(t)||null),!a)return;let l={organizationId:this.organizationId,environment:this.environment,testId:o.id,testValue:a,visitorId:this.#s,pointer:this.#i.pointer,device:this.#i.device,screen:(r=window.innerWidth)<=768?"small":r<=1024?"medium":r<=1200?"large":"huge",browser:this.#i.browser,os:this.#i.os,visitor:this.#e?"recurring":"new",event:i,message:e||""};return this.#n&&(s={event:i,improve:{test:t,variant:a,visitorId:this.#s},_improve:!0},"u">typeof window&&(window.dataLayer=window.dataLayer||[],window.dataLayer.push(s))),fetch(this.#o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)})},this.#o=`${this._baseUrl}${d}`,this.#n=t.dataLayer??!0}};
package/dist/client.d.ts CHANGED
@@ -43,4 +43,5 @@ declare class ImproveClientSDK extends BaseImproveSDK {
43
43
  postAnalytic: (testSlug: string, event: string, message?: string) => Promise<Response>;
44
44
  }
45
45
 
46
- export { type CreateAnalytic, ImproveClientSDK };
46
+ export { ImproveClientSDK };
47
+ export type { CreateAnalytic };
package/dist/client.mjs CHANGED
@@ -1 +1 @@
1
- import i from"ua-parser-js";let t={device:["wearable","mobile","tablet","console","smarttv","embedded","desktop"],browser:["chrome","safari","firefox","edge","ie","samsung internet","social","other"],os:["mac os","ios","android","windows","unix"]},e=i=>i&&t.device.includes(i)?i:"desktop",s=["tiktok","wechat","weibo","snapchat","klarna","Line","instagram","facebook","alipay","Baidu"],r=(i="")=>{if(!i)return"other";let e=i.toLowerCase();return t.browser.find(i=>e.includes(i))||(s.includes(e)?"social":"other")},o=["wearable","mobile","tablet"],n=i=>o.includes(i)?"coarse":"fine",l=(i="")=>{if(!i)return"unix";let e=i.toLowerCase();return t.os.find(i=>e.includes(i))||"unix"},a=t=>{if(!t||"string"!=typeof t)return null;let s=new i(t).getResult(),o=e(s.device.type);return{pointer:n(o),device:o,browser:r(s.browser.name),os:l(s.os.name)}},h=(i,t)=>!i||Object.entries(i).every(([i,e])=>t[i]===e),u=i=>{if(0===i.length)return null;if(1===i.length)return i[0].slug;let t=Math.random()*i.reduce((i,{split:t})=>i+t,0);return(i.find(({split:i})=>(t-=i)<=0)||i[0]).slug},c="visi",f="https://improve.obelism.studio",d="/api/log",g="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",v=i=>new Promise(t=>setTimeout(t,i)),p=async(i=1e3,t)=>(await v(i),t?.abort(),null),m=(i=3e3,t,e)=>{let s=new AbortController;return Promise.race([fetch(t,{...e,signal:s.signal}),p(i,s)])};class w{#i;constructor({organizationId:i,environment:t,state:e,config:s,fetchTimeout:r,baseUrl:o}){this.environment="develop",this.#i=null,this.config=null,this._fetchConfig=async i=>{if(this.config)return;if(!this.#i)throw Error("No config fetch setup provided");let t=await m(this.#i.timeout,this.#i.url,i);if(!t||!t.ok)throw Error("Configuration fetch timed-out");return this.config=await t.json(),this.config},this.loadConfig=i=>{this.config=i},this.generateVisitorId=()=>[c,(function(i=5){return i&&"number"==typeof i?Array(i).fill("").reduce(i=>i+=g.charAt(Math.floor(Math.random()*g.length)),""):""})(26).toUpperCase()].join("_"),this.getVisitorCookieName=()=>"visitorId",this.validateTestValue=(i,t)=>{if(!this.config)throw Error("Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup");let e=this.config.tests[i];if(!e)throw Error(`No config found for ${i}`);return!!e.options.find(i=>i.slug===t)},this.validateVisitorId=i=>{let t=i.split("_");if(2!==t.length)return!1;let[e,s]=t;return e===c&&26===s.length},this.organizationId=i,this.environment=t,this.state=e,this._baseUrl=o||f,s?this.config=s:this.#i={url:[`${this._baseUrl}/config`,this.organizationId,this.environment,this.state||"active"].join("/"),timeout:r||3e3}}}let b=i=>{if(!i)return!1;let t=document.cookie.split("; ").find(t=>{let[e]=t.split("=");return i===e});return!!t&&t.split("=")[1]},y=(i,t)=>{let e=new Date;e.setMonth(e.getMonth()+1),document.cookie=`${i}=${t};path=/;expires=${e.toUTCString()}`},I=()=>{let i=window.innerWidth;return i<=768?"small":i<=1024?"medium":i<=1200?"large":"huge"};class V extends w{#t;#e;#s;#r;#o;constructor(i){super(i),this.#e=!1,this.#s="",this.#r={},this.#o=`${f}${d}`,this.fetchConfig=this._fetchConfig,this.setupVisitor=(i=window.navigator.userAgent)=>{let t=b(this.getVisitorCookieName()),e=t&&this.validateVisitorId(t);this.#e=e,this.#s=e?t:this.generateVisitorId();let s=a(i);return s?(this.#t=s,y(this.getVisitorCookieName(),this.#s),this.#s):null},this.getFlagValue=i=>{if(!this.config)return null;let t=this.config.flags[i];if(!t||!t.options[0])return null;if(this.#t||this.setupVisitor(),!this.#s||!this.#t)return t.options[0].slug;if(this.#t?.[i])return this.#t[i];if(!h(this.config.audience[t.audience],this.#t))return t.options[0].slug;let e=b(i)||u(t.options);return e?(this.#t[i]=e,y(i,e),e):null},this.getTestValue=i=>{if(!this.config)return null;let t=this.config.tests[i];if(!t)return null;if(this.#t||this.setupVisitor(),!this.#s||!this.#t)return t.defaultValue;if(this.#t?.[i])return this.#t[i];if(!h(this.config.audience[t.audience],this.#t))return t.defaultValue;if(t.allocation<100&&100*Math.random()>t.allocation)return this.#t[i]=t.defaultValue,this.#t?.[i];let e=b(i),s=e&&this.validateTestValue(i,e)?e:u(t.options);return s?(this.#t[i]=s,y(i,s),s):null},this.setAnalyticsUrls=i=>{this.#o=i},this.postAnalytic=(i,t,e)=>{if(!this.config)return null;let s=this.config.tests[i];if(this.#t||this.setupVisitor(),!s||!this.#t||this.#r?.[i]?.[t])return null;let r=this.#r[i]||{};r[t]=!0,this.#r[i]=r;let o=this.#t?.[i]||null;if(o||(o=this.getTestValue(i)||null),!o)return;let n={organizationId:this.organizationId,environment:this.environment,testId:s.id,testValue:o,visitorId:this.#s,pointer:this.#t.pointer,device:this.#t.device,screen:I(),browser:this.#t.browser,os:this.#t.os,visitor:this.#e?"recurring":"new",event:t,message:e||""};return fetch(this.#o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n)})},this.#o=`${this._baseUrl}${d}`}}export{V as ImproveClientSDK};
1
+ import i from"ua-parser-js";let t=["wearable","mobile","tablet","console","smarttv","embedded","desktop"],e=["chrome","safari","firefox","edge","ie","samsung internet","social","other"],s=["mac os","ios","android","windows","unix"],r=["tiktok","wechat","weibo","snapchat","klarna","Line","instagram","facebook","alipay","Baidu"],o=["wearable","mobile","tablet"],n=(i,t)=>!i||Object.entries(i).every(([i,e])=>t[i]===e),a=i=>{if(0===i.length)return null;if(1===i.length)return i[0].slug;let t=Math.random()*i.reduce((i,{split:t})=>i+t,0);return(i.find(({split:i})=>(t-=i)<=0)||i[0]).slug},l="visi",h="https://improve.obelism.studio",u="/api/log",c="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",d=async(i=1e3,t)=>(await new Promise(t=>setTimeout(t,i)),t?.abort(),null);class f{#i;constructor({organizationId:i,environment:t,state:e,config:s,fetchTimeout:r,baseUrl:o}){this.environment="develop",this.#i=null,this.config=null,this._fetchConfig=async i=>{if(this.config)return;if(!this.#i)throw Error("No config fetch setup provided");let t=await ((i=3e3,t,e)=>{let s=new AbortController;return Promise.race([fetch(t,{...e,signal:s.signal}),d(i,s)])})(this.#i.timeout,this.#i.url,i);if(!t||!t.ok)throw Error("Configuration fetch timed-out");return this.config=await t.json(),this.config},this.loadConfig=i=>{this.config=i},this.generateVisitorId=()=>[l,(function(i=5){return i&&"number"==typeof i?Array(i).fill("").reduce(i=>i+=c.charAt(Math.floor(Math.random()*c.length)),""):""})(26).toUpperCase()].join("_"),this.getVisitorCookieName=()=>"visitorId",this.validateTestValue=(i,t)=>{if(!this.config)throw Error("Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup");let e=this.config.tests[i];if(!e)throw Error(`No config found for ${i}`);return!!e.options.find(i=>i.slug===t)},this.validateVisitorId=i=>{let t=i.split("_");if(2!==t.length)return!1;let[e,s]=t;return e===l&&26===s.length},this.organizationId=i,this.environment=t,this.state=e,this._baseUrl=o||h,s?this.config=s:this.#i={url:[`${this._baseUrl}/config`,this.organizationId,this.environment,this.state||"active"].join("/"),timeout:r||3e3}}}let g=i=>{if(!i)return!1;let t=document.cookie.split("; ").find(t=>{let[e]=t.split("=");return i===e});return!!t&&t.split("=")[1]},v=(i,t)=>{let e=new Date;e.setDate(e.getDate()+30),document.cookie=`${i}=${t};path=/;expires=${e.toUTCString()};SameSite=Lax;Secure`};class p extends f{#t;#e;#s;#r;#o;#n;constructor(l){super(l),this.#e=!1,this.#s="",this.#r={},this.#o=`${h}${u}`,this.#n=!0,this.fetchConfig=this._fetchConfig,this.setupVisitor=(n=window.navigator.userAgent)=>{let a=g(this.getVisitorCookieName()),l=a&&this.validateVisitorId(a);this.#e=l,this.#s=l?a:this.generateVisitorId();let h=(n=>{var a;if(!n||"string"!=typeof n)return null;let l=new i(n).getResult(),h=(a=l.device.type)&&t.includes(a)?a:"desktop";return{pointer:o.includes(h)?"coarse":"fine",device:h,browser:((i="")=>{if(!i)return"other";let t=i.toLowerCase(),s=e.find(i=>t.includes(i));return s||(r.includes(t)?"social":"other")})(l.browser.name),os:((i="")=>{if(!i)return"unix";let t=i.toLowerCase(),e=s.find(i=>t.includes(i));return e||"unix"})(l.os.name)}})(n);return h?(this.#t=h,v(this.getVisitorCookieName(),this.#s),this.#s):null},this.getFlagValue=i=>{if(!this.config)return null;let t=this.config.flags[i];if(!t||!t.options[0])return null;if(this.#t||this.setupVisitor(),!this.#s||!this.#t)return t.options[0].slug;if(this.#t?.[i])return this.#t[i];if(!n(this.config.audience[t.audience],this.#t))return t.options[0].slug;let e=g(i)||a(t.options);return e?(this.#t[i]=e,v(i,e),e):null},this.getTestValue=i=>{if(!this.config)return null;let t=this.config.tests[i];if(!t)return null;if(this.#t||this.setupVisitor(),!this.#s||!this.#t)return t.defaultValue;if(this.#t?.[i])return this.#t[i];if(!n(this.config.audience[t.audience],this.#t))return t.defaultValue;if(t.allocation<100&&100*Math.random()>t.allocation)return this.#t[i]=t.defaultValue,this.#t?.[i];let e=g(i),s=e&&this.validateTestValue(i,e)?e:a(t.options);return s?(this.#t[i]=s,v(i,s),s):null},this.setAnalyticsUrls=i=>{this.#o=i},this.postAnalytic=(i,t,e)=>{var s;let r;if(!this.config)return null;let o=this.config.tests[i];if(this.#t||this.setupVisitor(),!o||!this.#t||this.#r?.[i]?.[t])return null;let n=this.#r[i]||{};n[t]=!0,this.#r[i]=n;let a=this.#t?.[i]||null;if(a||(a=this.getTestValue(i)||null),!a)return;let l={organizationId:this.organizationId,environment:this.environment,testId:o.id,testValue:a,visitorId:this.#s,pointer:this.#t.pointer,device:this.#t.device,screen:(r=window.innerWidth)<=768?"small":r<=1024?"medium":r<=1200?"large":"huge",browser:this.#t.browser,os:this.#t.os,visitor:this.#e?"recurring":"new",event:t,message:e||""};return this.#n&&(s={event:t,improve:{test:i,variant:a,visitorId:this.#s},_improve:!0},"u">typeof window&&(window.dataLayer=window.dataLayer||[],window.dataLayer.push(s))),fetch(this.#o,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(l)})},this.#o=`${this._baseUrl}${u}`,this.#n=l.dataLayer??!0}}export{p as ImproveClientSDK};
package/dist/server.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,"__esModule",{value:!0});var t=require("ua-parser-js"),i=t&&t.__esModule?t:{default:t};const e={device:["wearable","mobile","tablet","console","smarttv","embedded","desktop"],browser:["chrome","safari","firefox","edge","ie","samsung internet","social","other"],os:["mac os","ios","android","windows","unix"]},s=t=>t&&e.device.includes(t)?t:"desktop",r=["tiktok","wechat","weibo","snapchat","klarna","Line","instagram","facebook","alipay","Baidu"],o=(t="")=>{if(!t)return"other";let i=t.toLowerCase();return e.browser.find(t=>i.includes(t))||(r.includes(i)?"social":"other")},n=["wearable","mobile","tablet"],a=t=>n.includes(t)?"coarse":"fine",l=(t="")=>{if(!t)return"unix";let i=t.toLowerCase();return e.os.find(t=>i.includes(t))||"unix"},h=t=>{if(!t||"string"!=typeof t)return null;let e=new i.default(t).getResult(),r=s(e.device.type);return{pointer:a(r),device:r,browser:o(e.browser.name),os:l(e.os.name)}},u=(t,i)=>!t||Object.entries(t).every(([t,e])=>i[t]===e),f=t=>{if(0===t.length)return null;if(1===t.length)return t[0].slug;let i=Math.random()*t.reduce((t,{split:i})=>t+i,0);return(t.find(({split:t})=>(i-=t)<=0)||t[0]).slug},c="visi",g="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",d=t=>new Promise(i=>setTimeout(i,t)),v=async(t=1e3,i)=>(await d(t),i?.abort(),null),p=(t=3e3,i,e)=>{let s=new AbortController;return Promise.race([fetch(i,{...e,signal:s.signal}),v(t,s)])};class m{#t;constructor({organizationId:t,environment:i,state:e,config:s,fetchTimeout:r,baseUrl:o}){this.environment="develop",this.#t=null,this.config=null,this._fetchConfig=async t=>{if(this.config)return;if(!this.#t)throw Error("No config fetch setup provided");let i=await p(this.#t.timeout,this.#t.url,t);if(!i||!i.ok)throw Error("Configuration fetch timed-out");return this.config=await i.json(),this.config},this.loadConfig=t=>{this.config=t},this.generateVisitorId=()=>[c,(function(t=5){return t&&"number"==typeof t?Array(t).fill("").reduce(t=>t+=g.charAt(Math.floor(Math.random()*g.length)),""):""})(26).toUpperCase()].join("_"),this.getVisitorCookieName=()=>"visitorId",this.validateTestValue=(t,i)=>{if(!this.config)throw Error("Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup");let e=this.config.tests[t];if(!e)throw Error(`No config found for ${t}`);return!!e.options.find(t=>t.slug===i)},this.validateVisitorId=t=>{let i=t.split("_");if(2!==i.length)return!1;let[e,s]=i;return e===c&&26===s.length},this.organizationId=t,this.environment=i,this.state=e,this._baseUrl=o||"https://improve.obelism.studio",s?this.config=s:this.#t={url:[`${this._baseUrl}/config`,this.organizationId,this.environment,this.state||"active"].join("/"),timeout:r||3e3}}}exports.ImproveServerSDK=class extends m{#i;#e;constructor({token:t,...i}){super(i),this.#i={},this.fetchConfig=async t=>this._fetchConfig({...t,headers:{...t?.headers,token:this.#e}}),this.getFlagConfig=t=>this.config?.flags?.[t],this.getTestConfig=t=>this.config?.tests?.[t],this.getFlagValue=(t,i,e)=>{let s=this.getFlagConfig(t);if(!s||!this.config)return null;if(!i)return s.options[0].slug;if(this.#i?.[i]?.[e]?.[t])return this.#i[i][e][t];if(this.#i[i]=this.#i[i]||{},this.#i[i][e]=this.#i[i][e]||h(e),!u(this.config.audience[s.audience],this.#i[i][e]))return s.options[0].slug;let r=f(s.options);return r?(this.#i[i][e][t]=r,r):null},this.getTestValue=(t,i,e)=>{let s=this.getTestConfig(t);if(!s||!this.config)return null;if(!i||!e)return s.defaultValue;if(this.#i?.[i]?.[e]?.[t])return this.#i[i][e][t];if(this.#i[i]=this.#i[i]||{},this.#i[i][e]=this.#i[i][e]||h(e),!u(this.config.audience[s.audience],this.#i[i][e]))return s.defaultValue;if(s.allocation<100&&100*Math.random()>s.allocation)return this.#i[i][e][t]=s.defaultValue,this.#i[i][e][t];let r=f(s.options);return r?(this.#i[i][e][t]=r,r):null},this.#e=t}};
1
+ Object.defineProperty(exports,"__esModule",{value:!0});var t=require("ua-parser-js"),e=t&&t.__esModule?t:{default:t};let i=["wearable","mobile","tablet","console","smarttv","embedded","desktop"],s=["chrome","safari","firefox","edge","ie","samsung internet","social","other"],r=["mac os","ios","android","windows","unix"],o=["tiktok","wechat","weibo","snapchat","klarna","Line","instagram","facebook","alipay","Baidu"],n=["wearable","mobile","tablet"],a=(t,e)=>!t||Object.entries(t).every(([t,i])=>e[t]===i),l=t=>{if(0===t.length)return null;if(1===t.length)return t[0].slug;let e=Math.random()*t.reduce((t,{split:e})=>t+e,0);return(t.find(({split:t})=>(e-=t)<=0)||t[0]).slug},u="visi",h="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",f=async(t=1e3,e)=>(await new Promise(e=>setTimeout(e,t)),e?.abort(),null);class c{#t;constructor({organizationId:t,environment:e,state:i,config:s,fetchTimeout:r,baseUrl:o}){this.environment="develop",this.#t=null,this.config=null,this._fetchConfig=async t=>{if(this.config)return;if(!this.#t)throw Error("No config fetch setup provided");let e=await ((t=3e3,e,i)=>{let s=new AbortController;return Promise.race([fetch(e,{...i,signal:s.signal}),f(t,s)])})(this.#t.timeout,this.#t.url,t);if(!e||!e.ok)throw Error("Configuration fetch timed-out");return this.config=await e.json(),this.config},this.loadConfig=t=>{this.config=t},this.generateVisitorId=()=>[u,(function(t=5){return t&&"number"==typeof t?Array(t).fill("").reduce(t=>t+=h.charAt(Math.floor(Math.random()*h.length)),""):""})(26).toUpperCase()].join("_"),this.getVisitorCookieName=()=>"visitorId",this.validateTestValue=(t,e)=>{if(!this.config)throw Error("Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup");let i=this.config.tests[t];if(!i)throw Error(`No config found for ${t}`);return!!i.options.find(t=>t.slug===e)},this.validateVisitorId=t=>{let e=t.split("_");if(2!==e.length)return!1;let[i,s]=e;return i===u&&26===s.length},this.organizationId=t,this.environment=e,this.state=i,this._baseUrl=o||"https://improve.obelism.studio",s?this.config=s:this.#t={url:[`${this._baseUrl}/config`,this.organizationId,this.environment,this.state||"active"].join("/"),timeout:r||3e3}}}exports.ImproveServerSDK=class extends c{#e;#i;#s;constructor({token:t,maxVisitors:u,...h}){super(h),this.#e=new Map,this.fetchConfig=async t=>this._fetchConfig({...t,headers:{...t?.headers,token:this.#s}}),this.getFlagConfig=t=>this.config?.flags?.[t],this.getTestConfig=t=>this.config?.tests?.[t],this.#r=(t,a)=>{let l=this.#e.get(t);if(l)this.#e.delete(t),this.#e.set(t,l);else{if(this.#e.size>=this.#i){let t=this.#e.keys().next().value;t&&this.#e.delete(t)}l={},this.#e.set(t,l)}return l[a]=l[a]||(t=>{var a;if(!t||"string"!=typeof t)return null;let l=new e.default(t).getResult(),u=(a=l.device.type)&&i.includes(a)?a:"desktop";return{pointer:n.includes(u)?"coarse":"fine",device:u,browser:((t="")=>{if(!t)return"other";let e=t.toLowerCase(),i=s.find(t=>e.includes(t));return i||(o.includes(e)?"social":"other")})(l.browser.name),os:((t="")=>{if(!t)return"unix";let e=t.toLowerCase(),i=r.find(t=>e.includes(t));return i||"unix"})(l.os.name)}})(a),l},this.getFlagValue=(t,e,i)=>{let s=this.getFlagConfig(t);if(!s||!this.config)return null;if(!e)return s.options[0].slug;let r=this.#r(e,i);if(r[i]?.[t])return r[i][t];if(!a(this.config.audience[s.audience],r[i]))return s.options[0].slug;let o=l(s.options);return o?(r[i][t]=o,o):null},this.getTestValue=(t,e,i)=>{let s=this.getTestConfig(t);if(!s||!this.config)return null;if(!e||!i)return s.defaultValue;let r=this.#r(e,i);if(r[i]?.[t])return r[i][t];if(!a(this.config.audience[s.audience],r[i]))return s.defaultValue;if(s.allocation<100&&100*Math.random()>s.allocation)return r[i][t]=s.defaultValue,r[i][t];let o=l(s.options);return o?(r[i][t]=o,o):null},this.#s=t,this.#i=u??1e4}#r};
package/dist/server.d.ts CHANGED
@@ -19,12 +19,14 @@ declare class BaseImproveSDK {
19
19
 
20
20
  type ImproveServerSetupArgs = (Omit<ImproveSetupArgs, 'config' | 'baseUrl'> & {
21
21
  config: ImproveConfiguration;
22
+ maxVisitors?: number;
22
23
  }) | (Omit<ImproveSetupArgs, 'config'> & {
23
24
  token: string;
25
+ maxVisitors?: number;
24
26
  });
25
27
  declare class ImproveServerSDK extends BaseImproveSDK {
26
28
  #private;
27
- constructor({ token, ...args }: ImproveServerSetupArgs);
29
+ constructor({ token, maxVisitors, ...args }: ImproveServerSetupArgs);
28
30
  fetchConfig: (config?: RequestInit) => Promise<ImproveConfiguration>;
29
31
  getFlagConfig: (flagSlug: string) => __types.ImproveFlag;
30
32
  getTestConfig: (testSlug: string) => __types.ImproveTest;
package/dist/server.mjs CHANGED
@@ -1 +1 @@
1
- import t from"ua-parser-js";let i={device:["wearable","mobile","tablet","console","smarttv","embedded","desktop"],browser:["chrome","safari","firefox","edge","ie","samsung internet","social","other"],os:["mac os","ios","android","windows","unix"]},e=t=>t&&i.device.includes(t)?t:"desktop",s=["tiktok","wechat","weibo","snapchat","klarna","Line","instagram","facebook","alipay","Baidu"],o=(t="")=>{if(!t)return"other";let e=t.toLowerCase();return i.browser.find(t=>e.includes(t))||(s.includes(e)?"social":"other")},r=["wearable","mobile","tablet"],n=t=>r.includes(t)?"coarse":"fine",a=(t="")=>{if(!t)return"unix";let e=t.toLowerCase();return i.os.find(t=>e.includes(t))||"unix"},h=i=>{if(!i||"string"!=typeof i)return null;let s=new t(i).getResult(),r=e(s.device.type);return{pointer:n(r),device:r,browser:o(s.browser.name),os:a(s.os.name)}},l=(t,i)=>!t||Object.entries(t).every(([t,e])=>i[t]===e),u=t=>{if(0===t.length)return null;if(1===t.length)return t[0].slug;let i=Math.random()*t.reduce((t,{split:i})=>t+i,0);return(t.find(({split:t})=>(i-=t)<=0)||t[0]).slug},f="visi",c="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",g=t=>new Promise(i=>setTimeout(i,t)),d=async(t=1e3,i)=>(await g(t),i?.abort(),null),v=(t=3e3,i,e)=>{let s=new AbortController;return Promise.race([fetch(i,{...e,signal:s.signal}),d(t,s)])};class m{#t;constructor({organizationId:t,environment:i,state:e,config:s,fetchTimeout:o,baseUrl:r}){this.environment="develop",this.#t=null,this.config=null,this._fetchConfig=async t=>{if(this.config)return;if(!this.#t)throw Error("No config fetch setup provided");let i=await v(this.#t.timeout,this.#t.url,t);if(!i||!i.ok)throw Error("Configuration fetch timed-out");return this.config=await i.json(),this.config},this.loadConfig=t=>{this.config=t},this.generateVisitorId=()=>[f,(function(t=5){return t&&"number"==typeof t?Array(t).fill("").reduce(t=>t+=c.charAt(Math.floor(Math.random()*c.length)),""):""})(26).toUpperCase()].join("_"),this.getVisitorCookieName=()=>"visitorId",this.validateTestValue=(t,i)=>{if(!this.config)throw Error("Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup");let e=this.config.tests[t];if(!e)throw Error(`No config found for ${t}`);return!!e.options.find(t=>t.slug===i)},this.validateVisitorId=t=>{let i=t.split("_");if(2!==i.length)return!1;let[e,s]=i;return e===f&&26===s.length},this.organizationId=t,this.environment=i,this.state=e,this._baseUrl=r||"https://improve.obelism.studio",s?this.config=s:this.#t={url:[`${this._baseUrl}/config`,this.organizationId,this.environment,this.state||"active"].join("/"),timeout:o||3e3}}}class p extends m{#i;#e;constructor({token:t,...i}){super(i),this.#i={},this.fetchConfig=async t=>this._fetchConfig({...t,headers:{...t?.headers,token:this.#e}}),this.getFlagConfig=t=>this.config?.flags?.[t],this.getTestConfig=t=>this.config?.tests?.[t],this.getFlagValue=(t,i,e)=>{let s=this.getFlagConfig(t);if(!s||!this.config)return null;if(!i)return s.options[0].slug;if(this.#i?.[i]?.[e]?.[t])return this.#i[i][e][t];if(this.#i[i]=this.#i[i]||{},this.#i[i][e]=this.#i[i][e]||h(e),!l(this.config.audience[s.audience],this.#i[i][e]))return s.options[0].slug;let o=u(s.options);return o?(this.#i[i][e][t]=o,o):null},this.getTestValue=(t,i,e)=>{let s=this.getTestConfig(t);if(!s||!this.config)return null;if(!i||!e)return s.defaultValue;if(this.#i?.[i]?.[e]?.[t])return this.#i[i][e][t];if(this.#i[i]=this.#i[i]||{},this.#i[i][e]=this.#i[i][e]||h(e),!l(this.config.audience[s.audience],this.#i[i][e]))return s.defaultValue;if(s.allocation<100&&100*Math.random()>s.allocation)return this.#i[i][e][t]=s.defaultValue,this.#i[i][e][t];let o=u(s.options);return o?(this.#i[i][e][t]=o,o):null},this.#e=t}}export{p as ImproveServerSDK};
1
+ import t from"ua-parser-js";let i=["wearable","mobile","tablet","console","smarttv","embedded","desktop"],e=["chrome","safari","firefox","edge","ie","samsung internet","social","other"],s=["mac os","ios","android","windows","unix"],r=["tiktok","wechat","weibo","snapchat","klarna","Line","instagram","facebook","alipay","Baidu"],o=["wearable","mobile","tablet"],n=(t,i)=>!t||Object.entries(t).every(([t,e])=>i[t]===e),a=t=>{if(0===t.length)return null;if(1===t.length)return t[0].slug;let i=Math.random()*t.reduce((t,{split:i})=>t+i,0);return(t.find(({split:t})=>(i-=t)<=0)||t[0]).slug},l="visi",h="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",u=async(t=1e3,i)=>(await new Promise(i=>setTimeout(i,t)),i?.abort(),null);class f{#t;constructor({organizationId:t,environment:i,state:e,config:s,fetchTimeout:r,baseUrl:o}){this.environment="develop",this.#t=null,this.config=null,this._fetchConfig=async t=>{if(this.config)return;if(!this.#t)throw Error("No config fetch setup provided");let i=await ((t=3e3,i,e)=>{let s=new AbortController;return Promise.race([fetch(i,{...e,signal:s.signal}),u(t,s)])})(this.#t.timeout,this.#t.url,t);if(!i||!i.ok)throw Error("Configuration fetch timed-out");return this.config=await i.json(),this.config},this.loadConfig=t=>{this.config=t},this.generateVisitorId=()=>[l,(function(t=5){return t&&"number"==typeof t?Array(t).fill("").reduce(t=>t+=h.charAt(Math.floor(Math.random()*h.length)),""):""})(26).toUpperCase()].join("_"),this.getVisitorCookieName=()=>"visitorId",this.validateTestValue=(t,i)=>{if(!this.config)throw Error("Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup");let e=this.config.tests[t];if(!e)throw Error(`No config found for ${t}`);return!!e.options.find(t=>t.slug===i)},this.validateVisitorId=t=>{let i=t.split("_");if(2!==i.length)return!1;let[e,s]=i;return e===l&&26===s.length},this.organizationId=t,this.environment=i,this.state=e,this._baseUrl=o||"https://improve.obelism.studio",s?this.config=s:this.#t={url:[`${this._baseUrl}/config`,this.organizationId,this.environment,this.state||"active"].join("/"),timeout:r||3e3}}}class c extends f{#i;#e;#s;constructor({token:l,maxVisitors:h,...u}){super(u),this.#i=new Map,this.fetchConfig=async t=>this._fetchConfig({...t,headers:{...t?.headers,token:this.#s}}),this.getFlagConfig=t=>this.config?.flags?.[t],this.getTestConfig=t=>this.config?.tests?.[t],this.#r=(n,a)=>{let l=this.#i.get(n);if(l)this.#i.delete(n),this.#i.set(n,l);else{if(this.#i.size>=this.#e){let t=this.#i.keys().next().value;t&&this.#i.delete(t)}l={},this.#i.set(n,l)}return l[a]=l[a]||(n=>{var a;if(!n||"string"!=typeof n)return null;let l=new t(n).getResult(),h=(a=l.device.type)&&i.includes(a)?a:"desktop";return{pointer:o.includes(h)?"coarse":"fine",device:h,browser:((t="")=>{if(!t)return"other";let i=t.toLowerCase(),s=e.find(t=>i.includes(t));return s||(r.includes(i)?"social":"other")})(l.browser.name),os:((t="")=>{if(!t)return"unix";let i=t.toLowerCase(),e=s.find(t=>i.includes(t));return e||"unix"})(l.os.name)}})(a),l},this.getFlagValue=(t,i,e)=>{let s=this.getFlagConfig(t);if(!s||!this.config)return null;if(!i)return s.options[0].slug;let r=this.#r(i,e);if(r[e]?.[t])return r[e][t];if(!n(this.config.audience[s.audience],r[e]))return s.options[0].slug;let o=a(s.options);return o?(r[e][t]=o,o):null},this.getTestValue=(t,i,e)=>{let s=this.getTestConfig(t);if(!s||!this.config)return null;if(!i||!e)return s.defaultValue;let r=this.#r(i,e);if(r[e]?.[t])return r[e][t];if(!n(this.config.audience[s.audience],r[e]))return s.defaultValue;if(s.allocation<100&&100*Math.random()>s.allocation)return r[e][t]=s.defaultValue,r[e][t];let o=a(s.options);return o?(r[e][t]=o,o):null},this.#s=l,this.#e=h??1e4}#r}export{c as ImproveServerSDK};
package/dist/types.d.ts CHANGED
@@ -19,6 +19,12 @@ type ImproveSetupArgs = {
19
19
  config?: ImproveConfiguration;
20
20
  baseUrl?: string;
21
21
  fetchTimeout?: number;
22
+ /**
23
+ * Mirror analytics onto the GTM dataLayer (window.dataLayer) with experiment
24
+ * dimensions, so they can drive Google Tag Manager / Google Ads conversions.
25
+ * Enabled by default; set to `false` to opt out. No effect server-side.
26
+ */
27
+ dataLayer?: boolean;
22
28
  };
23
29
  type ImproveFlagOption = {
24
30
  name: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@obelism/improve-sdk",
3
3
  "description": "Obelism Improve SDK",
4
- "version": "0.3.6",
4
+ "version": "0.5.0",
5
5
  "keywords": [
6
6
  "ab-tests",
7
7
  "feature-flags",
@@ -14,7 +14,7 @@
14
14
  "type": "git",
15
15
  "url": "https://github.com/Obelism/improve-sdk"
16
16
  },
17
- "main": "./src/server.ts",
17
+ "sideEffects": false,
18
18
  "files": [
19
19
  "dist"
20
20
  ],