@obelism/improve-sdk 0.3.6 → 1.0.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,2 @@
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 i=require("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`};exports.ImproveClientSDK=class 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.UAParser(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}};
2
+ //# sourceMappingURL=client.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.cjs","sources":["../src/config/audiences.ts","../src/utils/parseUserAgent.ts","../src/utils/getVisitorMatchesAudience.ts","../src/utils/getRandomTestValue.ts","../src/config/constants.ts","../src/config/urls.ts","../src/utils/getRandomString.ts","../src/utils/getTimeoutError.ts","../src/utils/delay.ts","../src/base.ts","../src/utils/timeoutFetch.ts","../src/utils/clientCookie.ts","../src/client.ts","../src/utils/pushDataLayer.ts","../src/utils/getScreenSize.ts"],"sourcesContent":["/**\n * @constant AUDIENCE_PARAMS\n * @description All posibile audience tracking options\n */\nexport const AUDIENCE_PARAMS = {\n\t//? Technical\n\tpointer: ['coarse', 'fine'],\n\tdevice: [\n\t\t'wearable',\n\t\t'mobile',\n\t\t'tablet',\n\t\t'console',\n\t\t'smarttv',\n\t\t'embedded',\n\t\t'desktop',\n\t],\n\tbrowser: [\n\t\t'chrome',\n\t\t'safari',\n\t\t'firefox',\n\t\t'edge',\n\t\t'ie',\n\t\t'samsung internet',\n\t\t'social',\n\t\t'other',\n\t],\n\tos: ['mac os', 'ios', 'android', 'windows', 'unix'],\n} as const\n\n//? Generated types\nexport type AudienceParamKey = keyof typeof AUDIENCE_PARAMS\n\nexport type AudienceParamPointer = (typeof AUDIENCE_PARAMS.pointer)[number]\n\nexport type AudienceParamDevice = (typeof AUDIENCE_PARAMS.device)[number]\n\nexport type AudienceParamBrowser = (typeof AUDIENCE_PARAMS.browser)[number]\n\nexport type AudienceParamOs = (typeof AUDIENCE_PARAMS.os)[number]\n\nexport const AUDIENCE_PARAM_KEYS = Object.keys(\n\tAUDIENCE_PARAMS,\n) as ReadonlyArray<AudienceParamKey>\n","import { UAParser } from 'ua-parser-js'\n\nimport {\n\tAUDIENCE_PARAMS,\n\tAudienceParamBrowser,\n\tAudienceParamDevice,\n\tAudienceParamOs,\n\tAudienceParamPointer,\n} from '../config/audiences'\n\nexport type ParsedUserAgent = {\n\tpointer: AudienceParamPointer\n\tdevice: AudienceParamDevice\n\tbrowser: AudienceParamBrowser\n\tos: AudienceParamOs\n}\n\nconst formatDeviceType = (deviceType?: string): AudienceParamDevice => {\n\tif (\n\t\tdeviceType &&\n\t\tAUDIENCE_PARAMS.device.includes(deviceType as AudienceParamDevice)\n\t) {\n\t\treturn deviceType as AudienceParamDevice\n\t}\n\treturn 'desktop'\n}\n\nconst SOCIAL_BROWSERS: ReadonlyArray<string> = [\n\t'tiktok',\n\t'wechat',\n\t'weibo',\n\t'snapchat',\n\t'klarna',\n\t'Line',\n\t'instagram',\n\t'facebook',\n\t'alipay',\n\t'Baidu',\n]\n\nconst formatBrowser = (browserName: string = ''): AudienceParamBrowser => {\n\tif (!browserName) return 'other'\n\n\tconst name = browserName.toLowerCase()\n\n\tconst browserTypeMatch = AUDIENCE_PARAMS.browser.find((browserType) => {\n\t\treturn name.includes(browserType)\n\t})\n\n\tif (browserTypeMatch) return browserTypeMatch\n\n\tif (SOCIAL_BROWSERS.includes(name)) return 'social'\n\treturn 'other'\n}\n\nconst PRIMARY_TOUCH_DEVICES: ReadonlyArray<AudienceParamDevice> = [\n\t'wearable',\n\t'mobile',\n\t'tablet',\n]\n\nconst formatPointer = (device: AudienceParamDevice) => {\n\treturn PRIMARY_TOUCH_DEVICES.includes(device) ? 'coarse' : 'fine'\n}\n\nconst formatOs = (osName: string = ''): AudienceParamOs => {\n\tif (!osName) return 'unix'\n\n\tconst os = osName.toLowerCase()\n\n\tconst osTypeMatch = AUDIENCE_PARAMS.os.find((osType) => {\n\t\treturn os.includes(osType)\n\t})\n\n\tif (osTypeMatch) return osTypeMatch\n\treturn 'unix'\n}\n\nexport const parseUserAgent = (userAgent: string): ParsedUserAgent | null => {\n\tif (!userAgent || typeof userAgent !== 'string') return null\n\n\tconst parser = new UAParser(userAgent)\n\tconst results = parser.getResult()\n\n\tconst device = formatDeviceType(results.device.type)\n\n\treturn {\n\t\tpointer: formatPointer(device),\n\t\tdevice: device,\n\t\tbrowser: formatBrowser(results.browser.name),\n\t\tos: formatOs(results.os.name),\n\t}\n}\n","import { ImproveAudienceValue } from '../types'\nimport { AudienceParamKey } from '../config/audiences'\nimport { ParsedUserAgent } from './parseUserAgent'\n\nexport const getVisitorMatchesAudience = (\n\taudience: ImproveAudienceValue | undefined,\n\tvisitorParams: ParsedUserAgent,\n) => {\n\tif (!audience) return true\n\treturn Object.entries(audience).every(([paramKey, paramValue]) => {\n\t\treturn visitorParams[paramKey as AudienceParamKey] === paramValue\n\t})\n}\n","import { ImproveTestOption } from '../types'\n\n// function cryptoRandom() {\n// \treturn crypto.getRandomValues(new Uint32Array(1))[0] / (0xffffffff + 1)\n// }\n\nexport const getRandomTestValue = (options: ImproveTestOption[]) => {\n\tif (options.length === 0) return null\n\tif (options.length === 1) return options[0].slug\n\n\t// Get a random number between 0 and 1\n\tconst sum = options.reduce((acc, { split }) => acc + split, 0)\n\tlet value = Math.random() * sum\n\n\tconst match =\n\t\toptions.find(({ split }) => {\n\t\t\tvalue -= split\n\t\t\treturn value <= 0\n\t\t}) || options[0]\n\n\treturn match.slug\n}\n","export const COOKIE_NAME_VISITOR = 'visitorId'\n\nexport const VISITOR_ID_PREFIX = 'visi'\n\nexport const VISITOR_ID_LENGTH = 26\n\nexport const VISITOR_ID_SEPARATOR = '_'\n","export const BASE_URL = 'https://improve.obelism.studio'\nexport const CONFIG_PATH = `/config`\nexport const ANALYTICS_PATH = `/api/log`\n","/**\n * @constant CHAR_SET\n * @description Key/value set of characters to be used for generation strings\n */\nconst CHAR_SET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n\n/**\n * @function getRandomString\n * @description Generate a random string\n */\nexport function getRandomString(characters: number = 5): string {\n\tif (!characters || typeof characters !== 'number') return ''\n\treturn Array(characters)\n\t\t.fill('')\n\t\t.reduce((acc) => {\n\t\t\tacc += CHAR_SET.charAt(Math.floor(Math.random() * CHAR_SET.length))\n\t\t\treturn acc\n\t\t}, '')\n}\n","import { delay } from './delay'\n\n/**\n * @async @function getTimeoutError\n * @description Throw an error after a delay\n *\n * @param {number} [timeout] - time in ms after to reject default: 1000\n * @param {AbortController} [controller] - (optional) AbortController to abort the request\n */\nexport const getTimeoutError = async (\n\ttimeout: number = 1000,\n\tcontroller: AbortController,\n): Promise<null> => {\n\tawait delay(timeout)\n\tcontroller?.abort()\n\treturn null\n}\n","export const delay = (ms: number) =>\n\tnew Promise((resolve) => setTimeout(resolve, ms))\n","import {\n\tCOOKIE_NAME_VISITOR,\n\tVISITOR_ID_LENGTH,\n\tVISITOR_ID_PREFIX,\n\tVISITOR_ID_SEPARATOR,\n} from './config/constants'\nimport { CONFIG_PATH, BASE_URL } from './config/urls'\nimport {\n\tImproveConfiguration,\n\tImproveEnvironmentOption,\n\tImproveSetupArgs,\n\tImproveTestState,\n} from './types'\nimport { getRandomString } from './utils/getRandomString'\nimport { timeoutFetch } from './utils/timeoutFetch'\n\ntype ConfigFetch = {\n\turl: string\n\ttimeout: number\n}\n\nexport class BaseImproveSDK {\n\torganizationId: string\n\tenvironment: ImproveEnvironmentOption = 'develop'\n\tstate: ImproveTestState\n\n\t#configFetch: ConfigFetch | null = null\n\n\tconfig: ImproveConfiguration | null = null\n\n\t_baseUrl: undefined | string\n\n\tconstructor({\n\t\torganizationId,\n\t\tenvironment,\n\t\tstate,\n\t\tconfig,\n\t\tfetchTimeout,\n\t\tbaseUrl,\n\t}: ImproveSetupArgs) {\n\t\tthis.organizationId = organizationId\n\t\tthis.environment = environment\n\t\tthis.state = state\n\t\tthis._baseUrl = baseUrl || BASE_URL\n\n\t\tif (config) {\n\t\t\tthis.config = config\n\t\t} else {\n\t\t\tthis.#configFetch = {\n\t\t\t\turl: [\n\t\t\t\t\t`${this._baseUrl}${CONFIG_PATH}`,\n\t\t\t\t\tthis.organizationId,\n\t\t\t\t\tthis.environment,\n\t\t\t\t\tthis.state || 'active',\n\t\t\t\t].join('/'),\n\t\t\t\ttimeout: fetchTimeout || 3000,\n\t\t\t}\n\t\t}\n\t}\n\n\t_fetchConfig = async (config?: RequestInit) => {\n\t\tif (this.config) return\n\n\t\tif (!this.#configFetch) throw new Error('No config fetch setup provided')\n\n\t\tconst res = await timeoutFetch(\n\t\t\tthis.#configFetch.timeout,\n\t\t\tthis.#configFetch.url,\n\t\t\tconfig,\n\t\t)\n\t\tif (!res || !res.ok) throw new Error('Configuration fetch timed-out')\n\n\t\tthis.config = await res.json()\n\t\treturn this.config\n\t}\n\n\tloadConfig = (config: ImproveConfiguration) => {\n\t\tthis.config = config\n\t}\n\n\tgenerateVisitorId = () => {\n\t\treturn [\n\t\t\tVISITOR_ID_PREFIX,\n\t\t\tgetRandomString(VISITOR_ID_LENGTH).toUpperCase(),\n\t\t].join(VISITOR_ID_SEPARATOR)\n\t}\n\n\tgetVisitorCookieName = () => COOKIE_NAME_VISITOR\n\n\tvalidateTestValue = (testName: string, testValue: string) => {\n\t\tif (!this.config)\n\t\t\tthrow new Error(\n\t\t\t\t'Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup',\n\t\t\t)\n\n\t\tconst testConfig = this.config.tests[testName]\n\n\t\tif (!testConfig) throw new Error(`No config found for ${testName}`)\n\n\t\treturn Boolean(\n\t\t\ttestConfig.options.find((option) => option.slug === testValue),\n\t\t)\n\t}\n\n\tvalidateVisitorId = (possibleVisitorId: string) => {\n\t\tconst visitorIdParts = possibleVisitorId.split(VISITOR_ID_SEPARATOR)\n\t\tif (visitorIdParts.length !== 2) return false\n\t\tconst [key, value] = visitorIdParts as [string, string]\n\t\treturn key === VISITOR_ID_PREFIX && value.length === VISITOR_ID_LENGTH\n\t}\n}\n","import { getTimeoutError } from './getTimeoutError'\n\n/**\n * @async @function timeoutFetch\n * @description Fetch with a timeout\n *\n * @param {number} timeout - time in ms after to reject default: 3000\n * @param {string} url - url to fetch\n * @param {Object} [options] - (optional) options to pass to fetch\n */\nexport const timeoutFetch = (\n\ttimeout: number = 3000,\n\turl: string,\n\toptions?: object,\n) => {\n\tconst controller = new AbortController()\n\treturn Promise.race([\n\t\tfetch(url, {\n\t\t\t...options,\n\t\t\tsignal: controller.signal,\n\t\t}),\n\t\tgetTimeoutError(timeout, controller),\n\t])\n}\n","const ROW_DELIMITER = '; '\nconst ENTRY_DELIMITER = '='\n\nexport const getCookie = (name: string) => {\n\tif (!name) return false\n\tconst cookie = document.cookie.split(ROW_DELIMITER).find((row) => {\n\t\tconst [key] = row.split(ENTRY_DELIMITER)\n\t\treturn name === key\n\t})\n\treturn cookie ? cookie.split(ENTRY_DELIMITER)[1] : false\n}\n\nexport const setCookie = (name: string, value: string) => {\n\tconst now = new Date()\n\tnow.setDate(now.getDate() + 30)\n\tdocument.cookie = `${name}=${value};path=/;expires=${now.toUTCString()};SameSite=Lax;Secure`\n}\n","import { ParsedUserAgent, parseUserAgent } from './utils/parseUserAgent'\nimport { getVisitorMatchesAudience } from './utils/getVisitorMatchesAudience'\nimport { getRandomTestValue } from './utils/getRandomTestValue'\nimport { BaseImproveSDK } from './base'\nimport { getCookie, setCookie } from './utils/clientCookie'\nimport { ANALYTICS_PATH, BASE_URL } from './config/urls'\nimport { getScreenSize } from './utils/getScreenSize'\nimport { pushDataLayer } from './utils/pushDataLayer'\nimport { ImproveEnvironmentOption, ImproveSetupArgs } from './types'\n\ntype Visitor = ParsedUserAgent & {\n\t[testSlug: string]: string\n}\n\nexport type CreateAnalytic = {\n\torganizationId: string\n\tenvironment: ImproveEnvironmentOption\n\n\ttestId: string\n\ttestValue: string\n\tvisitorId: string\n\n\tpointer: string\n\tdevice: string\n\tscreen: string\n\tbrowser: string\n\tos: string\n\tvisitor: string\n\n\tevent: string\n\tmessage: string\n}\n\ntype TrackedAnalytics = {\n\t[TestSlug: string]: {\n\t\t[Event: string]: boolean\n\t}\n}\n\nexport class ImproveClientSDK extends BaseImproveSDK {\n\t#visitor: Visitor | undefined\n\t#visitorRecurring: boolean = false\n\n\t#visitorId: string = ''\n\n\t#analytics: TrackedAnalytics = {}\n\n\t#analyticsUrl = `${BASE_URL}${ANALYTICS_PATH}`\n\n\t#dataLayerEnabled: boolean = true\n\n\tfetchConfig = this._fetchConfig\n\n\tconstructor(args: ImproveSetupArgs) {\n\t\tsuper(args)\n\t\tthis.#analyticsUrl = `${this._baseUrl}${ANALYTICS_PATH}`\n\t\tthis.#dataLayerEnabled = args.dataLayer ?? true\n\t}\n\n\tsetupVisitor = (userAgent: string = window.navigator.userAgent) => {\n\t\tconst cookieVisitorId = getCookie(this.getVisitorCookieName())\n\t\tconst validCookieVisitorId =\n\t\t\tcookieVisitorId && this.validateVisitorId(cookieVisitorId)\n\n\t\tthis.#visitorRecurring = validCookieVisitorId\n\t\tthis.#visitorId = validCookieVisitorId\n\t\t\t? cookieVisitorId\n\t\t\t: this.generateVisitorId()\n\n\t\tconst parsedUserAgent = parseUserAgent(userAgent)\n\n\t\tif (!parsedUserAgent) return null\n\n\t\tthis.#visitor = parsedUserAgent\n\n\t\tsetCookie(this.getVisitorCookieName(), this.#visitorId)\n\n\t\treturn this.#visitorId\n\t}\n\n\tgetFlagValue = (flagSlug: string) => {\n\t\tif (!this.config) return null\n\n\t\tconst flagConfig = this.config.flags[flagSlug]\n\n\t\tif (!flagConfig || !flagConfig.options[0]) return null\n\n\t\tif (!this.#visitor) this.setupVisitor()\n\t\tif (!this.#visitorId || !this.#visitor) return flagConfig.options[0].slug\n\t\tif (this.#visitor?.[flagSlug]) return this.#visitor[flagSlug]\n\n\t\tconst visitorMatchesAudience = getVisitorMatchesAudience(\n\t\t\tthis.config.audience[flagConfig.audience],\n\t\t\tthis.#visitor,\n\t\t)\n\n\t\tif (!visitorMatchesAudience) return flagConfig.options[0].slug\n\n\t\tconst flagValue =\n\t\t\tgetCookie(flagSlug) || getRandomTestValue(flagConfig.options)\n\n\t\tif (!flagValue) return null\n\n\t\tthis.#visitor[flagSlug] = flagValue\n\n\t\tsetCookie(flagSlug, flagValue)\n\n\t\treturn flagValue\n\t}\n\n\tgetTestValue = (testSlug: string) => {\n\t\tif (!this.config) return null\n\n\t\tconst testConfig = this.config.tests[testSlug]\n\n\t\tif (!testConfig) return null\n\n\t\tif (!this.#visitor) this.setupVisitor()\n\n\t\tif (!this.#visitorId || !this.#visitor) return testConfig.defaultValue\n\t\tif (this.#visitor?.[testSlug]) return this.#visitor[testSlug]\n\n\t\tconst visitorMatchesAudience = getVisitorMatchesAudience(\n\t\t\tthis.config.audience[testConfig.audience],\n\t\t\tthis.#visitor,\n\t\t)\n\n\t\tif (!visitorMatchesAudience) return testConfig.defaultValue\n\n\t\tif (\n\t\t\ttestConfig.allocation < 100 &&\n\t\t\tMath.random() * 100 > testConfig.allocation\n\t\t) {\n\t\t\tthis.#visitor[testSlug] = testConfig.defaultValue\n\t\t\treturn this.#visitor?.[testSlug]\n\t\t}\n\n\t\tconst cookieTestValue = getCookie(testSlug)\n\t\tconst validCookieTestValue =\n\t\t\tcookieTestValue && this.validateTestValue(testSlug, cookieTestValue)\n\t\tconst testValue = validCookieTestValue\n\t\t\t? cookieTestValue\n\t\t\t: getRandomTestValue(testConfig.options)\n\n\t\tif (!testValue) return null\n\n\t\tthis.#visitor[testSlug] = testValue\n\n\t\tsetCookie(testSlug, testValue)\n\n\t\treturn testValue\n\t}\n\n\tsetAnalyticsUrls = (url: string) => {\n\t\tthis.#analyticsUrl = url\n\t}\n\n\tpostAnalytic = (testSlug: string, event: string, message?: string) => {\n\t\tif (!this.config) return null\n\n\t\tconst testConfig = this.config.tests[testSlug]\n\n\t\tif (!this.#visitor) this.setupVisitor()\n\t\tif (!testConfig || !this.#visitor || this.#analytics?.[testSlug]?.[event]) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst testSlugAnalytics = this.#analytics[testSlug] || {}\n\t\ttestSlugAnalytics[event] = true\n\t\tthis.#analytics[testSlug] = testSlugAnalytics\n\n\t\tlet testValue = this.#visitor?.[testSlug] || null\n\t\tif (!testValue) {\n\t\t\ttestValue = this.getTestValue(testSlug) || null\n\t\t}\n\n\t\tif (!testValue) return\n\n\t\tconst body: CreateAnalytic = {\n\t\t\torganizationId: this.organizationId,\n\t\t\tenvironment: this.environment,\n\n\t\t\ttestId: testConfig.id,\n\t\t\ttestValue: testValue,\n\t\t\tvisitorId: this.#visitorId,\n\t\t\tpointer: this.#visitor.pointer,\n\t\t\tdevice: this.#visitor.device,\n\t\t\tscreen: getScreenSize(),\n\t\t\tbrowser: this.#visitor.browser,\n\t\t\tos: this.#visitor.os,\n\t\t\tvisitor: this.#visitorRecurring ? 'recurring' : 'new',\n\t\t\tevent: event,\n\t\t\tmessage: message || '',\n\t\t}\n\n\t\tif (this.#dataLayerEnabled) {\n\t\t\tpushDataLayer({\n\t\t\t\tevent,\n\t\t\t\timprove: {\n\t\t\t\t\ttest: testSlug,\n\t\t\t\t\tvariant: testValue,\n\t\t\t\t\tvisitorId: this.#visitorId,\n\t\t\t\t},\n\t\t\t\t_improve: true,\n\t\t\t})\n\t\t}\n\n\t\treturn fetch(this.#analyticsUrl, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify(body),\n\t\t})\n\t}\n}\n","export type ImproveDataLayerEntry = {\n\tevent: string\n\timprove: {\n\t\ttest: string\n\t\tvariant: string\n\t\tvisitorId: string\n\t}\n\t/**\n\t * Marks the entry as originating from Improve so platform-side dataLayer\n\t * importers can ignore it (loop prevention).\n\t */\n\t_improve: true\n}\n\ndeclare global {\n\tinterface Window {\n\t\tdataLayer?: Record<string, unknown>[]\n\t}\n}\n\n/**\n * Mirror an analytic onto the GTM dataLayer (with experiment dimensions) so it\n * can drive Google Tag Manager / Google Ads conversions. No-op outside the\n * browser. Initializes window.dataLayer if GTM hasn't yet.\n */\nexport const pushDataLayer = (entry: ImproveDataLayerEntry) => {\n\tif (typeof window === 'undefined') return\n\n\twindow.dataLayer = window.dataLayer || []\n\twindow.dataLayer.push(entry)\n}\n","type SizeOptions = 'small' | 'medium' | 'large' | 'huge'\n\nexport const getScreenSize = (): SizeOptions => {\n\tconst size = window.innerWidth\n\tif (size <= 768) return 'small'\n\tif (size <= 1024) return 'medium'\n\tif (size <= 1200) return 'large'\n\treturn 'huge'\n}\n"],"names":["SOCIAL_BROWSERS","PRIMARY_TOUCH_DEVICES","getVisitorMatchesAudience","audience","visitorParams","Object","entries","every","paramKey","paramValue","getRandomTestValue","options","length","slug","value","Math","random","reduce","acc","split","match","find","VISITOR_ID_PREFIX","BASE_URL","ANALYTICS_PATH","CHAR_SET","getTimeoutError","timeout","controller","Promise","resolve","setTimeout","abort","BaseImproveSDK","organizationId","environment","state","config","fetchTimeout","baseUrl","_fetchConfig","Error","res","timeoutFetch","url","AbortController","race","fetch","signal","ok","json","loadConfig","generateVisitorId","getRandomString","characters","Array","fill","charAt","floor","toUpperCase","join","getVisitorCookieName","validateTestValue","testName","testValue","testConfig","tests","Boolean","option","validateVisitorId","possibleVisitorId","visitorIdParts","key","_baseUrl","getCookie","name","cookie","document","row","setCookie","now","Date","setDate","getDate","toUTCString","args","fetchConfig","setupVisitor","userAgent","window","navigator","cookieVisitorId","validCookieVisitorId","parsedUserAgent","parseUserAgent","deviceType","results","parser","UAParser","getResult","device","type","AUDIENCE_PARAMS","includes","pointer","browser","formatBrowser","browserName","toLowerCase","browserTypeMatch","browserType","os","formatOs","osName","osTypeMatch","osType","getFlagValue","flagSlug","flagConfig","flags","flagValue","getTestValue","testSlug","defaultValue","allocation","cookieTestValue","validCookieTestValue","setAnalyticsUrls","postAnalytic","event","message","entry","size","testSlugAnalytics","body","testId","id","visitorId","screen","innerWidth","visitor","improve","test","variant","_improve","dataLayer","push","method","headers","JSON","stringify"],"mappings":"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"}
package/dist/client.d.ts CHANGED
@@ -43,4 +43,6 @@ 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 };
48
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sources":["../src/base.ts","../src/client.ts"],"mappings":";;;AACA,cAAc,cAAc;AAC5B;AACA;AACA,iBAAiB,wBAAwB;AACzC,WAAW,gBAAgB;AAC3B,YAAY,oBAAoB;AAChC;AACA,wFAAwF,gBAAgB;AACxG,4BAA4B,WAAW,KAAK,OAAO,CAAC,oBAAoB;AACxE,yBAAyB,oBAAoB;AAC7C;AACA;AACA;AACA;AACA;;ACZA,KAAK,cAAc;AACnB;AACA,iBAAiB,wBAAwB;AACzC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,cAAc,gBAAgB,SAAS,cAAc;AACrD;AACA,2BAA2B,WAAW,KAAK,OAAO,CAAC,OAAO,CAAC,oBAAoB;AAC/E,sBAAsB,gBAAgB;AACtC;AACA;AACA;AACA;AACA,yEAAyE,OAAO,CAAC,QAAQ;AACzF;;;;","names":[]}
package/dist/client.mjs CHANGED
@@ -1 +1,2 @@
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{UAParser as 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};
2
+ //# sourceMappingURL=client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.mjs","sources":["../src/config/audiences.ts","../src/utils/parseUserAgent.ts","../src/utils/getVisitorMatchesAudience.ts","../src/utils/getRandomTestValue.ts","../src/config/constants.ts","../src/config/urls.ts","../src/utils/getRandomString.ts","../src/utils/getTimeoutError.ts","../src/utils/delay.ts","../src/base.ts","../src/utils/timeoutFetch.ts","../src/utils/clientCookie.ts","../src/client.ts","../src/utils/pushDataLayer.ts","../src/utils/getScreenSize.ts"],"sourcesContent":["/**\n * @constant AUDIENCE_PARAMS\n * @description All posibile audience tracking options\n */\nexport const AUDIENCE_PARAMS = {\n\t//? Technical\n\tpointer: ['coarse', 'fine'],\n\tdevice: [\n\t\t'wearable',\n\t\t'mobile',\n\t\t'tablet',\n\t\t'console',\n\t\t'smarttv',\n\t\t'embedded',\n\t\t'desktop',\n\t],\n\tbrowser: [\n\t\t'chrome',\n\t\t'safari',\n\t\t'firefox',\n\t\t'edge',\n\t\t'ie',\n\t\t'samsung internet',\n\t\t'social',\n\t\t'other',\n\t],\n\tos: ['mac os', 'ios', 'android', 'windows', 'unix'],\n} as const\n\n//? Generated types\nexport type AudienceParamKey = keyof typeof AUDIENCE_PARAMS\n\nexport type AudienceParamPointer = (typeof AUDIENCE_PARAMS.pointer)[number]\n\nexport type AudienceParamDevice = (typeof AUDIENCE_PARAMS.device)[number]\n\nexport type AudienceParamBrowser = (typeof AUDIENCE_PARAMS.browser)[number]\n\nexport type AudienceParamOs = (typeof AUDIENCE_PARAMS.os)[number]\n\nexport const AUDIENCE_PARAM_KEYS = Object.keys(\n\tAUDIENCE_PARAMS,\n) as ReadonlyArray<AudienceParamKey>\n","import { UAParser } from 'ua-parser-js'\n\nimport {\n\tAUDIENCE_PARAMS,\n\tAudienceParamBrowser,\n\tAudienceParamDevice,\n\tAudienceParamOs,\n\tAudienceParamPointer,\n} from '../config/audiences'\n\nexport type ParsedUserAgent = {\n\tpointer: AudienceParamPointer\n\tdevice: AudienceParamDevice\n\tbrowser: AudienceParamBrowser\n\tos: AudienceParamOs\n}\n\nconst formatDeviceType = (deviceType?: string): AudienceParamDevice => {\n\tif (\n\t\tdeviceType &&\n\t\tAUDIENCE_PARAMS.device.includes(deviceType as AudienceParamDevice)\n\t) {\n\t\treturn deviceType as AudienceParamDevice\n\t}\n\treturn 'desktop'\n}\n\nconst SOCIAL_BROWSERS: ReadonlyArray<string> = [\n\t'tiktok',\n\t'wechat',\n\t'weibo',\n\t'snapchat',\n\t'klarna',\n\t'Line',\n\t'instagram',\n\t'facebook',\n\t'alipay',\n\t'Baidu',\n]\n\nconst formatBrowser = (browserName: string = ''): AudienceParamBrowser => {\n\tif (!browserName) return 'other'\n\n\tconst name = browserName.toLowerCase()\n\n\tconst browserTypeMatch = AUDIENCE_PARAMS.browser.find((browserType) => {\n\t\treturn name.includes(browserType)\n\t})\n\n\tif (browserTypeMatch) return browserTypeMatch\n\n\tif (SOCIAL_BROWSERS.includes(name)) return 'social'\n\treturn 'other'\n}\n\nconst PRIMARY_TOUCH_DEVICES: ReadonlyArray<AudienceParamDevice> = [\n\t'wearable',\n\t'mobile',\n\t'tablet',\n]\n\nconst formatPointer = (device: AudienceParamDevice) => {\n\treturn PRIMARY_TOUCH_DEVICES.includes(device) ? 'coarse' : 'fine'\n}\n\nconst formatOs = (osName: string = ''): AudienceParamOs => {\n\tif (!osName) return 'unix'\n\n\tconst os = osName.toLowerCase()\n\n\tconst osTypeMatch = AUDIENCE_PARAMS.os.find((osType) => {\n\t\treturn os.includes(osType)\n\t})\n\n\tif (osTypeMatch) return osTypeMatch\n\treturn 'unix'\n}\n\nexport const parseUserAgent = (userAgent: string): ParsedUserAgent | null => {\n\tif (!userAgent || typeof userAgent !== 'string') return null\n\n\tconst parser = new UAParser(userAgent)\n\tconst results = parser.getResult()\n\n\tconst device = formatDeviceType(results.device.type)\n\n\treturn {\n\t\tpointer: formatPointer(device),\n\t\tdevice: device,\n\t\tbrowser: formatBrowser(results.browser.name),\n\t\tos: formatOs(results.os.name),\n\t}\n}\n","import { ImproveAudienceValue } from '../types'\nimport { AudienceParamKey } from '../config/audiences'\nimport { ParsedUserAgent } from './parseUserAgent'\n\nexport const getVisitorMatchesAudience = (\n\taudience: ImproveAudienceValue | undefined,\n\tvisitorParams: ParsedUserAgent,\n) => {\n\tif (!audience) return true\n\treturn Object.entries(audience).every(([paramKey, paramValue]) => {\n\t\treturn visitorParams[paramKey as AudienceParamKey] === paramValue\n\t})\n}\n","import { ImproveTestOption } from '../types'\n\n// function cryptoRandom() {\n// \treturn crypto.getRandomValues(new Uint32Array(1))[0] / (0xffffffff + 1)\n// }\n\nexport const getRandomTestValue = (options: ImproveTestOption[]) => {\n\tif (options.length === 0) return null\n\tif (options.length === 1) return options[0].slug\n\n\t// Get a random number between 0 and 1\n\tconst sum = options.reduce((acc, { split }) => acc + split, 0)\n\tlet value = Math.random() * sum\n\n\tconst match =\n\t\toptions.find(({ split }) => {\n\t\t\tvalue -= split\n\t\t\treturn value <= 0\n\t\t}) || options[0]\n\n\treturn match.slug\n}\n","export const COOKIE_NAME_VISITOR = 'visitorId'\n\nexport const VISITOR_ID_PREFIX = 'visi'\n\nexport const VISITOR_ID_LENGTH = 26\n\nexport const VISITOR_ID_SEPARATOR = '_'\n","export const BASE_URL = 'https://improve.obelism.studio'\nexport const CONFIG_PATH = `/config`\nexport const ANALYTICS_PATH = `/api/log`\n","/**\n * @constant CHAR_SET\n * @description Key/value set of characters to be used for generation strings\n */\nconst CHAR_SET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n\n/**\n * @function getRandomString\n * @description Generate a random string\n */\nexport function getRandomString(characters: number = 5): string {\n\tif (!characters || typeof characters !== 'number') return ''\n\treturn Array(characters)\n\t\t.fill('')\n\t\t.reduce((acc) => {\n\t\t\tacc += CHAR_SET.charAt(Math.floor(Math.random() * CHAR_SET.length))\n\t\t\treturn acc\n\t\t}, '')\n}\n","import { delay } from './delay'\n\n/**\n * @async @function getTimeoutError\n * @description Throw an error after a delay\n *\n * @param {number} [timeout] - time in ms after to reject default: 1000\n * @param {AbortController} [controller] - (optional) AbortController to abort the request\n */\nexport const getTimeoutError = async (\n\ttimeout: number = 1000,\n\tcontroller: AbortController,\n): Promise<null> => {\n\tawait delay(timeout)\n\tcontroller?.abort()\n\treturn null\n}\n","export const delay = (ms: number) =>\n\tnew Promise((resolve) => setTimeout(resolve, ms))\n","import {\n\tCOOKIE_NAME_VISITOR,\n\tVISITOR_ID_LENGTH,\n\tVISITOR_ID_PREFIX,\n\tVISITOR_ID_SEPARATOR,\n} from './config/constants'\nimport { CONFIG_PATH, BASE_URL } from './config/urls'\nimport {\n\tImproveConfiguration,\n\tImproveEnvironmentOption,\n\tImproveSetupArgs,\n\tImproveTestState,\n} from './types'\nimport { getRandomString } from './utils/getRandomString'\nimport { timeoutFetch } from './utils/timeoutFetch'\n\ntype ConfigFetch = {\n\turl: string\n\ttimeout: number\n}\n\nexport class BaseImproveSDK {\n\torganizationId: string\n\tenvironment: ImproveEnvironmentOption = 'develop'\n\tstate: ImproveTestState\n\n\t#configFetch: ConfigFetch | null = null\n\n\tconfig: ImproveConfiguration | null = null\n\n\t_baseUrl: undefined | string\n\n\tconstructor({\n\t\torganizationId,\n\t\tenvironment,\n\t\tstate,\n\t\tconfig,\n\t\tfetchTimeout,\n\t\tbaseUrl,\n\t}: ImproveSetupArgs) {\n\t\tthis.organizationId = organizationId\n\t\tthis.environment = environment\n\t\tthis.state = state\n\t\tthis._baseUrl = baseUrl || BASE_URL\n\n\t\tif (config) {\n\t\t\tthis.config = config\n\t\t} else {\n\t\t\tthis.#configFetch = {\n\t\t\t\turl: [\n\t\t\t\t\t`${this._baseUrl}${CONFIG_PATH}`,\n\t\t\t\t\tthis.organizationId,\n\t\t\t\t\tthis.environment,\n\t\t\t\t\tthis.state || 'active',\n\t\t\t\t].join('/'),\n\t\t\t\ttimeout: fetchTimeout || 3000,\n\t\t\t}\n\t\t}\n\t}\n\n\t_fetchConfig = async (config?: RequestInit) => {\n\t\tif (this.config) return\n\n\t\tif (!this.#configFetch) throw new Error('No config fetch setup provided')\n\n\t\tconst res = await timeoutFetch(\n\t\t\tthis.#configFetch.timeout,\n\t\t\tthis.#configFetch.url,\n\t\t\tconfig,\n\t\t)\n\t\tif (!res || !res.ok) throw new Error('Configuration fetch timed-out')\n\n\t\tthis.config = await res.json()\n\t\treturn this.config\n\t}\n\n\tloadConfig = (config: ImproveConfiguration) => {\n\t\tthis.config = config\n\t}\n\n\tgenerateVisitorId = () => {\n\t\treturn [\n\t\t\tVISITOR_ID_PREFIX,\n\t\t\tgetRandomString(VISITOR_ID_LENGTH).toUpperCase(),\n\t\t].join(VISITOR_ID_SEPARATOR)\n\t}\n\n\tgetVisitorCookieName = () => COOKIE_NAME_VISITOR\n\n\tvalidateTestValue = (testName: string, testValue: string) => {\n\t\tif (!this.config)\n\t\t\tthrow new Error(\n\t\t\t\t'Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup',\n\t\t\t)\n\n\t\tconst testConfig = this.config.tests[testName]\n\n\t\tif (!testConfig) throw new Error(`No config found for ${testName}`)\n\n\t\treturn Boolean(\n\t\t\ttestConfig.options.find((option) => option.slug === testValue),\n\t\t)\n\t}\n\n\tvalidateVisitorId = (possibleVisitorId: string) => {\n\t\tconst visitorIdParts = possibleVisitorId.split(VISITOR_ID_SEPARATOR)\n\t\tif (visitorIdParts.length !== 2) return false\n\t\tconst [key, value] = visitorIdParts as [string, string]\n\t\treturn key === VISITOR_ID_PREFIX && value.length === VISITOR_ID_LENGTH\n\t}\n}\n","import { getTimeoutError } from './getTimeoutError'\n\n/**\n * @async @function timeoutFetch\n * @description Fetch with a timeout\n *\n * @param {number} timeout - time in ms after to reject default: 3000\n * @param {string} url - url to fetch\n * @param {Object} [options] - (optional) options to pass to fetch\n */\nexport const timeoutFetch = (\n\ttimeout: number = 3000,\n\turl: string,\n\toptions?: object,\n) => {\n\tconst controller = new AbortController()\n\treturn Promise.race([\n\t\tfetch(url, {\n\t\t\t...options,\n\t\t\tsignal: controller.signal,\n\t\t}),\n\t\tgetTimeoutError(timeout, controller),\n\t])\n}\n","const ROW_DELIMITER = '; '\nconst ENTRY_DELIMITER = '='\n\nexport const getCookie = (name: string) => {\n\tif (!name) return false\n\tconst cookie = document.cookie.split(ROW_DELIMITER).find((row) => {\n\t\tconst [key] = row.split(ENTRY_DELIMITER)\n\t\treturn name === key\n\t})\n\treturn cookie ? cookie.split(ENTRY_DELIMITER)[1] : false\n}\n\nexport const setCookie = (name: string, value: string) => {\n\tconst now = new Date()\n\tnow.setDate(now.getDate() + 30)\n\tdocument.cookie = `${name}=${value};path=/;expires=${now.toUTCString()};SameSite=Lax;Secure`\n}\n","import { ParsedUserAgent, parseUserAgent } from './utils/parseUserAgent'\nimport { getVisitorMatchesAudience } from './utils/getVisitorMatchesAudience'\nimport { getRandomTestValue } from './utils/getRandomTestValue'\nimport { BaseImproveSDK } from './base'\nimport { getCookie, setCookie } from './utils/clientCookie'\nimport { ANALYTICS_PATH, BASE_URL } from './config/urls'\nimport { getScreenSize } from './utils/getScreenSize'\nimport { pushDataLayer } from './utils/pushDataLayer'\nimport { ImproveEnvironmentOption, ImproveSetupArgs } from './types'\n\ntype Visitor = ParsedUserAgent & {\n\t[testSlug: string]: string\n}\n\nexport type CreateAnalytic = {\n\torganizationId: string\n\tenvironment: ImproveEnvironmentOption\n\n\ttestId: string\n\ttestValue: string\n\tvisitorId: string\n\n\tpointer: string\n\tdevice: string\n\tscreen: string\n\tbrowser: string\n\tos: string\n\tvisitor: string\n\n\tevent: string\n\tmessage: string\n}\n\ntype TrackedAnalytics = {\n\t[TestSlug: string]: {\n\t\t[Event: string]: boolean\n\t}\n}\n\nexport class ImproveClientSDK extends BaseImproveSDK {\n\t#visitor: Visitor | undefined\n\t#visitorRecurring: boolean = false\n\n\t#visitorId: string = ''\n\n\t#analytics: TrackedAnalytics = {}\n\n\t#analyticsUrl = `${BASE_URL}${ANALYTICS_PATH}`\n\n\t#dataLayerEnabled: boolean = true\n\n\tfetchConfig = this._fetchConfig\n\n\tconstructor(args: ImproveSetupArgs) {\n\t\tsuper(args)\n\t\tthis.#analyticsUrl = `${this._baseUrl}${ANALYTICS_PATH}`\n\t\tthis.#dataLayerEnabled = args.dataLayer ?? true\n\t}\n\n\tsetupVisitor = (userAgent: string = window.navigator.userAgent) => {\n\t\tconst cookieVisitorId = getCookie(this.getVisitorCookieName())\n\t\tconst validCookieVisitorId =\n\t\t\tcookieVisitorId && this.validateVisitorId(cookieVisitorId)\n\n\t\tthis.#visitorRecurring = validCookieVisitorId\n\t\tthis.#visitorId = validCookieVisitorId\n\t\t\t? cookieVisitorId\n\t\t\t: this.generateVisitorId()\n\n\t\tconst parsedUserAgent = parseUserAgent(userAgent)\n\n\t\tif (!parsedUserAgent) return null\n\n\t\tthis.#visitor = parsedUserAgent\n\n\t\tsetCookie(this.getVisitorCookieName(), this.#visitorId)\n\n\t\treturn this.#visitorId\n\t}\n\n\tgetFlagValue = (flagSlug: string) => {\n\t\tif (!this.config) return null\n\n\t\tconst flagConfig = this.config.flags[flagSlug]\n\n\t\tif (!flagConfig || !flagConfig.options[0]) return null\n\n\t\tif (!this.#visitor) this.setupVisitor()\n\t\tif (!this.#visitorId || !this.#visitor) return flagConfig.options[0].slug\n\t\tif (this.#visitor?.[flagSlug]) return this.#visitor[flagSlug]\n\n\t\tconst visitorMatchesAudience = getVisitorMatchesAudience(\n\t\t\tthis.config.audience[flagConfig.audience],\n\t\t\tthis.#visitor,\n\t\t)\n\n\t\tif (!visitorMatchesAudience) return flagConfig.options[0].slug\n\n\t\tconst flagValue =\n\t\t\tgetCookie(flagSlug) || getRandomTestValue(flagConfig.options)\n\n\t\tif (!flagValue) return null\n\n\t\tthis.#visitor[flagSlug] = flagValue\n\n\t\tsetCookie(flagSlug, flagValue)\n\n\t\treturn flagValue\n\t}\n\n\tgetTestValue = (testSlug: string) => {\n\t\tif (!this.config) return null\n\n\t\tconst testConfig = this.config.tests[testSlug]\n\n\t\tif (!testConfig) return null\n\n\t\tif (!this.#visitor) this.setupVisitor()\n\n\t\tif (!this.#visitorId || !this.#visitor) return testConfig.defaultValue\n\t\tif (this.#visitor?.[testSlug]) return this.#visitor[testSlug]\n\n\t\tconst visitorMatchesAudience = getVisitorMatchesAudience(\n\t\t\tthis.config.audience[testConfig.audience],\n\t\t\tthis.#visitor,\n\t\t)\n\n\t\tif (!visitorMatchesAudience) return testConfig.defaultValue\n\n\t\tif (\n\t\t\ttestConfig.allocation < 100 &&\n\t\t\tMath.random() * 100 > testConfig.allocation\n\t\t) {\n\t\t\tthis.#visitor[testSlug] = testConfig.defaultValue\n\t\t\treturn this.#visitor?.[testSlug]\n\t\t}\n\n\t\tconst cookieTestValue = getCookie(testSlug)\n\t\tconst validCookieTestValue =\n\t\t\tcookieTestValue && this.validateTestValue(testSlug, cookieTestValue)\n\t\tconst testValue = validCookieTestValue\n\t\t\t? cookieTestValue\n\t\t\t: getRandomTestValue(testConfig.options)\n\n\t\tif (!testValue) return null\n\n\t\tthis.#visitor[testSlug] = testValue\n\n\t\tsetCookie(testSlug, testValue)\n\n\t\treturn testValue\n\t}\n\n\tsetAnalyticsUrls = (url: string) => {\n\t\tthis.#analyticsUrl = url\n\t}\n\n\tpostAnalytic = (testSlug: string, event: string, message?: string) => {\n\t\tif (!this.config) return null\n\n\t\tconst testConfig = this.config.tests[testSlug]\n\n\t\tif (!this.#visitor) this.setupVisitor()\n\t\tif (!testConfig || !this.#visitor || this.#analytics?.[testSlug]?.[event]) {\n\t\t\treturn null\n\t\t}\n\n\t\tconst testSlugAnalytics = this.#analytics[testSlug] || {}\n\t\ttestSlugAnalytics[event] = true\n\t\tthis.#analytics[testSlug] = testSlugAnalytics\n\n\t\tlet testValue = this.#visitor?.[testSlug] || null\n\t\tif (!testValue) {\n\t\t\ttestValue = this.getTestValue(testSlug) || null\n\t\t}\n\n\t\tif (!testValue) return\n\n\t\tconst body: CreateAnalytic = {\n\t\t\torganizationId: this.organizationId,\n\t\t\tenvironment: this.environment,\n\n\t\t\ttestId: testConfig.id,\n\t\t\ttestValue: testValue,\n\t\t\tvisitorId: this.#visitorId,\n\t\t\tpointer: this.#visitor.pointer,\n\t\t\tdevice: this.#visitor.device,\n\t\t\tscreen: getScreenSize(),\n\t\t\tbrowser: this.#visitor.browser,\n\t\t\tos: this.#visitor.os,\n\t\t\tvisitor: this.#visitorRecurring ? 'recurring' : 'new',\n\t\t\tevent: event,\n\t\t\tmessage: message || '',\n\t\t}\n\n\t\tif (this.#dataLayerEnabled) {\n\t\t\tpushDataLayer({\n\t\t\t\tevent,\n\t\t\t\timprove: {\n\t\t\t\t\ttest: testSlug,\n\t\t\t\t\tvariant: testValue,\n\t\t\t\t\tvisitorId: this.#visitorId,\n\t\t\t\t},\n\t\t\t\t_improve: true,\n\t\t\t})\n\t\t}\n\n\t\treturn fetch(this.#analyticsUrl, {\n\t\t\tmethod: 'POST',\n\t\t\theaders: { 'Content-Type': 'application/json' },\n\t\t\tbody: JSON.stringify(body),\n\t\t})\n\t}\n}\n","export type ImproveDataLayerEntry = {\n\tevent: string\n\timprove: {\n\t\ttest: string\n\t\tvariant: string\n\t\tvisitorId: string\n\t}\n\t/**\n\t * Marks the entry as originating from Improve so platform-side dataLayer\n\t * importers can ignore it (loop prevention).\n\t */\n\t_improve: true\n}\n\ndeclare global {\n\tinterface Window {\n\t\tdataLayer?: Record<string, unknown>[]\n\t}\n}\n\n/**\n * Mirror an analytic onto the GTM dataLayer (with experiment dimensions) so it\n * can drive Google Tag Manager / Google Ads conversions. No-op outside the\n * browser. Initializes window.dataLayer if GTM hasn't yet.\n */\nexport const pushDataLayer = (entry: ImproveDataLayerEntry) => {\n\tif (typeof window === 'undefined') return\n\n\twindow.dataLayer = window.dataLayer || []\n\twindow.dataLayer.push(entry)\n}\n","type SizeOptions = 'small' | 'medium' | 'large' | 'huge'\n\nexport const getScreenSize = (): SizeOptions => {\n\tconst size = window.innerWidth\n\tif (size <= 768) return 'small'\n\tif (size <= 1024) return 'medium'\n\tif (size <= 1200) return 'large'\n\treturn 'huge'\n}\n"],"names":["SOCIAL_BROWSERS","PRIMARY_TOUCH_DEVICES","getVisitorMatchesAudience","audience","visitorParams","Object","entries","every","paramKey","paramValue","getRandomTestValue","options","length","slug","value","Math","random","reduce","acc","split","match","find","VISITOR_ID_PREFIX","BASE_URL","ANALYTICS_PATH","CHAR_SET","getTimeoutError","timeout","controller","Promise","resolve","setTimeout","abort","BaseImproveSDK","organizationId","environment","state","config","fetchTimeout","baseUrl","_fetchConfig","Error","res","timeoutFetch","url","AbortController","race","fetch","signal","ok","json","loadConfig","generateVisitorId","getRandomString","characters","Array","fill","charAt","floor","toUpperCase","join","getVisitorCookieName","validateTestValue","testName","testValue","testConfig","tests","Boolean","option","validateVisitorId","possibleVisitorId","visitorIdParts","key","_baseUrl","getCookie","name","cookie","document","row","setCookie","now","Date","setDate","getDate","toUTCString","ImproveClientSDK","args","fetchConfig","setupVisitor","userAgent","window","navigator","cookieVisitorId","validCookieVisitorId","parsedUserAgent","parseUserAgent","deviceType","results","parser","UAParser","getResult","device","type","AUDIENCE_PARAMS","includes","pointer","browser","formatBrowser","browserName","toLowerCase","browserTypeMatch","browserType","os","formatOs","osName","osTypeMatch","osType","getFlagValue","flagSlug","flagConfig","flags","flagValue","getTestValue","testSlug","defaultValue","allocation","cookieTestValue","validCookieTestValue","setAnalyticsUrls","postAnalytic","event","message","entry","size","testSlugAnalytics","body","testId","id","visitorId","screen","innerWidth","visitor","improve","test","variant","_improve","dataLayer","push","method","headers","JSON","stringify"],"mappings":"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"}
package/dist/server.cjs CHANGED
@@ -1 +1,2 @@
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");let e=["wearable","mobile","tablet","console","smarttv","embedded","desktop"],i=["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,e)=>!t||Object.entries(t).every(([t,i])=>e[t]===i),a=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},l="visi",h="ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",u=async(t=1e3,e)=>(await new Promise(e=>setTimeout(e,t)),e?.abort(),null);class f{#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}),u(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=()=>[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,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===l&&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 f{#e;#i;#s;constructor({token:l,maxVisitors:h,...u}){super(u),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=(n,a)=>{let l=this.#e.get(n);if(l)this.#e.delete(n),this.#e.set(n,l);else{if(this.#e.size>=this.#i){let t=this.#e.keys().next().value;t&&this.#e.delete(t)}l={},this.#e.set(n,l)}return l[a]=l[a]||(n=>{var a;if(!n||"string"!=typeof n)return null;let l=new t.UAParser(n).getResult(),h=(a=l.device.type)&&e.includes(a)?a:"desktop";return{pointer:o.includes(h)?"coarse":"fine",device:h,browser:((t="")=>{if(!t)return"other";let e=t.toLowerCase(),s=i.find(t=>e.includes(t));return s||(r.includes(e)?"social":"other")})(l.browser.name),os:((t="")=>{if(!t)return"unix";let e=t.toLowerCase(),i=s.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(!n(this.config.audience[s.audience],r[i]))return s.options[0].slug;let o=a(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(!n(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=a(s.options);return o?(r[i][t]=o,o):null},this.#s=l,this.#i=h??1e4}#r};
2
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.cjs","sources":["../src/config/audiences.ts","../src/utils/parseUserAgent.ts","../src/utils/getVisitorMatchesAudience.ts","../src/utils/getRandomTestValue.ts","../src/config/constants.ts","../src/utils/getRandomString.ts","../src/utils/getTimeoutError.ts","../src/utils/delay.ts","../src/base.ts","../src/utils/timeoutFetch.ts","../src/config/urls.ts","../src/server.ts"],"sourcesContent":["/**\n * @constant AUDIENCE_PARAMS\n * @description All posibile audience tracking options\n */\nexport const AUDIENCE_PARAMS = {\n\t//? Technical\n\tpointer: ['coarse', 'fine'],\n\tdevice: [\n\t\t'wearable',\n\t\t'mobile',\n\t\t'tablet',\n\t\t'console',\n\t\t'smarttv',\n\t\t'embedded',\n\t\t'desktop',\n\t],\n\tbrowser: [\n\t\t'chrome',\n\t\t'safari',\n\t\t'firefox',\n\t\t'edge',\n\t\t'ie',\n\t\t'samsung internet',\n\t\t'social',\n\t\t'other',\n\t],\n\tos: ['mac os', 'ios', 'android', 'windows', 'unix'],\n} as const\n\n//? Generated types\nexport type AudienceParamKey = keyof typeof AUDIENCE_PARAMS\n\nexport type AudienceParamPointer = (typeof AUDIENCE_PARAMS.pointer)[number]\n\nexport type AudienceParamDevice = (typeof AUDIENCE_PARAMS.device)[number]\n\nexport type AudienceParamBrowser = (typeof AUDIENCE_PARAMS.browser)[number]\n\nexport type AudienceParamOs = (typeof AUDIENCE_PARAMS.os)[number]\n\nexport const AUDIENCE_PARAM_KEYS = Object.keys(\n\tAUDIENCE_PARAMS,\n) as ReadonlyArray<AudienceParamKey>\n","import { UAParser } from 'ua-parser-js'\n\nimport {\n\tAUDIENCE_PARAMS,\n\tAudienceParamBrowser,\n\tAudienceParamDevice,\n\tAudienceParamOs,\n\tAudienceParamPointer,\n} from '../config/audiences'\n\nexport type ParsedUserAgent = {\n\tpointer: AudienceParamPointer\n\tdevice: AudienceParamDevice\n\tbrowser: AudienceParamBrowser\n\tos: AudienceParamOs\n}\n\nconst formatDeviceType = (deviceType?: string): AudienceParamDevice => {\n\tif (\n\t\tdeviceType &&\n\t\tAUDIENCE_PARAMS.device.includes(deviceType as AudienceParamDevice)\n\t) {\n\t\treturn deviceType as AudienceParamDevice\n\t}\n\treturn 'desktop'\n}\n\nconst SOCIAL_BROWSERS: ReadonlyArray<string> = [\n\t'tiktok',\n\t'wechat',\n\t'weibo',\n\t'snapchat',\n\t'klarna',\n\t'Line',\n\t'instagram',\n\t'facebook',\n\t'alipay',\n\t'Baidu',\n]\n\nconst formatBrowser = (browserName: string = ''): AudienceParamBrowser => {\n\tif (!browserName) return 'other'\n\n\tconst name = browserName.toLowerCase()\n\n\tconst browserTypeMatch = AUDIENCE_PARAMS.browser.find((browserType) => {\n\t\treturn name.includes(browserType)\n\t})\n\n\tif (browserTypeMatch) return browserTypeMatch\n\n\tif (SOCIAL_BROWSERS.includes(name)) return 'social'\n\treturn 'other'\n}\n\nconst PRIMARY_TOUCH_DEVICES: ReadonlyArray<AudienceParamDevice> = [\n\t'wearable',\n\t'mobile',\n\t'tablet',\n]\n\nconst formatPointer = (device: AudienceParamDevice) => {\n\treturn PRIMARY_TOUCH_DEVICES.includes(device) ? 'coarse' : 'fine'\n}\n\nconst formatOs = (osName: string = ''): AudienceParamOs => {\n\tif (!osName) return 'unix'\n\n\tconst os = osName.toLowerCase()\n\n\tconst osTypeMatch = AUDIENCE_PARAMS.os.find((osType) => {\n\t\treturn os.includes(osType)\n\t})\n\n\tif (osTypeMatch) return osTypeMatch\n\treturn 'unix'\n}\n\nexport const parseUserAgent = (userAgent: string): ParsedUserAgent | null => {\n\tif (!userAgent || typeof userAgent !== 'string') return null\n\n\tconst parser = new UAParser(userAgent)\n\tconst results = parser.getResult()\n\n\tconst device = formatDeviceType(results.device.type)\n\n\treturn {\n\t\tpointer: formatPointer(device),\n\t\tdevice: device,\n\t\tbrowser: formatBrowser(results.browser.name),\n\t\tos: formatOs(results.os.name),\n\t}\n}\n","import { ImproveAudienceValue } from '../types'\nimport { AudienceParamKey } from '../config/audiences'\nimport { ParsedUserAgent } from './parseUserAgent'\n\nexport const getVisitorMatchesAudience = (\n\taudience: ImproveAudienceValue | undefined,\n\tvisitorParams: ParsedUserAgent,\n) => {\n\tif (!audience) return true\n\treturn Object.entries(audience).every(([paramKey, paramValue]) => {\n\t\treturn visitorParams[paramKey as AudienceParamKey] === paramValue\n\t})\n}\n","import { ImproveTestOption } from '../types'\n\n// function cryptoRandom() {\n// \treturn crypto.getRandomValues(new Uint32Array(1))[0] / (0xffffffff + 1)\n// }\n\nexport const getRandomTestValue = (options: ImproveTestOption[]) => {\n\tif (options.length === 0) return null\n\tif (options.length === 1) return options[0].slug\n\n\t// Get a random number between 0 and 1\n\tconst sum = options.reduce((acc, { split }) => acc + split, 0)\n\tlet value = Math.random() * sum\n\n\tconst match =\n\t\toptions.find(({ split }) => {\n\t\t\tvalue -= split\n\t\t\treturn value <= 0\n\t\t}) || options[0]\n\n\treturn match.slug\n}\n","export const COOKIE_NAME_VISITOR = 'visitorId'\n\nexport const VISITOR_ID_PREFIX = 'visi'\n\nexport const VISITOR_ID_LENGTH = 26\n\nexport const VISITOR_ID_SEPARATOR = '_'\n","/**\n * @constant CHAR_SET\n * @description Key/value set of characters to be used for generation strings\n */\nconst CHAR_SET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n\n/**\n * @function getRandomString\n * @description Generate a random string\n */\nexport function getRandomString(characters: number = 5): string {\n\tif (!characters || typeof characters !== 'number') return ''\n\treturn Array(characters)\n\t\t.fill('')\n\t\t.reduce((acc) => {\n\t\t\tacc += CHAR_SET.charAt(Math.floor(Math.random() * CHAR_SET.length))\n\t\t\treturn acc\n\t\t}, '')\n}\n","import { delay } from './delay'\n\n/**\n * @async @function getTimeoutError\n * @description Throw an error after a delay\n *\n * @param {number} [timeout] - time in ms after to reject default: 1000\n * @param {AbortController} [controller] - (optional) AbortController to abort the request\n */\nexport const getTimeoutError = async (\n\ttimeout: number = 1000,\n\tcontroller: AbortController,\n): Promise<null> => {\n\tawait delay(timeout)\n\tcontroller?.abort()\n\treturn null\n}\n","export const delay = (ms: number) =>\n\tnew Promise((resolve) => setTimeout(resolve, ms))\n","import {\n\tCOOKIE_NAME_VISITOR,\n\tVISITOR_ID_LENGTH,\n\tVISITOR_ID_PREFIX,\n\tVISITOR_ID_SEPARATOR,\n} from './config/constants'\nimport { CONFIG_PATH, BASE_URL } from './config/urls'\nimport {\n\tImproveConfiguration,\n\tImproveEnvironmentOption,\n\tImproveSetupArgs,\n\tImproveTestState,\n} from './types'\nimport { getRandomString } from './utils/getRandomString'\nimport { timeoutFetch } from './utils/timeoutFetch'\n\ntype ConfigFetch = {\n\turl: string\n\ttimeout: number\n}\n\nexport class BaseImproveSDK {\n\torganizationId: string\n\tenvironment: ImproveEnvironmentOption = 'develop'\n\tstate: ImproveTestState\n\n\t#configFetch: ConfigFetch | null = null\n\n\tconfig: ImproveConfiguration | null = null\n\n\t_baseUrl: undefined | string\n\n\tconstructor({\n\t\torganizationId,\n\t\tenvironment,\n\t\tstate,\n\t\tconfig,\n\t\tfetchTimeout,\n\t\tbaseUrl,\n\t}: ImproveSetupArgs) {\n\t\tthis.organizationId = organizationId\n\t\tthis.environment = environment\n\t\tthis.state = state\n\t\tthis._baseUrl = baseUrl || BASE_URL\n\n\t\tif (config) {\n\t\t\tthis.config = config\n\t\t} else {\n\t\t\tthis.#configFetch = {\n\t\t\t\turl: [\n\t\t\t\t\t`${this._baseUrl}${CONFIG_PATH}`,\n\t\t\t\t\tthis.organizationId,\n\t\t\t\t\tthis.environment,\n\t\t\t\t\tthis.state || 'active',\n\t\t\t\t].join('/'),\n\t\t\t\ttimeout: fetchTimeout || 3000,\n\t\t\t}\n\t\t}\n\t}\n\n\t_fetchConfig = async (config?: RequestInit) => {\n\t\tif (this.config) return\n\n\t\tif (!this.#configFetch) throw new Error('No config fetch setup provided')\n\n\t\tconst res = await timeoutFetch(\n\t\t\tthis.#configFetch.timeout,\n\t\t\tthis.#configFetch.url,\n\t\t\tconfig,\n\t\t)\n\t\tif (!res || !res.ok) throw new Error('Configuration fetch timed-out')\n\n\t\tthis.config = await res.json()\n\t\treturn this.config\n\t}\n\n\tloadConfig = (config: ImproveConfiguration) => {\n\t\tthis.config = config\n\t}\n\n\tgenerateVisitorId = () => {\n\t\treturn [\n\t\t\tVISITOR_ID_PREFIX,\n\t\t\tgetRandomString(VISITOR_ID_LENGTH).toUpperCase(),\n\t\t].join(VISITOR_ID_SEPARATOR)\n\t}\n\n\tgetVisitorCookieName = () => COOKIE_NAME_VISITOR\n\n\tvalidateTestValue = (testName: string, testValue: string) => {\n\t\tif (!this.config)\n\t\t\tthrow new Error(\n\t\t\t\t'Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup',\n\t\t\t)\n\n\t\tconst testConfig = this.config.tests[testName]\n\n\t\tif (!testConfig) throw new Error(`No config found for ${testName}`)\n\n\t\treturn Boolean(\n\t\t\ttestConfig.options.find((option) => option.slug === testValue),\n\t\t)\n\t}\n\n\tvalidateVisitorId = (possibleVisitorId: string) => {\n\t\tconst visitorIdParts = possibleVisitorId.split(VISITOR_ID_SEPARATOR)\n\t\tif (visitorIdParts.length !== 2) return false\n\t\tconst [key, value] = visitorIdParts as [string, string]\n\t\treturn key === VISITOR_ID_PREFIX && value.length === VISITOR_ID_LENGTH\n\t}\n}\n","import { getTimeoutError } from './getTimeoutError'\n\n/**\n * @async @function timeoutFetch\n * @description Fetch with a timeout\n *\n * @param {number} timeout - time in ms after to reject default: 3000\n * @param {string} url - url to fetch\n * @param {Object} [options] - (optional) options to pass to fetch\n */\nexport const timeoutFetch = (\n\ttimeout: number = 3000,\n\turl: string,\n\toptions?: object,\n) => {\n\tconst controller = new AbortController()\n\treturn Promise.race([\n\t\tfetch(url, {\n\t\t\t...options,\n\t\t\tsignal: controller.signal,\n\t\t}),\n\t\tgetTimeoutError(timeout, controller),\n\t])\n}\n","export const BASE_URL = 'https://improve.obelism.studio'\nexport const CONFIG_PATH = `/config`\nexport const ANALYTICS_PATH = `/api/log`\n","import { ParsedUserAgent, parseUserAgent } from './utils/parseUserAgent'\nimport { getVisitorMatchesAudience } from './utils/getVisitorMatchesAudience'\nimport { getRandomTestValue } from './utils/getRandomTestValue'\nimport { BaseImproveSDK } from './base'\nimport { ImproveConfiguration, ImproveSetupArgs } from './types'\n\nconst DEFAULT_MAX_VISITORS = 10_000\n\ntype VisitorData = {\n\t[userAgent: string]: ParsedUserAgent & {\n\t\t[testSlug: string]: string\n\t}\n}\n\ntype ImproveServerSetupArgs =\n\t| (Omit<ImproveSetupArgs, 'config' | 'baseUrl'> & {\n\t\t\tconfig: ImproveConfiguration\n\t\t\tmaxVisitors?: number\n\t })\n\t| (Omit<ImproveSetupArgs, 'config'> & {\n\t\t\ttoken: string\n\t\t\tmaxVisitors?: number\n\t })\n\nexport class ImproveServerSDK extends BaseImproveSDK {\n\t#visitors: Map<string, VisitorData> = new Map()\n\t#maxVisitors: number\n\t#token: string\n\n\t// @ts-ignore It could be there\n\tconstructor({ token, maxVisitors, ...args }: ImproveServerSetupArgs) {\n\t\tsuper(args)\n\t\tthis.#token = token\n\t\tthis.#maxVisitors = maxVisitors ?? DEFAULT_MAX_VISITORS\n\t}\n\n\tfetchConfig = async (config?: RequestInit) => {\n\t\treturn this._fetchConfig({\n\t\t\t...config,\n\t\t\theaders: {\n\t\t\t\t...config?.headers,\n\t\t\t\ttoken: this.#token,\n\t\t\t},\n\t\t})\n\t}\n\n\tgetFlagConfig = (flagSlug: string) => this.config?.flags?.[flagSlug]\n\n\tgetTestConfig = (testSlug: string) => this.config?.tests?.[testSlug]\n\n\t#getVisitor = (visitorId: string, userAgent: string) => {\n\t\tlet visitor = this.#visitors.get(visitorId)\n\t\tif (visitor) {\n\t\t\t// Move to end for LRU freshness\n\t\t\tthis.#visitors.delete(visitorId)\n\t\t\tthis.#visitors.set(visitorId, visitor)\n\t\t} else {\n\t\t\t// Evict oldest entries when at capacity\n\t\t\tif (this.#visitors.size >= this.#maxVisitors) {\n\t\t\t\tconst oldest = this.#visitors.keys().next().value\n\t\t\t\tif (oldest) this.#visitors.delete(oldest)\n\t\t\t}\n\t\t\tvisitor = {}\n\t\t\tthis.#visitors.set(visitorId, visitor)\n\t\t}\n\t\tvisitor[userAgent] = visitor[userAgent] || parseUserAgent(userAgent)\n\t\treturn visitor\n\t}\n\n\tgetFlagValue = (flagSlug: string, visitorId: string, userAgent: string) => {\n\t\tconst flagConfig = this.getFlagConfig(flagSlug)\n\n\t\tif (!flagConfig || !this.config) return null\n\t\tif (!visitorId) return flagConfig.options[0].slug\n\n\t\tconst visitor = this.#getVisitor(visitorId, userAgent)\n\n\t\tif (visitor[userAgent]?.[flagSlug]) {\n\t\t\treturn visitor[userAgent][flagSlug]\n\t\t}\n\n\t\tconst visitorMatchesAudience = getVisitorMatchesAudience(\n\t\t\tthis.config.audience[flagConfig.audience],\n\t\t\tvisitor[userAgent],\n\t\t)\n\n\t\tif (!visitorMatchesAudience) return flagConfig.options[0].slug\n\n\t\tconst flagValue = getRandomTestValue(flagConfig.options)\n\n\t\tif (!flagValue) return null\n\n\t\tvisitor[userAgent][flagSlug] = flagValue\n\t\treturn flagValue\n\t}\n\n\tgetTestValue = (testSlug: string, visitorId: string, userAgent: string) => {\n\t\tconst testConfig = this.getTestConfig(testSlug)\n\n\t\tif (!testConfig || !this.config) return null\n\n\t\tif (!visitorId || !userAgent) return testConfig.defaultValue\n\n\t\tconst visitor = this.#getVisitor(visitorId, userAgent)\n\n\t\tif (visitor[userAgent]?.[testSlug]) {\n\t\t\treturn visitor[userAgent][testSlug]\n\t\t}\n\n\t\tconst visitorMatchesAudience = getVisitorMatchesAudience(\n\t\t\tthis.config.audience[testConfig.audience],\n\t\t\tvisitor[userAgent],\n\t\t)\n\n\t\tif (!visitorMatchesAudience) return testConfig.defaultValue\n\n\t\tif (\n\t\t\ttestConfig.allocation < 100 &&\n\t\t\tMath.random() * 100 > testConfig.allocation\n\t\t) {\n\t\t\tvisitor[userAgent][testSlug] = testConfig.defaultValue\n\t\t\treturn visitor[userAgent][testSlug]\n\t\t}\n\n\t\tconst testValue = getRandomTestValue(testConfig.options)\n\n\t\tif (!testValue) return null\n\n\t\tvisitor[userAgent][testSlug] = testValue\n\t\treturn testValue\n\t}\n}\n"],"names":["SOCIAL_BROWSERS","PRIMARY_TOUCH_DEVICES","getVisitorMatchesAudience","audience","visitorParams","Object","entries","every","paramKey","paramValue","getRandomTestValue","options","length","slug","value","Math","random","reduce","acc","split","match","find","VISITOR_ID_PREFIX","CHAR_SET","getTimeoutError","timeout","controller","Promise","resolve","setTimeout","abort","BaseImproveSDK","organizationId","environment","state","config","fetchTimeout","baseUrl","_fetchConfig","Error","res","timeoutFetch","url","AbortController","race","fetch","signal","ok","json","loadConfig","generateVisitorId","getRandomString","characters","Array","fill","charAt","floor","toUpperCase","join","getVisitorCookieName","validateTestValue","testName","testValue","testConfig","tests","Boolean","option","validateVisitorId","possibleVisitorId","visitorIdParts","key","_baseUrl","token","maxVisitors","args","Map","fetchConfig","headers","getFlagConfig","flagSlug","flags","getTestConfig","testSlug","visitorId","userAgent","visitor","get","delete","set","size","oldest","keys","next","parseUserAgent","deviceType","results","parser","UAParser","getResult","device","type","AUDIENCE_PARAMS","includes","pointer","browser","formatBrowser","browserName","name","toLowerCase","browserTypeMatch","browserType","os","formatOs","osName","osTypeMatch","osType","getFlagValue","flagConfig","flagValue","getTestValue","defaultValue","allocation"],"mappings":"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"}
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;
@@ -33,3 +35,4 @@ declare class ImproveServerSDK extends BaseImproveSDK {
33
35
  }
34
36
 
35
37
  export { ImproveServerSDK };
38
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sources":["../src/base.ts","../src/server.ts"],"mappings":";;;AACA,cAAc,cAAc;AAC5B;AACA;AACA,iBAAiB,wBAAwB;AACzC,WAAW,gBAAgB;AAC3B,YAAY,oBAAoB;AAChC;AACA,wFAAwF,gBAAgB;AACxG,4BAA4B,WAAW,KAAK,OAAO,CAAC,oBAAoB;AACxE,yBAAyB,oBAAoB;AAC7C;AACA;AACA;AACA;AACA;;ACZA,KAAK,sBAAsB,IAAI,IAAI,CAAC,gBAAgB;AACpD,YAAY,oBAAoB;AAChC;AACA,MAAM,IAAI,CAAC,gBAAgB;AAC3B;AACA;AACA;AACA,cAAc,gBAAgB,SAAS,cAAc;AACrD;AACA,iDAAiD,sBAAsB;AACvE,2BAA2B,WAAW,KAAK,OAAO,CAAC,oBAAoB;AACvE,yCAAyC,OAAO,CAAC,WAAW;AAC5D,yCAAyC,OAAO,CAAC,WAAW;AAC5D;AACA;AACA;;;;","names":[]}
package/dist/server.mjs CHANGED
@@ -1 +1,2 @@
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{UAParser as 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};
2
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.mjs","sources":["../src/config/audiences.ts","../src/utils/parseUserAgent.ts","../src/utils/getVisitorMatchesAudience.ts","../src/utils/getRandomTestValue.ts","../src/config/constants.ts","../src/utils/getRandomString.ts","../src/utils/getTimeoutError.ts","../src/utils/delay.ts","../src/base.ts","../src/utils/timeoutFetch.ts","../src/config/urls.ts","../src/server.ts"],"sourcesContent":["/**\n * @constant AUDIENCE_PARAMS\n * @description All posibile audience tracking options\n */\nexport const AUDIENCE_PARAMS = {\n\t//? Technical\n\tpointer: ['coarse', 'fine'],\n\tdevice: [\n\t\t'wearable',\n\t\t'mobile',\n\t\t'tablet',\n\t\t'console',\n\t\t'smarttv',\n\t\t'embedded',\n\t\t'desktop',\n\t],\n\tbrowser: [\n\t\t'chrome',\n\t\t'safari',\n\t\t'firefox',\n\t\t'edge',\n\t\t'ie',\n\t\t'samsung internet',\n\t\t'social',\n\t\t'other',\n\t],\n\tos: ['mac os', 'ios', 'android', 'windows', 'unix'],\n} as const\n\n//? Generated types\nexport type AudienceParamKey = keyof typeof AUDIENCE_PARAMS\n\nexport type AudienceParamPointer = (typeof AUDIENCE_PARAMS.pointer)[number]\n\nexport type AudienceParamDevice = (typeof AUDIENCE_PARAMS.device)[number]\n\nexport type AudienceParamBrowser = (typeof AUDIENCE_PARAMS.browser)[number]\n\nexport type AudienceParamOs = (typeof AUDIENCE_PARAMS.os)[number]\n\nexport const AUDIENCE_PARAM_KEYS = Object.keys(\n\tAUDIENCE_PARAMS,\n) as ReadonlyArray<AudienceParamKey>\n","import { UAParser } from 'ua-parser-js'\n\nimport {\n\tAUDIENCE_PARAMS,\n\tAudienceParamBrowser,\n\tAudienceParamDevice,\n\tAudienceParamOs,\n\tAudienceParamPointer,\n} from '../config/audiences'\n\nexport type ParsedUserAgent = {\n\tpointer: AudienceParamPointer\n\tdevice: AudienceParamDevice\n\tbrowser: AudienceParamBrowser\n\tos: AudienceParamOs\n}\n\nconst formatDeviceType = (deviceType?: string): AudienceParamDevice => {\n\tif (\n\t\tdeviceType &&\n\t\tAUDIENCE_PARAMS.device.includes(deviceType as AudienceParamDevice)\n\t) {\n\t\treturn deviceType as AudienceParamDevice\n\t}\n\treturn 'desktop'\n}\n\nconst SOCIAL_BROWSERS: ReadonlyArray<string> = [\n\t'tiktok',\n\t'wechat',\n\t'weibo',\n\t'snapchat',\n\t'klarna',\n\t'Line',\n\t'instagram',\n\t'facebook',\n\t'alipay',\n\t'Baidu',\n]\n\nconst formatBrowser = (browserName: string = ''): AudienceParamBrowser => {\n\tif (!browserName) return 'other'\n\n\tconst name = browserName.toLowerCase()\n\n\tconst browserTypeMatch = AUDIENCE_PARAMS.browser.find((browserType) => {\n\t\treturn name.includes(browserType)\n\t})\n\n\tif (browserTypeMatch) return browserTypeMatch\n\n\tif (SOCIAL_BROWSERS.includes(name)) return 'social'\n\treturn 'other'\n}\n\nconst PRIMARY_TOUCH_DEVICES: ReadonlyArray<AudienceParamDevice> = [\n\t'wearable',\n\t'mobile',\n\t'tablet',\n]\n\nconst formatPointer = (device: AudienceParamDevice) => {\n\treturn PRIMARY_TOUCH_DEVICES.includes(device) ? 'coarse' : 'fine'\n}\n\nconst formatOs = (osName: string = ''): AudienceParamOs => {\n\tif (!osName) return 'unix'\n\n\tconst os = osName.toLowerCase()\n\n\tconst osTypeMatch = AUDIENCE_PARAMS.os.find((osType) => {\n\t\treturn os.includes(osType)\n\t})\n\n\tif (osTypeMatch) return osTypeMatch\n\treturn 'unix'\n}\n\nexport const parseUserAgent = (userAgent: string): ParsedUserAgent | null => {\n\tif (!userAgent || typeof userAgent !== 'string') return null\n\n\tconst parser = new UAParser(userAgent)\n\tconst results = parser.getResult()\n\n\tconst device = formatDeviceType(results.device.type)\n\n\treturn {\n\t\tpointer: formatPointer(device),\n\t\tdevice: device,\n\t\tbrowser: formatBrowser(results.browser.name),\n\t\tos: formatOs(results.os.name),\n\t}\n}\n","import { ImproveAudienceValue } from '../types'\nimport { AudienceParamKey } from '../config/audiences'\nimport { ParsedUserAgent } from './parseUserAgent'\n\nexport const getVisitorMatchesAudience = (\n\taudience: ImproveAudienceValue | undefined,\n\tvisitorParams: ParsedUserAgent,\n) => {\n\tif (!audience) return true\n\treturn Object.entries(audience).every(([paramKey, paramValue]) => {\n\t\treturn visitorParams[paramKey as AudienceParamKey] === paramValue\n\t})\n}\n","import { ImproveTestOption } from '../types'\n\n// function cryptoRandom() {\n// \treturn crypto.getRandomValues(new Uint32Array(1))[0] / (0xffffffff + 1)\n// }\n\nexport const getRandomTestValue = (options: ImproveTestOption[]) => {\n\tif (options.length === 0) return null\n\tif (options.length === 1) return options[0].slug\n\n\t// Get a random number between 0 and 1\n\tconst sum = options.reduce((acc, { split }) => acc + split, 0)\n\tlet value = Math.random() * sum\n\n\tconst match =\n\t\toptions.find(({ split }) => {\n\t\t\tvalue -= split\n\t\t\treturn value <= 0\n\t\t}) || options[0]\n\n\treturn match.slug\n}\n","export const COOKIE_NAME_VISITOR = 'visitorId'\n\nexport const VISITOR_ID_PREFIX = 'visi'\n\nexport const VISITOR_ID_LENGTH = 26\n\nexport const VISITOR_ID_SEPARATOR = '_'\n","/**\n * @constant CHAR_SET\n * @description Key/value set of characters to be used for generation strings\n */\nconst CHAR_SET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n\n/**\n * @function getRandomString\n * @description Generate a random string\n */\nexport function getRandomString(characters: number = 5): string {\n\tif (!characters || typeof characters !== 'number') return ''\n\treturn Array(characters)\n\t\t.fill('')\n\t\t.reduce((acc) => {\n\t\t\tacc += CHAR_SET.charAt(Math.floor(Math.random() * CHAR_SET.length))\n\t\t\treturn acc\n\t\t}, '')\n}\n","import { delay } from './delay'\n\n/**\n * @async @function getTimeoutError\n * @description Throw an error after a delay\n *\n * @param {number} [timeout] - time in ms after to reject default: 1000\n * @param {AbortController} [controller] - (optional) AbortController to abort the request\n */\nexport const getTimeoutError = async (\n\ttimeout: number = 1000,\n\tcontroller: AbortController,\n): Promise<null> => {\n\tawait delay(timeout)\n\tcontroller?.abort()\n\treturn null\n}\n","export const delay = (ms: number) =>\n\tnew Promise((resolve) => setTimeout(resolve, ms))\n","import {\n\tCOOKIE_NAME_VISITOR,\n\tVISITOR_ID_LENGTH,\n\tVISITOR_ID_PREFIX,\n\tVISITOR_ID_SEPARATOR,\n} from './config/constants'\nimport { CONFIG_PATH, BASE_URL } from './config/urls'\nimport {\n\tImproveConfiguration,\n\tImproveEnvironmentOption,\n\tImproveSetupArgs,\n\tImproveTestState,\n} from './types'\nimport { getRandomString } from './utils/getRandomString'\nimport { timeoutFetch } from './utils/timeoutFetch'\n\ntype ConfigFetch = {\n\turl: string\n\ttimeout: number\n}\n\nexport class BaseImproveSDK {\n\torganizationId: string\n\tenvironment: ImproveEnvironmentOption = 'develop'\n\tstate: ImproveTestState\n\n\t#configFetch: ConfigFetch | null = null\n\n\tconfig: ImproveConfiguration | null = null\n\n\t_baseUrl: undefined | string\n\n\tconstructor({\n\t\torganizationId,\n\t\tenvironment,\n\t\tstate,\n\t\tconfig,\n\t\tfetchTimeout,\n\t\tbaseUrl,\n\t}: ImproveSetupArgs) {\n\t\tthis.organizationId = organizationId\n\t\tthis.environment = environment\n\t\tthis.state = state\n\t\tthis._baseUrl = baseUrl || BASE_URL\n\n\t\tif (config) {\n\t\t\tthis.config = config\n\t\t} else {\n\t\t\tthis.#configFetch = {\n\t\t\t\turl: [\n\t\t\t\t\t`${this._baseUrl}${CONFIG_PATH}`,\n\t\t\t\t\tthis.organizationId,\n\t\t\t\t\tthis.environment,\n\t\t\t\t\tthis.state || 'active',\n\t\t\t\t].join('/'),\n\t\t\t\ttimeout: fetchTimeout || 3000,\n\t\t\t}\n\t\t}\n\t}\n\n\t_fetchConfig = async (config?: RequestInit) => {\n\t\tif (this.config) return\n\n\t\tif (!this.#configFetch) throw new Error('No config fetch setup provided')\n\n\t\tconst res = await timeoutFetch(\n\t\t\tthis.#configFetch.timeout,\n\t\t\tthis.#configFetch.url,\n\t\t\tconfig,\n\t\t)\n\t\tif (!res || !res.ok) throw new Error('Configuration fetch timed-out')\n\n\t\tthis.config = await res.json()\n\t\treturn this.config\n\t}\n\n\tloadConfig = (config: ImproveConfiguration) => {\n\t\tthis.config = config\n\t}\n\n\tgenerateVisitorId = () => {\n\t\treturn [\n\t\t\tVISITOR_ID_PREFIX,\n\t\t\tgetRandomString(VISITOR_ID_LENGTH).toUpperCase(),\n\t\t].join(VISITOR_ID_SEPARATOR)\n\t}\n\n\tgetVisitorCookieName = () => COOKIE_NAME_VISITOR\n\n\tvalidateTestValue = (testName: string, testValue: string) => {\n\t\tif (!this.config)\n\t\t\tthrow new Error(\n\t\t\t\t'Config is required before validating, either use `.fetchConfig()`, .loadConfig(config) or provide it during setup',\n\t\t\t)\n\n\t\tconst testConfig = this.config.tests[testName]\n\n\t\tif (!testConfig) throw new Error(`No config found for ${testName}`)\n\n\t\treturn Boolean(\n\t\t\ttestConfig.options.find((option) => option.slug === testValue),\n\t\t)\n\t}\n\n\tvalidateVisitorId = (possibleVisitorId: string) => {\n\t\tconst visitorIdParts = possibleVisitorId.split(VISITOR_ID_SEPARATOR)\n\t\tif (visitorIdParts.length !== 2) return false\n\t\tconst [key, value] = visitorIdParts as [string, string]\n\t\treturn key === VISITOR_ID_PREFIX && value.length === VISITOR_ID_LENGTH\n\t}\n}\n","import { getTimeoutError } from './getTimeoutError'\n\n/**\n * @async @function timeoutFetch\n * @description Fetch with a timeout\n *\n * @param {number} timeout - time in ms after to reject default: 3000\n * @param {string} url - url to fetch\n * @param {Object} [options] - (optional) options to pass to fetch\n */\nexport const timeoutFetch = (\n\ttimeout: number = 3000,\n\turl: string,\n\toptions?: object,\n) => {\n\tconst controller = new AbortController()\n\treturn Promise.race([\n\t\tfetch(url, {\n\t\t\t...options,\n\t\t\tsignal: controller.signal,\n\t\t}),\n\t\tgetTimeoutError(timeout, controller),\n\t])\n}\n","export const BASE_URL = 'https://improve.obelism.studio'\nexport const CONFIG_PATH = `/config`\nexport const ANALYTICS_PATH = `/api/log`\n","import { ParsedUserAgent, parseUserAgent } from './utils/parseUserAgent'\nimport { getVisitorMatchesAudience } from './utils/getVisitorMatchesAudience'\nimport { getRandomTestValue } from './utils/getRandomTestValue'\nimport { BaseImproveSDK } from './base'\nimport { ImproveConfiguration, ImproveSetupArgs } from './types'\n\nconst DEFAULT_MAX_VISITORS = 10_000\n\ntype VisitorData = {\n\t[userAgent: string]: ParsedUserAgent & {\n\t\t[testSlug: string]: string\n\t}\n}\n\ntype ImproveServerSetupArgs =\n\t| (Omit<ImproveSetupArgs, 'config' | 'baseUrl'> & {\n\t\t\tconfig: ImproveConfiguration\n\t\t\tmaxVisitors?: number\n\t })\n\t| (Omit<ImproveSetupArgs, 'config'> & {\n\t\t\ttoken: string\n\t\t\tmaxVisitors?: number\n\t })\n\nexport class ImproveServerSDK extends BaseImproveSDK {\n\t#visitors: Map<string, VisitorData> = new Map()\n\t#maxVisitors: number\n\t#token: string\n\n\t// @ts-ignore It could be there\n\tconstructor({ token, maxVisitors, ...args }: ImproveServerSetupArgs) {\n\t\tsuper(args)\n\t\tthis.#token = token\n\t\tthis.#maxVisitors = maxVisitors ?? DEFAULT_MAX_VISITORS\n\t}\n\n\tfetchConfig = async (config?: RequestInit) => {\n\t\treturn this._fetchConfig({\n\t\t\t...config,\n\t\t\theaders: {\n\t\t\t\t...config?.headers,\n\t\t\t\ttoken: this.#token,\n\t\t\t},\n\t\t})\n\t}\n\n\tgetFlagConfig = (flagSlug: string) => this.config?.flags?.[flagSlug]\n\n\tgetTestConfig = (testSlug: string) => this.config?.tests?.[testSlug]\n\n\t#getVisitor = (visitorId: string, userAgent: string) => {\n\t\tlet visitor = this.#visitors.get(visitorId)\n\t\tif (visitor) {\n\t\t\t// Move to end for LRU freshness\n\t\t\tthis.#visitors.delete(visitorId)\n\t\t\tthis.#visitors.set(visitorId, visitor)\n\t\t} else {\n\t\t\t// Evict oldest entries when at capacity\n\t\t\tif (this.#visitors.size >= this.#maxVisitors) {\n\t\t\t\tconst oldest = this.#visitors.keys().next().value\n\t\t\t\tif (oldest) this.#visitors.delete(oldest)\n\t\t\t}\n\t\t\tvisitor = {}\n\t\t\tthis.#visitors.set(visitorId, visitor)\n\t\t}\n\t\tvisitor[userAgent] = visitor[userAgent] || parseUserAgent(userAgent)\n\t\treturn visitor\n\t}\n\n\tgetFlagValue = (flagSlug: string, visitorId: string, userAgent: string) => {\n\t\tconst flagConfig = this.getFlagConfig(flagSlug)\n\n\t\tif (!flagConfig || !this.config) return null\n\t\tif (!visitorId) return flagConfig.options[0].slug\n\n\t\tconst visitor = this.#getVisitor(visitorId, userAgent)\n\n\t\tif (visitor[userAgent]?.[flagSlug]) {\n\t\t\treturn visitor[userAgent][flagSlug]\n\t\t}\n\n\t\tconst visitorMatchesAudience = getVisitorMatchesAudience(\n\t\t\tthis.config.audience[flagConfig.audience],\n\t\t\tvisitor[userAgent],\n\t\t)\n\n\t\tif (!visitorMatchesAudience) return flagConfig.options[0].slug\n\n\t\tconst flagValue = getRandomTestValue(flagConfig.options)\n\n\t\tif (!flagValue) return null\n\n\t\tvisitor[userAgent][flagSlug] = flagValue\n\t\treturn flagValue\n\t}\n\n\tgetTestValue = (testSlug: string, visitorId: string, userAgent: string) => {\n\t\tconst testConfig = this.getTestConfig(testSlug)\n\n\t\tif (!testConfig || !this.config) return null\n\n\t\tif (!visitorId || !userAgent) return testConfig.defaultValue\n\n\t\tconst visitor = this.#getVisitor(visitorId, userAgent)\n\n\t\tif (visitor[userAgent]?.[testSlug]) {\n\t\t\treturn visitor[userAgent][testSlug]\n\t\t}\n\n\t\tconst visitorMatchesAudience = getVisitorMatchesAudience(\n\t\t\tthis.config.audience[testConfig.audience],\n\t\t\tvisitor[userAgent],\n\t\t)\n\n\t\tif (!visitorMatchesAudience) return testConfig.defaultValue\n\n\t\tif (\n\t\t\ttestConfig.allocation < 100 &&\n\t\t\tMath.random() * 100 > testConfig.allocation\n\t\t) {\n\t\t\tvisitor[userAgent][testSlug] = testConfig.defaultValue\n\t\t\treturn visitor[userAgent][testSlug]\n\t\t}\n\n\t\tconst testValue = getRandomTestValue(testConfig.options)\n\n\t\tif (!testValue) return null\n\n\t\tvisitor[userAgent][testSlug] = testValue\n\t\treturn testValue\n\t}\n}\n"],"names":["SOCIAL_BROWSERS","PRIMARY_TOUCH_DEVICES","getVisitorMatchesAudience","audience","visitorParams","Object","entries","every","paramKey","paramValue","getRandomTestValue","options","length","slug","value","Math","random","reduce","acc","split","match","find","VISITOR_ID_PREFIX","CHAR_SET","getTimeoutError","timeout","controller","Promise","resolve","setTimeout","abort","BaseImproveSDK","organizationId","environment","state","config","fetchTimeout","baseUrl","_fetchConfig","Error","res","timeoutFetch","url","AbortController","race","fetch","signal","ok","json","loadConfig","generateVisitorId","getRandomString","characters","Array","fill","charAt","floor","toUpperCase","join","getVisitorCookieName","validateTestValue","testName","testValue","testConfig","tests","Boolean","option","validateVisitorId","possibleVisitorId","visitorIdParts","key","_baseUrl","ImproveServerSDK","token","maxVisitors","args","Map","fetchConfig","headers","getFlagConfig","flagSlug","flags","getTestConfig","testSlug","visitorId","userAgent","visitor","get","delete","set","size","oldest","keys","next","parseUserAgent","deviceType","results","parser","UAParser","getResult","device","type","AUDIENCE_PARAMS","includes","pointer","browser","formatBrowser","browserName","name","toLowerCase","browserTypeMatch","browserType","os","formatOs","osName","osTypeMatch","osType","getFlagValue","flagConfig","flagValue","getTestValue","defaultValue","allocation"],"mappings":"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"}
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;
@@ -86,3 +92,4 @@ type ImproveConfiguration = {
86
92
  };
87
93
 
88
94
  export type { ImproveAudience, ImproveAudienceValue, ImproveConfiguration, ImproveEnvironmentOption, ImproveEvents, ImproveFlag, ImproveFlagOption, ImproveFlags, ImproveResult, ImproveSetupArgs, ImproveTest, ImproveTestOption, ImproveTestState, ImproveTests };
95
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sources":["../src/config/audiences.ts","../src/types.ts"],"mappings":"AAAA;AACA;AACA;AACA;AACA,cAAc,eAAe;AAC7B;AACA;AACA;AACA;AACA;AACA,KAAK,gBAAgB,gBAAgB,eAAe;;ACTpD,KAAK,wBAAwB;AAC7B,KAAK,gBAAgB;AACrB,KAAK,gBAAgB;AACrB;AACA,iBAAiB,wBAAwB;AACzC,YAAY,gBAAgB;AAC5B,aAAa,oBAAoB;AACjC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,iBAAiB;AACtB;AACA;AACA;AACA;AACA;AACA,KAAK,iBAAiB;AACtB;AACA;AACA;AACA;AACA;AACA,KAAK,WAAW;AAChB;AACA;AACA;AACA,aAAa,iBAAiB;AAC9B;AACA,KAAK,YAAY;AACjB,0BAA0B,WAAW;AACrC;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA;AACA,KAAK,aAAa;AAClB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK,WAAW;AAChB;AACA;AACA;AACA;AACA;AACA,aAAa,iBAAiB;AAC9B,YAAY,aAAa;AACzB;AACA,KAAK,YAAY;AACjB,0BAA0B,WAAW;AACrC;AACA,KAAK,oBAAoB;AACzB,YAAY,gBAAgB;AAC5B;AACA,KAAK,eAAe;AACpB,8BAA8B,oBAAoB;AAClD;AACA,KAAK,oBAAoB;AACzB;AACA;AACA,WAAW,YAAY;AACvB,WAAW,YAAY;AACvB,cAAc,eAAe;AAC7B;;;;","names":[]}
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": "1.0.0",
5
5
  "keywords": [
6
6
  "ab-tests",
7
7
  "feature-flags",
@@ -12,9 +12,10 @@
12
12
  "author": "Obelism <hello@obelism.studio> (https://www.obelism.studio)",
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "https://github.com/Obelism/improve-sdk"
15
+ "url": "https://github.com/Obelism/improve-sdk",
16
+ "directory": "packages/sdk"
16
17
  },
17
- "main": "./src/server.ts",
18
+ "sideEffects": false,
18
19
  "files": [
19
20
  "dist"
20
21
  ],
@@ -58,21 +59,17 @@
58
59
  "clean": "rm -rf ./node_modules && rm -rf ./dist && rm -rf ./.turbo"
59
60
  },
60
61
  "peerDependencies": {
61
- "ua-parser-js": "^1.0.38"
62
+ "ua-parser-js": "^2.0.0"
62
63
  },
63
64
  "devDependencies": {
64
65
  "@obelism/improve-sdk-eslint-config": "^0.0.0",
65
66
  "@obelism/improve-sdk-typescript-config": "^0.0.0",
66
- "@types/ua-parser-js": "^0.7.39",
67
- "@typescript-eslint/eslint-plugin": "^8.1.0",
68
- "bunchee": "^5.3.1",
69
- "eslint": "^8.56.0",
70
- "eslint-config-prettier": "^9.1.0",
71
- "eslint-plugin-no-relative-import-paths": "^1.5.3",
72
- "eslint-plugin-simple-import-sort": "^12.1.1",
73
- "lint-staged": "^15.2.0",
67
+ "bunchee": "^6.11.0",
68
+ "eslint": "^10.6.0",
69
+ "lint-staged": "^17.0.8",
74
70
  "npm-run-all": "^4.1.5",
75
- "typescript": "^5.3.3",
76
- "vitest": "^2.0.5"
71
+ "typescript": "^6.0.3",
72
+ "ua-parser-js": "^2.0.10",
73
+ "vitest": "^4.1.9"
77
74
  }
78
75
  }