@abconvert/headless 0.0.0-stage → 0.1.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/next.js ADDED
@@ -0,0 +1,8 @@
1
+ import{NextResponse}from"next/server";import{headers}from"next/headers";function murmurhash3_32(str,seed=0){let remainder=str.length%4,bytes=str.length-remainder,h1=seed>>>0,c1=3432918353,c2=461845907,k1,i=0;for(;i<bytes;)k1=str.charCodeAt(i)&255|(str.charCodeAt(i+1)&255)<<8|(str.charCodeAt(i+2)&255)<<16|(str.charCodeAt(i+3)&255)<<24,i+=4,k1=Math.imul(k1,c1),k1=k1<<15|k1>>>17,k1=Math.imul(k1,c2),h1^=k1,h1=h1<<13|h1>>>19,h1=Math.imul(h1,5)+3864292196;switch(k1=0,remainder){case 3:k1^=(str.charCodeAt(i+2)&255)<<16;case 2:k1^=(str.charCodeAt(i+1)&255)<<8;case 1:k1^=str.charCodeAt(i)&255,k1=Math.imul(k1,c1),k1=k1<<15|k1>>>17,k1=Math.imul(k1,c2),h1^=k1}return h1^=str.length,h1^=h1>>>16,h1=Math.imul(h1,2246822507),h1^=h1>>>13,h1=Math.imul(h1,3266489909),h1^=h1>>>16,h1>>>0}function deterministicHash(str){return murmurhash3_32(str)%100}function checkNamespace(visitorId,experimentId,groups,store){if(!groups)return!0;let foundGroupId=null,foundConfig=null;for(let[groupId,config]of Object.entries(groups))if(config.memberExperimentIds.includes(experimentId)){foundGroupId=groupId,foundConfig=config;break}if(!foundGroupId||!foundConfig)return!0;let storedExperimentId=store.get(foundGroupId);if(storedExperimentId!=null){if(foundConfig.excludedExperimentIds?.includes(storedExperimentId)||foundConfig.inactiveExperimentIds?.includes(storedExperimentId))return!1;if(!foundConfig.memberExperimentIds.includes(storedExperimentId))store.clear(foundGroupId);else return storedExperimentId===experimentId}let range=foundConfig.ranges[experimentId];if(!range)return!1;let[start,end]=range,hashValue=deterministicHash(`${visitorId}:namespace:${foundGroupId}`)/100,inRange=hashValue>=start&&hashValue<end;return inRange&&store.set(foundGroupId,experimentId),inRange}function checkTrafficAllocation(visitorId,experimentId,allocationPercent,store){let storedDecision2=store.get(experimentId);if(storedDecision2!=null)return storedDecision2;if(allocationPercent>=100)return store.set(experimentId,!0),!0;let hashInput=`${visitorId}:${experimentId}:traffic`,inTest2=deterministicHash(hashInput)<allocationPercent;return store.set(experimentId,inTest2),inTest2}function calculateGroupFromHash(visitorId,experimentId,percentages){let hashInput=`${visitorId}:${experimentId}:group`,hashValue=deterministicHash(hashInput),cumulative=0;for(let i=0;i<percentages.length;i++)if(cumulative+=percentages[i],hashValue<cumulative)return i;return percentages.length-1}function parseForceAssignFromUrl(search){let urlParams=new URLSearchParams(search),previewParam=urlParams.get("preview"),forceParam=urlParams.get("abconvert_force");if(forceParam!==null&&forceParam.includes(":")){let[experimentId,groupIndexStr]=forceParam.split(":"),groupIndex=Number(groupIndexStr);return experimentId&&!isNaN(groupIndex)?{experimentId,value:groupIndex.toString()}:null}return forceParam!==null&&!isNaN(Number(forceParam))?{value:forceParam}:previewParam!==null&&!isNaN(Number(previewParam))?{value:previewParam,fromPreview:!0}:null}function isValidForcedGroupIndex(value){return Number.isInteger(value)&&value>=0}function parseForcedGroupIndex(value){if(value===null||value==="")return null;let parsed=Number(value);return isValidForcedGroupIndex(parsed)?parsed:null}function normalizeToken(value){return value.trim().toLowerCase()}function evaluateOperator(operator,actualValue,expectedValue){let actual=actualValue??"",expected=Array.isArray(expectedValue)?expectedValue:[expectedValue];switch(operator){case"equals":return actual===expected[0];case"not_equals":return actual!==expected[0];case"contains":return actual.includes(expected[0]);case"not_contains":return!actual.includes(expected[0]);case"in":return expected.includes(actual);case"not_in":return!expected.includes(actual);case"exists":return actualValue!=null&&actualValue!=="";case"not_exists":return actualValue==null||actualValue==="";case"starts_with":return actual.startsWith(expected[0]);case"regex":try{return new RegExp(expected[0]).test(actual)}catch{return!1}default:return!1}}var PATH_OPERATORS=["equals","not_equals","starts_with","in","not_in"],URL_OPERATORS=["contains","not_contains","regex"];function actualValueFor(operator,url){return PATH_OPERATORS.indexOf(operator)!==-1?url.pathname:URL_OPERATORS.indexOf(operator)!==-1?url.href:null}function ruleMatches(rule,url){let actual=actualValueFor(rule.operator,url);return actual===null?!1:evaluateOperator(rule.operator,actual,rule.value)}function isActivatedByPageTargeting(targeting,url=window.location){if(!targeting)return!0;if((targeting.excludeRules??[]).some(rule=>ruleMatches(rule,url)))return!1;let includeRules=targeting.includeRules??[];return includeRules.length===0?!0:includeRules.some(rule=>ruleMatches(rule,url))}var sources=[],conditionTypes=new Map,sourceByConditionType=new Map,dynamicSourceCount=0;function registerAudienceModule(module){let{source,conditions}=module,effectiveSource=source;if(source){let existing=sources.find(s=>s.id===source.id);existing?effectiveSource=existing:(sources.push(source),source.class==="dynamic"&&dynamicSourceCount++)}for(let condition of conditions??[])conditionTypes.has(condition.type)||(conditionTypes.set(condition.type,condition),effectiveSource&&sourceByConditionType.set(condition.type,effectiveSource))}function getConditionType(type){return conditionTypes.get(type)}function referencedFilters(cfg){let targetingFilters=cfg.audienceTargeting?.enabled?cfg.audienceTargeting.groups?.flatMap(g=>g.filters??[])??[]:[],forceAssignFilters=cfg.forceAssignRules?.flatMap(r=>r.filters??[])??[];return[...targetingFilters,...forceAssignFilters]}function referencedFilterTypes(cfg){let types=new Set;for(let filter of referencedFilters(cfg))filter.type&&types.add(filter.type);return types}function evaluateFilter(filter,context2){return getConditionType(filter.type)?.evaluate(filter,context2)??!1}function checkAudienceTargeting(targeting,context2){if(!targeting||!targeting.enabled||!targeting.groups||targeting.groups.length===0)return!0;for(let group of targeting.groups){if(!group.filters||group.filters.length===0)continue;if(!group.filters.some(filter=>evaluateFilter(filter,context2)))return!1}return!0}function evaluateForceAssignRules(rules,context2){if(!rules||rules.length===0)return null;let sortedRules=[...rules].sort((a,b)=>a.priority-b.priority);for(let rule of sortedRules)if(rule.filters.every(filter=>evaluateFilter(filter,context2)))return{matched:!0,action:rule.action,groupIndex:rule.groupIndex};return null}var countryCondition={type:"country",evaluate:(filter,context2)=>evaluateOperator(filter.operator,context2.country,filter.value)};var VISITOR_PREFIX="abconvert.visitor",VISITOR_FIRST_SEEN_KEY=`${VISITOR_PREFIX}.first_seen`,VISITOR_CUSTOMER_KEY=`${VISITOR_PREFIX}.customer`,VISITOR_COUNTRY_KEY=`${VISITOR_PREFIX}.country_detected`,VISITOR_KLAVIYO_SEGMENTS_KEY=`${VISITOR_PREFIX}.klaviyo_segments`;var SESSION_PREFIX="abconvert.session",SESSION_DEVICE_KEY=`${SESSION_PREFIX}.device`,SESSION_TRAFFIC_PLATFORM_KEY=`${SESSION_PREFIX}.traffic_source.platform`,SESSION_TRAFFIC_CHANNEL_KEY=`${SESSION_PREFIX}.traffic_source.channel`,SESSION_REFERRAL_DOMAIN_KEY=`${SESSION_PREFIX}.referral_domain`,SESSION_LANDING_PAGE_KEY=`${SESSION_PREFIX}.landing_page`,SESSION_UTM_SOURCE_KEY=`${SESSION_PREFIX}.utm.source`,SESSION_UTM_MEDIUM_KEY=`${SESSION_PREFIX}.utm.medium`,SESSION_UTM_CAMPAIGN_KEY=`${SESSION_PREFIX}.utm.campaign`,SESSION_UTM_TERM_KEY=`${SESSION_PREFIX}.utm.term`,SESSION_UTM_CONTENT_KEY=`${SESSION_PREFIX}.utm.content`,SESSION_GCLID_KEY=`${SESSION_PREFIX}.gclid`,SESSION_FBCLID_KEY=`${SESSION_PREFIX}.fbclid`,SESSION_TTCLID_KEY=`${SESSION_PREFIX}.ttclid`,SESSION_FORCE_ASSIGN_GLOBAL_KEY=`${SESSION_PREFIX}.force_assign.global`,SESSION_FORCE_ASSIGN_PREFIX=`${SESSION_PREFIX}.force_assign.experiment.`;var CUSTOMER_KEYS={logged_in:{field:"loggedIn",evaluate:evaluateLoggedIn},tags:{field:"tags",evaluate:evaluateTags},orders_count:{field:"ordersCount",evaluate:evaluateNumeric},total_spent:{field:"totalSpent",evaluate:evaluateNumeric}};function specFor(key){return key?CUSTOMER_KEYS[key]:void 0}function valuesOf(expected){return Array.isArray(expected)?expected:[expected]}function firstValue(expected){return valuesOf(expected)[0]}function toBoolean(value){return typeof value=="boolean"?value:typeof value=="string"&&value.trim().toLowerCase()==="true"}function toNumber(value){if(typeof value=="number")return Number.isFinite(value)?value:NaN;if(typeof value!="string")return NaN;let trimmed=value.trim();if(trimmed==="")return NaN;let parsed=Number(trimmed);return Number.isFinite(parsed)?parsed:NaN}function normalizeTags(values){return values.map(value=>typeof value=="string"?normalizeToken(value):"").filter(value=>value!=="")}function evaluateLoggedIn(operator,actual,expected){return operator!=="equals"?!1:!!actual===toBoolean(firstValue(expected))}function evaluateTags(operator,actual,expected){let tags=normalizeTags(Array.isArray(actual)?actual:[]),wanted=normalizeTags(valuesOf(expected));switch(operator){case"contains":return wanted.length>0&&tags.includes(wanted[0]);case"not_contains":return!(wanted.length>0&&tags.includes(wanted[0]));case"in":return wanted.some(tag=>tags.includes(tag));case"not_in":return!wanted.some(tag=>tags.includes(tag));case"exists":return tags.length>0;case"not_exists":return tags.length===0;default:return!1}}function evaluateNumeric(operator,actual,expected){let left=toNumber(actual),right=toNumber(firstValue(expected));if(Number.isNaN(left)||Number.isNaN(right))return!1;switch(operator){case"equals":return left===right;case"greater_than":return left>right;case"less_than":return left<right;case"greater_or_equal":return left>=right;case"less_or_equal":return left<=right;default:return!1}}var customerCondition={type:"customer",evaluate:(filter,context2)=>{let customer=context2.customer;if(!customer)return!1;let spec=specFor(filter.key);return spec?spec.evaluate(filter.operator,customer[spec.field],filter.value):!1},watchedValue:(filter,context2)=>{let spec=specFor(filter.key);return spec?context2.customer?.[spec.field]:void 0}};function deviceFromUserAgent(ua){return/mobile|iphone|android|tablet|ipad/i.test(ua)?"mobile":"desktop"}var deviceCondition={type:"device",evaluate:(filter,context2)=>evaluateOperator(filter.operator,context2.device,filter.value)};function visitorTypeOf(firstSeen,experimentStartTime,now){let startTime=experimentStartTime!=null?new Date(experimentStartTime).valueOf():now;return firstSeen>startTime?"new":"returning"}var visitorTypeCondition={type:"visitor_type",evaluate:(filter,context2)=>evaluateOperator(filter.operator,context2.visitorType,filter.value)};function deriveTraffic({search,referrer:rawReferrer,userAgent,hostname}){let referrer=rawReferrer.toLowerCase(),params=new URLSearchParams(search),utmSource=params.get("utm_source")?.toLowerCase(),utmMedium=params.get("utm_medium")?.toLowerCase(),gclid=params.get("gclid"),gadSource=params.get("gad_source"),fbclid=params.get("fbclid"),ttclid=params.get("ttclid"),msclkid=params.get("msclkid"),ua=userAgent.toLowerCase(),inAppBrowser=ua.includes("instagram")?"instagram":ua.includes("fbav")||ua.includes("fban")?"facebook":ua.includes("musical_ly")||ua.includes("tiktok")?"tiktok":null,isPaidMedium=/^(cpc|ppc|paid|retargeting|banner|ads?)$/.test(utmMedium||""),referrerIncludes=(...terms)=>terms.some(t=>referrer.includes(t)),platform="direct",channel="direct";return gclid||gadSource||utmSource==="google"||referrerIncludes("google.")?(platform="google",channel=gclid||gadSource||isPaidMedium?"paid_search":"organic_search"):fbclid||utmSource==="facebook"||utmSource==="fb"||referrerIncludes("facebook.com","fb.com")||inAppBrowser==="facebook"?(platform="facebook",channel=fbclid||isPaidMedium?"paid_social":"organic_social"):utmSource==="instagram"||utmSource==="ig"||referrerIncludes("instagram.com")||inAppBrowser==="instagram"?(platform="instagram",channel=isPaidMedium?"paid_social":"organic_social"):ttclid||utmSource==="tiktok"||referrerIncludes("tiktok.com")||inAppBrowser==="tiktok"?(platform="tiktok",channel=ttclid||isPaidMedium?"paid_social":"organic_social"):utmSource==="twitter"||utmSource==="x"||referrerIncludes("twitter.com","x.com","t.co")?(platform="twitter",channel=isPaidMedium?"paid_social":"organic_social"):utmSource==="youtube"||utmSource==="yt"||referrerIncludes("youtube.com","youtu.be")?(platform="youtube",channel=isPaidMedium?"paid_social":"organic_social"):msclkid||utmSource==="bing"||referrerIncludes("bing.com")?(platform="bing",channel=msclkid||isPaidMedium?"paid_search":"organic_search"):referrerIncludes("yahoo.com","duckduckgo.com","baidu.com","yandex.")?(platform="other_search",channel="organic_search"):["email","e-mail","e_mail"].includes(utmSource||"")||["email","e-mail","e_mail"].includes(utmMedium||"")?(platform="email",channel="email"):["sms","text"].includes(utmSource||"")||["sms","text"].includes(utmMedium||"")?(platform="sms",channel="sms"):utmMedium==="affiliate"?(platform="affiliate",channel="affiliate"):referrer&&!referrer.includes(hostname)?(platform="referral",channel="referral"):utmSource&&(platform=utmSource,channel="referral"),{platform,channel}}var trafficCondition={type:"traffic_source",evaluate:(filter,context2)=>{let{operator,value}=filter,values=Array.isArray(value)?value:[value],channel=context2.trafficChannel??"",platform=context2.trafficPlatform??"";switch(operator){case"in":return values.includes(channel)||values.includes(platform);case"not_in":return!values.includes(channel)&&!values.includes(platform);case"equals":return channel===values[0]||platform===values[0];case"not_equals":return channel!==values[0]&&platform!==values[0];default:return!1}}};function isSameDomain(hostname1,hostname2){return hostname1===hostname2?!0:hostname1.endsWith("."+hostname2)||hostname2.endsWith("."+hostname1)}function referralDomainOf(referrer,hostname){if(!referrer)return"";try{let referrerHostname=new URL(referrer).hostname;return isSameDomain(referrerHostname,hostname)?"":referrerHostname}catch{return""}}var referralDomainCondition={type:"referral_domain",evaluate:(filter,context2)=>evaluateOperator(filter.operator,context2.referralDomain,filter.value)};var landingPageCondition={type:"landing_page",evaluate:(filter,context2)=>evaluateOperator(filter.operator,context2.landingPage,filter.value)};function queryParamsOf(search){let params={};return new URLSearchParams(search).forEach((value,key)=>{params[key]=value}),params}var queryParamCondition={type:"query_param",evaluate:(filter,context2)=>evaluateOperator(filter.operator,filter.key?context2.queryParams[filter.key]:null,filter.value)};function cookieMapOf(cookieHeader){let cookies={};return cookieHeader.split(";").forEach(cookie=>{let[key,value]=cookie.trim().split("=");key&&(cookies[key]=value||"")}),cookies}var cookieCondition={type:"cookie",evaluate:(filter,context2)=>evaluateOperator(filter.operator,filter.key?context2.cookies[filter.key]:null,filter.value)};var SDK_CONDITIONS=[countryCondition,customerCondition,deviceCondition,visitorTypeCondition,trafficCondition,referralDomainCondition,landingPageCondition,queryParamCondition,cookieCondition];function registerConditions(){registerAudienceModule({conditions:SDK_CONDITIONS})}registerConditions();var asEngineConfig=cfg=>({audienceTargeting:cfg.audience,forceAssignRules:cfg.forceAssignRules});function unknownSource(cfg,location){for(let type of referencedFilterTypes(asEngineConfig(cfg)))if(!getConditionType(type)||type==="country"&&location===null)return type;return null}var DYNAMIC_CONDITIONS=new Set(["customer"]);function mayStillQualify(cfg){for(let type of referencedFilterTypes(asEngineConfig(cfg)))if(DYNAMIC_CONDITIONS.has(type))return!0;return!1}function evaluate(cfg,context2){return{eligible:checkAudienceTargeting(cfg.audience,context2),forced:evaluateForceAssignRules(cfg.forceAssignRules,context2)}}var CUSTOMER_FIELDS=[["logged_in","loggedIn"],["tags","tags"],["orders_count","ordersCount"],["total_spent","totalSpent"]],SLOT_OF_KEY=new Map(CUSTOMER_FIELDS.map(([key],slot)=>[key,slot]));function readsCustomer(cfg){return referencedFilterTypes(asEngineConfig(cfg)).has("customer")}function tokenOf(field,value){return field==="loggedIn"?value?"1":"0":value===void 0?"n":field==="tags"?murmurhash3_32(JSON.stringify(value)).toString(36):String(value)}function customerMark(customer,stored){let signedIn=!!customer?.loggedIn,before=stored?.split("_")??[];return CUSTOMER_FIELDS.map(([,field],slot)=>slot===0||signedIn?tokenOf(field,customer?.[field]):before[slot]??"").join("_")}function reviewStoredDecision(cfg,groupIndex,stored,current2,context2){let before=stored.split("_"),after=current2.split("_");if(!referencedFilters(asEngineConfig(cfg)).some(filter=>{let slot=filter.type==="customer"&&filter.key!==void 0?SLOT_OF_KEY.get(filter.key):void 0;return slot!==void 0&&(before[slot]??"")!==after[slot]}))return"keep";if(groupIndex<0)return"redecide";let{eligible,forced}=evaluate(cfg,context2);return eligible&&forced?.action!=="exclude"?"refresh":"suppress"}var GLOBAL_FORCE="*";function numericIdOf(indexes,id){return indexes.byObjectId.get(id)?.id??id}function applyUrlForces(state,url,indexes){let force=parseForceAssignFromUrl(url.search);if(!force)return;let groupIndex=parseForcedGroupIndex(force.value);groupIndex!==null&&setForce(state,force.experimentId===void 0?GLOBAL_FORCE:numericIdOf(indexes,force.experimentId),groupIndex)}function linkedTestId(url){return url.searchParams.get("abconvert_experiment_id")||parseForceAssignFromUrl(url.search)?.experimentId||void 0}function setForce(state,id,groupIndex){let{[id]:_previous,...rest}=state.f??{},forces=groupIndex===null?rest:{...rest,[id]:groupIndex};Object.keys(forces).length?state.f=forces:delete state.f}function forcedDecision(experiment,groupIndex){return{experiment,inTest:!0,groupIndex:groupIndex<experiment.testGroups.length?groupIndex:0,reason:"url_force_assign",isNew:!1}}function forcedIndex(state,experiment){let forced=state.f?.[experiment.id]??state.f?.[experiment.objectId]??state.f?.[GLOBAL_FORCE];return typeof forced=="number"&&isValidForcedGroupIndex(forced)?forced:null}function stamp(experiment,groupIndex,customer){let version=experiment.traffic.assignmentVersion;return customer!==void 0?[groupIndex,version,customer]:version===void 0?[groupIndex]:[groupIndex,version]}function setSuppressed(state,id,suppressed){let rest=(state.s??[]).filter(entry=>entry!==id),next=suppressed?[...rest,id]:rest;next.length?state.s=next:delete state.s}function trafficStore(state,indexes){return{get(objectId){let id=numericIdOf(indexes,objectId);return state.t?.includes(id)?!1:id in state.d?!0:null},set(objectId,inTest2){inTest2||(state.t=[...state.t??[],numericIdOf(indexes,objectId)])}}}function namespaceStore(state){return{get:groupId=>state.n[groupId],set:(groupId,objectId)=>{state.n[groupId]=objectId},clear:groupId=>{delete state.n[groupId]}}}function decideOne(input,experiment){let{doc,indexes,request,state,visitorId,url,now}=input,{traffic,testGroups}=experiment,out=(reason,isNew=!0)=>({experiment,inTest:!1,groupIndex:-1,reason,isNew}),inTest2=(groupIndex,reason,isNew)=>({experiment,inTest:!0,groupIndex,reason,isNew}),forced=forcedIndex(state,experiment);if(forced!==null)return forcedDecision(experiment,forced);let stored=state.d[experiment.id]??null,outdated=stored!==null&&traffic.isPersonalization===!0&&traffic.assignmentVersion!==void 0&&stored[1]!==traffic.assignmentVersion;if(stored===null&&!isActivatedByPageTargeting(traffic.pageTargeting,{pathname:url.pathname,href:url.href})||(stored===null||outdated)&&unknownSource(traffic,request.location)!==null)return null;if(!checkNamespace(visitorId,experiment.objectId,doc.namespaceGroups,namespaceStore(state)))return out("namespace_excluded",stored===null);if(!checkTrafficAllocation(visitorId,experiment.objectId,traffic.allocationPercent,trafficStore(state,indexes)))return out("traffic_allocation_excluded",stored===null);let context2=request.visitorTypeFor(experiment.startedAt,now),sticky=stored!==null&&!outdated&&stored[0]<testGroups.length,customer=readsCustomer(traffic)?customerMark(context2.customer,sticky?stored[2]:void 0):void 0;if(sticky){let review=customer===void 0||stored[2]===void 0||unknownSource(traffic,request.location)!==null?"keep":reviewStoredDecision(traffic,stored[0],stored[2],customer,context2);if(customer!==void 0&&(setSuppressed(state,experiment.id,review==="suppress"),(review==="refresh"||stored[2]===void 0)&&(state.d[experiment.id]=[stored[0],stored[1],customer])),review==="suppress")return out("eligibility_withdrawn",!1);if(review!=="redecide")return stored[0]<0?out("stored_exclusion",!1):inTest2(stored[0],"random_split",!1)}let verdict=evaluate(traffic,context2),decision;return verdict.eligible?verdict.forced?.action==="exclude"?decision=out("force_assign_excluded"):verdict.forced?.action==="assign_group"?decision=inTest2(verdict.forced.groupIndex,"force_assign_group",!0):decision=inTest2(calculateGroupFromHash(visitorId,experiment.objectId,testGroups.map(group=>group.split)),"random_split",!0):decision=out("audience_targeting_excluded"),state.d[experiment.id]=stamp(experiment,decision.groupIndex,customer),customer!==void 0&&setSuppressed(state,experiment.id,!1),noteCopy(state,doc),decision}function noteCopy(state,doc){let builtAt2=Date.parse(doc.generatedAt);doc.preview||!Number.isFinite(builtAt2)||builtAt2>(state.g??0)&&(state.g=builtAt2)}function decide(input){let decisions=new Map,decided=new Set;for(let experiment of input.doc.experiments){if(experiment.status==="preview"&&!input.previewMode)continue;decided.add(experiment.id);let decision=decideOne(input,experiment);decision&&decisions.set(experiment.id,decision)}let{state}=input;if(state.s){let running=state.s.filter(id=>decided.has(id));running.length?state.s=running:delete state.s}return decisions}var SHIPPING_V2_PROPERTY_KEYS={LINE_ITEM:"_abconvert-shipping",ITEM_PRICE_PRESENTMENT:"_abconvert-item-price-presentment",PRESENTMENT_CURRENCY:"_abconvert-presentment-currency"},LINE_ITEM_PROPERTY_KEYS={ASSIGNMENT:"_abconvert-assignment",VISITOR:"_abconvert-visitor",SESSION:"_abconvert-session",COUNTRY:"_abconvert-country",SOURCE:"_abconvert-source"},CART_ATTRIBUTE_KEYS={SESSION_ID:"abconvert-session",VISITOR_ID:"abconvert-visitor",COUNTRY:"abconvertCountry",SEARCH_PARAMS:"abconvertSearchParams",ANALYTICS_SESSION:"abconvert-analytics-session",EXPERIMENT_IDS:"abconvert-experiment-ids",ASSIGNMENT:"abconvert-assignment",SOURCE:"abconvert-source",SHIPPING_V2:"abconvert-shipping",VISUAL_EDITOR:"abconvert-visual-editor",TEMPLATE:"abconvert-template",THEME:"abconvert-theme",URL_REDIRECT:"abconvert-url-redirect",PRICE_V1:"groupInfoPriceTestV1",PRICE_V2:"groupInfo",CHECKOUT_UI:"groupInfoCheckoutUITest",PAYMENT_CUSTOMIZATION:"groupInfoPaymentCustomizationTest",DELIVERY_CUSTOMIZATION:"groupInfoDeliveryCustomizationTest",CHECKOUT_TEST:"groupInfoCheckoutTest",OFFER:"abconvert-offer"},CART_SOURCE_SDK="sdk";var KEY_BY_CHANGE={shipping:CART_ATTRIBUTE_KEYS.SHIPPING_V2,offer:CART_ATTRIBUTE_KEYS.OFFER,checkout:CART_ATTRIBUTE_KEYS.CHECKOUT_TEST},inTest=decisions=>[...decisions.values()].filter(decision=>decision.inTest),pairs=list=>list.map(({experiment,groupIndex})=>`${experiment.objectId}:${groupIndex}`).join(",");function cartAttributes(decisions,indexes,visitor){let source={key:CART_ATTRIBUTE_KEYS.SOURCE,value:CART_SOURCE_SDK},assigned=inTest(decisions);if(!assigned.length)return[source];let carrying=change=>assigned.filter(({experiment})=>indexes.changeTypes.get(experiment.id)?.has(change)),attributes=[source,{key:CART_ATTRIBUTE_KEYS.VISITOR_ID,value:visitor.visitorId},{key:CART_ATTRIBUTE_KEYS.SESSION_ID,value:visitor.sessionId},{key:CART_ATTRIBUTE_KEYS.COUNTRY,value:visitor.country},{key:CART_ATTRIBUTE_KEYS.EXPERIMENT_IDS,value:JSON.stringify(assigned.map(({experiment})=>experiment.objectId).sort())}],priced=carrying("price");if(priced.length){let groupInfo=Object.fromEntries(priced.map(({experiment,groupIndex})=>[experiment.objectId,groupIndex]));attributes.push({key:CART_ATTRIBUTE_KEYS.PRICE_V2,value:JSON.stringify(groupInfo)})}for(let[change,key]of Object.entries(KEY_BY_CHANGE)){let holding=carrying(change);holding.length&&attributes.push({key,value:pairs(holding)})}return attributes}function cartLineAttributes(decisions,indexes,visitor){let source={key:LINE_ITEM_PROPERTY_KEYS.SOURCE,value:CART_SOURCE_SDK},assigned=inTest(decisions);if(!assigned.length)return[source];let assignment=Object.fromEntries(assigned.map(({experiment,groupIndex})=>[experiment.objectId,groupIndex])),attributes=[{key:LINE_ITEM_PROPERTY_KEYS.ASSIGNMENT,value:JSON.stringify(assignment)},{key:LINE_ITEM_PROPERTY_KEYS.VISITOR,value:visitor.visitorId},{key:LINE_ITEM_PROPERTY_KEYS.SESSION,value:visitor.sessionId},{key:LINE_ITEM_PROPERTY_KEYS.COUNTRY,value:visitor.country},source],shipping=assigned.filter(({experiment})=>indexes.changeTypes.get(experiment.id)?.has("shipping"));return shipping.length&&attributes.push({key:SHIPPING_V2_PROPERTY_KEYS.LINE_ITEM,value:pairs(shipping)}),attributes}var SESSION_COOKIE_NAME="abconvert-session";function calculateSessionExpiration(nowMs=Date.now()){let thirtyMinutesFromNow=nowMs+18e5,now=new Date(nowMs),nextMidnightUTC=Date.UTC(now.getUTCFullYear(),now.getUTCMonth(),now.getUTCDate()+1,0,0,0,0);return Math.min(thirtyMinutesFromNow,nextMidnightUTC)}var COOKIE_NAMES={visitor:"abconvert-visitor",session:SESSION_COOKIE_NAME,state:"abconvert-state"},YEAR_SECONDS=365*24*60*60;function parseCookieHeader(header){let cookies={};if(!header)return cookies;for(let part of header.split(";")){let at=part.indexOf("=");if(at<0)continue;let name=part.slice(0,at).trim();if(!name||name in cookies)continue;let raw=part.slice(at+1).trim();try{cookies[name]=decodeURIComponent(raw)}catch{cookies[name]=raw}}return cookies}function toBase64Url(text){let binary="";for(let byte of new TextEncoder().encode(text))binary+=String.fromCharCode(byte);return btoa(binary).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function fromBase64Url(value){let binary=atob(value.replace(/-/g,"+").replace(/_/g,"/"));return new TextDecoder().decode(Uint8Array.from(binary,char=>char.charCodeAt(0)))}var emptyState=()=>({v:1,d:{},n:{}});function packDecisions(d){return Object.entries(d).map(([id,[index,version,customer]])=>[id,index,...version!==void 0||customer!==void 0?[version??""]:[],...customer!==void 0?[customer]:[]].join(":")).join(",")}function unpackDecisions(packed){let d={};if(typeof packed!="string"||packed==="")return d;for(let entry of packed.split(",")){let[id,index,version,customer]=entry.split(":"),groupIndex=Number(index);if(!id||!Number.isInteger(groupIndex))continue;let stamped=version!==void 0&&version!==""&&Number.isInteger(Number(version))?Number(version):void 0;customer!==void 0?d[id]=[groupIndex,stamped,customer]:d[id]=stamped===void 0?[groupIndex]:[groupIndex,stamped]}return d}var packList=list=>list?.length?list.join(","):void 0,unpackList=packed=>typeof packed=="string"&&packed?packed.split(","):void 0;function encodeState(state){let{t,s,...rest}=state;return toBase64Url(JSON.stringify({...rest,d:packDecisions(state.d),t:packList(t),s:packList(s)}))}function decodeState(value){if(!value)return emptyState();try{let parsed=JSON.parse(fromBase64Url(value));if(parsed?.v!==1||typeof parsed.d!="string")return emptyState();let{t,s,preview,...rest}=parsed,traffic=unpackList(t),suppressed=unpackList(s);return{...rest,...typeof preview=="string"&&preview?{preview}:{},d:unpackDecisions(parsed.d),...traffic?{t:traffic}:{},...suppressed?{s:suppressed}:{},n:parsed.n&&typeof parsed.n=="object"?parsed.n:{}}}catch{return emptyState()}}function sessionPreviewKey(cookies){let state=decodeState(cookies[COOKIE_NAMES.state]),session=cookies[COOKIE_NAMES.session];return session&&state.session?.id===session?state.preview:void 0}function serialize(name,value,maxAgeSeconds){return`${name}=${value}; Max-Age=${maxAgeSeconds}; Path=/; SameSite=Lax; Secure`}var sessionSecondsAt=now=>Math.ceil((calculateSessionExpiration(now)-now)/1e3);function visitorCookies(visitorId,sessionId,stateCookie,now){return[serialize(COOKIE_NAMES.visitor,visitorId,YEAR_SECONDS),serialize(COOKIE_NAMES.session,sessionId,sessionSecondsAt(now)),stateCookieHeader(stateCookie)]}function sessionCookieHeader(sessionId,now){return serialize(COOKIE_NAMES.session,sessionId,sessionSecondsAt(now))}function stateCookieHeader(stateCookie){return serialize(COOKIE_NAMES.state,stateCookie,YEAR_SECONDS)}var MAILBOX_COOKIE="abconvert-pending",KEY_COOKIE="abconvert-key",DAY_SECONDS=1440*60,MAILBOX_SIZE=50,MAILBOX_MARKS={random_split:"",force_assign_group:"!",audience_targeting_excluded:"-",force_assign_excluded:"~"},MARKED=new Map(Object.entries(MAILBOX_MARKS).map(([reason,mark])=>[mark,reason]));function encodeMailbox({country,assignments}){let a=assignments.slice(-MAILBOX_SIZE).map(([id,reason])=>`${id}${MAILBOX_MARKS[reason]}`).join(",");return toBase64Url(JSON.stringify({...country?{c:country}:{},a}))}function decodeMailbox(value){if(!value)return{assignments:[]};try{let parsed=JSON.parse(fromBase64Url(value));if(typeof parsed?.a!="string")return{assignments:[]};let assignments=[];for(let entry of parsed.a.split(",")){let[,id,mark]=/^(\d+)(\D?)$/.exec(entry)??[],reason=MARKED.get(mark);id&&reason&&assignments.push([id,reason])}return{...typeof parsed.c=="string"&&/^[A-Z]{2}$/.test(parsed.c)?{country:parsed.c}:{},assignments:assignments.slice(-MAILBOX_SIZE)}}catch{return{assignments:[]}}}function mailboxCookieHeader(value){return serialize(MAILBOX_COOKIE,value,value?DAY_SECONDS:0)}function keyCookieHeader(value){return serialize(KEY_COOKIE,value,value?YEAR_SECONDS:0)}function consentFromShopifyCookie(value,whenAbsent=!0){if(!value)return whenAbsent;try{let parsed=JSON.parse(value);if(typeof parsed?.purposes?.a=="boolean")return parsed.purposes.a;let answer=parsed?.con?.CMP?.a;return answer==="1"?!0:answer==="0"?!1:!parsed?.reg}catch{return!0}}function createCookieJar({cookies,now,randomUUID,write:write2="all"}){let storedVisitor=cookies[COOKIE_NAMES.visitor],visitorId=storedVisitor||`abc_${randomUUID()}`,storedSession=cookies[COOKIE_NAMES.session],sessionId=storedSession||randomUUID(),rawState=cookies[COOKIE_NAMES.state],state=decodeState(rawState),initial=rawState?encodeState(state):null;return state.session?.id!==sessionId&&(delete state.session,delete state.preview,delete state.f,delete state.triggered),typeof state.firstSeen!="number"&&(state.firstSeen=now),{visitorId,sessionId,state,setCookieHeaders(written=state){if(write2==="none")return[];let headers2=[];storedVisitor||headers2.push(serialize(COOKIE_NAMES.visitor,visitorId,YEAR_SECONDS)),(write2==="all"||!storedSession)&&headers2.push(serialize(COOKIE_NAMES.session,sessionId,sessionSecondsAt(now)));let encoded=encodeState(written);return encoded!==initial&&headers2.push(serialize(COOKIE_NAMES.state,encoded,YEAR_SECONDS)),headers2}}}var VARIANT_GID=/^gid:\/\/shopify\/ProductVariant\/(\d+)$/;function variantIdOf(variantId){let id=String(variantId??"").trim(),numeric=VARIANT_GID.exec(id)?.[1]??id;if(!/^\d+$/.test(numeric))throw new TypeError(`[ABConvert] getCheckoutUrl: ${id||"a line"} is not a product variant ID`);return numeric}function quantityOf(quantity){if(!Number.isInteger(quantity)||quantity<1)throw new TypeError("[ABConvert] getCheckoutUrl: quantity must be a positive integer");return quantity}function checkoutUrl({shop,lines,payment,attributes,lineAttributes}){if(!Array.isArray(lines)||lines.length===0)throw new TypeError("[ABConvert] getCheckoutUrl: lines must list at least one line");if(payment!==void 0&&payment!=="shop_pay")throw new TypeError("[ABConvert] getCheckoutUrl: payment must be 'shop_pay' or left out");let path=lines.map(line=>`${variantIdOf(line?.variantId)}:${quantityOf(line?.quantity)}`).join(","),query=attributes.map(({key,value})=>`attributes[${encodeURIComponent(key)}]=${encodeURIComponent(value)}`);return lineAttributes.length&&query.push(`properties=${toBase64Url(JSON.stringify(Object.fromEntries(lineAttributes.map(({key,value})=>[key,value]))))}`),payment&&query.push(`payment=${payment}`),`https://${shop}/cart/${path}${query.length?`?${query.join("&")}`:""}`}var ASSIGNMENT_TYPES=["price","shipping","theme","template","url_redirect","checkout","offer","visual_editor","combined","personalization"],ASSIGNMENT_REASONS=["random_split","force_assign_group","preview_override","url_force_assign"];function copyTestGroup(testGroup){return{...testGroup}}function normalizeExperimentId(value){return typeof value=="number"&&Number.isInteger(value)&&value>=0?String(value):typeof value=="string"&&/^\d+$/.test(value)?value:null}var REASONS=new Set(ASSIGNMENT_REASONS);function bundleAssignment(type,experiment,groupIndex,reason){let experimentId=normalizeExperimentId(experiment.numericId),group=experiment.testGroups?.[groupIndex];if(experimentId===null||!group||!REASONS.has(reason))return null;let personalization=experiment.isPersonalization===!0;return{experimentId,experimentName:experiment.name??"",type:personalization?"personalization":experiment.combinedExperimentId!=null?"combined":type,status:String(experiment.status??"").toLowerCase()==="preview"?"preview":"active",testGroup:{index:groupIndex,name:group.name,control:personalization?!1:group.isControl??groupIndex===0,split:group.percentage},reason}}function emptyDocument(shop){return{version:1,generatedAt:"",shop:{domain:shop,currency:"USD",defaultCountry:""},customEvents:[],namespaceGroups:{},experiments:[],markets:[]}}function memoByDoc(build){let cache=new WeakMap;return doc=>{let value=cache.get(doc);return value===void 0&&(value=build(doc),cache.set(doc,value)),value}}function push(map,key,value){let list=map.get(key);list?list.push(value):map.set(key,[value])}function checkoutOrder(a,b){let layer=+(a.traffic.isPersonalization===!0)-+(b.traffic.isPersonalization===!0);return layer!==0||!a.traffic.isPersonalization?layer:(b.startedAt??"").localeCompare(a.startedAt??"")}function buildIndexes(doc){let indexes={byNumericId:new Map,byObjectId:new Map,variantToExperiments:new Map,productToExperiments:new Map,variantToProduct:new Map,changeTypes:new Map,featureKeyToExperiment:new Map,handleToTests:new Map,handlelessTests:new Set};for(let experiment of[...doc.experiments].sort(checkoutOrder)){indexes.byNumericId.set(experiment.id,experiment),indexes.byObjectId.set(experiment.objectId,experiment),indexes.changeTypes.set(experiment.id,new Set(experiment.testGroups.flatMap(group=>Object.keys(group.changes)))),experiment.featureKey&&indexes.featureKeyToExperiment.set(experiment.featureKey,experiment);for(let[productId,{variantIds,handle}]of Object.entries(experiment.products??{})){if(push(indexes.productToExperiments,productId,experiment),!handle)indexes.handlelessTests.add(experiment.id);else{let key=handle.toLowerCase(),tests=indexes.handleToTests.get(key);tests?tests.add(experiment.id):indexes.handleToTests.set(key,new Set([experiment.id]))}for(let variantId of variantIds)push(indexes.variantToExperiments,variantId,experiment),indexes.variantToProduct.set(`${experiment.objectId}:${variantId}`,productId)}}return indexes}function productOfVariant(indexes,experiment,variantId){return indexes.variantToProduct.get(`${experiment.objectId}:${variantId}`)}var indexesOf=memoByDoc(buildIndexes);var SHOPIFY_GID=/^gid:\/\/shopify\/[A-Za-z]+\/(\d+)$/;function toBrowserId(value){let match=SHOPIFY_GID.exec(value);return match?match[1]:value}function isRestMoney(value){return typeof value.amount=="string"&&typeof value.currency=="string"}function toCamelCase(key){return key.replace(/_([a-z0-9])/g,(_match,char)=>char.toUpperCase())}function toBrowserShape(value){if(Array.isArray(value))return value.map(item=>toBrowserShape(item));if(typeof value=="string")return toBrowserId(value);if(value===null||typeof value!="object")return value;let source=value,money=isRestMoney(source),projected={};for(let key of Object.keys(source))projected[toCamelCase(key)]=money&&key==="amount"?Number(source.amount):toBrowserShape(source[key]);return projected}function numericIdOf2(value,type){let id=String(value).trim();return id.startsWith("gid://")&&!id.startsWith(`gid://shopify/${type}/`)?"":toBrowserId(id)}function createPriceReads({doc,indexes,assignmentOf,country:defaultCountry}){let shopCurrency=doc.shop.currency;function amountsFor(experiment,change,country){let market=change.countries?.[country];return market?{...market,currency:experiment.countryCurrencies?.[country]??shopCurrency}:country===doc.shop.defaultCountry||experiment.countryCurrencies?.[country]===shopCurrency?{...change,currency:shopCurrency}:null}function priceFor(experiment,assignment,variantId,productId,country){let change=experiment.testGroups[assignment.testGroup.index]?.changes.price?.[variantId];if(!change)return null;let served=experiment.products?.[productId]?.served;if(served&&!served.includes(country))return null;let amounts=amountsFor(experiment,change,country);return amounts?{experimentId:assignment.experimentId,testGroup:{...assignment.testGroup,variables:{...assignment.testGroup.variables}},amount:amounts.amount,compareAtAmount:amounts.compareAtAmount||null,currency:amounts.currency,country,productId,variantId}:null}function assigned(experiments){for(let experiment of experiments??[]){let assignment=assignmentOf(experiment.id);if(assignment)return[experiment,assignment]}return null}let countryOf=options=>options?.country?String(options.country).toUpperCase():defaultCountry;return{getPriceByVariantId(variantId,options){let id=numericIdOf2(variantId,"ProductVariant"),found=assigned(indexes.variantToExperiments.get(id));if(!found)return null;let productId=productOfVariant(indexes,found[0],id);return productId===void 0?null:priceFor(found[0],found[1],id,productId,countryOf(options))},getPriceByProductId(productId,options){let id=numericIdOf2(productId,"Product"),found=assigned(indexes.productToExperiments.get(id));if(!found)return null;let[experiment,assignment]=found,country=countryOf(options),prices2=(experiment.products?.[id]?.variantIds??[]).map(variantId=>priceFor(experiment,assignment,variantId,id,country)).filter(price=>price!==null);if(!prices2.length)return null;if(options?.aggregate==="first")return prices2[0];let wantMax=options?.aggregate==="max";return prices2.reduce((best,price)=>(wantMax?price.amount>best.amount:price.amount<best.amount)?price:best)}}}var ZERO_DECIMAL_CURRENCIES=new Set(["BIF","CLP","DJF","GNF","ISK","JPY","KMF","KRW","PYG","RWF","UGX","VND","VUV","XAF","XOF","XPF"]);function storedAmountFactor(currency){return ZERO_DECIMAL_CURRENCIES.has(String(currency??"").toUpperCase())?1:100}function fromStoredAmount(value,currency){let amount=Number(value??0);return Number.isFinite(amount)?amount/storedAmountFactor(currency):0}function assignedGroups(experiments,assignments){if(!Array.isArray(experiments))return[];let byId=new Map(assignments.map(assignment=>[assignment.experimentId,assignment])),matched=[];for(let experiment of experiments){let assignment=byId.get(String(experiment?.numericId));assignment&&matched.push({assignment,group:experiment.testGroups?.[assignment.testGroup.index]})}return matched}function cheapestFirst(rates){let byCurrency=new Map;for(let rate of rates){let group=byCurrency.get(rate.currency);group?group.push(rate):byCurrency.set(rate.currency,[rate])}return[...byCurrency.values()].flatMap(group=>group.sort((a,b)=>a.amount-b.amount))}function createShippingMethods(readDict,getAssignments){function assignedTests(){return assignedGroups(readDict()?.experiments,getAssignments())}function zonesOf(tests){let zones=[];for(let{assignment,group}of tests)for(let zoneRate of group?.zoneRates??[])zones.push({assignment,zone:String(zoneRate?.zoneName??""),rates:zoneRate?.rates??[]});return zones}function toRate(assignment,zone,rate){let condition=rate.condition,bound=value=>typeof value!="number"||!Number.isFinite(value)?null:condition?.type==="price"?fromStoredAmount(value,condition.currency||rate.currency):value;return{experimentId:assignment.experimentId,testGroup:copyTestGroup(assignment.testGroup),zone,name:String(rate.name??""),amount:fromStoredAmount(rate.price,rate.currency),currency:String(rate.currency??""),condition:condition?.type?{type:condition.type,minimum:bound(condition.minimum),maximum:bound(condition.maximum),unit:condition.unit??null}:null}}return{getShippingRates(options){let tests=assignedTests();if(!tests.length)return null;let wanted=options?.zone,rates=[];for(let{assignment,zone,rates:dictRates}of zonesOf(tests))wanted!=null&&zone!==wanted||rates.push(...cheapestFirst(dictRates.map(rate=>toRate(assignment,zone,rate))));return rates},getFreeShippingThreshold(options){let tests=assignedTests();if(!tests.length)return null;let zones=zonesOf(tests),names=new Set(zones.map(({zone})=>zone)),wanted=options?.zone??(names.size===1?[...names][0]:null);if(wanted==null)return null;let candidates=[];for(let{assignment,zone,rates}of zones)if(zone===wanted)for(let rate of rates){if(Number(rate.price??0)!==0||rate.condition?.type==="weight")continue;let minimum=rate.condition?.minimum;candidates.push({experimentId:assignment.experimentId,testGroup:copyTestGroup(assignment.testGroup),zone:wanted,amount:minimum==null?0:fromStoredAmount(minimum,rate.condition?.currency||rate.currency),currency:String(rate.currency??"")})}return!candidates.length||new Set(candidates.map(candidate=>candidate.currency)).size>1?null:candidates.reduce((best,candidate)=>candidate.amount<best.amount?candidate:best)}}}var shippingDictOf=memoByDoc(doc=>({experiments:doc.experiments.filter(experiment=>experiment.testGroups.some(group=>group.changes.shipping)).map(experiment=>({numericId:Number(experiment.id),testGroups:experiment.testGroups.map(group=>({zoneRates:(group.changes.shipping?.zones??[]).map(zone=>({zoneName:zone.name,rates:zone.rates}))}))}))}));function createShippingReads(doc,getAssignments){return createShippingMethods(()=>shippingDictOf(doc),getAssignments)}function createOfferMethods(readDict,getAssignments){return{getOffers(){return assignedGroups(readDict()?.experiments,getAssignments()).filter(({group})=>group?.publicOffer).map(({assignment,group})=>({experimentId:assignment.experimentId,testGroup:copyTestGroup(assignment.testGroup),title:String(group.publicOffer.title??""),discounts:toBrowserShape(group.publicOffer.discounts??[])}))}}}var offerDictOf=memoByDoc(doc=>({experiments:doc.experiments.filter(experiment=>experiment.testGroups.some(group=>group.changes.offer)).map(experiment=>({numericId:Number(experiment.id),testGroups:experiment.testGroups.map(({changes:{offer}})=>offer?.discounts?.length?{publicOffer:offer}:{})}))}));function createOfferReads(doc,getAssignments){return createOfferMethods(()=>offerDictOf(doc),getAssignments)}var TYPES=new Set(ASSIGNMENT_TYPES);function toAssignment(decision){let{experiment}=decision;if(!decision.inTest||!TYPES.has(experiment.type))return null;let theme=bundleAssignment(experiment.type,{numericId:Number(experiment.id),name:experiment.name,status:experiment.status,isPersonalization:experiment.traffic.isPersonalization===!0,testGroups:experiment.testGroups.map(group=>({name:group.name,percentage:group.split,isControl:group.control}))},decision.groupIndex,decision.reason);return theme?{...theme,testGroup:{...theme.testGroup,variables:{}},...experiment.featureKey?{featureKey:experiment.featureKey}:{}}:null}function requireExperimentId(experimentId){let id=String(experimentId??"").trim();if(!id)throw new TypeError("[ABConvert] experimentId is required");return id}function requireIndex(index){if(!Number.isInteger(index)||index<0)throw new TypeError("[ABConvert] index must be a non-negative integer");return index}var runtimeLocale;function defaultLocale(){return runtimeLocale??(runtimeLocale=new Intl.NumberFormat().resolvedOptions().locale),runtimeLocale}var formatters=new Map;function formatPrice(amount,currency,locale){try{let key=`${locale}|${currency}`,formatter=formatters.get(key);return formatter||(formatter=new Intl.NumberFormat(locale,{style:"currency",currency}),formatters.set(key,formatter)),formatter.format(amount)}catch{return`${currency} ${Number(amount).toFixed(2)}`}}var copy=assignment=>({...assignment,testGroup:{...assignment.testGroup,variables:{...assignment.testGroup.variables}}});function createReads({doc,indexes,decisions,visitorId,country,locale}){let builtFrom=null,byId=new Map,assignments=()=>{let current2=decisions();if(current2!==builtFrom){byId=new Map;for(let[id,decision]of current2){let assignment=toAssignment(decision);assignment&&byId.set(id,assignment)}builtFrom=current2}return byId},getAssignments=()=>[...assignments().values()].map(copy),getAssignment=experimentId=>{let assignment=assignments().get(String(experimentId??"").trim());return assignment?copy(assignment):null},prices2=createPriceReads({doc,indexes,country,assignmentOf:id=>assignments().get(id)}),shipping=createShippingReads(doc,getAssignments),offers=createOfferReads(doc,getAssignments);return{getAssignment,getAssignments,getFeature(key){let experiment=indexes.featureKeyToExperiment.get(String(key));return experiment?getAssignment(experiment.id):null},getPriceByVariantId:(variantId,options)=>prices2.getPriceByVariantId(variantId,options),getPriceByProductId:(productId,options)=>prices2.getPriceByProductId(productId,options),formatPrice:(amount,currency)=>formatPrice(amount,currency||doc.shop.currency,locale),getShippingRates:options=>shipping.getShippingRates(options),getFreeShippingThreshold:options=>shipping.getFreeShippingThreshold(options),getOffers:()=>offers.getOffers(),getVisitorId:()=>visitorId,getCountry:()=>country}}var BROWSER_PATTERNS=[["Edge",/Edg(?:e|A|iOS)?\/(\d+[\d.]*)/],["Opera",/(?:OPR|Opera)\/(\d+[\d.]*)/],["Samsung Internet",/SamsungBrowser\/(\d+[\d.]*)/],["Chrome",/(?:Chrome|CriOS)\/(\d+[\d.]*)/],["Firefox",/(?:Firefox|FxiOS)\/(\d+[\d.]*)/],["Safari",/Version\/(\d+[\d.]*).*Safari/]],OS_PATTERNS=[["iOS",/(?:iPhone|iPad|iPod).*OS (\d+[_\d]*)/],["Android",/Android (\d+[\d.]*)/],["Windows",/Windows NT (\d+[\d.]*)/],["macOS",/Mac OS X (\d+[_\d.]*)/],["ChromeOS",/CrOS \w+ ([\d.]+)/],["Linux",/Linux/]];function parseBrowser(ua){for(let[name,regex]of BROWSER_PATTERNS){let match=ua.match(regex);if(match)return{name,version:match[1]}}return{name:"",version:""}}function parseOS(ua){for(let[name,regex]of OS_PATTERNS){let match=ua.match(regex);if(match)return{name,version:(match[1]||"").replace(/_/g,".")}}return{name:"",version:""}}var SESSION_PARAMS=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","gclid","fbclid","ttclid"],LOCATION_HEADERS=["oxygen-buyer-country","cf-ipcountry","x-vercel-ip-country","x-country"];function locationOf(headers2){for(let name of LOCATION_HEADERS){let value=headers2.get(name)?.trim();if(value&&/^[a-z]{2}$/i.test(value)&&!/^(xx|t1)$/i.test(value))return value.toUpperCase()}return null}function customerOf(option){if(!option)return{loggedIn:!1};let customer={loggedIn:option.loggedIn??!0};return option.tags!==void 0&&(customer.tags=option.tags),option.ordersCount!==void 0&&(customer.ordersCount=option.ordersCount),option.totalSpent!==void 0&&(customer.totalSpent=option.totalSpent),customer}function sessionOf({url,headers:headers2,state,sessionId},userAgent){if(state.session?.id===sessionId)return state.session;let referrer=headers2.get("referer")??"",traffic=deriveTraffic({search:url.search,referrer,userAgent,hostname:url.hostname}),landing=queryParamsOf(url.search),params={};for(let name of SESSION_PARAMS)landing[name]&&(params[name]=name==="utm_source"||name==="utm_medium"?landing[name].toLowerCase():landing[name]);return state.session={id:sessionId,landingPage:url.pathname,referralDomain:referralDomainOf(referrer,url.hostname),trafficPlatform:traffic.platform,trafficChannel:traffic.channel,...Object.keys(params).length?{params}:{}},state.session}function buildContext(input){let{url,headers:headers2,state,options}=input,userAgent=headers2.get("user-agent")??"",location=locationOf(headers2),session=sessionOf(input,userAgent),locale=headers2.get("accept-language")?.split(",")[0]?.split(";")[0]?.trim()||void 0,context2={country:location??"",market:void 0,customer:customerOf(options.customer),klaviyoSegments:void 0,device:deviceFromUserAgent(userAgent),trafficPlatform:session.trafficPlatform,trafficChannel:session.trafficChannel,referralDomain:session.referralDomain,landingPage:session.landingPage,queryParams:{...session.params,...queryParamsOf(url.search)},cookies:cookieMapOf(headers2.get("cookie")??""),browser:parseBrowser(userAgent),os:parseOS(userAgent),locale};return{location,context:context2,visitorTypeFor:(experimentStartTime,now)=>({...context2,visitorType:visitorTypeOf(state.firstSeen??now,experimentStartTime??null,now)})}}var DEFAULT_BASE_URL="https://app.abconvert.io";var LoadError=class extends Error{constructor(message,status,settled=!1){super(message);this.status=status;this.settled=settled;this.name="ABConvertLoadError"}},DOCUMENT_VERSION=1;function readDocument(body){let doc=body;if(!doc||typeof doc!="object")throw new LoadError("ABConvert: the tests could not be read");if(doc.version!==DOCUMENT_VERSION)throw new LoadError(`ABConvert: the tests are document version ${String(doc.version)}, and this @abconvert/headless reads version ${DOCUMENT_VERSION}: update the package`,void 0,!0);if(!Array.isArray(doc.experiments))throw new LoadError("ABConvert: the tests could not be read");return doc}var isSettled=error=>error instanceof LoadError&&error.settled;function safeReport(onError,error){try{onError?.(error instanceof Error?error:new LoadError(String(error)))}catch{}}var report=(request,error)=>safeReport(request.onError,error);function configUrl(request,query){let url=`${request.baseUrl.replace(/\/+$/,"")}/api/public/headless/config/${encodeURIComponent(request.storefrontKey)}`,search=query?new URLSearchParams(query).toString():"";return search?`${url}?${search}`:url}var hasTest=(doc,id)=>doc.experiments.some(experiment=>experiment.objectId===id||experiment.id===id);function createLoader({fetch,now,persist}){let shared=new Map,previews=new Map,misses=new Map,previewFetches=new Map,previewTickets=0,entryFor=request=>{let key=request.storefrontKey,entry=shared.get(key);if(!entry){let saved=persist?.read(key);entry={doc:null,etag:null,fetchedAt:0,inflight:null,inflightSince:0,retryAt:0,started:0,kept:0,...saved&&now()-saved.fetchedAt<request.cacheTtl*1e3?saved:null},shared.set(key,entry)}return entry};function fetchShared(request,{bypassCache=!1}={}){let entry=entryFor(request);if(entry.inflight&&!bypassCache&&now()-entry.inflightSince<1e4)return entry.inflight;let headers2={accept:"application/json"};entry.etag&&entry.doc&&(headers2["if-none-match"]=entry.etag),bypassCache&&(headers2["cache-control"]="no-cache");let url=configUrl(request,bypassCache?{fresh:String(now())}:void 0),ticket=++entry.started,run=(async()=>{try{let response=await fetch(url,{headers:headers2});if(ticket<entry.kept)return entry.doc;if(response.status===304&&entry.doc)return entry.fetchedAt=now(),persist?.write(request.storefrontKey,{doc:entry.doc,etag:entry.etag,fetchedAt:entry.fetchedAt}),entry.doc;if(response.status===404)throw entry.doc=null,entry.etag=null,new LoadError("ABConvert: storefront key not found, or the headless SDK is off for this shop",404);if(!response.ok)throw new LoadError(`ABConvert: loading tests failed with HTTP ${response.status}`,response.status);let doc=readDocument(await response.json());return ticket<entry.kept||(entry.kept=ticket,entry.doc=doc,entry.etag=response.headers.get("etag"),entry.fetchedAt=now(),entry.retryAt=0,persist?.write(request.storefrontKey,{doc,etag:entry.etag,fetchedAt:entry.fetchedAt})),entry.doc}catch(error){if(report(request,error),!entry.doc){let settled=error instanceof LoadError&&error.status===404||isSettled(error);entry.retryAt=now()+(settled?request.cacheTtl*1e3:5e3)}return entry.doc}})().finally(()=>{entry.inflight===run&&(entry.inflight=null)});return entry.inflight=run,entry.inflightSince=now(),run}function withTimeout(request,pending){return timeoutAfter(pending,request.timeout,()=>report(request,new LoadError(`ABConvert: loading tests timed out after ${request.timeout} ms`)))}async function previewCopy(request,previewKey){let cacheKey=`${request.storefrontKey}
2
+ ${previewKey}`,testId=request.previewTestId,cached=previews.get(cacheKey),fresh=at=>at!==void 0&&now()-at<request.cacheTtl*1e3,settled=!cached?.doc||testId===void 0||hasTest(cached.doc,testId)||fresh(misses.get(`${cacheKey}
3
+ ${testId}`));if(cached&&fresh(cached.fetchedAt)&&settled)return cached.doc;try{return await withTimeout(request,fetchPreview(request,previewKey,testId))}catch(error){return report(request,error),null}}function fetchPreview(request,previewKey,testId){let cacheKey=`${request.storefrontKey}
4
+ ${previewKey}`,pending=previewFetches.get(cacheKey);if(!pending||now()-pending.since>=1e4){let testIds=new Set,ticket=++previewTickets,run=fetch(configUrl(request,{preview_key:previewKey}),{headers:{accept:"application/json","cache-control":"no-cache"}}).then(async res=>{if(!res.ok)throw new LoadError(`ABConvert: loading the preview failed with HTTP ${res.status}`,res.status);let doc;try{doc=readDocument(await res.json())}catch(error){throw isSettled(error)&&!((previews.get(cacheKey)?.ticket??0)>ticket)&&remember(previews,cacheKey,{doc:null,fetchedAt:now(),ticket}),error}let newer=previews.get(cacheKey),kept;newer&&newer.ticket>ticket?kept=newer.doc:(kept=doc.preview===!0?doc:null,remember(previews,cacheKey,{doc:kept,fetchedAt:now(),ticket}));for(let id of testIds)kept&&!hasTest(kept,id)&&remember(misses,`${cacheKey}
5
+ ${id}`,now());return kept}).finally(()=>{previewFetches.get(cacheKey)?.run===run&&previewFetches.delete(cacheKey)});pending={run,since:now(),testIds},previewFetches.set(cacheKey,pending)}return testId!==void 0&&pending.testIds.add(testId),pending.run}function remember(cache,cacheKey,entry){for(cache.delete(cacheKey),cache.set(cacheKey,entry);cache.size>8;)cache.delete(cache.keys().next().value)}async function loadShared(request){let entry=entryFor(request);return entry.doc?(now()-entry.fetchedAt>=request.cacheTtl*1e3&&fetchShared(request),entry.doc):now()<entry.retryAt?null:withTimeout(request,fetchShared(request))}return{async load(request){return(request.previewKey?await previewCopy(request,request.previewKey):null)??loadShared(request)},async refresh(request){await fetchShared(request,{bypassCache:!0})}}}async function fetchCurrentCopy(request,fetch=(...args)=>globalThis.fetch(...args)){try{let response=await fetch(configUrl(request,{fresh:String(Date.now())}),{headers:{accept:"application/json"}});return response.ok?readDocument(await response.json()):null}catch{return null}}function timeoutAfter(pending,ms,onTimeout){let timer,timeout=new Promise(resolve=>{timer=setTimeout(()=>{onTimeout?.(),resolve(null)},ms)});return Promise.race([pending,timeout]).finally(()=>clearTimeout(timer))}var defaultLoader=createLoader({fetch:(...args)=>globalThis.fetch(...args),now:()=>Date.now()});var lightEntries=memoByDoc(()=>new Map);function lightEntry(doc,experiment){let cache=lightEntries(doc),entry=cache.get(experiment.id);return entry||(entry=buildLightEntry(experiment),cache.set(experiment.id,entry)),entry}function buildLightEntry(experiment){let{products:_products,countryCurrencies:_currencies,traffic,testGroups,...rest}=experiment;return{...rest,traffic:{allocationPercent:traffic.allocationPercent,...traffic.isPersonalization?{isPersonalization:traffic.isPersonalization}:{},...traffic.assignmentVersion!==void 0?{assignmentVersion:traffic.assignmentVersion}:{},...traffic.namespaceGroupId?{namespaceGroupId:traffic.namespaceGroupId}:{}},testGroups:testGroups.map(group=>({...group,changes:Object.fromEntries(Object.entries(group.changes).map(([type,change])=>[type,type==="urlRedirect"?change:{}]))}))}}var onPage=(doc,experiment)=>experiment.status!=="preview"||doc.preview===!0;var TRACKED=new Set(["random_split","force_assign_group"]),TRACKED_EXCLUSIONS=new Set(["audience_targeting_excluded","force_assign_excluded"]);function isReported(decision){return decision.experiment.status==="preview"?!1:decision.inTest?TRACKED.has(decision.reason):TRACKED_EXCLUSIONS.has(decision.reason)&&!mayStillQualify(decision.experiment.traffic)}function deepFreeze(value){if(value&&typeof value=="object"&&!Object.isFrozen(value)){Object.freeze(value);for(let child of Object.values(value))deepFreeze(child)}return value}var slicesOf=memoByDoc(()=>new Map);function sliceOf(doc,experiment,groupIndex){let cache=slicesOf(doc),key=`${experiment.id}:${groupIndex}`,slice=cache.get(key);if(!slice){let testGroups=experiment.testGroups.map(group=>group.index===groupIndex?group:{...group,changes:{}});slice={...experiment,testGroups},cache.set(key,deepFreeze(slice))}return slice}function assignedSlice(doc,decisions){let experiments=doc.experiments.flatMap(experiment=>{let decision=decisions.get(experiment.id);return decision?.inTest?[sliceOf(doc,experiment,decision.groupIndex)]:[]});return{...doc,shop:{...doc.shop},customEvents:doc.customEvents.map(event=>({...event})),markets:doc.markets.map(market=>({...market,countries:[...market.countries]})),namespaceGroups:{},experiments}}function pendingEntry(decision,reason,request,now){let{context:context2}=request,{visitorType}=request.visitorTypeFor(decision.experiment.startedAt,now),version=decision.experiment.traffic.assignmentVersion;return{experimentId:decision.experiment.id,objectId:decision.experiment.objectId,groupIndex:decision.groupIndex,reason,...version!==void 0?{assignmentVersion:version}:{},assignedAt:new Date(now).toISOString(),snapshot:{country:context2.country||void 0,device:context2.device,visitorType,trafficPlatform:context2.trafficPlatform,trafficChannel:context2.trafficChannel,referralDomain:context2.referralDomain,landingPage:context2.landingPage,browser:context2.browser,os:context2.os,locale:context2.locale}}}function pendingAssignments(decisions,request,now){return[...decisions.values()].filter(decision=>decision.isNew&&isReported(decision)).map(decision=>pendingEntry(decision,decision.reason,request,now))}function waitingEntries(assignments,state){return assignments.filter(entry=>waitsInMailbox(entry,state))}function waitsInMailbox([id],state){return state.s?.includes(id)??!1}function mailboxDecision(entry,decisions,experimentOf,state){let decision=heldDecision(entry,decisions,experimentOf,state);return decision&&decision.experiment.status!=="preview"?decision:null}function heldDecision([id,reason],decisions,experimentOf,state){if(TRACKED_EXCLUSIONS.has(reason)){let experiment=experimentOf(id),stored=state.d[id];return experiment&&stored&&stored[0]<0?{experiment,inTest:!1,groupIndex:-1,reason,isNew:!1}:null}let decision=decisions.get(id);return decision?.inTest&&TRACKED.has(decision.reason)?decision:null}var hash36=text=>murmurhash3_32(text).toString(36),DECIDES_ONLY=new Set(["split","allocationPercent"]),withoutDeciding=(key,value)=>DECIDES_ONLY.has(key)?void 0:value,pageHash=doc=>hash36(JSON.stringify([doc.shop,doc.markets,doc.customEvents,doc.experiments.filter(experiment=>onPage(doc,experiment)).map(experiment=>lightEntry(doc,experiment))],withoutDeciding)),renderHashes=memoByDoc(doc=>({shop:pageHash(doc),byId:new Map}));function keyVersion(doc,ids){let memo=renderHashes(doc),hashOf=id=>{let hash=memo.byId.get(id);if(hash===void 0){let experiment=indexesOf(doc).byNumericId.get(id);hash=experiment?hash36(JSON.stringify([experiment.status,experiment.testGroups.map(({index,name,control,changes})=>[index,name,control,changes]),experiment.products,experiment.countryCurrencies])):"-",memo.byId.set(id,hash)}return hash};return hash36([memo.shop,...[...ids].sort().map(hashOf)].join("."))}var COUNTRY_CODE=/^[A-Z]{2}$/;function tail(country,decisions){let code=country&&(COUNTRY_CODE.test(country)?country:"ZZ");return[...code?[`c${code}`]:[],...[...decisions].sort((a,b)=>Number(a[0])-Number(b[0])).map(([id,index])=>`${id}-${index}`)]}function encodePageKey({version,previewKey,country,decisions}){return decisions.length?[`v${version}`,...previewKey?[`p${toBase64Url(previewKey)}`]:[],...tail(country,decisions)].join("."):""}var DECISION=/^(\d+)-(\d+)$/,VERSION=/^v([a-z0-9]+)$/,COUNTRY=/^c([A-Z]{2})$/,PREVIEW=/^p([A-Za-z0-9_-]+)$/,NO_KEY=Object.freeze({version:"",decisions:[]});function decodePageKey(key){if(key===""||key==="-")return{...NO_KEY,decisions:[]};let[first,...rest]=key.split("."),version=VERSION.exec(first)?.[1];if(!version)return null;let parts={version,decisions:[]};for(let segment of rest){let decision=DECISION.exec(segment);if(decision){parts.decisions.push([decision[1],Number(decision[2])]);continue}let country=COUNTRY.exec(segment)?.[1],preview=PREVIEW.exec(segment)?.[1];if(country&&!parts.country&&!parts.decisions.length)parts.country=country;else if(preview&&!parts.previewKey&&!parts.country&&!parts.decisions.length)try{parts.previewKey=fromBase64Url(preview)}catch{return null}else return null}return parts.decisions.length?parts:null}function scopeOf(url){let match=/(?:^|\/)products?\/([^/]+)\/?$/.exec(url.pathname);if(match)try{return{product:decodeURIComponent(match[1]).toLowerCase()}}catch{return{product:match[1].toLowerCase()}}return/^\/(?:[a-z]{2,3}(?:-[a-z0-9]{2,4})?\/?)?$/i.test(url.pathname)||/(?:^|\/)(?:collections|search)(?:\/|$)/.test(url.pathname)?"listing":null}var prices=(indexes,experiment)=>!experiment.featureKey&&indexes.changeTypes.get(experiment.id)?.has("price")===!0;function changesPage(indexes,experiment,scope,onlyOwnProductPrices){return!prices(indexes,experiment)||scope===null?!1:scope==="listing"||!onlyOwnProductPrices?!0:indexes.handlelessTests.has(experiment.id)||indexes.handleToTests.get(scope.product)?.has(experiment.id)===!0}function pageKeyOf({doc,indexes,decisions,url,country,previewKey,onlyOwnProductPrices=!1}){let scope=scopeOf(url),found=[...decisions.values()].filter(decision=>decision.inTest&&changesPage(indexes,decision.experiment,scope,onlyOwnProductPrices)),experiments=found.map(decision=>decision.experiment);return encodePageKey({version:keyVersion(doc,experiments.map(experiment=>experiment.id)),...doc.preview&&previewKey?{previewKey}:{},...country?{country}:{},decisions:found.map(decision=>[decision.experiment.id,decision.groupIndex])})}function pageTested({doc,indexes,url,onlyOwnProductPrices=!1}){let scope=scopeOf(url);return doc.experiments.some(experiment=>changesPage(indexes,experiment,scope,onlyOwnProductPrices))}function keyCookieOf({indexes,decisions,country}){let found=[...decisions.values()].filter(decision=>prices(indexes,decision.experiment));return found.length?tail(country||void 0,found.map(decision=>[decision.experiment.id,decision.inTest?decision.groupIndex:"x"])).join("."):""}var VISITOR_NEEDED="[ABConvert] this needs the visitor, and a client made from a page key has none: in a server action or route handler, create one without pageKey (createABConvert(options) on @abconvert/headless/next, createABConvert({ request }) elsewhere).";function needsVisitor(){throw new TypeError(VISITOR_NEEDED)}function keyDecisions(doc,indexes,parts){let decisions=new Map;for(let[id,groupIndex]of parts.decisions){let experiment=indexes.byNumericId.get(id);!experiment||groupIndex>=experiment.testGroups.length||experiment.status==="preview"&&!doc.preview||decisions.set(id,{experiment,inTest:!0,groupIndex,reason:"random_split",isNew:!1})}return decisions}function withoutForce(decisions,keyed,id){let next=new Map(decisions),original=keyed.get(id);return original?next.set(id,original):next.delete(id),next}var pageDocs=memoByDoc(()=>new Map);function pageDocOf(doc,decisions,pageKey){let cache=pageDocs(doc),built=cache.get(pageKey);if(!built){let slice=assignedSlice(doc,decisions),sliced=new Map(slice.experiments.map(experiment=>[experiment.id,experiment])),experiments=doc.experiments.flatMap(experiment=>decisions.has(experiment.id)?sliced.get(experiment.id)??[]:onPage(doc,experiment)?[lightEntry(doc,experiment)]:[]);built={...slice,experiments},cache.size>=256&&cache.clear(),cache.set(pageKey,built)}return built}function buildPageClient({doc,indexes,shop,parts,country:fallback,baseUrl,storefrontKey,refresh,tracking}){let keyed=keyDecisions(doc,indexes,parts),decisions=keyed,country=parts.country??fallback,pageKey=encodePageKey({...parts,decisions:[...keyed.values()].map(decision=>[decision.experiment.id,decision.groupIndex])}),locale=defaultLocale(),reads=createReads({doc,indexes,decisions:()=>decisions,visitorId:"",country,locale});return{page:{...reads,getPageKey:()=>pageKey,getPageState:()=>({version:2,shop,pageKey,country,assignments:reads.getAssignments(),doc:pageDocOf(doc,keyed,pageKey),locale,...baseUrl?{baseUrl}:{},...storefrontKey?{storefrontKey}:{}}),forceTestGroup(experimentId,index){let experiment=indexes.byNumericId.get(numericIdOf(indexes,requireExperimentId(experimentId)));experiment&&(decisions=new Map(decisions).set(experiment.id,forcedDecision(experiment,requireIndex(index))))},clearForcedTestGroup(experimentId){let id=numericIdOf(indexes,requireExperimentId(experimentId));decisions.get(id)?.reason==="url_force_assign"&&(decisions=withoutForce(decisions,keyed,id))},getCartAttributes:needsVisitor,getCartLineAttributes:needsVisitor,getCheckoutUrl:needsVisitor,getClientState:()=>{throw new TypeError("[ABConvert] a client made from a page key has no visitor: hand the browser getPageState() instead.")},getRedirectUrl:()=>null,getSetCookieHeaders:()=>[],refreshTests:refresh,...tracking},complete:keyed.size===parts.decisions.length}}var CONDITION={MATCHES_EXACTLY:"match_exactly",MATCHES_REGEX_EXACTLY:"match_regex_exactly",CONTAINS:"contains",ENDS_WITH:"ends_with",STARTS_WITH:"starts_with",NOT_MATCH_EXACTLY:"not_match_exactly",NOT_MATCH_REGEX_EXACTLY:"not_match_regex_exactly",NOT_CONTAINS:"not_contains",NOT_ENDS_WITH:"not_ends_with",NOT_STARTS_WITH:"not_starts_with"};function normalizeShopifyPath(shopifyRoot,targetPath){if(!shopifyRoot||shopifyRoot==="/")return targetPath;let normalizedRoot=shopifyRoot.endsWith("/")?shopifyRoot.slice(0,-1):shopifyRoot;if(targetPath===normalizedRoot||targetPath===normalizedRoot+"/"||targetPath.startsWith(normalizedRoot+"/"))return targetPath;let normalizedPath=targetPath.startsWith("/")?targetPath:"/"+targetPath;return normalizedRoot+normalizedPath}var URL_BASE="https://storefront.invalid";function safeDecodeURI(value){try{return decodeURI(value)}catch{return value}}function validateRule(condition,conditionValue,currentValue){let decodedCurrentValue=decodeURI(currentValue);switch(condition){case CONDITION.MATCHES_EXACTLY:return decodeURI(conditionValue)===decodedCurrentValue;case CONDITION.MATCHES_REGEX_EXACTLY:return new RegExp(conditionValue).test(decodedCurrentValue);case CONDITION.CONTAINS:return decodedCurrentValue.includes(decodeURI(conditionValue));case CONDITION.STARTS_WITH:return decodedCurrentValue.startsWith(decodeURI(conditionValue));case CONDITION.ENDS_WITH:return decodedCurrentValue.endsWith(decodeURI(conditionValue));case CONDITION.NOT_MATCH_EXACTLY:return decodeURI(conditionValue)!==decodedCurrentValue;case CONDITION.NOT_MATCH_REGEX_EXACTLY:return!new RegExp(conditionValue).test(decodedCurrentValue);case CONDITION.NOT_CONTAINS:return!decodedCurrentValue.includes(decodeURI(conditionValue));case CONDITION.NOT_STARTS_WITH:return!decodedCurrentValue.startsWith(decodeURI(conditionValue));case CONDITION.NOT_ENDS_WITH:return!decodedCurrentValue.endsWith(decodeURI(conditionValue));default:return!1}}function matchesTriggerRule(rule,path,search){try{let trigger=new URL(safeDecodeURI(rule.triggerUrl),URL_BASE);if(!validateRule(rule.matchType,trigger.pathname,path))return!1;let currentQuery=new URLSearchParams(search);for(let[key,value]of trigger.searchParams)if(!currentQuery.getAll(key).includes(value))return!1;return!0}catch{return!1}}function findMatchingRule(group,path,search){try{return(group?.variation?.rules??[]).find(rule=>matchesTriggerRule(rule,path,search))}catch{return}}function destinationCoversPath(destinationUrl,path,shopifyRoot){try{if(!destinationUrl)return!1;let destPath=safeDecodeURI(new URL(safeDecodeURI(destinationUrl),URL_BASE).pathname);return normalizeShopifyPath(shopifyRoot||"/",destPath)===safeDecodeURI(path)}catch{return!1}}var REDIRECTED_MARKER="_abr",IGNORED_PARAMS=[REDIRECTED_MARKER,"preview_theme_id"],URL_BASE2="https://storefront.invalid";function coversPage(experiment,url){return experiment.testGroups.some(({changes})=>(changes.urlRedirect?.rules??[]).some(rule=>matchesTriggerRule(rule,url.pathname,url.search)||destinationCoversPath(rule.destinationUrl,url.pathname,"/")))}function mergeQuery(current2,destination){let merged=new URLSearchParams(current2);return destination.forEach((value,key)=>{merged.has(key)||merged.set(key,value)}),merged.toString()}function buildDestination(destinationUrl,url,preserveQuery){let target;try{target=new URL(decodeURI(destinationUrl),URL_BASE2)}catch{return null}let query=preserveQuery?mergeQuery(url.searchParams,target.searchParams):target.search.slice(1);return`${target.pathname.replace(/^\/{2,}/,"/")}${query?`?${query}`:""}${target.hash}`}function comparable(url){let copy2=new URL(url.href);for(let name of IGNORED_PARAMS)copy2.searchParams.delete(name);return`${copy2.pathname}${copy2.search}`}function landsHere(rules,url){return rules.some(rule=>destinationCoversPath(rule.destinationUrl,url.pathname,"/"))}function findRedirect({indexes,decisions,url,triggered=[],preview=!1}){let exposed=[],fired=[];if(url.searchParams.has(REDIRECTED_MARKER))return{destination:null,exposed,fired};let here=comparable(url),pageClaimed=!1;for(let experiment of indexes.byNumericId.values()){let covers=coversPage(experiment,url);if(experiment.traffic.isPersonalization&&covers&&pageClaimed)continue;let decision=decisions.get(experiment.id);if(!decision?.inTest)continue;pageClaimed||(pageClaimed=covers);let group=experiment.testGroups[decision.groupIndex],change=group?.changes.urlRedirect;if(!change)continue;let counts2=TRACKED.has(decision.reason)&&!preview,rule=findMatchingRule({variation:change},url.pathname,url.search);if(!rule){counts2&&!group.control&&landsHere(change.rules,url)&&exposed.push(experiment.id);continue}let once=change.mode==="once"&&triggered.includes(experiment.id);if(counts2&&!once&&fired.push(experiment.id),group.control||!rule.destinationUrl){counts2&&exposed.push(experiment.id);continue}if(once)continue;let destination=buildDestination(rule.destinationUrl,url,change.preserveQuery);if(destination&&comparable(new URL(destination,url))!==here)return{destination,exposed:[],fired};counts2&&exposed.push(experiment.id)}return{destination:null,exposed,fired}}var globals=()=>globalThis;function hasDocument(){return typeof globals().document<"u"}function storage(kind){try{return globals()[kind]??null}catch{return null}}function customerPrivacyAllows(){try{let api=globals().Shopify?.customerPrivacy;return typeof api?.analyticsProcessingAllowed!="function"?null:api.analyticsProcessingAllowed()===!0}catch{return null}}function attempt(read2,fallback){try{return read2()??fallback}catch{return fallback}}var answered=value=>value==="yes"||value==="no",isObject=value=>typeof value=="object"&&value!==null;function customerPrivacyVerdict(){let api=attempt(()=>globals().Shopify?.customerPrivacy,null);if(!api||!attempt(()=>api.consentStatus==="loaded",!1))return"unknown";let consent=attempt(()=>api.currentVisitorConsent?.(),{});return consent.analytics==="no"?"deny":attempt(()=>isObject(globals().privacyBanner)&&api.shouldShowBanner?.(),!1)===!0&&![consent.analytics,consent.marketing,consent.preferences].some(answered)?"unknown":attempt(()=>api.analyticsProcessingAllowed?.(),!1)===!0?"allow":"unknown"}function readCookies(){try{return parseCookieHeader(globals().document?.cookie)}catch{return{}}}function writeCookie(cookie){try{globals().document.cookie=cookie}catch{}}function pageUrl(){try{return new URL(globals().location.href)}catch{return null}}function dispatch(name,detail){try{globals().dispatchEvent?.(new CustomEvent(name,{detail}))}catch(error){console.error(`[ABConvert] A listener for ${name} failed:`,error)}}function listen(name,handler,target=globals().document,options){try{return target.addEventListener(name,handler,options),()=>target.removeEventListener(name,handler,options)}catch{return()=>{}}}function timezone(){try{return Intl.DateTimeFormat().resolvedOptions().timeZone||void 0}catch{return}}function normalizePagePath(pathname,localeRoot){if(!pathname)return"/";let canonical=stripLocaleRoot(pathname,localeRoot);return(canonical.length>1?canonical.replace(/\/+$/,""):canonical)||"/"}function stripLocaleRoot(pathname,localeRoot){if(typeof localeRoot!="string"||localeRoot==="/"||!localeRoot)return pathname;let prefix=localeRoot.endsWith("/")?localeRoot.slice(0,-1):localeRoot;return pathname===prefix?"/":pathname.startsWith(`${prefix}/`)?pathname.slice(prefix.length):pathname}var FLAG_PREFIX="abconvert:engagement:",PREVPAGE_KEY="abconvert:engagement:prevpage",current=null;function read(key){try{return storage("sessionStorage")?.getItem(key)??null}catch{return null}}function write(key,value){try{let store=storage("sessionStorage");value===null?store?.removeItem(key):store?.setItem(key,value)}catch{}}var flag=(type,{sessionId,path})=>`${FLAG_PREFIX}${type}:${sessionId}:${path}`,context=path=>({template:null,pathNormalized:path});function emit(visit,type,key,payload,about=visit.path,sent){visit.emit({key,sessionId:visit.sessionId,eventType:`engagement:${type}`,payload:{page_context:context(about),...payload},path:visit.path,sent})}function emitOnce(visit,type,payload={}){let key=flag(type,visit);read(key)===null&&emit(visit,type,key,payload,visit.path,()=>write(key,"1"))}function cameFrom(page2){try{let previous=JSON.parse(read(PREVPAGE_KEY)??"null"),from=typeof previous?.pageUrl=="string"?previous.pageUrl:null;return previous?.sessionId===page2.sessionId&&from!==null&&from!==page2.path?from:null}catch{return null}}function clickThrough(visit){let from=cameFrom(visit);from!==null&&emit(visit,"click_through",`${flag("click_through",visit)}:${from}`,{source:from,destination:visit.path},from),write(PREVPAGE_KEY,JSON.stringify({sessionId:visit.sessionId,pageUrl:visit.path,pageContext:context(visit.path),ts:visit.now()}))}function stayingTime(page2){page2.visibleSince!==null&&(page2.active+=page2.now()-page2.visibleSince,page2.visibleSince=null),emitOnce(page2,"staying_time",{active_time:page2.active,wall_time:page2.now()-page2.startedAt})}function attach(page2){let{document:document2}=globalThis,offScroll=listen("scroll",()=>{globalThis.scrollY>0&&(offScroll(),emitOnce(page2,"scroll"))},globalThis,{passive:!0}),undo=[offScroll,listen("pagehide",()=>stayingTime(page2),globalThis),listen("visibilitychange",()=>{document2.visibilityState==="hidden"?stayingTime(page2):page2.visibleSince===null&&(page2.visibleSince=page2.now(),write(flag("staying_time",page2),null))},document2)];return()=>undo.forEach(off=>off())}var normalize=path=>normalizePagePath(path,void 0);function pageViewed(input){let visit={sessionId:input.sessionId,path:normalize(input.path),emit:input.emit,now:input.now};if(current&&current.path===visit.path&&current.sessionId===visit.sessionId)return;if(visit.path!==normalize(input.currentPath)){emitOnce(visit,"page_view"),clickThrough(visit);return}if(current){let left=current;current=null,left.detach(),stayingTime(left)}let at=visit.now(),page2={...visit,startedAt:at,active:0,visibleSince:globalThis.document?.visibilityState==="hidden"?null:at,detach:()=>{}};current=page2,write(flag("staying_time",page2),null),emitOnce(page2,"page_view"),clickThrough(page2),page2.detach=attach(page2)}var beacon=(url,body)=>{try{let{navigator:navigator2}=globalThis;if(typeof navigator2?.sendBeacon=="function"&&navigator2.sendBeacon(url,body))return}catch{}try{globalThis.fetch(url,{method:"POST",headers:{"Content-Type":"text/plain"},body,keepalive:!0})?.catch?.(()=>{})}catch{}};var ASSIGNMENT_READY_EVENT="abconvert:assignment-ready",CONSENT_EVENT="visitorConsentCollected",PRIVACY_API_LOADED_EVENTS=["consentTrackingApiLoaded","shopifyCustomerPrivacyApiLoaded"],EXPOSURE_FLAG_PREFIX="abconvert:exposed:",CUSTOM_EVENT_PREFIX="abconvert:custom_event:custom:",MAX_QUEUE=100,SILENT_REASONS=new Set(["url_force_assign","preview_override"]);function counts(assignment){return!SILENT_REASONS.has(assignment.reason)&&assignment.status!=="preview"}var newPage=(consent={banner:null,setConsent:null})=>({queue:[],heldExposures:new Map,heldEngagement:new Map,live:[],handoff:[],pending:{assignments:new Map,exposures:new Set},assignments:new Set,readyEvents:new Set,callbacks:new Set,exposures:new Set,warned:new Set,consent}),page=newPage();function shopifyId(value){return value==null||value===""?void 0:toBrowserId(String(value).trim())}function pathOf(url){try{let parsed=typeof url=="string"?new URL(url):null;return parsed&&parsed.origin===pageUrl()?.origin?parsed.pathname:void 0}catch{return}}function snapshotOf(pending){return{experimentId:pending.objectId,testGroup:pending.groupIndex,assignmentReason:pending.reason,createdAt:pending.assignedAt,...pending.snapshot}}function createTracker({state,doc,experimentOf,assignment,assignments,productOf,stateCookie,mailboxLeft=()=>"",onAssignment,expectCustomerPrivacy=!1,send=beacon,now=Date.now}){let base=(state.baseUrl||DEFAULT_BASE_URL).replace(/\/+$/,""),endpoint=kind=>`${base}/api/track/${kind}?shop=${encodeURIComponent(state.shop)}`,pendingByObjectId=new Map,keepSnapshot=pending=>{pending.groupIndex>=0&&pendingByObjectId.set(pending.objectId,pending)};state.pending.assignments.forEach(keepSnapshot);let started=!1,everStarted=!1,flushed=!1,cookiesWritten=!1,detach=()=>{};function consent(){if(expectCustomerPrivacy)return page.consent.banner===null?customerPrivacyVerdict():page.consent.banner?"allow":"deny";let api=customerPrivacyAllows();return page.consent.banner??api??page.consent.setConsent??state.consent.analytics===!0?"allow":page.consent.banner===!1||api===null&&page.consent.setConsent===!1?"deny":"unknown"}let sessionId=()=>readCookies()[COOKIE_NAMES.session]||state.sessionId,here=()=>({path:pageUrl()?.pathname??"",at:new Date(now()).toISOString()});function writeCookies(){if(!(cookiesWritten||!state.visitorId)){cookiesWritten=!0;for(let cookie of visitorCookies(state.visitorId,state.sessionId,stateCookie(),now()))writeCookie(cookie)}}function emptyMailbox(){let left=mailboxLeft();(readCookies()[MAILBOX_COOKIE]!==void 0||left)&&writeCookie(mailboxCookieHeader(left))}function rollSession(){if(!cookiesWritten||!started)return;let current2=readCookies()[COOKIE_NAMES.session]||globalThis.crypto.randomUUID();writeCookie(sessionCookieHeader(current2,now()))}function postAssignment(visitorId,pending){let key=`${visitorId}:${pending.objectId}`,experiment=experimentOf(pending.objectId);page.assignments.has(key)||experiment?.status==="preview"||(page.assignments.add(key),send(endpoint("assignment"),JSON.stringify({sessionId:sessionId(),visitorId,source:"sdk",assignmentVersion:pending.assignmentVersion,...snapshotOf(pending),timezone:timezone()})))}function announce(){for(let current2 of assignments()){if(!counts(current2))continue;let key=`${state.visitorId}:${current2.experimentId}:${current2.testGroup.index}`;if(page.readyEvents.has(key)||(page.readyEvents.add(key),dispatch(ASSIGNMENT_READY_EVENT,current2)),onAssignment&&!page.callbacks.has(key)){page.callbacks.add(key);try{onAssignment({...current2,testGroup:{...current2.testGroup}})}catch(error){console.error("[ABConvert] onAssignment failed:",error)}}}}function claimExposure(objectId,session,groupIndex){let memoryKey=`${objectId}:${session}`;if(page.exposures.has(memoryKey))return!1;page.exposures.add(memoryKey);let key=EXPOSURE_FLAG_PREFIX+objectId;try{let store=storage("sessionStorage");if(JSON.parse(store?.getItem(key)??"null")?.sessionId===session)return!1;store?.setItem(key,JSON.stringify({sessionId:session,groupIndex}))}catch{}return!0}function sendExposure(experimentId,{path,at}){let experiment=experimentOf(experimentId),current2=experiment&&assignment(experiment.id);if(!experiment||!current2||!counts(current2))return;let session=sessionId();if(!claimExposure(experiment.objectId,session,current2.testGroup.index))return;let pending=pendingByObjectId.get(experiment.objectId);send(endpoint("event"),JSON.stringify({sessionId:session,visitorId:state.visitorId,eventType:"abconvert:exposure",source:"sdk",experimentIds:[experiment.objectId],assignmentSnapshots:pending?[snapshotOf(pending)]:[],pagePath:path,createdAt:at,payload:{groupIndex:current2.testGroup.index}}))}function flush(){flushed=!0,writeCookies();for(let{visitorId,pending}of page.pending.assignments.values())visitorId===state.visitorId&&postAssignment(visitorId,pending);let at=here();for(let id of page.pending.exposures)sendExposure(id,at);let held=page.heldExposures;page.heldExposures=new Map;for(let[id,at2]of held)sendExposure(id,at2);announce();let bodies=page.queue;page.queue=[];for(let body of bodies)body.visitorId===state.visitorId&&sendEvent(body);let engagement=page.heldEngagement;page.heldEngagement=new Map;for(let held2 of engagement.values())held2.visitorId===state.visitorId&&sendEngagement(held2.engagement,held2.at);emptyMailbox()}function sendEvent(body){send(endpoint("event"),JSON.stringify(body.sessionId?body:{sessionId:sessionId(),...body}))}function ready(){return!started||consent()!=="allow"?!1:(flushed||flush(),!0)}function apply(){if(!started)return;let verdict=consent();verdict==="allow"?flushed||flush():verdict==="deny"&&(flushed=!1,page.queue=[],page.heldExposures=new Map,page.heldEngagement=new Map)}function eventBody({eventType,payload,custom,path,at,sessionId:session}){let experimentIds=assignments().filter(counts).flatMap(current2=>experimentOf(current2.experimentId)?.objectId??[]);if(!experimentIds.length&&!custom)return null;let snapshots=experimentIds.map(id=>pendingByObjectId.get(id));return{...session?{sessionId:session}:{},visitorId:state.visitorId,eventType,source:"sdk",experimentIds,...snapshots.length&&snapshots.every(Boolean)?{assignmentSnapshots:snapshots.map(snapshotOf)}:{},pagePath:path??pageUrl()?.pathname??"",createdAt:at??new Date(now()).toISOString(),...payload?{payload}:{}}}function sendEngagement({sessionId:session,eventType,payload,path,sent},at){let body=eventBody({eventType,payload,custom:!1,path,at,sessionId:session});body&&(sendEvent(body),sent?.())}function holdEngagement(engagement,visitorId){let held=page.heldEngagement;(held.has(engagement.key)||held.size<MAX_QUEUE)&&held.set(engagement.key,{engagement,at:new Date(now()).toISOString(),visitorId})}function productIds(payload){let variantId=shopifyId(payload?.variantId),productId=shopifyId(payload?.productId)??(variantId&&productOf?.(variantId));return{productIds:productId?[productId]:[],variantIds:variantId?[variantId]:[]}}function toEvent(name,payload){if(name==="page_viewed")return{eventType:name,custom:!1,path:pathOf(payload?.url)};if(name==="product_viewed")return{eventType:name,payload:productIds(payload),custom:!1,path:pathOf(payload?.url)};if(name==="product_added_to_cart")return{eventType:name,payload:{...productIds(payload),quantity:payload?.quantity},custom:!1};let rule=doc.customEvents.find(candidate=>candidate.name===name);return rule?{eventType:CUSTOM_EVENT_PREFIX+rule.ruleId,payload,custom:!0}:(page.warned.has(name)||(page.warned.add(name),console.warn(`[ABConvert] track("${name}") was dropped: no custom event has that name in ABConvert.`)),null)}let replacement=()=>started?void 0:page.live[page.live.length-1];function routed(call,park=()=>{page.handoff.length<MAX_QUEUE&&page.handoff.push(call)}){let live=replacement();if(live)call(live);else if(!started&&everStarted)park();else return!1;return!0}function setConsent(answer){let live=replacement();if(live)return live.setConsent(answer);page.consent.setConsent=answer?.analytics===!0,(expectCustomerPrivacy||customerPrivacyAllows()!==null)&&!page.warned.has("setConsent")&&(page.warned.add("setConsent"),console.warn("[ABConvert] setConsent is ignored: on this page, the visitor's answer in Shopify's Customer Privacy API decides.")),apply()}let tracker={start(){if(started)return;started=!0,everStarted=!0,page.live.push(tracker);for(let pending of state.pending.assignments)page.pending.assignments.set(`${state.visitorId}:${pending.objectId}`,{visitorId:state.visitorId,pending});for(let id of state.pending.exposures)page.pending.exposures.add(String(id));let undo=[listen(CONSENT_EVENT,event=>{let analytics=event.detail?.analyticsAllowed;typeof analytics=="boolean"&&(page.consent.banner=analytics,apply())}),...expectCustomerPrivacy?PRIVACY_API_LOADED_EVENTS.map(name=>listen(name,apply)):[]];detach=()=>undo.forEach(off=>off());for(let held of page.heldEngagement.values())held.visitorId??(held.visitorId=state.visitorId);writeCookies(),apply();for(let call of page.handoff.splice(0))call(tracker)},stop(){started=!1,page.live=page.live.filter(candidate=>candidate!==tracker),detach(),detach=()=>{}},track(name,payload){if(routed(tracker2=>tracker2.track(name,payload)))return;name==="page_viewed"&&rollSession();let event=toEvent(String(name),payload&&typeof payload=="object"?payload:void 0),body=event&&eventBody(event);body&&(ready()?sendEvent(body):page.queue.length<MAX_QUEUE&&page.queue.push(body),name==="page_viewed"&&hasDocument()&&pageViewed({path:String(body.pagePath),currentPath:pageUrl()?.pathname??"",sessionId:sessionId(),emit:tracker.engage,now}))},engage(engagement){routed(tracker2=>tracker2.engage(engagement),()=>holdEngagement(engagement))||(ready()?sendEngagement(engagement):holdEngagement(engagement,state.visitorId))},trackExposure(experimentId){if(routed(tracker2=>tracker2.trackExposure(experimentId)))return;let experiment=experimentOf(String(experimentId??"").trim());experiment&&(ready()?sendExposure(experiment.id,here()):page.heldExposures.has(experiment.id)||page.heldExposures.set(experiment.id,here()))},setConsent,stateCookieChanged(){cookiesWritten&&writeCookie(stateCookieHeader(stateCookie()))},report({assignments:found,exposures}){for(let pending of found)keepSnapshot(pending),page.pending.assignments.set(`${state.visitorId}:${pending.objectId}`,{visitorId:state.visitorId,pending});for(let id of exposures)page.pending.exposures.add(String(id));if(!started||!flushed||consent()!=="allow")return;for(let pending of found)postAssignment(state.visitorId,pending);let at=here();for(let id of exposures)sendExposure(String(id),at);announce(),emptyMailbox()}};return tracker}function readPageState(input){let candidate=input,doc=candidate?.doc;return candidate?.version===2&&typeof candidate.pageKey=="string"&&Array.isArray(candidate.assignments)&&Array.isArray(doc?.experiments)?candidate:{version:2,shop:"",pageKey:"",country:"",assignments:[],doc:emptyDocument(""),locale:""}}var currentCopies=new WeakMap,builtAt=doc=>Date.parse(doc.generatedAt)||0;function testsBehind(doc,indexes,state){return(state.g??0)<=builtAt(doc)?[]:Object.entries(state.d).flatMap(([id,[index]])=>index>=0&&!indexes.byNumericId.has(id)?[id]:[])}function placedElsewhere(experiment,state){let group=experiment.traffic.namespaceGroupId;return!!(group&&state.n[group]&&state.n[group]!==experiment.objectId)}function storedDecision(experiment,state){let forced=forcedIndex(state,experiment);if(forced!==null)return forcedDecision(experiment,forced);if(experiment.status==="preview"&&!state.preview||state.t?.includes(experiment.id)||state.s?.includes(experiment.id)||placedElsewhere(experiment,state))return null;let stored=state.d[experiment.id];return!stored||stored[0]<0||stored[0]>=experiment.testGroups.length?null:{experiment,inTest:!0,groupIndex:stored[0],reason:"random_split",isNew:!1}}function browserRequest(url,cookieHeader){let{navigator:navigator2,document:document2}=globalThis,values={"user-agent":navigator2?.userAgent??"",referer:document2?.referrer??"","accept-language":navigator2?.language??"",cookie:cookieHeader};return{url,headers:{get:name=>values[name.toLowerCase()]||null}}}function visitorDecisions(doc,keyed,state){let decisions=new Map;for(let experiment of doc.experiments){let stored=storedDecision(experiment,state);stored&&decisions.set(experiment.id,stored)}let recorded=[];for(let[id,decision]of keyed){if(forcedIndex(state,decision.experiment)!==null){decisions.set(id,forcedDecision(decision.experiment,decision.groupIndex));continue}if(placedElsewhere(decision.experiment,state)){decisions.delete(id);continue}if(decisions.set(id,decision),!(id in state.d)&&decision.experiment.status==="active"){state.d[id]=stamp(decision.experiment,decision.groupIndex);let group=decision.experiment.traffic.namespaceGroupId;group&&(state.n[group]=decision.experiment.objectId),recorded.push(decision)}}return{decisions,recorded}}function pendingWork(mailbox,decisions,recorded,indexes,state,request,at){let located={...request,context:{...request.context,country:mailbox.country??""}},pending=new Map,unknown=[],waiting=[],kept=[];for(let entry of mailbox.assignments){let[id,reason]=entry,decision=mailboxDecision(entry,decisions,test=>indexes.byNumericId.get(test),state);if(decision){pending.set(id,pendingEntry(decision,reason,located,at));continue}if(!indexes.byNumericId.has(id))unknown.push(entry);else if(waitsInMailbox(entry,state))waiting.push(entry);else continue;kept.push(entry)}for(let decision of recorded)pending.has(decision.experiment.id)||pending.set(decision.experiment.id,pendingEntry(decision,"random_split",request,at));return{pending:[...pending.values()],unknown,waiting,left:leftIn(mailbox,kept)}}function leftIn(mailbox,entries){return entries.length?encodeMailbox({country:mailbox.country,assignments:entries}):""}function hydratePage(input,options={}){let page2=readPageState(input),doc=page2.doc,indexes=indexesOf(doc),now=options.now??Date.now,randomUUID=options.randomUUID??(()=>globalThis.crypto.randomUUID()),loadTests=options.loadTests??fetchCurrentCopy,keyed=new Map;for(let assignment of page2.assignments){let experiment=indexes.byNumericId.get(String(assignment.experimentId));experiment&&keyed.set(experiment.id,{experiment,inTest:!0,groupIndex:assignment.testGroup.index,reason:assignment.reason,isNew:!1})}let rendered=keyed,listeners=new Set,reads=createReads({doc,indexes,decisions:()=>rendered,visitorId:"",country:page2.country,locale:page2.locale||void 0}),tracking=indexes,experimentOf=id=>tracking.byNumericId.get(id)??tracking.byObjectId.get(id),productOf=variantId=>(indexes.variantToExperiments.get(variantId)??[]).map(experiment=>productOfVariant(indexes,experiment,variantId)).find(Boolean),assignmentsOf=decisions=>[...decisions.values()].flatMap(decision=>toAssignment(decision)??[]),visitor=null,tracked=new Map;function resolve(){if(visitor)return visitor;let at=now(),cookies=readCookies(),jar=createCookieJar({cookies,now:at,randomUUID,write:"none"}),{state}=jar,url=pageUrl()??new URL("https://shop.invalid/");applyUrlForces(state,url,indexes);let request=buildContext({...browserRequest(url,globalThis.document?.cookie??""),state,sessionId:jar.sessionId,options:{}}),{decisions,recorded}=visitorDecisions(doc,keyed,state),mailbox=decodeMailbox(cookies[MAILBOX_COOKIE]),{pending,unknown,waiting,left}=pendingWork(mailbox,decisions,recorded,indexes,state,request,at),mailboxLeft=left,missing=new Set([...unknown.map(([id])=>id),...testsBehind(doc,indexes,state)]);noteCopy(state,doc),tracked=decisions;let identity={version:1,shop:page2.shop,visitorId:jar.visitorId,sessionId:jar.sessionId,country:page2.country,consent:{analytics:consentFromShopifyCookie(cookies._tracking_consent,!options.expectCustomerPrivacy)},preview:!!state.preview,assignments:assignmentsOf(decisions),doc,pending:{assignments:pending,exposures:findRedirect({indexes,decisions,url,triggered:state.triggered,preview:!!state.preview}).exposed},locale:page2.locale,stateCookie:encodeState(state),...page2.baseUrl?{baseUrl:page2.baseUrl}:{}},tracker=createTracker({state:identity,doc,experimentOf,productOf,assignments:()=>assignmentsOf(tracked),assignment:id=>{let decision=tracked.get(id);return decision?toAssignment(decision):null},stateCookie:()=>identity.stateCookie,mailboxLeft:()=>mailboxLeft,...options});visitor={identity,tracker};async function repair(){if(!page2.storefrontKey)return;let source={baseUrl:page2.baseUrl||DEFAULT_BASE_URL,storefrontKey:page2.storefrontKey},copies=currentCopies.get(loadTests)??new Map;currentCopies.set(loadTests,copies);let held=copies.get(`${source.baseUrl}
6
+ ${source.storefrontKey}`),current2=held&&builtAt(held)>=(state.g??0)?held:await loadTests(source);if(!current2)return;copies.set(`${source.baseUrl}
7
+ ${source.storefrontKey}`,current2);let currentIndexes=indexesOf(current2),added=[...missing].flatMap(id=>{let experiment=currentIndexes.byNumericId.get(id);return experiment&&onPage(current2,experiment)?[lightEntry(current2,experiment)]:[]});tracking=indexesOf({...doc,experiments:[...doc.experiments,...added]});let next=new Map(tracked);for(let experiment of added){let stored=storedDecision(experiment,state);stored&&next.set(experiment.id,stored)}tracked=next;let ids=new Set(added.map(experiment=>experiment.id)),work=pendingWork({...mailbox,assignments:unknown.filter(([id])=>ids.has(id))},tracked,[],tracking,state,request,at),{exposed}=findRedirect({indexes:tracking,decisions:tracked,url,triggered:state.triggered,preview:!!state.preview}),still=new Set([...waiting,...work.waiting]);mailboxLeft=leftIn(mailbox,mailbox.assignments.filter(entry=>still.has(entry))),tracker.report({assignments:work.pending,exposures:exposed.filter(id=>ids.has(id))})}return missing.size&&repair(),visitor}let trackedNow=()=>(resolve(),tracked),who=()=>{let{identity}=resolve();return{visitorId:identity.visitorId,sessionId:readCookies()[COOKIE_NAMES.session]||identity.sessionId,country:page2.country}};function persistForce(id,index){let{identity,tracker}=resolve(),state=decodeState(identity.stateCookie);setForce(state,id,index),identity.stateCookie=encodeState(state),tracker.stateCookieChanged()}function changed(next){rendered=next;let merged=new Map(tracked);for(let[id,decision]of next)merged.set(id,decision);tracked=merged;for(let listener of listeners)listener()}return{...reads,getVisitorId:()=>visitor?.identity.visitorId??"",getCartAttributes:()=>cartAttributes(trackedNow(),tracking,who()),getCartLineAttributes:()=>cartLineAttributes(trackedNow(),tracking,who()),forceTestGroup(experimentId,index){let id=numericIdOf(indexes,requireExperimentId(experimentId));requireIndex(index),persistForce(id,index);let experiment=indexes.byNumericId.get(id);experiment&&changed(new Map(rendered).set(id,forcedDecision(experiment,index)))},clearForcedTestGroup(experimentId){let id=numericIdOf(indexes,requireExperimentId(experimentId));rendered.get(id)?.reason==="url_force_assign"&&(persistForce(id,null),changed(withoutForce(rendered,keyed,id)))},getCheckoutUrl:checkout=>{let visitorNow=who();return checkoutUrl({...checkout,shop:page2.shop,attributes:cartAttributes(trackedNow(),tracking,visitorNow),lineAttributes:cartLineAttributes(trackedNow(),tracking,visitorNow)})},getRedirectUrl:()=>null,getClientState:()=>({...resolve().identity}),getPageKey:()=>page2.pageKey,getSetCookieHeaders:()=>[],refreshTests:async()=>{},track:(name,payload)=>resolve().tracker.track(name,payload),trackExposure:experimentId=>resolve().tracker.trackExposure(experimentId),setConsent:consent=>resolve().tracker.setConsent(consent),start:()=>resolve().tracker.start(),stop:()=>visitor?.tracker.stop(),subscribe(listener){return listeners.add(listener),()=>listeners.delete(listener)}}}function emptyState2(){return{version:1,shop:"",visitorId:"",sessionId:"",country:"",consent:{analytics:!1},preview:!1,assignments:[],doc:emptyDocument(""),pending:{assignments:[],exposures:[]},locale:"",stateCookie:""}}function readState(state){let candidate=state,doc=candidate?.doc;return candidate?.version===1&&typeof candidate.visitorId=="string"&&Array.isArray(candidate.assignments)&&Array.isArray(doc?.experiments)&&Array.isArray(candidate.pending?.assignments)?{...candidate,pending:{assignments:candidate.pending.assignments,exposures:candidate.pending.exposures??[]}}:emptyState2()}function ownVisitor(state,{randomUUID=()=>globalThis.crypto.randomUUID()}){if(!hasDocument()||!state.visitorId)return state;let cookies=readCookies(),held=cookies[COOKIE_NAMES.visitor];return held===state.visitorId?state:{...state,visitorId:held||`abc_${randomUUID()}`,sessionId:cookies[COOKIE_NAMES.session]||randomUUID(),stateCookie:cookies[COOKIE_NAMES.state]||encodeState({v:1,d:{},n:{}}),pending:{assignments:[],exposures:[]}}}function withCopyNoted(stateCookie,doc){let visitorState=decodeState(stateCookie),before=visitorState.g;return noteCopy(visitorState,doc),visitorState.g===before?stateCookie:encodeState(visitorState)}function hydrateABConvert(input,options={}){if(input?.version===2)return hydratePage(input,options);let state=ownVisitor(readState(input),options),doc=state.doc,indexes=buildIndexes(doc),fromServer=new Map;for(let assignment of state.assignments){let experiment=indexes.byNumericId.get(String(assignment.experimentId));experiment&&fromServer.set(experiment.id,{experiment,inTest:!0,groupIndex:assignment.testGroup.index,reason:assignment.reason,isNew:!1})}let decisions=fromServer,listeners=new Set,visitor=()=>({visitorId:state.visitorId,sessionId:readCookies()[COOKIE_NAMES.session]||state.sessionId,country:state.country}),reads=createReads({doc,indexes,decisions:()=>decisions,visitorId:state.visitorId,country:state.country,locale:state.locale||void 0}),experimentOf=id=>indexes.byNumericId.get(id)??indexes.byObjectId.get(id),productOf=variantId=>(indexes.variantToExperiments.get(variantId)??[]).map(experiment=>productOfVariant(indexes,experiment,variantId)).find(Boolean),stateCookie=withCopyNoted(state.stateCookie,doc),tracker=createTracker({state,doc,experimentOf,productOf,assignments:reads.getAssignments,assignment:reads.getAssignment,stateCookie:()=>stateCookie,mailboxLeft:()=>{let mailbox=decodeMailbox(readCookies()[MAILBOX_COOKIE]),waiting=waitingEntries(mailbox.assignments,decodeState(stateCookie));return waiting.length?encodeMailbox({country:mailbox.country,assignments:waiting}):""},...options});function persistForce(id,index){if(!stateCookie)return;let visitorState=decodeState(stateCookie);setForce(visitorState,id,index),stateCookie=encodeState(visitorState),tracker.stateCookieChanged()}function changed(next){decisions=next;for(let listener of listeners)listener()}return{...reads,getCartAttributes:()=>cartAttributes(decisions,indexes,visitor()),getCartLineAttributes:()=>cartLineAttributes(decisions,indexes,visitor()),forceTestGroup(experimentId,index){let id=numericIdOf(indexes,requireExperimentId(experimentId));requireIndex(index),persistForce(id,index);let experiment=indexes.byNumericId.get(id);experiment&&changed(new Map(decisions).set(id,forcedDecision(experiment,index)))},clearForcedTestGroup(experimentId){let id=numericIdOf(indexes,requireExperimentId(experimentId));if(decisions.get(id)?.reason!=="url_force_assign")return;persistForce(id,null);let next=new Map(decisions),original=fromServer.get(id);original&&original.reason!=="url_force_assign"?next.set(id,original):next.delete(id),changed(next)},getCheckoutUrl:checkout=>checkoutUrl({...checkout,shop:state.shop,attributes:cartAttributes(decisions,indexes,visitor()),lineAttributes:cartLineAttributes(decisions,indexes,visitor())}),getRedirectUrl:()=>null,getClientState:()=>({...state,assignments:reads.getAssignments(),stateCookie}),getPageKey:()=>"",getSetCookieHeaders:()=>[],refreshTests:async()=>{},track:tracker.track,trackExposure:tracker.trackExposure,setConsent:tracker.setConsent,start:tracker.start,stop:tracker.stop,subscribe(listener){return listeners.add(listener),()=>listeners.delete(listener)}}}var savedCopyKey=storefrontKey=>`abconvert:headless:${storefrontKey}`,persistedCopy={read(storefrontKey){try{let saved=JSON.parse(storage("localStorage")?.getItem(savedCopyKey(storefrontKey))??"null");return typeof saved?.fetchedAt!="number"?null:{...saved,doc:readDocument(saved.doc)}}catch{return null}},write(storefrontKey,copy2){try{storage("localStorage")?.setItem(savedCopyKey(storefrontKey),JSON.stringify(copy2))}catch{}}};function pageRequest(){if(!hasDocument())return null;try{let{document:document2,navigator:navigator2,location}=globalThis;return{url:location.href,headers:{cookie:document2.cookie,"user-agent":navigator2.userAgent,referer:document2.referrer}}}catch{return null}}function oncePerPage(load){let loads=new Map;return options=>{let key=[options?.shop,options?.storefrontKey,pageUrl()?.href].join(`
8
+ `),loaded=loads.get(key);return loaded||(loaded=load(options),loads.set(key,loaded)),loaded}}var browserLoader=createLoader({fetch:(...args)=>globalThis.fetch(...args),now:()=>Date.now(),persist:persistedCopy});var SERVER_TRACKING={track(){},trackExposure(){},setConsent(){}},DEFAULT_KEYED_TIMEOUT_MS=1e4,pricingCountry=(options,doc)=>String(options?.country||doc.shop.defaultCountry||"").toUpperCase();function loadRequestOf(options,onError){let shop=typeof options?.shop=="string"?options.shop.trim():"";if(!shop)throw new TypeError("[ABConvert] shop is required");if(typeof options.storefrontKey!="string"||!options.storefrontKey.trim())throw new TypeError("[ABConvert] storefrontKey is required");return{shop,request:{baseUrl:options.baseUrl||DEFAULT_BASE_URL,storefrontKey:options.storefrontKey.trim(),cacheTtl:options.cacheTtl??DEFAULT_CACHE_TTL_SECONDS,timeout:options.timeout??DEFAULT_TIMEOUT_MS,onError}}}var DEFAULT_TIMEOUT_MS=1e3,DEFAULT_CACHE_TTL_SECONDS=60;function headersOf(request){let{headers:headers2}=request;if(typeof headers2.get=="function")return headers2;let lower=new Map;for(let[name,value]of Object.entries(headers2)){if(value===void 0)continue;let key=name.toLowerCase();lower.set(key,Array.isArray(value)?value.join(key==="cookie"?"; ":", "):String(value))}return{get:name=>lower.get(name.toLowerCase())??null}}function buildClient({runtime:runtime2,options,shop,doc,url,headers:headers2,cookies,consent,loaded,record,write:write2,previewKey,refresh}){let now=runtime2.now(),mode=!loaded||!record?"none":write2??"all",jar=createCookieJar({cookies,now,randomUUID:runtime2.randomUUID,write:mode}),{state}=jar,previewMode=doc.preview===!0;previewMode&&previewKey&&(state.preview=previewKey);let indexes=indexesOf(doc);applyUrlForces(state,url,indexes);let storedDecisions={...state.d},request=buildContext({url,headers:headers2,state,sessionId:jar.sessionId,options:{customer:options.customer}}),country=pricingCountry(options,doc),visitor={visitorId:jar.visitorId,sessionId:jar.sessionId,country},pending=new Map,redecide=()=>{let made=decide({doc,indexes,request,state,visitorId:jar.visitorId,url,previewMode,now});for(let entry of pendingAssignments(made,request,now))pending.set(entry.experimentId,entry);return made},decisions=redecide(),redirecting=!1,deferred=()=>({...state,d:storedDecisions}),locale=defaultLocale(),reads=createReads({doc,indexes,decisions:()=>decisions,visitorId:jar.visitorId,country,locale}),touched=new Set,readVisitor=!1,touch=experiments=>{for(let experiment of experiments??[])touched.add(experiment.id)},ofType=change=>[...indexes.byNumericId.values()].filter(experiment=>indexes.changeTypes.get(experiment.id)?.has(change)),client={...reads,getAssignments:()=>(touch(indexes.byNumericId.values()),reads.getAssignments()),getAssignment:experimentId=>{let experiment=indexes.byNumericId.get(String(experimentId??"").trim())??indexes.byObjectId.get(String(experimentId??"").trim());return experiment&&touch([experiment]),reads.getAssignment(experimentId)},getFeature:key=>{let experiment=indexes.featureKeyToExperiment.get(String(key));return experiment&&touch([experiment]),reads.getFeature(key)},getPriceByVariantId:(variantId,priceOptions)=>(touch(indexes.variantToExperiments.get(String(variantId))),reads.getPriceByVariantId(variantId,priceOptions)),getPriceByProductId:(productId,priceOptions)=>(touch(indexes.productToExperiments.get(String(productId))),reads.getPriceByProductId(productId,priceOptions)),getShippingRates:shippingOptions=>(touch(ofType("shipping")),reads.getShippingRates(shippingOptions)),getFreeShippingThreshold:shippingOptions=>(touch(ofType("shipping")),reads.getFreeShippingThreshold(shippingOptions)),getOffers:()=>(touch(ofType("offer")),reads.getOffers()),getCartAttributes:()=>cartAttributes(decisions,indexes,visitor),getCartLineAttributes:()=>cartLineAttributes(decisions,indexes,visitor),forceTestGroup(experimentId,index){setForce(state,numericIdOf(indexes,requireExperimentId(experimentId)),requireIndex(index)),decisions=redecide()},clearForcedTestGroup(experimentId){let id=numericIdOf(indexes,requireExperimentId(experimentId)),keys=[id,indexes.byNumericId.get(id)?.objectId].filter(key=>!!key&&!!state.f&&key in state.f);if(keys.length){for(let key of keys)setForce(state,key,null);decisions=redecide()}},getCheckoutUrl:checkout=>checkoutUrl({...checkout,shop,attributes:cartAttributes(decisions,indexes,visitor),lineAttributes:cartLineAttributes(decisions,indexes,visitor)}),getRedirectUrl(target){let page2;try{page2=new URL(String(target),url)}catch{return null}let{destination,fired}=findRedirect({indexes,decisions,url:page2,triggered:state.triggered,preview:previewMode});return fired.length&&(state.triggered=[...new Set([...state.triggered??[],...fired])]),destination&&(redirecting=!0),destination},getClientState(){readVisitor=!0;let{exposed}=findRedirect({indexes,decisions,url,triggered:state.triggered,preview:previewMode});return{version:1,shop,...visitor,consent:{analytics:consent},preview:previewMode,assignments:reads.getAssignments(),doc:assignedSlice(doc,decisions),pending:record?{assignments:JSON.parse(JSON.stringify([...pending.values(),...mailboxPending()])),exposures:exposed}:{assignments:[],exposures:[]},locale,stateCookie:encodeState(state),...options.baseUrl?{baseUrl:options.baseUrl}:{}}},getPageKey:()=>pageKeyOf({doc,indexes,decisions,url,country,previewKey,onlyOwnProductPrices:options.onlyOwnProductPrices}),getPageState:()=>{let keyed=new Map([...decisions].filter(([id,decision])=>touched.has(id)&&decision.inTest)),ids=[...keyed.keys()],pageKey=ids.length?encodePageKey({version:keyVersion(doc,ids),...doc.preview&&previewKey?{previewKey}:{},...country?{country}:{},decisions:[...keyed.values()].map(decision=>[decision.experiment.id,decision.groupIndex])}):"";return{version:2,shop,pageKey,country,assignments:[...keyed.values()].flatMap(decision=>toAssignment(decision)??[]),doc:pageDocOf(doc,keyed,pageKey),locale,...options.baseUrl?{baseUrl:options.baseUrl}:{},...options.storefrontKey?{storefrontKey:options.storefrontKey}:{}}},isPageTested:()=>pageTested({doc,indexes,url,onlyOwnProductPrices:options.onlyOwnProductPrices})||redirectPage()||touched.size>0||readVisitor||previewMode,isTesting:testing,getSetCookieHeaders:()=>{if(!testing())return[];let headers3=jar.setCookieHeaders(redirecting?deferred():state),mailbox=mode!=="none"&&!redirecting?mailboxHeader():null;return mailbox?[...headers3,mailbox]:headers3},refreshTests:refresh,...SERVER_TRACKING};function testing(){return doc.experiments.some(experiment=>experiment.status==="active"||previewMode)}function redirectPage(){return[...indexes.byNumericId.values()].some(experiment=>indexes.changeTypes.get(experiment.id)?.has("urlRedirect")&&coversPage(experiment,url))}function mailboxPending(){let mailbox=decodeMailbox(cookies[MAILBOX_COOKIE]),located={...request,context:{...request.context,country:mailbox.country??""}};return mailbox.assignments.flatMap(entry=>{let decision=pending.has(entry[0])?null:mailboxDecision(entry,decisions,id=>indexes.byNumericId.get(id),state);return decision?[pendingEntry(decision,entry[1],located,now)]:[]})}function mailboxHeader(){if(!pending.size)return null;let sent=cookies[MAILBOX_COOKIE],{assignments}=decodeMailbox(sent),fresh=new Set(pending.keys()),merged=[...assignments.filter(([id])=>!fresh.has(id)),...[...pending.values()].map(entry=>[entry.experimentId,entry.reason])],value=encodeMailbox({country:request.location??void 0,assignments:merged});return value===sent?null:mailboxCookieHeader(value)}return{client,version:doc.preview?"":doc.generatedAt,handOff:()=>({visitor:jar.visitorId,session:jar.sessionId,state:encodeState(deferred())}),cacheFacts:()=>({get keyCookie(){return keyCookieOf({doc,indexes,decisions,country})},preview:doc.preview===!0,writes:mode!=="none"&&testing(),get redirectPage(){return redirectPage()}})}}var refreshers=new WeakMap;function refreshOnceWith(loader,now){let refreshOnce=refreshers.get(loader);if(!refreshOnce){let refreshes=new Map;refreshOnce=request=>{let last=refreshes.get(request.storefrontKey);if(last&&now()-last.at<Math.max(request.cacheTtl,1)*1e3)return last.run;let run=loader.refresh(request).catch(()=>{});return refreshes.set(request.storefrontKey,{at:now(),run}),run},refreshers.set(loader,refreshOnce)}return refreshOnce}function openClientWith(runtime2){return async function(options,{record=!0,cookies:overrides={},write:write2,decidedOn}={}){let onError=error=>safeReport(options?.onError,error),shop=typeof options?.shop=="string"?options.shop.trim():"",base={runtime:runtime2,options:options??{},shop,record,write:write2};try{let{request:loadRequest}=loadRequestOf(options,onError),page2=options.request?null:pageRequest(),request=options.request??page2;if(!request)throw new TypeError("[ABConvert] request is required on the server");let loader=page2&&runtime2.browserLoader||runtime2.loader,url=new URL(request.url),headers2=headersOf(request),cookies=parseCookieHeader(headers2.get("cookie"));for(let[key,value]of Object.entries(overrides))value&&(cookies[COOKIE_NAMES[key]]=value);let refresh=()=>loader.refresh(loadRequest),previewKey=url.searchParams.get("abconvert_preview_key")||sessionPreviewKey(cookies),load={...loadRequest,previewKey,previewTestId:previewKey&&linkedTestId(url)},doc=await loader.load(load),floor=Math.max(decodeState(cookies[COOKIE_NAMES.state]).g??0,Date.parse(decidedOn??"")||0);doc&&!doc.preview&&Date.parse(doc.generatedAt)<floor&&(await timeoutAfter(refreshOnceWith(loader,runtime2.now)(loadRequest),loadRequest.timeout,()=>onError(new Error(`[ABConvert] a newer copy of the tests did not load within ${loadRequest.timeout} ms: deciding on the older copy this server holds.`))),doc=await loader.load(load)??doc);let consent=doc!==null&&(options.consent?.analytics??consentFromShopifyCookie(cookies._tracking_consent,!options.expectCustomerPrivacy));return buildClient({...base,doc:doc??emptyDocument(shop),url,headers:headers2,cookies,consent,loaded:doc!==null,previewKey,refresh})}catch(error){return onError(error),buildClient({...base,doc:emptyDocument(shop),url:new URL("https://shop.invalid/"),headers:{get:()=>null},cookies:{},consent:!1,loaded:!1,refresh:async()=>{}})}}}function openPageWith(runtime2){let refreshOnce=refreshOnceWith(runtime2.loader,runtime2.now);return async function(options){let onError=error=>safeReport(options?.onError,error),decoded=decodePageKey(options.pageKey),parts=decoded??NO_KEY,shop=typeof options?.shop=="string"?options.shop.trim():"",loading=null,doc=null,stale=!1;try{if(loading=loadRequestOf(options,onError).request,!decoded)throw new TypeError(`[ABConvert] ${JSON.stringify(options.pageKey)} is not a page key: rendering the original store.`);let keyed=parts.decisions.length>0,request={...loading,previewKey:parts.previewKey,...keyed?{timeout:Math.max(loading.timeout,DEFAULT_KEYED_TIMEOUT_MS)}:{}};doc=await runtime2.loader.load(request);let ids=parts.decisions.map(([id])=>id);if(doc&&keyed&&!parts.previewKey&&keyVersion(doc,ids)!==parts.version){let refreshed=await timeoutAfter(refreshOnce(loading).then(()=>!0),loading.timeout);doc=await runtime2.loader.load(request)??doc,stale=refreshed===null&&keyVersion(doc,ids)!==parts.version}}catch(error){onError(error)}let loaded=doc??emptyDocument(shop),{page:page2,complete}=buildPageClient({doc:loaded,indexes:indexesOf(loaded),shop,parts,country:pricingCountry(options,loaded),baseUrl:options?.baseUrl,...loading?{storefrontKey:loading.storefrontKey}:{},refresh:async()=>{loading&&await runtime2.loader.refresh(loading)},tracking:SERVER_TRACKING});if(!complete||stale)throw new Error(`[ABConvert] could not load the tests page key ${options.pageKey} names: refusing to render the original store under it, where a cache would keep it.`);return page2}}function entryPoints(runtime2){let open=openClientWith(runtime2),openPage=openPageWith(runtime2),build=async options=>(await open(options)).client,browserOnly=options=>!options?.request&&hasDocument(),loadPage=oncePerPage(options=>build(options).then(client=>{for(let cookie of client.getSetCookieHeaders())writeCookie(cookie);return client.getClientState()}));return{createABConvert:(async options=>{if(typeof options?.pageKey=="string")return openPage(options);if(!browserOnly(options))return build(options);let browser=hydrateABConvert(await loadPage(options));return browser.start(),browser}),async loadClientState(options){return browserOnly(options)?loadPage(options):(await build(options)).getClientState()}}}var runtime={loader:defaultLoader,browserLoader,now:()=>Date.now(),randomUUID:()=>globalThis.crypto.randomUUID()},{createABConvert,loadClientState}=entryPoints(runtime),openClient=openClientWith(runtime);var FORWARDED={visitor:"x-abconvert-visitor",session:"x-abconvert-session",state:"x-abconvert-state",url:"x-abconvert-url",version:"x-abconvert-version"},CARRIED=["visitor","session","state"],DEFAULT_REWRITE_PREFIX="abconvert",COOKIE_SAFE=/^[A-Za-z0-9_-]+$/,VERSION_SAFE=/^[0-9TZ:.+-]{1,40}$/,NOT_A_PAGE=/^\/(_next|api)\/|\.(ico|png|jpe?g|gif|svg|webp|avif|css|m?js|map|json|xml|txt|webmanifest|woff2?|ttf|otf|eot|mp4|webm|pdf)$/i;function requestKind(request){if(request.method!=="GET"&&request.method!=="HEAD"||NOT_A_PAGE.test(new URL(request.url).pathname))return null;let{headers:headers2}=request;if(headers2.has("next-url"))return"router";let dest=headers2.get("sec-fetch-dest");return dest===null||dest==="document"?"document":dest==="empty"&&request.method==="GET"?"router":null}function withoutForwarded(request){let headers2=new Headers(request.headers);for(let header of Object.values(FORWARDED))headers2.delete(header);return headers2}var cacheControl=policy=>policy==="shared"?{}:{"cache-control":policy==="private"?"private":"private, no-store"};function withoutPrefix(url,prefix){let head=`/${prefix}`;if(url.pathname!==head&&!url.pathname.startsWith(`${head}/`))return null;let afterPrefix=url.pathname.slice(head.length),segment=/^\/([^/]*)/.exec(afterPrefix)?.[1]??"",rest=(decodePageKey(segment)?afterPrefix.slice(segment.length+1):afterPrefix).replace(/^\/+/,"/");return`${url.origin}${rest||"/"}${url.search}`}async function planMiddleware(request,options,open){let kind=requestKind(request),{rewritePrefix,cdnKeyedByCookie,...clientOptions}=options;if(!kind){let mutation=request.method!=="GET"&&request.method!=="HEAD",{version:version2}=mutation?await open({...clientOptions,request},{record:!1}):{version:""},sent2=Object.values(FORWARDED).some(header=>request.headers.has(header));if(!version2&&!sent2)return null;let requestHeaders2=withoutForwarded(request);return version2&&requestHeaders2.set(FORWARDED.version,version2),{requestHeaders:requestHeaders2,setCookies:[],responseHeaders:{}}}let prefix=(rewritePrefix||DEFAULT_REWRITE_PREFIX).replace(/^\/+|\/+$/g,""),url=new URL(request.url),clean=withoutPrefix(url,prefix);if(clean)return{redirect:clean,redirectStatus:308,setCookies:[],responseHeaders:{}};let{client,handOff,cacheFacts,version}=await open({...clientOptions,request},{record:kind==="document",write:"changed"}),destination=client.getRedirectUrl(request.url),setCookies=client.getSetCookieHeaders();if(destination)return{redirect:new URL(destination,request.url).href,redirectStatus:307,setCookies,responseHeaders:cacheControl("private")};let requestHeaders=withoutForwarded(request);if(requestHeaders.set(FORWARDED.url,request.url),version&&requestHeaders.set(FORWARDED.version,version),kind==="document"){let values=handOff();for(let name of CARRIED)requestHeaders.set(FORWARDED[name],values[name])}let pageKey=client.getPageKey(),facts=cacheFacts(),tested=client.isPageTested(),keyedByCookie=tested&&cdnKeyedByCookie===!0&&facts.writes,sent=keyedByCookie?parseCookieHeader(request.headers.get("cookie"))[KEY_COOKIE]??"":"";keyedByCookie&&sent!==facts.keyCookie&&setCookies.push(keyCookieHeader(facts.keyCookie));let shared=(!tested||keyedByCookie&&sent!==""&&sent===facts.keyCookie&&!facts.redirectPage)&&setCookies.length===0;return{...pageKey?{rewritePath:`/${prefix}/${pageKey}${url.pathname}${url.search}`}:{},pageKey,requestHeaders,setCookies,responseHeaders:cacheControl(facts.preview?"no-store":shared?"shared":"private")}}async function clientFromHeaders(headers2,options,open){let cookies=Object.fromEntries(CARRIED.flatMap(name=>{let value=headers2.get(FORWARDED[name]);return value&&COOKIE_SAFE.test(value)?[[name,value]]:[]})),url=headers2.get(FORWARDED.url)||`https://${headers2.get("host")||"localhost"}/`,version=headers2.get(FORWARDED.version),decidedOn=version&&VERSION_SAFE.test(version)?version:void 0;return(await open({...options,request:{url,headers:headers2}},{cookies,decidedOn})).client}function planMiddleware2(request,options){return planMiddleware(request,options,openClient)}async function abconvertMiddleware(request,options){let planned=await planMiddleware2(request,options);if(!planned)return NextResponse.next();let response;planned.redirect?response=NextResponse.redirect(planned.redirect,planned.redirectStatus):planned.rewritePath?response=NextResponse.rewrite(new URL(planned.rewritePath,request.url),{request:{headers:planned.requestHeaders}}):response=NextResponse.next({request:{headers:planned.requestHeaders}});for(let cookie of planned.setCookies)response.headers.append("set-cookie",cookie);for(let[name,value]of Object.entries(planned.responseHeaders))response.headers.set(name,value);return response}async function createABConvert2(options){let{pageKey}=options;return typeof pageKey=="string"?createABConvert({...options,pageKey}):clientFromHeaders(await headers(),options,openClient)}export{abconvertMiddleware,createABConvert2 as createABConvert,planMiddleware2 as planMiddleware};
package/dist/react.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use client';
2
+ var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})},__copyProps=(to,from,except,desc)=>{if(from&&typeof from=="object"||typeof from=="function")for(let key of __getOwnPropNames(from))!__hasOwnProp.call(to,key)&&key!==except&&__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable});return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:!0}),mod);var index_exports={};__export(index_exports,{ABConvertProvider:()=>ABConvertProvider,useABConvert:()=>useABConvert});module.exports=__toCommonJS(index_exports);var import_react=require("react"),import_headless=require("@abconvert/headless"),Context=(0,import_react.createContext)(null),EMPTY={...(0,import_headless.hydrateABConvert)(null),isReady:!1},MAX_HELD_CALLS=100;function standIn(held){let hold=call=>{held.length<MAX_HELD_CALLS&&held.push(call)};return{...(0,import_headless.hydrateABConvert)(null),track:(name,payload)=>hold(client=>client.track(name,payload)),trackExposure:experimentId=>hold(client=>client.trackExposure(experimentId)),setConsent:consent=>hold(client=>client.setConsent(consent))}}function ABConvertProvider({state,pageState,options,onAssignment,expectCustomerPrivacy=!1,children}){let callback=(0,import_react.useRef)(onAssignment);callback.current=onAssignment;let hooks={onAssignment:assignment=>callback.current?.(assignment),expectCustomerPrivacy},handedOver=state??pageState??null,fromState=(0,import_react.useMemo)(()=>handedOver?(0,import_headless.hydrateABConvert)(handedOver,hooks):null,[handedOver,expectCustomerPrivacy]),[loaded,setLoaded]=(0,import_react.useState)(null),held=(0,import_react.useRef)([]),waiting=(0,import_react.useRef)(null),browserOptions=handedOver?void 0:options;(0,import_react.useEffect)(()=>{if(!browserOptions)return;let live=!0;return(0,import_headless.loadClientState)(browserOptions).then(loadedState=>{live&&setLoaded((0,import_headless.hydrateABConvert)(loadedState,hooks))}),()=>{live=!1}},[browserOptions?.shop,browserOptions?.storefrontKey,expectCustomerPrivacy]);let ready=fromState??(browserOptions?loaded:null),isReady=ready!==null,client=ready??waiting.current??(waiting.current=standIn(held.current));(0,import_react.useEffect)(()=>{if(isReady)for(let call of held.current.splice(0))call(client);return client.start(),()=>client.stop()},[client]);let[version,setVersion]=(0,import_react.useState)(0);(0,import_react.useEffect)(()=>client.subscribe(()=>setVersion(current=>current+1)),[client]);let value=(0,import_react.useMemo)(()=>({...client,isReady}),[client,isReady,version]);return(0,import_react.createElement)(Context.Provider,{value},children)}var warnedOutside=!1;function useABConvert(){let value=(0,import_react.useContext)(Context);return value||(warnedOutside||(warnedOutside=!0,console.warn("[ABConvert] useABConvert() was called outside <ABConvertProvider>: it answers as your original store.")),EMPTY)}
@@ -0,0 +1,29 @@
1
+ import { type ReactElement, type ReactNode } from 'react';
2
+ import { type ABConvert, type ABConvertOptions, type Assignment, type ClientState, type PageState } from '@abconvert/headless';
3
+ export interface ABConvertProviderProps {
4
+ /** `getClientState()` from your server: a page rendered for its visitor. */
5
+ state?: ClientState | null;
6
+ /**
7
+ * `getPageState()` from a client made with a page key: a page rendered for
8
+ * its test groups, which a cache may serve to everyone in them. The provider
9
+ * reads the visitor from the cookies once it mounts.
10
+ */
11
+ pageState?: PageState | null;
12
+ /** Browser-only mode, with no server: the `createABConvert` options. The tests load after the page does. */
13
+ options?: Omit<ABConvertOptions, 'request'>;
14
+ /** Runs once for each test the visitor is in, on every page load. Not for forced or preview assignments. */
15
+ onAssignment?: (assignment: Assignment) => void;
16
+ /**
17
+ * Hold all tracking, cookies included, until Shopify's Customer Privacy API
18
+ * loads and allows analytics. Set it when your storefront loads that API.
19
+ * The Hydrogen entries set it for you.
20
+ */
21
+ expectCustomerPrivacy?: boolean;
22
+ children?: ReactNode;
23
+ }
24
+ export type UseABConvert = ABConvert & {
25
+ /** False until the visitor is assigned: only in browser-only mode, before the tests load. */
26
+ isReady: boolean;
27
+ };
28
+ export declare function ABConvertProvider({ state, pageState, options, onAssignment, expectCustomerPrivacy, children }: ABConvertProviderProps): ReactElement;
29
+ export declare function useABConvert(): UseABConvert;
@@ -0,0 +1,29 @@
1
+ import { type ReactElement, type ReactNode } from 'react';
2
+ import { type ABConvert, type ABConvertOptions, type Assignment, type ClientState, type PageState } from '@abconvert/headless';
3
+ export interface ABConvertProviderProps {
4
+ /** `getClientState()` from your server: a page rendered for its visitor. */
5
+ state?: ClientState | null;
6
+ /**
7
+ * `getPageState()` from a client made with a page key: a page rendered for
8
+ * its test groups, which a cache may serve to everyone in them. The provider
9
+ * reads the visitor from the cookies once it mounts.
10
+ */
11
+ pageState?: PageState | null;
12
+ /** Browser-only mode, with no server: the `createABConvert` options. The tests load after the page does. */
13
+ options?: Omit<ABConvertOptions, 'request'>;
14
+ /** Runs once for each test the visitor is in, on every page load. Not for forced or preview assignments. */
15
+ onAssignment?: (assignment: Assignment) => void;
16
+ /**
17
+ * Hold all tracking, cookies included, until Shopify's Customer Privacy API
18
+ * loads and allows analytics. Set it when your storefront loads that API.
19
+ * The Hydrogen entries set it for you.
20
+ */
21
+ expectCustomerPrivacy?: boolean;
22
+ children?: ReactNode;
23
+ }
24
+ export type UseABConvert = ABConvert & {
25
+ /** False until the visitor is assigned: only in browser-only mode, before the tests load. */
26
+ isReady: boolean;
27
+ };
28
+ export declare function ABConvertProvider({ state, pageState, options, onAssignment, expectCustomerPrivacy, children }: ABConvertProviderProps): ReactElement;
29
+ export declare function useABConvert(): UseABConvert;
package/dist/react.js ADDED
@@ -0,0 +1,2 @@
1
+ 'use client';
2
+ import{createContext,createElement,useContext,useEffect,useMemo,useRef,useState}from"react";import{hydrateABConvert,loadClientState}from"@abconvert/headless";var Context=createContext(null),EMPTY={...hydrateABConvert(null),isReady:!1},MAX_HELD_CALLS=100;function standIn(held){let hold=call=>{held.length<MAX_HELD_CALLS&&held.push(call)};return{...hydrateABConvert(null),track:(name,payload)=>hold(client=>client.track(name,payload)),trackExposure:experimentId=>hold(client=>client.trackExposure(experimentId)),setConsent:consent=>hold(client=>client.setConsent(consent))}}function ABConvertProvider({state,pageState,options,onAssignment,expectCustomerPrivacy=!1,children}){let callback=useRef(onAssignment);callback.current=onAssignment;let hooks={onAssignment:assignment=>callback.current?.(assignment),expectCustomerPrivacy},handedOver=state??pageState??null,fromState=useMemo(()=>handedOver?hydrateABConvert(handedOver,hooks):null,[handedOver,expectCustomerPrivacy]),[loaded,setLoaded]=useState(null),held=useRef([]),waiting=useRef(null),browserOptions=handedOver?void 0:options;useEffect(()=>{if(!browserOptions)return;let live=!0;return loadClientState(browserOptions).then(loadedState=>{live&&setLoaded(hydrateABConvert(loadedState,hooks))}),()=>{live=!1}},[browserOptions?.shop,browserOptions?.storefrontKey,expectCustomerPrivacy]);let ready=fromState??(browserOptions?loaded:null),isReady=ready!==null,client=ready??waiting.current??(waiting.current=standIn(held.current));useEffect(()=>{if(isReady)for(let call of held.current.splice(0))call(client);return client.start(),()=>client.stop()},[client]);let[version,setVersion]=useState(0);useEffect(()=>client.subscribe(()=>setVersion(current=>current+1)),[client]);let value=useMemo(()=>({...client,isReady}),[client,isReady,version]);return createElement(Context.Provider,{value},children)}var warnedOutside=!1;function useABConvert(){let value=useContext(Context);return value||(warnedOutside||(warnedOutside=!0,console.warn("[ABConvert] useABConvert() was called outside <ABConvertProvider>: it answers as your original store.")),EMPTY)}export{ABConvertProvider,useABConvert};