@elevateab/sdk 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var D=Object.create;var p=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var N=Object.getPrototypeOf,$=Object.prototype.hasOwnProperty;var A=(t,e)=>{for(var n in e)p(t,n,{get:e[n],enumerable:!0})},h=(t,e,n,o)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of L(e))!$.call(t,a)&&a!==n&&p(t,a,{get:()=>e[a],enumerable:!(o=I(e,a))||o.enumerable});return t};var m=(t,e,n)=>(n=t!=null?D(N(t)):{},h(e||!t||!t.__esModule?p(n,"default",{value:t,enumerable:!0}):n,t)),j=t=>h(p({},"__esModule",{value:!0}),t);var S={};A(S,{DEFAULT_CONFIG:()=>O,ElevateProvider:()=>M,Experiment:()=>k,VERSION:()=>F,VariantDisplay:()=>w,assignVariant:()=>f,calculateRevenueLift:()=>b,generateExperimentId:()=>V,hashString:()=>v,useElevateConfig:()=>u,useExperiment:()=>B,validateConfig:()=>C});module.exports=j(S);function f(t,e){let n=t.reduce((i,r)=>i+r.weight,0),a=v(e)%n,s=0;for(let i of t)if(s+=i.weight,a<s)return i;return t[0]}function v(t){let e=0;for(let n=0;n<t.length;n++){let o=t.charCodeAt(n);e=(e<<5)-e+o,e=e&e}return Math.abs(e)}function C(t){return!t.testId||!t.name||!t.variations||t.variations.length===0?!1:t.variations.reduce((n,o)=>n+o.weight,0)===100}function V(t){let e=Date.now(),n=Math.random().toString(36).substring(2,9);return`exp-${t.toLowerCase().replace(/[^a-z0-9]/g,"-")}-${e}-${n}`}function b(t,e,n,o){let a=t/n,s=e/o,i=(s-a)/a*100,c=Math.sqrt((t+e)/(n+o))*Math.sqrt(1/n+1/o),g=Math.abs(s-a)/c,T=Math.min(99.9,g*34);return{lift:i,confidence:T}}function y(t){let e=[];for(let[n,o]of Object.entries(t.allTests)){if(!o.data.isLive)continue;let a=[];for(let[s,i]of Object.entries(o)){if(s==="data")continue;let r=i;typeof r=="object"&&r!==null&&"variationName"in r&&a.push({id:s,name:r.variationName,weight:r.trafficPercentage,isControl:r.isControl,productId:r.id,handle:r.handle,price:r.price})}a.length>0&&e.push({testId:n,name:o.data.name,enabled:o.data.isLive,type:o.data.type,variations:a})}return{tests:e,selectors:t.selectors}}var l=m(require("react"),1);var x=m(require("react"),1),E=x.default.createContext(null);function u(){let t=x.default.useContext(E);if(t===null)throw new Error("useElevateConfig must be used within ElevateProvider");return t}var P=l.default.createContext({assignedVariantId:null});function k({testId:t,userId:e,onVariantAssigned:n,children:o}){let{config:a}=u(),[s,i]=l.default.useState(null),r=l.default.useMemo(()=>a&&a.tests.find(c=>c.testId===t)||null,[a,t]);return l.default.useEffect(()=>{if(r&&r.enabled&&r.variations.length>0){let c=f(r.variations,e);i(c),n?.(c)}},[r,e,n]),!r||!r.enabled||!s?null:l.default.createElement(P.Provider,{value:{assignedVariantId:s.id}},l.default.createElement("div",{"data-experiment-id":r.testId,"data-variant-id":s.id},o))}function w({variantId:t,children:e}){let{assignedVariantId:n}=l.default.useContext(P);return n?n!==t?null:l.default.createElement(l.default.Fragment,null,e):l.default.createElement(l.default.Fragment,null,e)}function B(t,e){let{config:n}=u(),[o,a]=l.default.useState(null),[s,i]=l.default.useState(!0),r=l.default.useMemo(()=>n&&n.tests.find(c=>c.testId===t)||null,[n,t]);return l.default.useEffect(()=>{if(!r){console.error(`[ElevateAB] Test not found: ${t}`),i(!1);return}if(!r.enabled){console.error(`[ElevateAB] Test disabled: ${t}`),i(!1);return}let c=f(r.variations,e);a(c),i(!1)},[r,e,t]),{variant:o,isLoading:s}}var d=m(require("react"),1);function M({storeId:t,children:e}){let[n,o]=d.default.useState(null);d.default.useEffect(()=>{async function s(){try{let i=`https://configs.elevateab.com/config/${t}.json`,r=await fetch(i);if(r.status===404){o({tests:[],selectors:void 0});return}if(!r.ok)throw new Error(`Failed to fetch config: ${r.status} ${r.statusText}`);let c=await r.json(),g=y(c);o(g)}catch(i){console.error("[ElevateAB] Failed to load config:",i),o({tests:[],selectors:void 0})}}s()},[t]);let a=d.default.useMemo(()=>({config:n}),[n]);return d.default.createElement(E.Provider,{value:a},e)}var F="1.1.0",O={enabled:!0,trackingEndpoint:"https://analytics.elevateab.com/track",cacheDuration:3600};0&&(module.exports={DEFAULT_CONFIG,ElevateProvider,Experiment,VERSION,VariantDisplay,assignVariant,calculateRevenueLift,generateExperimentId,hashString,useElevateConfig,useExperiment,validateConfig});
1
+ "use strict";var M=Object.create;var p=Object.defineProperty;var A=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var $=Object.getPrototypeOf,F=Object.prototype.hasOwnProperty;var S=(t,e)=>{for(var n in e)p(t,n,{get:e[n],enumerable:!0})},V=(t,e,n,a)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of I(e))!F.call(t,o)&&o!==n&&p(t,o,{get:()=>e[o],enumerable:!(a=A(e,o))||a.enumerable});return t};var C=(t,e,n)=>(n=t!=null?M($(t)):{},V(e||!t||!t.__esModule?p(n,"default",{value:t,enumerable:!0}):n,t)),j=t=>V(p({},"__esModule",{value:!0}),t);var W={};S(W,{DEFAULT_CONFIG:()=>U,ElevateProvider:()=>L,Experiment:()=>k,VERSION:()=>O,VariantDisplay:()=>w,assignVariant:()=>f,calculateRevenueLift:()=>y,generateExperimentId:()=>b,hashString:()=>x,useElevateConfig:()=>u,useExperiment:()=>B,validateConfig:()=>P});module.exports=j(W);function f(t,e){let n=t.reduce((i,r)=>i+r.weight,0),o=x(e)%n,s=0;for(let i of t)if(s+=i.weight,o<s)return i;return t[0]}function x(t){let e=0;for(let n=0;n<t.length;n++){let a=t.charCodeAt(n);e=(e<<5)-e+a,e=e&e}return Math.abs(e)}function P(t){return!t.testId||!t.name||!t.variations||t.variations.length===0?!1:t.variations.reduce((n,a)=>n+a.weight,0)===100}function b(t){let e=Date.now(),n=Math.random().toString(36).substring(2,9);return`exp-${t.toLowerCase().replace(/[^a-z0-9]/g,"-")}-${e}-${n}`}function y(t,e,n,a){let o=t/n,s=e/a,i=(s-o)/o*100,l=Math.sqrt((t+e)/(n+a))*Math.sqrt(1/n+1/a),v=Math.abs(s-o)/l,m=Math.min(99.9,v*34);return{lift:i,confidence:m}}function g(t){let e=[];for(let[n,a]of Object.entries(t.allTests)){if(!a.data.isLive)continue;let o=[];for(let[s,i]of Object.entries(a)){if(s==="data")continue;let r=i;typeof r=="object"&&r!==null&&"variationName"in r&&o.push({id:s,name:r.variationName,weight:r.trafficPercentage,isControl:r.isControl,productId:r.id,handle:r.handle,price:r.price})}o.length>0&&e.push({testId:n,name:a.data.name,enabled:a.data.isLive,type:a.data.type,variations:o})}return{tests:e,selectors:t.selectors}}var c=C(require("react"),1);var E=C(require("react"),1),h=E.default.createContext(null);function u(){let t=E.default.useContext(h);if(t===null)throw new Error("useElevateConfig must be used within ElevateProvider");return t}var T=c.default.createContext({assignedVariantId:null});function k({testId:t,userId:e,onVariantAssigned:n,children:a}){let{config:o}=u(),[s,i]=c.default.useState(null),r=c.default.useMemo(()=>o&&o.tests.find(l=>l.testId===t)||null,[o,t]);return c.default.useEffect(()=>{if(r&&r.enabled&&r.variations.length>0){let l=f(r.variations,e);i(l),n?.(l)}},[r,e,n]),!r||!r.enabled||!s?null:c.default.createElement(T.Provider,{value:{assignedVariantId:s.id}},c.default.createElement("div",{"data-experiment-id":r.testId,"data-variant-id":s.id},a))}function w({variantId:t,children:e}){let{assignedVariantId:n}=c.default.useContext(T);return n?n!==t?null:c.default.createElement(c.default.Fragment,null,e):c.default.createElement(c.default.Fragment,null,e)}function B(t,e){let{config:n}=u(),[a,o]=c.default.useState(null),[s,i]=c.default.useState(!0),r=c.default.useMemo(()=>n&&n.tests.find(l=>l.testId===t)||null,[n,t]);return c.default.useEffect(()=>{if(!r){console.error(`[ElevateAB] Test not found: ${t}`),i(!1);return}if(!r.enabled){console.error(`[ElevateAB] Test disabled: ${t}`),i(!1);return}let l=f(r.variations,e);o(l),i(!1)},[r,e,t]),{variant:a,isLoading:s}}var d=C(require("react"),1);var N={allTests:{"test-product-title-1":{data:{name:"Product Title Test",isLive:!0,type:"product-title",testTrafficPercentage:100},"variation-control":{variationName:"Control",trafficPercentage:50,isControl:!0},"variation-test":{variationName:"Test Variation",trafficPercentage:50}},"test-price-display-1":{data:{name:"Price Display Test",isLive:!0,type:"price-display",testTrafficPercentage:100},"variation-a":{variationName:"Standard Price",trafficPercentage:50,isControl:!0},"variation-b":{variationName:"Savings Highlight",trafficPercentage:50}}},selectors:{selectorsV2:[]}},D=!0;function L({storeId:t,children:e}){let[n,a]=d.default.useState(null);d.default.useEffect(()=>{async function s(){try{let i=`https://configs.elevateab.com/config/${t}.json`,r=await fetch(i);if(r.status===404){if(D)a({tests:[],selectors:void 0});else{console.warn("[ElevateAB] CDN config not found. Using fallback config for development.");let m=g(N);a(m)}return}if(!r.ok)throw new Error(`Failed to fetch config: ${r.status} ${r.statusText}`);let l=await r.json(),v=g(l);a(v)}catch(i){if(console.error("[ElevateAB] Failed to load config:",i),D)a({tests:[],selectors:void 0});else{console.warn("[ElevateAB] Using fallback config for development due to CDN error.");let r=g(N);a(r)}}}s()},[t]);let o=d.default.useMemo(()=>({config:n}),[n]);return d.default.createElement(h.Provider,{value:o},e)}var O="1.1.0",U={enabled:!0,trackingEndpoint:"https://analytics.elevateab.com/track",cacheDuration:3600};0&&(module.exports={DEFAULT_CONFIG,ElevateProvider,Experiment,VERSION,VariantDisplay,assignVariant,calculateRevenueLift,generateExperimentId,hashString,useElevateConfig,useExperiment,validateConfig});
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/components/Experiment.tsx","../src/contexts/ElevateContext.tsx","../src/components/ElevateProvider.tsx"],"sourcesContent":["// Main entry point for the Elevate AB Testing NPM package\nexport type {\n Test,\n Variation,\n TrackingEvent,\n ExperimentStatus,\n ElevateConfig,\n ElevateProviderProps,\n ElevateContextValue,\n BackendConfig,\n} from \"./types\";\n\nexport {\n assignVariant,\n hashString,\n validateConfig,\n generateExperimentId,\n calculateRevenueLift,\n} from \"./utils\";\n\nexport {\n Experiment,\n VariantDisplay,\n useExperiment,\n} from \"./components/Experiment\";\n\nexport { ElevateProvider } from \"./components/ElevateProvider\";\nexport { useElevateConfig } from \"./contexts/ElevateContext\";\n\n// Package version\nexport const VERSION = \"1.1.0\";\n\n// Default configuration\nexport const DEFAULT_CONFIG = {\n enabled: true,\n trackingEndpoint: \"https://analytics.elevateab.com/track\",\n cacheDuration: 3600,\n};\n","import type { Variation, Test, BackendConfig, ElevateConfig } from \"./types\";\n\n/**\n * Assigns a variant based on weighted distribution\n */\nexport function assignVariant(\n variations: Variation[],\n userId: string\n): Variation {\n const totalWeight = variations.reduce((sum, v) => sum + v.weight, 0);\n const hash = hashString(userId);\n const normalized = hash % totalWeight;\n\n let cumulative = 0;\n for (const variation of variations) {\n cumulative += variation.weight;\n if (normalized < cumulative) {\n return variation;\n }\n }\n\n return variations[0];\n}\n\nexport function hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return Math.abs(hash);\n}\n\nexport function validateConfig(config: Test): boolean {\n if (!config.testId || !config.name) return false;\n if (!config.variations || config.variations.length === 0) return false;\n\n const totalWeight = config.variations.reduce((sum, v) => sum + v.weight, 0);\n return totalWeight === 100;\n}\n\nexport function generateExperimentId(name: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 9);\n const safeName = name.toLowerCase().replace(/[^a-z0-9]/g, \"-\");\n return `exp-${safeName}-${timestamp}-${random}`;\n}\n\nexport function calculateRevenueLift(\n controlRevenue: number,\n variantRevenue: number,\n controlSampleSize: number,\n variantSampleSize: number\n): { lift: number; confidence: number } {\n const controlMean = controlRevenue / controlSampleSize;\n const variantMean = variantRevenue / variantSampleSize;\n\n const lift = ((variantMean - controlMean) / controlMean) * 100;\n\n const pooledStdDev = Math.sqrt(\n (controlRevenue + variantRevenue) / (controlSampleSize + variantSampleSize)\n );\n const standardError =\n pooledStdDev * Math.sqrt(1 / controlSampleSize + 1 / variantSampleSize);\n const zScore = Math.abs(variantMean - controlMean) / standardError;\n const confidence = Math.min(99.9, zScore * 34);\n\n return { lift, confidence };\n}\n\n/**\n * Parse backend config format to SDK normalized format\n */\nexport function parseBackendConfig(backendData: BackendConfig): ElevateConfig {\n const tests: Test[] = [];\n\n for (const [testId, testData] of Object.entries(backendData.allTests)) {\n // Skip if not live\n if (!testData.data.isLive) continue;\n\n // Extract variations (all keys except 'data')\n const variations: Variation[] = [];\n for (const [key, value] of Object.entries(testData)) {\n if (key === \"data\") continue;\n\n const variation = value as BackendConfig[\"allTests\"][string][string];\n if (\n typeof variation === \"object\" &&\n variation !== null &&\n \"variationName\" in variation\n ) {\n variations.push({\n id: key,\n name: variation.variationName,\n weight: variation.trafficPercentage,\n isControl: variation.isControl,\n productId: variation.id,\n handle: variation.handle,\n price: variation.price,\n });\n }\n }\n\n // Only include tests with variations\n if (variations.length > 0) {\n tests.push({\n testId,\n name: testData.data.name,\n enabled: testData.data.isLive,\n type: testData.data.type,\n variations,\n });\n }\n }\n\n return {\n tests,\n selectors: backendData.selectors,\n };\n}\n","import React from \"react\";\nimport type { Variation, ExperimentContextValue } from \"../types\";\nimport { assignVariant } from \"../utils\";\nimport { useElevateConfig } from \"../contexts/ElevateContext\";\n\n// Internal context to pass assigned variant to VariantDisplay components\nconst ExperimentContext = React.createContext<ExperimentContextValue>({\n assignedVariantId: null,\n});\n\ninterface ExperimentProps {\n testId: string;\n userId: string;\n onVariantAssigned?: (variant: Variation) => void;\n children?: React.ReactNode;\n}\n\n/**\n * React component for A/B test experiments\n * Fetches config from ElevateProvider context based on testId\n */\nexport function Experiment({\n testId,\n userId,\n onVariantAssigned,\n children,\n}: ExperimentProps) {\n const { config } = useElevateConfig();\n const [assignedVariant, setAssignedVariant] =\n React.useState<Variation | null>(null);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n React.useEffect(() => {\n if (testConfig && testConfig.enabled && testConfig.variations.length > 0) {\n const variant = assignVariant(testConfig.variations, userId);\n setAssignedVariant(variant);\n onVariantAssigned?.(variant);\n }\n }, [testConfig, userId, onVariantAssigned]);\n\n if (!testConfig || !testConfig.enabled || !assignedVariant) {\n return null;\n }\n\n return (\n <ExperimentContext.Provider value={{ assignedVariantId: assignedVariant.id }}>\n <div\n data-experiment-id={testConfig.testId}\n data-variant-id={assignedVariant.id}\n >\n {children}\n </div>\n </ExperimentContext.Provider>\n );\n}\n\ninterface VariantDisplayProps {\n variantId: string;\n children: React.ReactNode;\n}\n\nexport function VariantDisplay({ variantId, children }: VariantDisplayProps) {\n const { assignedVariantId } = React.useContext(ExperimentContext);\n\n if (!assignedVariantId) {\n return <>{children}</>;\n }\n\n if (assignedVariantId !== variantId) {\n return null;\n }\n\n return <>{children}</>;\n}\n\nexport function useExperiment(testId: string, userId: string) {\n const { config } = useElevateConfig();\n const [variant, setVariant] = React.useState<Variation | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n React.useEffect(() => {\n if (!testConfig) {\n console.error(`[ElevateAB] Test not found: ${testId}`);\n setIsLoading(false);\n return;\n }\n\n if (!testConfig.enabled) {\n console.error(`[ElevateAB] Test disabled: ${testId}`);\n setIsLoading(false);\n return;\n }\n\n const assigned = assignVariant(testConfig.variations, userId);\n setVariant(assigned);\n setIsLoading(false);\n }, [testConfig, userId, testId]);\n\n return { variant, isLoading };\n}\n","import React from \"react\";\nimport type { ElevateContextValue } from \"../types\";\n\nexport const ElevateContext = React.createContext<ElevateContextValue | null>(\n null\n);\n\n/**\n * Hook to access Elevate config from context\n */\nexport function useElevateConfig(): ElevateContextValue {\n const context = React.useContext(ElevateContext);\n\n if (context === null) {\n throw new Error(\"useElevateConfig must be used within ElevateProvider\");\n }\n\n return context;\n}\n","import React from \"react\";\nimport type {\n ElevateConfig,\n ElevateProviderProps,\n BackendConfig,\n} from \"../types\";\nimport { ElevateContext } from \"../contexts/ElevateContext\";\nimport { parseBackendConfig } from \"../utils\";\n\nexport function ElevateProvider({ storeId, children }: ElevateProviderProps) {\n const [config, setConfig] = React.useState<ElevateConfig | null>(null);\n\n React.useEffect(() => {\n async function fetchConfig() {\n try {\n const url = `https://configs.elevateab.com/config/${storeId}.json`;\n const response = await fetch(url);\n\n if (response.status === 404) {\n setConfig({ tests: [], selectors: undefined });\n return;\n }\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch config: ${response.status} ${response.statusText}`\n );\n }\n\n const backendData: BackendConfig = await response.json();\n const parsedConfig = parseBackendConfig(backendData);\n setConfig(parsedConfig);\n } catch (err) {\n console.error(\"[ElevateAB] Failed to load config:\", err);\n setConfig({ tests: [], selectors: undefined });\n }\n }\n\n fetchConfig();\n }, [storeId]);\n\n const value = React.useMemo(() => ({ config }), [config]);\n\n return (\n <ElevateContext.Provider value={value}>{children}</ElevateContext.Provider>\n );\n}\n"],"mappings":"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","names":["index_exports","__export","DEFAULT_CONFIG","ElevateProvider","Experiment","VERSION","VariantDisplay","assignVariant","calculateRevenueLift","generateExperimentId","hashString","useElevateConfig","useExperiment","validateConfig","__toCommonJS","assignVariant","variations","userId","totalWeight","sum","v","normalized","hashString","cumulative","variation","str","hash","i","char","validateConfig","config","generateExperimentId","name","timestamp","random","calculateRevenueLift","controlRevenue","variantRevenue","controlSampleSize","variantSampleSize","controlMean","variantMean","lift","standardError","zScore","confidence","parseBackendConfig","backendData","tests","testId","testData","key","value","import_react","import_react","ElevateContext","React","useElevateConfig","context","ExperimentContext","React","Experiment","testId","userId","onVariantAssigned","children","config","useElevateConfig","assignedVariant","setAssignedVariant","testConfig","test","variant","assignVariant","VariantDisplay","variantId","assignedVariantId","useExperiment","setVariant","isLoading","setIsLoading","assigned","import_react","ElevateProvider","storeId","children","config","setConfig","React","fetchConfig","url","response","backendData","parsedConfig","parseBackendConfig","err","value","ElevateContext","VERSION","DEFAULT_CONFIG"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/components/Experiment.tsx","../src/contexts/ElevateContext.tsx","../src/components/ElevateProvider.tsx"],"sourcesContent":["// Main entry point for the Elevate AB Testing NPM package\nexport type {\n Test,\n Variation,\n TrackingEvent,\n ExperimentStatus,\n ElevateConfig,\n ElevateProviderProps,\n ElevateContextValue,\n BackendConfig,\n} from \"./types\";\n\nexport {\n assignVariant,\n hashString,\n validateConfig,\n generateExperimentId,\n calculateRevenueLift,\n} from \"./utils\";\n\nexport {\n Experiment,\n VariantDisplay,\n useExperiment,\n} from \"./components/Experiment\";\n\nexport { ElevateProvider } from \"./components/ElevateProvider\";\nexport { useElevateConfig } from \"./contexts/ElevateContext\";\n\n// Package version\nexport const VERSION = \"1.1.0\";\n\n// Default configuration\nexport const DEFAULT_CONFIG = {\n enabled: true,\n trackingEndpoint: \"https://analytics.elevateab.com/track\",\n cacheDuration: 3600,\n};\n","import type { Variation, Test, BackendConfig, ElevateConfig } from \"./types\";\n\n/**\n * Assigns a variant based on weighted distribution\n */\nexport function assignVariant(\n variations: Variation[],\n userId: string\n): Variation {\n const totalWeight = variations.reduce((sum, v) => sum + v.weight, 0);\n const hash = hashString(userId);\n const normalized = hash % totalWeight;\n\n let cumulative = 0;\n for (const variation of variations) {\n cumulative += variation.weight;\n if (normalized < cumulative) {\n return variation;\n }\n }\n\n return variations[0];\n}\n\nexport function hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return Math.abs(hash);\n}\n\nexport function validateConfig(config: Test): boolean {\n if (!config.testId || !config.name) return false;\n if (!config.variations || config.variations.length === 0) return false;\n\n const totalWeight = config.variations.reduce((sum, v) => sum + v.weight, 0);\n return totalWeight === 100;\n}\n\nexport function generateExperimentId(name: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 9);\n const safeName = name.toLowerCase().replace(/[^a-z0-9]/g, \"-\");\n return `exp-${safeName}-${timestamp}-${random}`;\n}\n\nexport function calculateRevenueLift(\n controlRevenue: number,\n variantRevenue: number,\n controlSampleSize: number,\n variantSampleSize: number\n): { lift: number; confidence: number } {\n const controlMean = controlRevenue / controlSampleSize;\n const variantMean = variantRevenue / variantSampleSize;\n\n const lift = ((variantMean - controlMean) / controlMean) * 100;\n\n const pooledStdDev = Math.sqrt(\n (controlRevenue + variantRevenue) / (controlSampleSize + variantSampleSize)\n );\n const standardError =\n pooledStdDev * Math.sqrt(1 / controlSampleSize + 1 / variantSampleSize);\n const zScore = Math.abs(variantMean - controlMean) / standardError;\n const confidence = Math.min(99.9, zScore * 34);\n\n return { lift, confidence };\n}\n\n/**\n * Parse backend config format to SDK normalized format\n */\nexport function parseBackendConfig(backendData: BackendConfig): ElevateConfig {\n const tests: Test[] = [];\n\n for (const [testId, testData] of Object.entries(backendData.allTests)) {\n // Skip if not live\n if (!testData.data.isLive) continue;\n\n // Extract variations (all keys except 'data')\n const variations: Variation[] = [];\n for (const [key, value] of Object.entries(testData)) {\n if (key === \"data\") continue;\n\n const variation = value as BackendConfig[\"allTests\"][string][string];\n if (\n typeof variation === \"object\" &&\n variation !== null &&\n \"variationName\" in variation\n ) {\n variations.push({\n id: key,\n name: variation.variationName,\n weight: variation.trafficPercentage,\n isControl: variation.isControl,\n productId: variation.id,\n handle: variation.handle,\n price: variation.price,\n });\n }\n }\n\n // Only include tests with variations\n if (variations.length > 0) {\n tests.push({\n testId,\n name: testData.data.name,\n enabled: testData.data.isLive,\n type: testData.data.type,\n variations,\n });\n }\n }\n\n return {\n tests,\n selectors: backendData.selectors,\n };\n}\n","import React from \"react\";\nimport type { Variation, ExperimentContextValue } from \"../types\";\nimport { assignVariant } from \"../utils\";\nimport { useElevateConfig } from \"../contexts/ElevateContext\";\n\n// Internal context to pass assigned variant to VariantDisplay components\nconst ExperimentContext = React.createContext<ExperimentContextValue>({\n assignedVariantId: null,\n});\n\ninterface ExperimentProps {\n testId: string;\n userId: string;\n onVariantAssigned?: (variant: Variation) => void;\n children?: React.ReactNode;\n}\n\n/**\n * React component for A/B test experiments\n * Fetches config from ElevateProvider context based on testId\n */\nexport function Experiment({\n testId,\n userId,\n onVariantAssigned,\n children,\n}: ExperimentProps) {\n const { config } = useElevateConfig();\n const [assignedVariant, setAssignedVariant] =\n React.useState<Variation | null>(null);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n React.useEffect(() => {\n if (testConfig && testConfig.enabled && testConfig.variations.length > 0) {\n const variant = assignVariant(testConfig.variations, userId);\n setAssignedVariant(variant);\n onVariantAssigned?.(variant);\n }\n }, [testConfig, userId, onVariantAssigned]);\n\n if (!testConfig || !testConfig.enabled || !assignedVariant) {\n return null;\n }\n\n return (\n <ExperimentContext.Provider value={{ assignedVariantId: assignedVariant.id }}>\n <div\n data-experiment-id={testConfig.testId}\n data-variant-id={assignedVariant.id}\n >\n {children}\n </div>\n </ExperimentContext.Provider>\n );\n}\n\ninterface VariantDisplayProps {\n variantId: string;\n children: React.ReactNode;\n}\n\nexport function VariantDisplay({ variantId, children }: VariantDisplayProps) {\n const { assignedVariantId } = React.useContext(ExperimentContext);\n\n if (!assignedVariantId) {\n return <>{children}</>;\n }\n\n if (assignedVariantId !== variantId) {\n return null;\n }\n\n return <>{children}</>;\n}\n\nexport function useExperiment(testId: string, userId: string) {\n const { config } = useElevateConfig();\n const [variant, setVariant] = React.useState<Variation | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n React.useEffect(() => {\n if (!testConfig) {\n console.error(`[ElevateAB] Test not found: ${testId}`);\n setIsLoading(false);\n return;\n }\n\n if (!testConfig.enabled) {\n console.error(`[ElevateAB] Test disabled: ${testId}`);\n setIsLoading(false);\n return;\n }\n\n const assigned = assignVariant(testConfig.variations, userId);\n setVariant(assigned);\n setIsLoading(false);\n }, [testConfig, userId, testId]);\n\n return { variant, isLoading };\n}\n","import React from \"react\";\nimport type { ElevateContextValue } from \"../types\";\n\nexport const ElevateContext = React.createContext<ElevateContextValue | null>(\n null\n);\n\n/**\n * Hook to access Elevate config from context\n */\nexport function useElevateConfig(): ElevateContextValue {\n const context = React.useContext(ElevateContext);\n\n if (context === null) {\n throw new Error(\"useElevateConfig must be used within ElevateProvider\");\n }\n\n return context;\n}\n","import React from \"react\";\nimport type {\n ElevateConfig,\n ElevateProviderProps,\n BackendConfig,\n} from \"../types\";\nimport { ElevateContext } from \"../contexts/ElevateContext\";\nimport { parseBackendConfig } from \"../utils\";\n\n// Hard-coded fallback config for development/testing\nconst FALLBACK_CONFIG: BackendConfig = {\n allTests: {\n \"test-product-title-1\": {\n data: {\n name: \"Product Title Test\",\n isLive: true,\n type: \"product-title\",\n testTrafficPercentage: 100,\n },\n \"variation-control\": {\n variationName: \"Control\",\n trafficPercentage: 50,\n isControl: true,\n },\n \"variation-test\": {\n variationName: \"Test Variation\",\n trafficPercentage: 50,\n },\n },\n \"test-price-display-1\": {\n data: {\n name: \"Price Display Test\",\n isLive: true,\n type: \"price-display\",\n testTrafficPercentage: 100,\n },\n \"variation-a\": {\n variationName: \"Standard Price\",\n trafficPercentage: 50,\n isControl: true,\n },\n \"variation-b\": {\n variationName: \"Savings Highlight\",\n trafficPercentage: 50,\n },\n },\n },\n selectors: {\n selectorsV2: [],\n },\n};\n\nconst isProduction = true;\n\nexport function ElevateProvider({ storeId, children }: ElevateProviderProps) {\n const [config, setConfig] = React.useState<ElevateConfig | null>(null);\n\n React.useEffect(() => {\n async function fetchConfig() {\n try {\n const url = `https://configs.elevateab.com/config/${storeId}.json`;\n const response = await fetch(url);\n\n if (response.status === 404) {\n if (isProduction) {\n // In production, no fallback - just empty config\n setConfig({ tests: [], selectors: undefined });\n } else {\n // In development, use fallback config\n console.warn(\n \"[ElevateAB] CDN config not found. Using fallback config for development.\"\n );\n const parsedConfig = parseBackendConfig(FALLBACK_CONFIG);\n setConfig(parsedConfig);\n }\n return;\n }\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch config: ${response.status} ${response.statusText}`\n );\n }\n\n const backendData: BackendConfig = await response.json();\n const parsedConfig = parseBackendConfig(backendData);\n setConfig(parsedConfig);\n } catch (err) {\n console.error(\"[ElevateAB] Failed to load config:\", err);\n\n if (isProduction) {\n // In production, no fallback - just empty config\n setConfig({ tests: [], selectors: undefined });\n } else {\n // In development, use fallback config\n console.warn(\n \"[ElevateAB] Using fallback config for development due to CDN error.\"\n );\n const parsedConfig = parseBackendConfig(FALLBACK_CONFIG);\n setConfig(parsedConfig);\n }\n }\n }\n\n fetchConfig();\n }, [storeId]);\n\n const value = React.useMemo(() => ({ config }), [config]);\n\n return (\n <ElevateContext.Provider value={value}>{children}</ElevateContext.Provider>\n );\n}\n"],"mappings":"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","names":["index_exports","__export","DEFAULT_CONFIG","ElevateProvider","Experiment","VERSION","VariantDisplay","assignVariant","calculateRevenueLift","generateExperimentId","hashString","useElevateConfig","useExperiment","validateConfig","__toCommonJS","assignVariant","variations","userId","totalWeight","sum","v","normalized","hashString","cumulative","variation","str","hash","i","char","validateConfig","config","generateExperimentId","name","timestamp","random","calculateRevenueLift","controlRevenue","variantRevenue","controlSampleSize","variantSampleSize","controlMean","variantMean","lift","standardError","zScore","confidence","parseBackendConfig","backendData","tests","testId","testData","key","value","import_react","import_react","ElevateContext","React","useElevateConfig","context","ExperimentContext","React","Experiment","testId","userId","onVariantAssigned","children","config","useElevateConfig","assignedVariant","setAssignedVariant","testConfig","test","variant","assignVariant","VariantDisplay","variantId","assignedVariantId","useExperiment","setVariant","isLoading","setIsLoading","assigned","import_react","FALLBACK_CONFIG","isProduction","ElevateProvider","storeId","children","config","setConfig","React","fetchConfig","url","response","parsedConfig","parseBackendConfig","backendData","err","value","ElevateContext","VERSION","DEFAULT_CONFIG"]}
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- function f(t,n){let r=t.reduce((a,e)=>a+e.weight,0),i=m(n)%r,s=0;for(let a of t)if(s+=a.weight,i<s)return a;return t[0]}function m(t){let n=0;for(let r=0;r<t.length;r++){let o=t.charCodeAt(r);n=(n<<5)-n+o,n=n&n}return Math.abs(n)}function C(t){return!t.testId||!t.name||!t.variations||t.variations.length===0?!1:t.variations.reduce((r,o)=>r+o.weight,0)===100}function V(t){let n=Date.now(),r=Math.random().toString(36).substring(2,9);return`exp-${t.toLowerCase().replace(/[^a-z0-9]/g,"-")}-${n}-${r}`}function b(t,n,r,o){let i=t/r,s=n/o,a=(s-i)/i*100,c=Math.sqrt((t+n)/(r+o))*Math.sqrt(1/r+1/o),p=Math.abs(s-i)/c,h=Math.min(99.9,p*34);return{lift:a,confidence:h}}function v(t){let n=[];for(let[r,o]of Object.entries(t.allTests)){if(!o.data.isLive)continue;let i=[];for(let[s,a]of Object.entries(o)){if(s==="data")continue;let e=a;typeof e=="object"&&e!==null&&"variationName"in e&&i.push({id:s,name:e.variationName,weight:e.trafficPercentage,isControl:e.isControl,productId:e.id,handle:e.handle,price:e.price})}i.length>0&&n.push({testId:r,name:o.data.name,enabled:o.data.isLive,type:o.data.type,variations:i})}return{tests:n,selectors:t.selectors}}import l from"react";import x from"react";var g=x.createContext(null);function u(){let t=x.useContext(g);if(t===null)throw new Error("useElevateConfig must be used within ElevateProvider");return t}var E=l.createContext({assignedVariantId:null});function y({testId:t,userId:n,onVariantAssigned:r,children:o}){let{config:i}=u(),[s,a]=l.useState(null),e=l.useMemo(()=>i&&i.tests.find(c=>c.testId===t)||null,[i,t]);return l.useEffect(()=>{if(e&&e.enabled&&e.variations.length>0){let c=f(e.variations,n);a(c),r?.(c)}},[e,n,r]),!e||!e.enabled||!s?null:l.createElement(E.Provider,{value:{assignedVariantId:s.id}},l.createElement("div",{"data-experiment-id":e.testId,"data-variant-id":s.id},o))}function P({variantId:t,children:n}){let{assignedVariantId:r}=l.useContext(E);return r?r!==t?null:l.createElement(l.Fragment,null,n):l.createElement(l.Fragment,null,n)}function k(t,n){let{config:r}=u(),[o,i]=l.useState(null),[s,a]=l.useState(!0),e=l.useMemo(()=>r&&r.tests.find(c=>c.testId===t)||null,[r,t]);return l.useEffect(()=>{if(!e){console.error(`[ElevateAB] Test not found: ${t}`),a(!1);return}if(!e.enabled){console.error(`[ElevateAB] Test disabled: ${t}`),a(!1);return}let c=f(e.variations,n);i(c),a(!1)},[e,n,t]),{variant:o,isLoading:s}}import d from"react";function w({storeId:t,children:n}){let[r,o]=d.useState(null);d.useEffect(()=>{async function s(){try{let a=`https://configs.elevateab.com/config/${t}.json`,e=await fetch(a);if(e.status===404){o({tests:[],selectors:void 0});return}if(!e.ok)throw new Error(`Failed to fetch config: ${e.status} ${e.statusText}`);let c=await e.json(),p=v(c);o(p)}catch(a){console.error("[ElevateAB] Failed to load config:",a),o({tests:[],selectors:void 0})}}s()},[t]);let i=d.useMemo(()=>({config:r}),[r]);return d.createElement(g.Provider,{value:i},n)}var O="1.1.0",S={enabled:!0,trackingEndpoint:"https://analytics.elevateab.com/track",cacheDuration:3600};export{S as DEFAULT_CONFIG,w as ElevateProvider,y as Experiment,O as VERSION,P as VariantDisplay,f as assignVariant,b as calculateRevenueLift,V as generateExperimentId,m as hashString,u as useElevateConfig,k as useExperiment,C as validateConfig};
1
+ function f(t,n){let r=t.reduce((o,e)=>o+e.weight,0),i=C(n)%r,s=0;for(let o of t)if(s+=o.weight,i<s)return o;return t[0]}function C(t){let n=0;for(let r=0;r<t.length;r++){let a=t.charCodeAt(r);n=(n<<5)-n+a,n=n&n}return Math.abs(n)}function P(t){return!t.testId||!t.name||!t.variations||t.variations.length===0?!1:t.variations.reduce((r,a)=>r+a.weight,0)===100}function b(t){let n=Date.now(),r=Math.random().toString(36).substring(2,9);return`exp-${t.toLowerCase().replace(/[^a-z0-9]/g,"-")}-${n}-${r}`}function y(t,n,r,a){let i=t/r,s=n/a,o=(s-i)/i*100,l=Math.sqrt((t+n)/(r+a))*Math.sqrt(1/r+1/a),g=Math.abs(s-i)/l,v=Math.min(99.9,g*34);return{lift:o,confidence:v}}function u(t){let n=[];for(let[r,a]of Object.entries(t.allTests)){if(!a.data.isLive)continue;let i=[];for(let[s,o]of Object.entries(a)){if(s==="data")continue;let e=o;typeof e=="object"&&e!==null&&"variationName"in e&&i.push({id:s,name:e.variationName,weight:e.trafficPercentage,isControl:e.isControl,productId:e.id,handle:e.handle,price:e.price})}i.length>0&&n.push({testId:r,name:a.data.name,enabled:a.data.isLive,type:a.data.type,variations:i})}return{tests:n,selectors:t.selectors}}import c from"react";import x from"react";var m=x.createContext(null);function d(){let t=x.useContext(m);if(t===null)throw new Error("useElevateConfig must be used within ElevateProvider");return t}var E=c.createContext({assignedVariantId:null});function T({testId:t,userId:n,onVariantAssigned:r,children:a}){let{config:i}=d(),[s,o]=c.useState(null),e=c.useMemo(()=>i&&i.tests.find(l=>l.testId===t)||null,[i,t]);return c.useEffect(()=>{if(e&&e.enabled&&e.variations.length>0){let l=f(e.variations,n);o(l),r?.(l)}},[e,n,r]),!e||!e.enabled||!s?null:c.createElement(E.Provider,{value:{assignedVariantId:s.id}},c.createElement("div",{"data-experiment-id":e.testId,"data-variant-id":s.id},a))}function k({variantId:t,children:n}){let{assignedVariantId:r}=c.useContext(E);return r?r!==t?null:c.createElement(c.Fragment,null,n):c.createElement(c.Fragment,null,n)}function w(t,n){let{config:r}=d(),[a,i]=c.useState(null),[s,o]=c.useState(!0),e=c.useMemo(()=>r&&r.tests.find(l=>l.testId===t)||null,[r,t]);return c.useEffect(()=>{if(!e){console.error(`[ElevateAB] Test not found: ${t}`),o(!1);return}if(!e.enabled){console.error(`[ElevateAB] Test disabled: ${t}`),o(!1);return}let l=f(e.variations,n);i(l),o(!1)},[e,n,t]),{variant:a,isLoading:s}}import p from"react";var h={allTests:{"test-product-title-1":{data:{name:"Product Title Test",isLive:!0,type:"product-title",testTrafficPercentage:100},"variation-control":{variationName:"Control",trafficPercentage:50,isControl:!0},"variation-test":{variationName:"Test Variation",trafficPercentage:50}},"test-price-display-1":{data:{name:"Price Display Test",isLive:!0,type:"price-display",testTrafficPercentage:100},"variation-a":{variationName:"Standard Price",trafficPercentage:50,isControl:!0},"variation-b":{variationName:"Savings Highlight",trafficPercentage:50}}},selectors:{selectorsV2:[]}},V=!0;function B({storeId:t,children:n}){let[r,a]=p.useState(null);p.useEffect(()=>{async function s(){try{let o=`https://configs.elevateab.com/config/${t}.json`,e=await fetch(o);if(e.status===404){if(V)a({tests:[],selectors:void 0});else{console.warn("[ElevateAB] CDN config not found. Using fallback config for development.");let v=u(h);a(v)}return}if(!e.ok)throw new Error(`Failed to fetch config: ${e.status} ${e.statusText}`);let l=await e.json(),g=u(l);a(g)}catch(o){if(console.error("[ElevateAB] Failed to load config:",o),V)a({tests:[],selectors:void 0});else{console.warn("[ElevateAB] Using fallback config for development due to CDN error.");let e=u(h);a(e)}}}s()},[t]);let i=p.useMemo(()=>({config:r}),[r]);return p.createElement(m.Provider,{value:i},n)}var U="1.1.0",W={enabled:!0,trackingEndpoint:"https://analytics.elevateab.com/track",cacheDuration:3600};export{W as DEFAULT_CONFIG,B as ElevateProvider,T as Experiment,U as VERSION,k as VariantDisplay,f as assignVariant,y as calculateRevenueLift,b as generateExperimentId,C as hashString,d as useElevateConfig,w as useExperiment,P as validateConfig};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils.ts","../src/components/Experiment.tsx","../src/contexts/ElevateContext.tsx","../src/components/ElevateProvider.tsx","../src/index.ts"],"sourcesContent":["import type { Variation, Test, BackendConfig, ElevateConfig } from \"./types\";\n\n/**\n * Assigns a variant based on weighted distribution\n */\nexport function assignVariant(\n variations: Variation[],\n userId: string\n): Variation {\n const totalWeight = variations.reduce((sum, v) => sum + v.weight, 0);\n const hash = hashString(userId);\n const normalized = hash % totalWeight;\n\n let cumulative = 0;\n for (const variation of variations) {\n cumulative += variation.weight;\n if (normalized < cumulative) {\n return variation;\n }\n }\n\n return variations[0];\n}\n\nexport function hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return Math.abs(hash);\n}\n\nexport function validateConfig(config: Test): boolean {\n if (!config.testId || !config.name) return false;\n if (!config.variations || config.variations.length === 0) return false;\n\n const totalWeight = config.variations.reduce((sum, v) => sum + v.weight, 0);\n return totalWeight === 100;\n}\n\nexport function generateExperimentId(name: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 9);\n const safeName = name.toLowerCase().replace(/[^a-z0-9]/g, \"-\");\n return `exp-${safeName}-${timestamp}-${random}`;\n}\n\nexport function calculateRevenueLift(\n controlRevenue: number,\n variantRevenue: number,\n controlSampleSize: number,\n variantSampleSize: number\n): { lift: number; confidence: number } {\n const controlMean = controlRevenue / controlSampleSize;\n const variantMean = variantRevenue / variantSampleSize;\n\n const lift = ((variantMean - controlMean) / controlMean) * 100;\n\n const pooledStdDev = Math.sqrt(\n (controlRevenue + variantRevenue) / (controlSampleSize + variantSampleSize)\n );\n const standardError =\n pooledStdDev * Math.sqrt(1 / controlSampleSize + 1 / variantSampleSize);\n const zScore = Math.abs(variantMean - controlMean) / standardError;\n const confidence = Math.min(99.9, zScore * 34);\n\n return { lift, confidence };\n}\n\n/**\n * Parse backend config format to SDK normalized format\n */\nexport function parseBackendConfig(backendData: BackendConfig): ElevateConfig {\n const tests: Test[] = [];\n\n for (const [testId, testData] of Object.entries(backendData.allTests)) {\n // Skip if not live\n if (!testData.data.isLive) continue;\n\n // Extract variations (all keys except 'data')\n const variations: Variation[] = [];\n for (const [key, value] of Object.entries(testData)) {\n if (key === \"data\") continue;\n\n const variation = value as BackendConfig[\"allTests\"][string][string];\n if (\n typeof variation === \"object\" &&\n variation !== null &&\n \"variationName\" in variation\n ) {\n variations.push({\n id: key,\n name: variation.variationName,\n weight: variation.trafficPercentage,\n isControl: variation.isControl,\n productId: variation.id,\n handle: variation.handle,\n price: variation.price,\n });\n }\n }\n\n // Only include tests with variations\n if (variations.length > 0) {\n tests.push({\n testId,\n name: testData.data.name,\n enabled: testData.data.isLive,\n type: testData.data.type,\n variations,\n });\n }\n }\n\n return {\n tests,\n selectors: backendData.selectors,\n };\n}\n","import React from \"react\";\nimport type { Variation, ExperimentContextValue } from \"../types\";\nimport { assignVariant } from \"../utils\";\nimport { useElevateConfig } from \"../contexts/ElevateContext\";\n\n// Internal context to pass assigned variant to VariantDisplay components\nconst ExperimentContext = React.createContext<ExperimentContextValue>({\n assignedVariantId: null,\n});\n\ninterface ExperimentProps {\n testId: string;\n userId: string;\n onVariantAssigned?: (variant: Variation) => void;\n children?: React.ReactNode;\n}\n\n/**\n * React component for A/B test experiments\n * Fetches config from ElevateProvider context based on testId\n */\nexport function Experiment({\n testId,\n userId,\n onVariantAssigned,\n children,\n}: ExperimentProps) {\n const { config } = useElevateConfig();\n const [assignedVariant, setAssignedVariant] =\n React.useState<Variation | null>(null);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n React.useEffect(() => {\n if (testConfig && testConfig.enabled && testConfig.variations.length > 0) {\n const variant = assignVariant(testConfig.variations, userId);\n setAssignedVariant(variant);\n onVariantAssigned?.(variant);\n }\n }, [testConfig, userId, onVariantAssigned]);\n\n if (!testConfig || !testConfig.enabled || !assignedVariant) {\n return null;\n }\n\n return (\n <ExperimentContext.Provider value={{ assignedVariantId: assignedVariant.id }}>\n <div\n data-experiment-id={testConfig.testId}\n data-variant-id={assignedVariant.id}\n >\n {children}\n </div>\n </ExperimentContext.Provider>\n );\n}\n\ninterface VariantDisplayProps {\n variantId: string;\n children: React.ReactNode;\n}\n\nexport function VariantDisplay({ variantId, children }: VariantDisplayProps) {\n const { assignedVariantId } = React.useContext(ExperimentContext);\n\n if (!assignedVariantId) {\n return <>{children}</>;\n }\n\n if (assignedVariantId !== variantId) {\n return null;\n }\n\n return <>{children}</>;\n}\n\nexport function useExperiment(testId: string, userId: string) {\n const { config } = useElevateConfig();\n const [variant, setVariant] = React.useState<Variation | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n React.useEffect(() => {\n if (!testConfig) {\n console.error(`[ElevateAB] Test not found: ${testId}`);\n setIsLoading(false);\n return;\n }\n\n if (!testConfig.enabled) {\n console.error(`[ElevateAB] Test disabled: ${testId}`);\n setIsLoading(false);\n return;\n }\n\n const assigned = assignVariant(testConfig.variations, userId);\n setVariant(assigned);\n setIsLoading(false);\n }, [testConfig, userId, testId]);\n\n return { variant, isLoading };\n}\n","import React from \"react\";\nimport type { ElevateContextValue } from \"../types\";\n\nexport const ElevateContext = React.createContext<ElevateContextValue | null>(\n null\n);\n\n/**\n * Hook to access Elevate config from context\n */\nexport function useElevateConfig(): ElevateContextValue {\n const context = React.useContext(ElevateContext);\n\n if (context === null) {\n throw new Error(\"useElevateConfig must be used within ElevateProvider\");\n }\n\n return context;\n}\n","import React from \"react\";\nimport type {\n ElevateConfig,\n ElevateProviderProps,\n BackendConfig,\n} from \"../types\";\nimport { ElevateContext } from \"../contexts/ElevateContext\";\nimport { parseBackendConfig } from \"../utils\";\n\nexport function ElevateProvider({ storeId, children }: ElevateProviderProps) {\n const [config, setConfig] = React.useState<ElevateConfig | null>(null);\n\n React.useEffect(() => {\n async function fetchConfig() {\n try {\n const url = `https://configs.elevateab.com/config/${storeId}.json`;\n const response = await fetch(url);\n\n if (response.status === 404) {\n setConfig({ tests: [], selectors: undefined });\n return;\n }\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch config: ${response.status} ${response.statusText}`\n );\n }\n\n const backendData: BackendConfig = await response.json();\n const parsedConfig = parseBackendConfig(backendData);\n setConfig(parsedConfig);\n } catch (err) {\n console.error(\"[ElevateAB] Failed to load config:\", err);\n setConfig({ tests: [], selectors: undefined });\n }\n }\n\n fetchConfig();\n }, [storeId]);\n\n const value = React.useMemo(() => ({ config }), [config]);\n\n return (\n <ElevateContext.Provider value={value}>{children}</ElevateContext.Provider>\n );\n}\n","// Main entry point for the Elevate AB Testing NPM package\nexport type {\n Test,\n Variation,\n TrackingEvent,\n ExperimentStatus,\n ElevateConfig,\n ElevateProviderProps,\n ElevateContextValue,\n BackendConfig,\n} from \"./types\";\n\nexport {\n assignVariant,\n hashString,\n validateConfig,\n generateExperimentId,\n calculateRevenueLift,\n} from \"./utils\";\n\nexport {\n Experiment,\n VariantDisplay,\n useExperiment,\n} from \"./components/Experiment\";\n\nexport { ElevateProvider } from \"./components/ElevateProvider\";\nexport { useElevateConfig } from \"./contexts/ElevateContext\";\n\n// Package version\nexport const VERSION = \"1.1.0\";\n\n// Default configuration\nexport const DEFAULT_CONFIG = {\n enabled: true,\n trackingEndpoint: \"https://analytics.elevateab.com/track\",\n cacheDuration: 3600,\n};\n"],"mappings":"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","names":["assignVariant","variations","userId","totalWeight","sum","v","normalized","hashString","cumulative","variation","str","hash","i","char","validateConfig","config","generateExperimentId","name","timestamp","random","calculateRevenueLift","controlRevenue","variantRevenue","controlSampleSize","variantSampleSize","controlMean","variantMean","lift","standardError","zScore","confidence","parseBackendConfig","backendData","tests","testId","testData","key","value","React","React","ElevateContext","useElevateConfig","context","ExperimentContext","React","Experiment","testId","userId","onVariantAssigned","children","config","useElevateConfig","assignedVariant","setAssignedVariant","testConfig","test","variant","assignVariant","VariantDisplay","variantId","assignedVariantId","useExperiment","setVariant","isLoading","setIsLoading","assigned","React","ElevateProvider","storeId","children","config","setConfig","React","fetchConfig","url","response","backendData","parsedConfig","parseBackendConfig","err","value","ElevateContext","VERSION","DEFAULT_CONFIG"]}
1
+ {"version":3,"sources":["../src/utils.ts","../src/components/Experiment.tsx","../src/contexts/ElevateContext.tsx","../src/components/ElevateProvider.tsx","../src/index.ts"],"sourcesContent":["import type { Variation, Test, BackendConfig, ElevateConfig } from \"./types\";\n\n/**\n * Assigns a variant based on weighted distribution\n */\nexport function assignVariant(\n variations: Variation[],\n userId: string\n): Variation {\n const totalWeight = variations.reduce((sum, v) => sum + v.weight, 0);\n const hash = hashString(userId);\n const normalized = hash % totalWeight;\n\n let cumulative = 0;\n for (const variation of variations) {\n cumulative += variation.weight;\n if (normalized < cumulative) {\n return variation;\n }\n }\n\n return variations[0];\n}\n\nexport function hashString(str: string): number {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash;\n }\n return Math.abs(hash);\n}\n\nexport function validateConfig(config: Test): boolean {\n if (!config.testId || !config.name) return false;\n if (!config.variations || config.variations.length === 0) return false;\n\n const totalWeight = config.variations.reduce((sum, v) => sum + v.weight, 0);\n return totalWeight === 100;\n}\n\nexport function generateExperimentId(name: string): string {\n const timestamp = Date.now();\n const random = Math.random().toString(36).substring(2, 9);\n const safeName = name.toLowerCase().replace(/[^a-z0-9]/g, \"-\");\n return `exp-${safeName}-${timestamp}-${random}`;\n}\n\nexport function calculateRevenueLift(\n controlRevenue: number,\n variantRevenue: number,\n controlSampleSize: number,\n variantSampleSize: number\n): { lift: number; confidence: number } {\n const controlMean = controlRevenue / controlSampleSize;\n const variantMean = variantRevenue / variantSampleSize;\n\n const lift = ((variantMean - controlMean) / controlMean) * 100;\n\n const pooledStdDev = Math.sqrt(\n (controlRevenue + variantRevenue) / (controlSampleSize + variantSampleSize)\n );\n const standardError =\n pooledStdDev * Math.sqrt(1 / controlSampleSize + 1 / variantSampleSize);\n const zScore = Math.abs(variantMean - controlMean) / standardError;\n const confidence = Math.min(99.9, zScore * 34);\n\n return { lift, confidence };\n}\n\n/**\n * Parse backend config format to SDK normalized format\n */\nexport function parseBackendConfig(backendData: BackendConfig): ElevateConfig {\n const tests: Test[] = [];\n\n for (const [testId, testData] of Object.entries(backendData.allTests)) {\n // Skip if not live\n if (!testData.data.isLive) continue;\n\n // Extract variations (all keys except 'data')\n const variations: Variation[] = [];\n for (const [key, value] of Object.entries(testData)) {\n if (key === \"data\") continue;\n\n const variation = value as BackendConfig[\"allTests\"][string][string];\n if (\n typeof variation === \"object\" &&\n variation !== null &&\n \"variationName\" in variation\n ) {\n variations.push({\n id: key,\n name: variation.variationName,\n weight: variation.trafficPercentage,\n isControl: variation.isControl,\n productId: variation.id,\n handle: variation.handle,\n price: variation.price,\n });\n }\n }\n\n // Only include tests with variations\n if (variations.length > 0) {\n tests.push({\n testId,\n name: testData.data.name,\n enabled: testData.data.isLive,\n type: testData.data.type,\n variations,\n });\n }\n }\n\n return {\n tests,\n selectors: backendData.selectors,\n };\n}\n","import React from \"react\";\nimport type { Variation, ExperimentContextValue } from \"../types\";\nimport { assignVariant } from \"../utils\";\nimport { useElevateConfig } from \"../contexts/ElevateContext\";\n\n// Internal context to pass assigned variant to VariantDisplay components\nconst ExperimentContext = React.createContext<ExperimentContextValue>({\n assignedVariantId: null,\n});\n\ninterface ExperimentProps {\n testId: string;\n userId: string;\n onVariantAssigned?: (variant: Variation) => void;\n children?: React.ReactNode;\n}\n\n/**\n * React component for A/B test experiments\n * Fetches config from ElevateProvider context based on testId\n */\nexport function Experiment({\n testId,\n userId,\n onVariantAssigned,\n children,\n}: ExperimentProps) {\n const { config } = useElevateConfig();\n const [assignedVariant, setAssignedVariant] =\n React.useState<Variation | null>(null);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n React.useEffect(() => {\n if (testConfig && testConfig.enabled && testConfig.variations.length > 0) {\n const variant = assignVariant(testConfig.variations, userId);\n setAssignedVariant(variant);\n onVariantAssigned?.(variant);\n }\n }, [testConfig, userId, onVariantAssigned]);\n\n if (!testConfig || !testConfig.enabled || !assignedVariant) {\n return null;\n }\n\n return (\n <ExperimentContext.Provider value={{ assignedVariantId: assignedVariant.id }}>\n <div\n data-experiment-id={testConfig.testId}\n data-variant-id={assignedVariant.id}\n >\n {children}\n </div>\n </ExperimentContext.Provider>\n );\n}\n\ninterface VariantDisplayProps {\n variantId: string;\n children: React.ReactNode;\n}\n\nexport function VariantDisplay({ variantId, children }: VariantDisplayProps) {\n const { assignedVariantId } = React.useContext(ExperimentContext);\n\n if (!assignedVariantId) {\n return <>{children}</>;\n }\n\n if (assignedVariantId !== variantId) {\n return null;\n }\n\n return <>{children}</>;\n}\n\nexport function useExperiment(testId: string, userId: string) {\n const { config } = useElevateConfig();\n const [variant, setVariant] = React.useState<Variation | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n\n const testConfig = React.useMemo(() => {\n if (!config) return null;\n return config.tests.find((test) => test.testId === testId) || null;\n }, [config, testId]);\n\n React.useEffect(() => {\n if (!testConfig) {\n console.error(`[ElevateAB] Test not found: ${testId}`);\n setIsLoading(false);\n return;\n }\n\n if (!testConfig.enabled) {\n console.error(`[ElevateAB] Test disabled: ${testId}`);\n setIsLoading(false);\n return;\n }\n\n const assigned = assignVariant(testConfig.variations, userId);\n setVariant(assigned);\n setIsLoading(false);\n }, [testConfig, userId, testId]);\n\n return { variant, isLoading };\n}\n","import React from \"react\";\nimport type { ElevateContextValue } from \"../types\";\n\nexport const ElevateContext = React.createContext<ElevateContextValue | null>(\n null\n);\n\n/**\n * Hook to access Elevate config from context\n */\nexport function useElevateConfig(): ElevateContextValue {\n const context = React.useContext(ElevateContext);\n\n if (context === null) {\n throw new Error(\"useElevateConfig must be used within ElevateProvider\");\n }\n\n return context;\n}\n","import React from \"react\";\nimport type {\n ElevateConfig,\n ElevateProviderProps,\n BackendConfig,\n} from \"../types\";\nimport { ElevateContext } from \"../contexts/ElevateContext\";\nimport { parseBackendConfig } from \"../utils\";\n\n// Hard-coded fallback config for development/testing\nconst FALLBACK_CONFIG: BackendConfig = {\n allTests: {\n \"test-product-title-1\": {\n data: {\n name: \"Product Title Test\",\n isLive: true,\n type: \"product-title\",\n testTrafficPercentage: 100,\n },\n \"variation-control\": {\n variationName: \"Control\",\n trafficPercentage: 50,\n isControl: true,\n },\n \"variation-test\": {\n variationName: \"Test Variation\",\n trafficPercentage: 50,\n },\n },\n \"test-price-display-1\": {\n data: {\n name: \"Price Display Test\",\n isLive: true,\n type: \"price-display\",\n testTrafficPercentage: 100,\n },\n \"variation-a\": {\n variationName: \"Standard Price\",\n trafficPercentage: 50,\n isControl: true,\n },\n \"variation-b\": {\n variationName: \"Savings Highlight\",\n trafficPercentage: 50,\n },\n },\n },\n selectors: {\n selectorsV2: [],\n },\n};\n\nconst isProduction = true;\n\nexport function ElevateProvider({ storeId, children }: ElevateProviderProps) {\n const [config, setConfig] = React.useState<ElevateConfig | null>(null);\n\n React.useEffect(() => {\n async function fetchConfig() {\n try {\n const url = `https://configs.elevateab.com/config/${storeId}.json`;\n const response = await fetch(url);\n\n if (response.status === 404) {\n if (isProduction) {\n // In production, no fallback - just empty config\n setConfig({ tests: [], selectors: undefined });\n } else {\n // In development, use fallback config\n console.warn(\n \"[ElevateAB] CDN config not found. Using fallback config for development.\"\n );\n const parsedConfig = parseBackendConfig(FALLBACK_CONFIG);\n setConfig(parsedConfig);\n }\n return;\n }\n\n if (!response.ok) {\n throw new Error(\n `Failed to fetch config: ${response.status} ${response.statusText}`\n );\n }\n\n const backendData: BackendConfig = await response.json();\n const parsedConfig = parseBackendConfig(backendData);\n setConfig(parsedConfig);\n } catch (err) {\n console.error(\"[ElevateAB] Failed to load config:\", err);\n\n if (isProduction) {\n // In production, no fallback - just empty config\n setConfig({ tests: [], selectors: undefined });\n } else {\n // In development, use fallback config\n console.warn(\n \"[ElevateAB] Using fallback config for development due to CDN error.\"\n );\n const parsedConfig = parseBackendConfig(FALLBACK_CONFIG);\n setConfig(parsedConfig);\n }\n }\n }\n\n fetchConfig();\n }, [storeId]);\n\n const value = React.useMemo(() => ({ config }), [config]);\n\n return (\n <ElevateContext.Provider value={value}>{children}</ElevateContext.Provider>\n );\n}\n","// Main entry point for the Elevate AB Testing NPM package\nexport type {\n Test,\n Variation,\n TrackingEvent,\n ExperimentStatus,\n ElevateConfig,\n ElevateProviderProps,\n ElevateContextValue,\n BackendConfig,\n} from \"./types\";\n\nexport {\n assignVariant,\n hashString,\n validateConfig,\n generateExperimentId,\n calculateRevenueLift,\n} from \"./utils\";\n\nexport {\n Experiment,\n VariantDisplay,\n useExperiment,\n} from \"./components/Experiment\";\n\nexport { ElevateProvider } from \"./components/ElevateProvider\";\nexport { useElevateConfig } from \"./contexts/ElevateContext\";\n\n// Package version\nexport const VERSION = \"1.1.0\";\n\n// Default configuration\nexport const DEFAULT_CONFIG = {\n enabled: true,\n trackingEndpoint: \"https://analytics.elevateab.com/track\",\n cacheDuration: 3600,\n};\n"],"mappings":"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","names":["assignVariant","variations","userId","totalWeight","sum","v","normalized","hashString","cumulative","variation","str","hash","i","char","validateConfig","config","generateExperimentId","name","timestamp","random","calculateRevenueLift","controlRevenue","variantRevenue","controlSampleSize","variantSampleSize","controlMean","variantMean","lift","standardError","zScore","confidence","parseBackendConfig","backendData","tests","testId","testData","key","value","React","React","ElevateContext","useElevateConfig","context","ExperimentContext","React","Experiment","testId","userId","onVariantAssigned","children","config","useElevateConfig","assignedVariant","setAssignedVariant","testConfig","test","variant","assignVariant","VariantDisplay","variantId","assignedVariantId","useExperiment","setVariant","isLoading","setIsLoading","assigned","React","FALLBACK_CONFIG","isProduction","ElevateProvider","storeId","children","config","setConfig","React","fetchConfig","url","response","parsedConfig","parseBackendConfig","backendData","err","value","ElevateContext","VERSION","DEFAULT_CONFIG"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elevateab/sdk",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Elevate AB Testing SDK for Hydrogen and Remix frameworks",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",