@abconvert/headless 0.1.0 → 0.1.1

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.
@@ -20,10 +20,12 @@ export interface MiddlewarePlan {
20
20
  redirectStatus?: 307 | 308;
21
21
  /**
22
22
  * The path to render instead, `/<prefix>/<page key><path><query>`, when a
23
- * test changes the page: each test group's copy is its own cache entry.
23
+ * test changes the page: each test group's copy is its own cache entry. Also
24
+ * on a server action from such a page, so Next.js renders the action's
25
+ * response from the same route.
24
26
  */
25
27
  rewritePath?: string;
26
- /** The page key: `''` when no test changes the page. Absent on a redirect and on a request left alone. */
28
+ /** The page key: `''` when no test changes the page. Absent on a redirect, on a request left alone, and on a server action from a page no test changes. */
27
29
  pageKey?: string;
28
30
  /** The request headers the page renders with, when there is no redirect. */
29
31
  requestHeaders?: Headers;
@@ -20,10 +20,12 @@ export interface MiddlewarePlan {
20
20
  redirectStatus?: 307 | 308;
21
21
  /**
22
22
  * The path to render instead, `/<prefix>/<page key><path><query>`, when a
23
- * test changes the page: each test group's copy is its own cache entry.
23
+ * test changes the page: each test group's copy is its own cache entry. Also
24
+ * on a server action from such a page, so Next.js renders the action's
25
+ * response from the same route.
24
26
  */
25
27
  rewritePath?: string;
26
- /** The page key: `''` when no test changes the page. Absent on a redirect and on a request left alone. */
28
+ /** The page key: `''` when no test changes the page. Absent on a redirect, on a request left alone, and on a server action from a page no test changes. */
27
29
  pageKey?: string;
28
30
  /** The request headers the page renders with, when there is no redirect. */
29
31
  requestHeaders?: Headers;
package/dist/next.cjs CHANGED
@@ -5,4 +5,4 @@ ${previewKey}`,pending=previewFetches.get(cacheKey);if(!pending||now()-pending.s
5
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
6
  ${source.storefrontKey}`),current2=held&&builtAt(held)>=(state.g??0)?held:await loadTests(source);if(!current2)return;copies.set(`${source.baseUrl}
7
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 import_server.NextResponse.next();let response;planned.redirect?response=import_server.NextResponse.redirect(planned.redirect,planned.redirectStatus):planned.rewritePath?response=import_server.NextResponse.rewrite(new URL(planned.rewritePath,request.url),{request:{headers:planned.requestHeaders}}):response=import_server.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(0,import_headers.headers)(),options,openClient)}
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,prefix=(rewritePrefix||DEFAULT_REWRITE_PREFIX).replace(/^\/+|\/+$/g,""),url=new URL(request.url);if(!kind){let opened=request.method!=="GET"&&request.method!=="HEAD"?await open({...clientOptions,request},{record:!1}):null,version2=opened?.version??"",sent2=Object.values(FORWARDED).some(header=>request.headers.has(header));if(!version2&&!sent2)return null;let requestHeaders2=withoutForwarded(request);version2&&requestHeaders2.set(FORWARDED.version,version2);let pageKey2=opened&&request.headers.has("next-action")&&!NOT_A_PAGE.test(url.pathname)?opened.client.getPageKey():"";return{...pageKey2?{rewritePath:`/${prefix}/${pageKey2}${url.pathname}${url.search}`,pageKey:pageKey2}:{},requestHeaders:requestHeaders2,setCookies:[],responseHeaders:{}}}let 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 import_server.NextResponse.next();let response;planned.redirect?response=import_server.NextResponse.redirect(planned.redirect,planned.redirectStatus):planned.rewritePath?response=import_server.NextResponse.rewrite(new URL(planned.rewritePath,request.url),{request:{headers:planned.requestHeaders}}):response=import_server.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(0,import_headers.headers)(),options,openClient)}
package/dist/next.js CHANGED
@@ -5,4 +5,4 @@ ${previewKey}`,pending=previewFetches.get(cacheKey);if(!pending||now()-pending.s
5
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
6
  ${source.storefrontKey}`),current2=held&&builtAt(held)>=(state.g??0)?held:await loadTests(source);if(!current2)return;copies.set(`${source.baseUrl}
7
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};
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,prefix=(rewritePrefix||DEFAULT_REWRITE_PREFIX).replace(/^\/+|\/+$/g,""),url=new URL(request.url);if(!kind){let opened=request.method!=="GET"&&request.method!=="HEAD"?await open({...clientOptions,request},{record:!1}):null,version2=opened?.version??"",sent2=Object.values(FORWARDED).some(header=>request.headers.has(header));if(!version2&&!sent2)return null;let requestHeaders2=withoutForwarded(request);version2&&requestHeaders2.set(FORWARDED.version,version2);let pageKey2=opened&&request.headers.has("next-action")&&!NOT_A_PAGE.test(url.pathname)?opened.client.getPageKey():"";return{...pageKey2?{rewritePath:`/${prefix}/${pageKey2}${url.pathname}${url.search}`,pageKey:pageKey2}:{},requestHeaders:requestHeaders2,setCookies:[],responseHeaders:{}}}let 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abconvert/headless",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "ABConvert A/B tests on a custom Shopify storefront: assignment, test prices, and cart attributes on the server, edge, or browser.",
5
5
  "keywords": ["abconvert", "shopify", "headless", "hydrogen", "nextjs", "ab-testing", "storefront-api"],
6
6
  "homepage": "https://docs.abconvert.io/headless-sdk/overview",