@thg-agentic/hyper-personalisation-web 3.18.0 → 3.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -178,6 +178,13 @@ and `productAdded` (your search engine's id for the result set, such as Algolia'
178
178
  `newsletterSubscribed`. A search page sends `productsSearched` with its results and never a
179
179
  `productListViewed` for the same grid.
180
180
 
181
+ Since 3.19 (spec 1.12), a search box's suggestions are their own event:
182
+
183
+ | Method | Event | Notes |
184
+ |---|---|---|
185
+ | `productsSearched` | Products Searched | A submitted search and the results page it produced: `query`, the results in `products`, and `pagination` with `total_results` |
186
+ | `searchSuggestionsViewed` | Search Suggestions Viewed | The products a search box suggested for a partly typed `query`, sent once per suggestion set with the products seen (`position` in the suggestions). Give each set its own `search_request_id`, and a click on one of its products carries that id on `productClicked`. Never `productsSearched`: suggestions and results are ranked by different models and are measured apart |
187
+
181
188
  ## Options
182
189
 
183
190
  | Option | Default | |
@@ -190,8 +197,8 @@ and `productAdded` (your search engine's id for the result set, such as Algolia'
190
197
  | `shipToCountry`, `getShipToCountry` | `null` | ISO-3166 alpha-2 destination, per event. |
191
198
  | `locale`, `getLocale` | `null` | The storefront locale served (BCP-47), per event. |
192
199
  | `country` | `null` | Visitor country; the ingest derives it at the edge and overwrites this. |
193
- | `consent` | none | A consent adapter (see below). Makes the SDK strict. |
194
- | `consentTimeoutMs` | `5000` | How long events wait for the CMP before they are dropped. |
200
+ | `consent` | none | A consent adapter (see below). Makes the SDK strict. Without one the SDK sends under your lawful basis (`basis: 'host'`) and says so once in the console (3.19). |
201
+ | `consentTimeoutMs` | `5000` | How long the CMP has to answer before the status is `unknown`. Events stay held, in memory, until it answers. |
195
202
  | `consentCookieDomain` | host only | `Domain` for the consent cookie. |
196
203
  | `onConsentStatus` | none | Called with the gate's status: `granted`, `denied` or `unknown` (timed out). |
197
204
  | `storageNames` | `thg_hp_*` | Rename what the SDK stores; see [Storage](#storage). |
@@ -211,8 +218,36 @@ and `productAdded` (your search engine's id for the result set, such as Algolia'
211
218
  The client has one method per browser event in the spec, plus `track`, `page`, `identify`,
212
219
  `reset`, `signedOut`, `flush`, `servingHeaders`, and the read-only `consent` and
213
220
  `lastPageMessageId`. Every method takes an optional last argument of context overrides, either as
214
- the overrides or as `{ context }`. An event method returns the event, or null when the tracking
215
- plan disables it. Declarations are in `types/`, and event properties are typed from the registry.
221
+ the overrides or as `{ context }`. An event method returns the event, or null when it is not sent
222
+ (the tracking plan disables it, the visitor is outside the sample, or `beforeSend` drops it).
223
+ Declarations are in `types/`, and event properties are typed from the registry.
224
+
225
+ - **No method throws into your page** (3.19). A call that fails, whatever it was passed, logs a
226
+ warning and answers what a client that cannot decide answers, failing closed: an event method
227
+ null, `inHoldout()` true, `personalisationGroup()` `'excluded'`, `servingHeaders()` and
228
+ `recommendationContext()` `{}`, `flush()` a resolved promise. The one exception is
229
+ `throwOnInvalid`, which you ask for in development.
230
+ - **One event is at most 55 KB** of JSON (3.19), so that it can always travel in a keepalive request
231
+ at unload. A bigger one is not sent and its method returns null; with `debug` the SDK logs it.
232
+ Segment's limit is 32 KB; this one is higher so that a long product list still fits.
233
+ - **A sign-out forgets the visit** (3.19). `signedOut()`, or `reset()` while a user is signed in,
234
+ also forgets the SDK's session, the decisions in force and the recommendation context, so the
235
+ next person on a shared device inherits none of them. `reset()` with nobody signed in changes
236
+ nothing else, so a host that calls it on every page keeps its session. The SDK's own device id
237
+ starts afresh too, as Segment's `analytics.reset()` does; a device id from the site's own cookie
238
+ (`getAnonymousId`, or a `site_cookie` source) stays, because the site owns it. On one page,
239
+ `identify` with a different `userId` than an earlier `identify` named is a sign-out first; the
240
+ same user again, a first `identify`, a `getUserId` cookie or bootstrap's `identity` option of
241
+ another form changes nothing (with `debug`, a mismatch with `identity` is logged). Across
242
+ pages, call `signedOut()` or `reset()` at the sign-out.
243
+ - **A refusal forgets the visit at once** (3.19): the kept recommendation context and the session
244
+ signals go when consent is refused or personalisation withdrawn, even if `beforeSend` drops the
245
+ `Consent Updated` event. Before the client starts, `hp.recommendationContext()` reads the kept
246
+ visit only when the consent cookie grants personalisation.
247
+ - **Retries wait out `Retry-After`** on a 429 or 5xx (3.19), as well as backing off exponentially
248
+ with jitter, up to 30 seconds. The browser lets the SDK read the header only from a first-party
249
+ proxy for now: cross-origin it needs `Access-Control-Expose-Headers: Retry-After`, which the HP
250
+ ingest does not send yet. Without it the backoff alone applies.
216
251
 
217
252
  ## Calling the serving API
218
253
 
@@ -415,8 +450,9 @@ With a `consent` adapter the SDK is strict:
415
450
  - **Analytics granted:** held events are sent, each carrying the answer in `context.consent`.
416
451
  - **Analytics refused:** held events are dropped, and so is everything after. Nothing goes into
417
452
  local or session storage.
418
- - **No answer by the timeout:** held and later events are dropped until an answer arrives. Strict
419
- is the only mode: an unanswered CMP is never a grant, however long it takes.
453
+ - **No answer by the timeout:** the status is `unknown`, and held and later events stay held, in
454
+ memory, while the SDK keeps listening for the answer. Strict is the only mode: an unanswered CMP
455
+ is never a grant, however long it takes.
420
456
  - **Page closed before an answer:** nothing is sent. Switching to another tab is not closing the
421
457
  page: held events wait for the visitor to come back.
422
458
  - **A navigation before an answer** (a sign-in that redirects) loses what the page held, unless
@@ -796,8 +832,10 @@ The `hp_cookie` cookie is one per browser, not per property. It is written with
796
832
  from the last visit. With a consent adapter it is never written before analytics is granted: until
797
833
  then the id lives in memory, and in the held store with `holdAcrossPages`, so a redirect keeps it.
798
834
  A refusal or withdrawal deletes it, and the id with it: a later grant, on the same page or another,
799
- starts a new device, as `analytics.reset()` does in analytics.js (3.18). Without a consent adapter
800
- you have decided the lawful basis yourself, so it is written at once.
835
+ starts a new device, as Segment's `analytics.reset()` does in analytics.js (3.18). A sign-out
836
+ (`signedOut()`, or `reset()` with a user signed in) deletes it too, and the next event mints a new
837
+ one (3.19). Without a consent adapter you have decided the lawful basis yourself, so it is written
838
+ at once.
801
839
 
802
840
  Changing a source's choice gives every returning visitor a new device id. A device id 3.5 minted
803
841
  into localStorage (`thg_hp_anonymous_id:<property>`) becomes the cookie's value under a grant, and
package/dist/hp.min.js CHANGED
@@ -1,3 +1,3 @@
1
- (()=>{var Pt=e=>{throw TypeError(e)};var ir=(e,t,n)=>t.has(e)||Pt("Cannot "+n);var Lt=(e,t,n)=>t.has(e)?Pt("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n);var N=(e,t,n)=>(ir(e,t,"access private method"),n);var ge=[];for(let e=2;ge.length<64;e++)ge.every(t=>e%t!==0)&&ge.push(e);var Ot=e=>(e-Math.floor(e))*2**32>>>0,ar=ge.slice(0,8).map(e=>Ot(Math.sqrt(e))),cr=ge.map(e=>Ot(Math.cbrt(e))),z=(e,t)=>e>>>t|e<<32-t;function Nt(e){let t=new TextEncoder().encode(String(e)),n=new Uint32Array((t.length+8>>6)*16+16);t.forEach((s,i)=>{n[i>>2]|=s<<24-i%4*8}),n[t.length>>2]|=128<<24-t.length%4*8,n[n.length-2]=Math.floor(t.length/2**29),n[n.length-1]=t.length*8;let r=Uint32Array.from(ar),o=new Uint32Array(64);for(let s=0;s<n.length;s+=16){for(let g=0;g<64;g++){if(g<16){o[g]=n[s+g];continue}let _=o[g-15],x=o[g-2],v=z(_,7)^z(_,18)^_>>>3,h=z(x,17)^z(x,19)^x>>>10;o[g]=o[g-16]+v+o[g-7]+h}let[i,a,u,d,p,f,m,b]=r;for(let g=0;g<64;g++){let _=z(p,6)^z(p,11)^z(p,25),x=b+_+(p&f^~p&m)+cr[g]+o[g]|0,h=(z(i,2)^z(i,13)^z(i,22))+(i&a^i&u^a&u)|0;[b,m,f,p,d,u,a,i]=[m,f,p,d+x|0,u,a,i,x+h|0]}[i,a,u,d,p,f,m,b].forEach((g,_)=>{r[_]+=g})}return Array.from(r,s=>s.toString(16).padStart(8,"0")).join("")}var He=(e,t)=>e<<t|e>>>32-t;function Tt(e){let t=new Uint32Array((e.length+8>>6)*16+16);e.forEach((s,i)=>{t[i>>2]|=s<<24-i%4*8}),t[e.length>>2]|=128<<24-e.length%4*8,t[t.length-2]=Math.floor(e.length/2**29),t[t.length-1]=e.length*8;let n=Uint32Array.of(1732584193,4023233417,2562383102,271733878,3285377520),r=new Uint32Array(80);for(let s=0;s<t.length;s+=16){for(let f=0;f<80;f++)r[f]=f<16?t[s+f]:He(r[f-3]^r[f-8]^r[f-14]^r[f-16],1);let[i,a,u,d,p]=n;for(let f=0;f<80;f++){let m,b;f<20?(m=a&u|~a&d,b=1518500249):f<40?(m=a^u^d,b=1859775393):f<60?(m=a&u|a&d|u&d,b=2400959708):(m=a^u^d,b=3395469782);let g=He(i,5)+m+p+b+r[f]|0;[p,d,u,a,i]=[d,u,He(a,30),i,g]}[i,a,u,d,p].forEach((f,m)=>{n[m]+=f})}let o=new Uint8Array(20);return n.forEach((s,i)=>{o[i*4]=s>>>24,o[i*4+1]=s>>>16,o[i*4+2]=s>>>8,o[i*4+3]=s}),o}var ur="e28e8ada-5b76-4209-bba9-79b823a0af75",lr=e=>Array.from(e,t=>t.toString(16).padStart(2,"0")).join("");function Rt(e,t){let n=Uint8Array.from(e.replace(/-/g,"").match(/../g),a=>Number.parseInt(a,16)),r=new TextEncoder().encode(t),o=new Uint8Array(16+r.length);o.set(n),o.set(r,16);let s=Tt(o).slice(0,16);s[6]=s[6]&15|80,s[8]=s[8]&63|128;let i=lr(s);return`${i.slice(0,8)}-${i.slice(8,12)}-${i.slice(12,16)}-${i.slice(16,20)}-${i.slice(20)}`}var ee=(e,t)=>typeof e=="string"&&e!==""&&e.length<=t?e:null;function be(e,t){return ee(e,64)??ee(t,256)}function Dt({sessionId:e,slot:t,arm:n}={}){let r=ee(e,256);return!r||!ee(t,256)||!ee(n,256)?null:Rt(ur,`${r}:${t}:${n}`)}var dr="4b1f2c7e-9a3d-4e58-b6c1-2d7f0a9e8c53";function Mt({sessionId:e,surface:t}={}){let n=ee(e,256);return!n||!ee(t,64)?null:Rt(dr,`${n}:${t}`)}var fr=Array.from({length:11},(e,t)=>t/10);function pr(e,t){if(!e.isIntersecting)return!1;let{intersectionRect:n,boundingClientRect:r,rootBounds:o}=e;if(!n||!r)return!0;let s=n.width*n.height,i=r.width*r.height,a=o?Math.min(i,o.width*o.height):i;return a>0&&s/a>=t}function Y(e){let{selector:t,onViewable:n,visibleRatio:r=.5,dwellMs:o=1e3,root:s=document}=e,i=new Map,a=new WeakSet,u=new Set,d=h=>{i.has(h)||i.set(h,setTimeout(()=>{i.delete(h),n(h)&&(f?.unobserve(h),u.delete(h))},o))},p=h=>{clearTimeout(i.get(h)),i.delete(h)},f=typeof IntersectionObserver>"u"?null:new IntersectionObserver(h=>{for(let E of h){let S=E.target;if(!pr(E,r)){u.delete(S),p(S);continue}u.add(S),m()&&d(S)}},{threshold:fr}),m=()=>typeof document>"u"||document.visibilityState!=="hidden",b=()=>{if(m())for(let h of u)d(h);else for(let h of u)p(h)};document.addEventListener("visibilitychange",b);let g=()=>{for(let h of s.querySelectorAll(t))a.has(h)||(a.add(h),f?.observe(h))},_=typeof MutationObserver>"u"?null:new MutationObserver(()=>g()),x=()=>{g(),_?.observe(s===document?document.body:s,{childList:!0,subtree:!0})},v=s===document&&!document.body;return v?document.addEventListener("DOMContentLoaded",x,{once:!0}):x(),()=>{v&&document.removeEventListener("DOMContentLoaded",x),document.removeEventListener("visibilitychange",b),f?.disconnect(),_?.disconnect();for(let h of i.values())clearTimeout(h);i.clear(),u.clear()}}function Ke(e,t={}){if(typeof window>"u"||typeof document>"u")return()=>{};let{selector:n="[data-hp-exposure]",visibleRatio:r=.5,dwellMs:o=1e3,root:s=document}=t;return Y({selector:n,root:s,visibleRatio:r,dwellMs:o,onViewable(i){let a=i.dataset?.hpExposure?.trim();return a&&e.surfaceViewed({surface:a}),!0}})}var Ut=e=>{try{return new URL(e,document.baseURI).toString()}catch{return e}};function Ge(e,t={}){if(typeof window>"u"||typeof document>"u")return()=>{};let{selector:n="[data-hp-list]",tileSelector:r="[data-hp-product]",visibleRatio:o=.5,dwellMs:s=1e3,root:i=document}=t,a=new Set,u=g=>g.closest?.(n)??null,d=g=>{let _=g.dataset.hpList?.trim();return _?{list_id:_,category:g.dataset.hpListCategory?.trim()||null}:null},p=g=>Array.from(g.querySelectorAll(r)).filter(_=>u(_)===g),f=(g,_)=>{try{let x=JSON.parse(g.dataset.hpProduct??"");return!x||typeof x!="object"||!x.product_id?null:{...x,position:x.position??_+1,...x.url?{url:Ut(x.url)}:{},...x.image_url?{image_url:Ut(x.image_url)}:{}}}catch{return null}},m=Y({selector:`${n} ${r}`,root:i,visibleRatio:o,dwellMs:s,onViewable(g){let _=u(g);if(!_||_.dataset.hpListView==="manual")return!0;let x=d(_);if(!x||a.has(x.list_id))return!0;let v=new Set,h=p(_).map(f).filter(E=>!E||v.has(E.product_id)?!1:(v.add(E.product_id),!0));return h.length===0||(a.add(x.list_id),e.productListViewed({...x,products:h})),!0}}),b=g=>{let x=g.target?.closest?.("a[href]")?.closest(r);if(!x)return;let v=u(x),h=f(x,v?p(v).indexOf(x):0);h&&e.productClicked({list_id:v?d(v)?.list_id??null:null,products:[h]})};return document.addEventListener("click",b,!0),()=>{document.removeEventListener("click",b,!0),m()}}function T(e,...t){let n=e.dataset??{};for(let r of t){let o=n[r];if(typeof o=="string"&&o.trim()!=="")return o.trim()}return null}function xe(e,...t){let n=T(e,...t);if(n===null)return null;let r=Number(n);return Number.isFinite(r)?Math.trunc(r):null}function mr(e,t){let n=T(e,"hpPromotion");return n?{promotion_id:n,name:T(e,"hpPromotionName"),creative:T(e,"hpPromotionCreative"),position:T(e,"hpPromotionSlot")??String(t+1)}:null}function We(e,t={}){if(typeof window>"u"||typeof document>"u")return()=>{};let{selector:n="[data-hp-promotion]",resolve:r=mr,visibleRatio:o=.5,dwellMs:s=1e3,root:i=document}=t,a=new Set,u=f=>{let m=Array.from(i.querySelectorAll(n)),b=m.indexOf(f);try{return r(f,b<0?m.length:b)}catch{return null}},d=Y({selector:n,root:i,visibleRatio:o,dwellMs:s,onViewable(f){let m=u(f);return!m||a.has(m.promotion_id)?!1:(a.add(m.promotion_id),e.promotionViewed(m),!0)}}),p=f=>{let m=f.target;if(!m||typeof m.closest!="function")return;let b=m.closest(n),g=m.closest("a[href]");if(!b||!g||!b.contains(g)&&!g.contains(b))return;let _=u(b);_&&e.promotionClicked({..._,href:g.getAttribute("href")})};return document.addEventListener("click",p,!0),()=>{document.removeEventListener("click",p,!0),d()}}function hr(e){let t=T(e,"hpRecoRequest"),n=T(e,"hpRecoPlacement"),r=T(e,"hpRecoStrategy");if(!t||!n||!r)return null;let o=T(e,"hpRecoHoldout");return{recommendation_request_id:t,placement:n,strategy:r,recipe:T(e,"hpRecoRecipe"),algorithm_version:T(e,"hpRecoAlgorithmVersion"),experiment:T(e,"hpRecoExperiment"),variant:T(e,"hpRecoVariant"),holdout:o===null?null:o==="true",assignment_bucket:xe(e,"hpRecoBucket"),assignment_unit_kind:T(e,"hpRecoUnitKind"),candidate_set_fingerprint:T(e,"hpRecoFingerprint"),candidate_set_size:xe(e,"hpRecoCandidates"),ranked_by:T(e,"hpRecoRankedBy"),served_at:T(e,"hpRecoServedAt")}}function gr(e,t){let n=T(e,"hpRecoProduct");if(!n)return null;let r=T(e,"hpRecoPrice");return{product_id:n,sku:T(e,"hpRecoSku"),name:T(e,"hpRecoName"),brand:T(e,"hpRecoBrand"),price:r===null||Number.isNaN(Number(r))?null:Number(r),position:xe(e,"hpRecoPosition")??t+1}}function ze(e,t={}){if(typeof window>"u"||typeof document>"u")return()=>{};let{selector:n="[data-hp-reco-request]",tileSelector:r="[data-hp-reco-product]",resolveRail:o=hr,resolveTile:s=gr,visibleRatio:i=.5,dwellMs:a=1e3,root:u=document}=t,d=new Set,p=new Map,f=new Map,m=(h,...E)=>{try{return h(...E)}catch{return null}},b=h=>typeof h.closest=="function"?h.closest(n):null,g=h=>Array.from(h.querySelectorAll(r)),_=(h,E)=>{if(f.delete(E.recommendation_request_id),d.has(E.recommendation_request_id))return;let S=g(h).map((w,L)=>m(s,w,L)).filter(w=>w?.product_id);if(S.length===0)return;d.add(E.recommendation_request_id);let y=[...p.get(E.recommendation_request_id)?.values()??[]].sort((w,L)=>w.position-L.position);e.recommendationsViewed({...E,products:S,viewable:y})},x=Y({selector:`${n} ${r}`,root:u,visibleRatio:i,dwellMs:a,onViewable(h){let E=b(h);if(!E)return!0;let S=m(o,E);if(!S)return!0;let y=S.recommendation_request_id,w=m(s,h,g(E).indexOf(h));if(w?.product_id){p.has(y)||p.set(y,new Map);let L=p.get(y);L.has(w.position)||L.set(w.position,{position:w.position,product_id:w.product_id,viewable_at:new Date().toISOString()})}return!d.has(y)&&!f.has(y)&&f.set(y,setTimeout(()=>_(E,S),0)),!0}}),v=h=>{let E=h.target;if(!E||typeof E.closest!="function")return;let S=E.closest(r);if(!S)return;let y=E.closest("[data-hp-reco-interaction]"),w=E.closest("a[href]"),L=w&&(S.contains(w)||w.contains(S));if(!(y&&S.contains(y))&&!L)return;let D=b(S);if(!D)return;let H=m(o,D);if(!H)return;let K=m(s,S,g(D).indexOf(S));if(!K?.product_id)return;let F=H.recommendation_request_id;d.has(F)||(clearTimeout(f.get(F)),_(D,H)),e.productClicked({recommendation_request_id:H.recommendation_request_id,interaction:T(y??S,"hpRecoInteraction")??"navigate",products:[K]})};return document.addEventListener("click",v,!0),()=>{document.removeEventListener("click",v,!0);for(let h of f.values())clearTimeout(h);f.clear(),x()}}var ae=new Set(["home","list","product","search","basket","checkout","order_confirmation","login","register","account","info","blog","other"]),yr=new Set(["products"]),$t=e=>e==null;function Je(e,t=new WeakSet){if(e instanceof Date)return e.toJSON();if(e===null||typeof e!="object"||t.has(e))return e;t.add(e);let n=Array.isArray(e)?e.map(r=>Je(r,t)):Object.fromEntries(Object.entries(e).filter(([,r])=>!$t(r)).map(([r,o])=>[r,Je(o,t)]));return t.delete(e),n}function Xe(e){if(!e)return e;let t=Je(e);for(let n of yr)n in e&&$t(e[n])&&(t[n]=e[n]);return t}function Vt(){try{let e=performance.getEntriesByType("navigation")[0];return e?["navigate","reload","back_forward","prerender"].includes(e.type)?e.type:"navigate":null}catch{return null}}var Sr=new Set(["","unknown","undefined","null","none","nil","nan","anonymous","guest","0","-1"]);function B(e){if(e==null)return null;let t=String(e).trim();return Sr.has(t.toLowerCase())?null:t}function Z(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}var jt="1.10";function U(e){if(typeof document>"u")return null;try{let t=document.cookie.split(";").map(n=>n.trim()).find(n=>n.startsWith(`${e}=`));return t&&decodeURIComponent(t.slice(e.length+1))||null}catch{return null}}var wr={gclid:"gclid",fbclid:"fbclid",ttclid:"ttclid"},_r=/[a-z0-9._%+-]+@[a-z0-9-]+(\.[a-z0-9-]+)*\.[a-z]{2,}/i,br=/\d(?:[ -]?\d){12,}/,xr=new Set(["gclid","fbclid","ttclid","msclkid"]);function qt(e,t=""){if(typeof e!="string"||e==="")return!1;let n=[e];try{let o=decodeURIComponent(e.replace(/\+/g," "));o!==e&&n.push(o)}catch{}let r=!xr.has(String(t).toLowerCase());return n.some(o=>_r.test(o)||r&&br.test(o))}function Ir(e){if(!e)return null;let t=new URLSearchParams(e),n={},r={utm_source:"source",utm_medium:"medium",utm_campaign:"name",utm_term:"term",utm_content:"content"};for(let[o,s]of[...Object.entries(r),...Object.entries(wr)]){let i=t.get(o);i&&!qt(i,o)&&(n[s]=i)}return Object.keys(n).length?n:null}var Ar=["email","e-mail","password","pass","pwd","token","auth","code","uid","customer_id","postcode","zip","phone","applyCode"],kr=["email","password","passwd","pwd","token","secret","auth","session","phone","postcode","zip","address","birth","card","cvv","iban","ssn","customerid","userid"],vr=["tel","uid","dob"],Er=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","gclid","fbclid","ttclid","msclkid"],Cr=new Set(Ar.map(e=>e.toLowerCase()));function Pr(e){let t=String(e).toLowerCase();if(Cr.has(t))return!0;let n=t.replace(/[-_.]/g,"");return kr.some(o=>n.includes(o))?!0:String(e).replace(/([a-z0-9])([A-Z])/g,"$1 $2").toLowerCase().split(/[^a-z0-9]+/).some(o=>vr.includes(o))}function Ye(e){return(e??[]).map(t=>{try{return new RegExp(`^${t}$`,"i")}catch{return{test:n=>n.toLowerCase()===String(t).toLowerCase()}}})}function Ft(e,t={}){if(!e)return null;try{let n=new URLSearchParams(e),r=t.queryParamAllowlist?Ye([...t.queryParamAllowlist,...Er]):null,o=Ye(t.queryParamBlocklist),s=Ye(t.sensitiveParamAllowlist);for(let a of new Set(n.keys()))(Pr(a)&&!s.some(d=>d.test(a))||n.getAll(a).some(d=>qt(d,a))||(r?!r.some(d=>d.test(a)):o.some(d=>d.test(a))))&&n.delete(a);let i=n.toString();return i?`?${i}`:""}catch{return""}}function Lr(e,t,n={}){if(!e)return null;try{let r=new URL(e);return t&&r.origin===t?r.origin+r.pathname+(Ft(r.search,n)??""):r.origin+r.pathname}catch{return null}}function Ze(e){if(typeof e!="string")return null;let t=e.trim().toUpperCase();return/^[A-Z]{3}$/.test(t)?t:null}function Bt(e){if(typeof e!="string")return null;let t=e.trim().toUpperCase();return/^[A-Z]{2}$/.test(t)?t:null}function Or(e){if(typeof e!="string")return null;let t=e.trim().replace(/_/g,"-");if(!/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(t))return null;try{return Intl.getCanonicalLocales(t)[0]}catch{return null}}function M(e,t){if(typeof e=="function")try{let n=e();if(n!=null)return n}catch{}return t}function Nr(e){return{property_id:String(e.propertyId),channel:e.channel??null,market:e.market??null,environment:e.environment??null}}function Tr(e){let t=typeof window<"u"?window.location:null,n=t?Ft(t.search,e):null;return{url:t?t.origin+t.pathname+(n??""):e.pageUrl??null,path:t?t.pathname:e.pagePath??null,search:n??null,referrer:typeof document<"u"?Lr(document.referrer,t?.origin,e):null,title:typeof document<"u"&&document.title||null,type:e.pageType??null}}function Ht(e,t={}){let n=typeof window<"u"?window.location:null,r={library:{name:e.libraryName,version:e.libraryVersion},spec:{version:jt},source:Nr(e),session_id:M(e.getSessionId,null),render_id:M(e.getRenderId,null),identity_trust:B(M(e.getAnonymousId,null))?"device":"none",page:Tr(e),campaign:n?Ir(n.search):null,device:{user_agent:typeof navigator<"u"?navigator.userAgent:null,type:null,viewport_width:typeof window<"u"?window.innerWidth:null,viewport_height:typeof window<"u"?window.innerHeight:null},currency:Ze(M(e.getCurrency,e.currency)),locale:Or(M(e.getLocale,e.locale)),timezone:(()=>{try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}})(),country:Bt(e.country),ship_to_country:Bt(M(e.getShipToCountry,e.shipToCountry)),ip:null,bot:null,consent:e.getConsent?.()??null,experiments:M(e.getExperiments,null),audiences:M(e.getAudiences,null),decisions:M(e.getDecisions,null),recommendations:M(e.getRecommendations,null),extensions:M(e.getExtensions,null)};return Kt(r,t)}function Kt(e,t){if(!t)return e;let n={...e};for(let[r,o]of Object.entries(t))o&&typeof o=="object"&&!Array.isArray(o)&&e[r]&&typeof e[r]=="object"&&!Array.isArray(e[r])?n[r]=Kt(e[r],o):o!==void 0&&(n[r]=o);return n}var re=Object.freeze({consent:"thg_hp_consent",retry:"thg_hp_retry",sessionSignals:"thg_hp_session_signals",identified:"thg_hp_identified",previousPage:"thg_hp_prev_page",decisions:"thg_hp_decisions",settings:"thg_hp_settings",held:"thg_hp_held",carried:"thg_hp_carried",recommendationContext:"thg_hp_recommendation_context",anonymousId:"thg_hp_anonymous_id",session:"thg_hp_session",preview:"thg_hp_preview",debug:"thg_hp_debug"}),Rr={retry:"cdp.retry",sessionSignals:"cdp_session_signals",identified:"cdp_identified",previousPage:"cdp_prev_page_message_id"},Qe={...re},Gt=!1;function Ie(e){let t={...re};for(let[n,r]of Object.entries(e??{})){if(!(n in re))throw new Error(`storageNames: unknown kind "${n}"`);if(typeof r!="string"||!/^[\w.-]+$/.test(r))throw new Error(`storageNames.${n} must be a non-empty cookie-safe name`);t[n]=r}Qe=t}function O(e){return Qe[e]}var Dr="_",Wt=e=>e==null||e===""?Dr:String(e);function q(e,t,n){let r=`${e}:${Wt(t)}`;return n==null||n===""?r:`${r}:${n}`}function zt(e,t){return`${e}:${Wt(t)}:`}function Ae(){if(!Gt){Gt=!0;for(let e of[()=>localStorage,()=>sessionStorage]){let t;try{if(t=e(),!t)continue;for(let n of Object.keys(t))for(let[r,o]of Object.entries(Rr)){if(n!==o&&!n.startsWith(`${o}:`))continue;let s=Qe[r]+n.slice(o.length);t.getItem(s)===null&&t.setItem(s,t.getItem(n)),t.removeItem(n)}}catch{}}}}var tt=1800*1e3,ke=()=>{try{return typeof sessionStorage>"u"?null:sessionStorage}catch{return null}},et=()=>{try{return typeof localStorage>"u"?null:localStorage}catch{return null}};function Jt(e,t=()=>Date.now()){return{read(){try{let n=ke()?.getItem(e),r=n?JSON.parse(n):null;return!r||t()-r.savedAt>tt?(r&&ke()?.removeItem(e),{items:[],anonymousId:null}):{items:Array.isArray(r.items)?r.items:[],anonymousId:typeof r.anonymousId=="string"?r.anonymousId:null}}catch{return{items:[],anonymousId:null}}},write(n,r=null){try{n.length===0?ke()?.removeItem(e):ke()?.setItem(e,JSON.stringify({savedAt:t(),items:n,anonymousId:r}))}catch{}}}}var Mr=400*24*60*60;function Q(e,t,n,r){if(typeof document>"u")return!1;try{let o=typeof location<"u"&&location.protocol==="https:"?"; Secure":"",s=r?`; Domain=${r}`:"",i=n===null?"":`; Max-Age=${n}`;return document.cookie=`${e}=${encodeURIComponent(t)}; Path=/${i}; SameSite=Lax`+s+o,!0}catch{return!1}}function Ur({name:e,domain:t=null,legacyKey:n,read:r,mint:o,mayStore:s,carried:i=null}){let a=null;try{a=et()?.getItem(n)||null}catch{a=null}let u=r(e)??a??i,d=!1,p=()=>{if(!(d||u===null||!s())&&(d=Q(e,u,Mr,t),!(!d||a===null)))try{et()?.removeItem(n),a=null}catch{}};return{get(){return u??(u=o()),p(),u},peek:()=>u,persist:p,forget(){Q(e,"",0,t),u=null,d=!1;try{et()?.removeItem(n)}catch{}a=null}}}function Xt(e,{carried:t=null,mayStore:n,orOwn:r=!1}){let o=e.anonymousIdSource,s=()=>Ur({name:O("anonymousId"),domain:e.consentCookieDomain??null,legacyKey:q(O("anonymousId"),e.propertyId),read:U,mint:Z,mayStore:n,carried:t});if(o?.source==="hp_cookie"){let u=s();return e.getAnonymousId=()=>u.get(),u}if(o?.source==="site_cookie"&&o.cookie){let u=o.cookie;e.getAnonymousId=()=>U(u)}if(!r)return null;let i=e.getAnonymousId,a=s();return e.getAnonymousId=()=>B(M(i,null))??a.get(),a}var $r=200;function Yt(e,t,n=()=>Date.now()){let r=()=>q(O("carried"),e),o=!1;function s(){try{let i=JSON.parse(sessionStorage.getItem(r())??"null");return!i||!(n()-i.savedAt<=tt)?[]:Array.isArray(i.calls)?i.calls:[]}catch{return[]}}return{take(){let i=s();try{sessionStorage.removeItem(r())}catch{}return i},save(i){try{return o||!t()?!1:(sessionStorage.setItem(r(),JSON.stringify({savedAt:n(),calls:[...s(),...i].slice(-$r)})),!0)}catch{return!1}},close(){o=!0}}}var Fs=re.consent,ve=Object.freeze({visitor:"visitor",host:"host"}),Zt=()=>({basis:ve.host}),Vr="hp-consent",oe=["analytics","marketing","personalisation","ad_user_data","ad_personalization"],Qt={analytics:"a",marketing:"m",personalisation:"p",ad_user_data:"aud",ad_personalization:"adp"},X=()=>typeof window<"u",en=()=>X()&&typeof window.cookieStore?.addEventListener=="function",jr=e=>e===!0?!0:e===!1?!1:null;function ce(e){if(!e||typeof e!="object")return null;let t={};for(let n of oe)t[n]=jr(e[n]);return t}function rt(e,t){return!e||!t?e===t:oe.every(n=>e[n]===t[n])}function ot(e){if(!e)return"";let t=[];for(let n of oe){let r=e[n];(r===!0||r===!1)&&t.push(`${Qt[n]}=${r?1:0}`)}return t.join("&")}function tn(e){return{[Vr]:ot(ce(e))}}function ye(e){let t=ce({});if(e==null)return t;let n=String(e).trim();if(n.length>=2&&n.startsWith('"')&&n.endsWith('"')&&(n=n.slice(1,-1)),n.includes("%"))try{n=decodeURIComponent(n.replace(/\+/g," "))}catch{return t}let r=Object.fromEntries(Object.entries(Qt).map(([s,i])=>[i,s])),o=new Set;for(let s of n.split("&")){let i=s.indexOf("=");if(i<=0)continue;let a=r[s.slice(0,i).trim()],u={1:!0,0:!1}[s.slice(i+1).trim()]??null;!a||u===null||o.has(a)||(t[a]===null?t[a]=u:t[a]!==u&&(t[a]=null,o.add(a)))}return t}function st(e,t){if(!t)return e;let n=r=>{if(!r)return r;let o={...r};for(let s of oe)s!=="analytics"&&t[s]!==!0&&(o[s]=null);return o};return{name:e.name,basis:e.basis,read:()=>n(e.read()),subscribe:r=>e.subscribe(o=>r(n(o))),get observesChanges(){return e.observesChanges}}}function it(e,t={}){return{...e,ad_user_data:t.ad_user_data??e.marketing,ad_personalization:t.ad_personalization??e.marketing}}function Ee(e,t){let n=!0;return{notify:()=>{if(n)try{let o=e();o&&t(o)}catch{}},stop:()=>n=!1}}var Br={analytics:["analytics","measure_content_performance","audience_measurement"],marketing:["marketing","targeting","advertising","select_personalized_ads","create_ads_profile"],personalisation:["personalisation","personalization","select_personalized_content","create_content_profile"]};function qr(e,t){if(!t?.length)return null;let n=!1;for(let[r,o]of Object.entries(e)){let s=String(o?.id??r).toLowerCase();if(t.some(a=>{let u=String(a).toLowerCase();return s===u||s.startsWith(`${u}-`)})&&(n=!0,o?.enabled===!0))return!0}return n?!1:null}function nn(e={}){let t={...Br,...e.purposes};function n(){let r=X()?window.Didomi:void 0;if(!r?.getCurrentUserStatus||r.shouldUserStatusBeCollected?.()!==!1||r.isUserStatusPartial?.()===!0)return null;let o=r.getCurrentUserStatus()?.purposes;if(!o||typeof o!="object")return null;let s=i=>qr(o,t[i]);return it({analytics:s("analytics"),marketing:s("marketing"),personalisation:s("personalisation")},{ad_user_data:s("ad_user_data"),ad_personalization:s("ad_personalization")})}return{name:"didomi",read:n,observesChanges:!0,subscribe(r){if(!X())return()=>{};let o=Ee(n,r),s={event:"consent.changed",listener:o.notify};return window.didomiEventListeners=window.didomiEventListeners||[],window.didomiEventListeners.push(s),window.didomiOnReady=window.didomiOnReady||[],window.didomiOnReady.push(i=>{o.notify(),(i??window.Didomi)?.on?.("consent.changed",o.notify)}),window.Didomi?.on?.("consent.changed",o.notify),o.stop}}}function nt(e){let t={};if(!e)return t;for(let n of String(e).split("&")){let r=n.indexOf("=");if(r<=0)continue;let o=n.slice(r+1).replace(/\+/g," ");try{o=decodeURIComponent(o)}catch{}t[n.slice(0,r)]=o}return t}function rn(e={}){let t={analytics:"C0002",personalisation:"C0003",marketing:"C0004",...e.groups},n=e.impliedConsent===!0;function r(){let i=X()?window.OnetrustActiveGroups:void 0;if(typeof i=="string"&&i.length){let d=new Set(i.split(",").map(p=>p.trim()).filter(Boolean));return p=>d.has(p)}let a=nt(U("OptanonConsent"));if(!a.groups)return null;let u={};for(let d of a.groups.split(",")){let[p,f]=d.split(":");p?.trim()&&(u[p.trim()]=f==="1"?!0:f==="0"?!1:null)}return d=>u[d]??null}function o(){if(U("OptanonAlertBoxClosed"))return!0;try{if(X()&&window.OneTrust?.IsAlertBoxClosed?.()===!0)return!0}catch{}return Number.parseInt(nt(U("OptanonConsent")).interactionCount,10)>0?!0:n&&X()&&(!!window.OneTrust||typeof window.OnetrustActiveGroups=="string")}function s(){if(!o())return null;let i=r();if(!i)return null;let a=f=>t[f]?i(t[f]):null,u=it({analytics:a("analytics"),marketing:a("marketing"),personalisation:a("personalisation")},{ad_user_data:a("ad_user_data"),ad_personalization:a("ad_personalization")}),d=nt(U("OptanonConsent"));return(typeof navigator<"u"&&navigator.globalPrivacyControl===!0||d.isGpcEnabled==="1")&&(u.marketing=!1,u.ad_user_data=!1,u.ad_personalization=!1),u}return{name:"onetrust",read:s,observesChanges:!0,subscribe(i){if(!X())return()=>{};let a=Ee(s,i),u=!1,d=()=>{u||!window.OneTrust?.OnConsentChanged||(u=!0,window.OneTrust.OnConsentChanged(a.notify))};window.addEventListener("OneTrustGroupsUpdated",a.notify);let p=window.OptanonWrapper;return window.OptanonWrapper=function(...m){try{typeof p=="function"&&p.apply(this,m)}finally{d(),a.notify()}},d(),()=>{a.stop(),window.removeEventListener("OneTrustGroupsUpdated",a.notify)}}}}function Fr(e){let t=String(e).split(",").map(n=>n.trim()).filter(Boolean);return t.length?new Set(t):null}function on(e){let{name:t,categories:n,parse:r=Fr,refusedValue:o,source:s="native",changeEvent:i="thg_hp_consent_change"}=e??{};if(typeof t!="string"||t==="")throw new Error("cookieConsent: `name` (the cookie your banner writes) is required");if(!n||typeof n.analytics!="string")throw new Error("cookieConsent: `categories.analytics` (the category id that grants analytics) is required");function a(){let u=U(t);if(u==null)return null;let d;if(o!==void 0&&u===o)d=new Set;else try{d=r(u)}catch{d=null}if(!(d instanceof Set))return null;let p=f=>n[f]?d.has(n[f]):null;return it({analytics:p("analytics"),marketing:p("marketing"),personalisation:p("personalisation")},{ad_user_data:p("ad_user_data"),ad_personalization:p("ad_personalization")})}return{name:s,read:a,get observesChanges(){return en()},subscribe(u){if(!X())return()=>{};let d=Ee(a,u),p=f=>{[...f.changed??[],...f.deleted??[]].some(b=>b?.name===t)&&d.notify()};return window.addEventListener(i,d.notify),window.cookieStore?.addEventListener?.("change",p),()=>{d.stop(),window.removeEventListener(i,d.notify),window.cookieStore?.removeEventListener?.("change",p)}}}}function sn(e={}){let t=e.name??O("consent"),n=()=>{let r=U(t);if(r==null)return null;let o=ye(r);return o.analytics==null?null:o};return{name:"forwarded",read:n,get observesChanges(){return en()},subscribe(r){if(!X())return()=>{};let o=Ee(n,r),s=i=>{[...i.changed??[],...i.deleted??[]].some(u=>u?.name===t)&&o.notify()};return window.cookieStore?.addEventListener?.("change",s),()=>{o.stop(),window.cookieStore?.removeEventListener?.("change",s)}}}}function an(e){let t=ce(e);return{name:"static",basis:ve.host,read:()=>t?{...t}:null,observesChanges:!0,subscribe:()=>()=>{}}}function cn(e){if(e==null)return null;let t=String(e).split("/").map(n=>n.trim()).filter(Boolean).join("/");return t===""?null:t}function Hr(e){let t=e.item_category;if(t!=null&&String(t).includes("/"))return cn(t);let n=[t,e.item_category2,e.item_category3,e.item_category4,e.item_category5].map(r=>r==null?"":String(r).trim()).filter(Boolean);return cn(n.join("/"))}var Kr=e=>{if(e==null||typeof e=="object")return null;let t=String(e).trim();return t===""?null:t};function Gr(...e){for(let t of e){if(typeof t=="number"&&Number.isFinite(t))return String(t);if(typeof t=="string"&&t.trim()!=="")return t.trim()}return null}var Ce=e=>Gr(e.item_group_id,e.item_id);function Wr(e,t=Ce){if(!e)return null;let n=a=>a==null||a===""||Number.isNaN(Number(a))?null:Number(a),r=a=>{let u=n(a);return u==null?null:Math.round(u*1e6)/1e6},o=a=>{let u=r(a);return u!=null&&u>=0?u:null},s=a=>{let u=n(a);return Number.isInteger(u)&&u>=0?u:null},i=Kr;return{product_id:i(t(e)),sku:i(e.item_id),name:i(e.item_name),brand:i(e.item_brand),variant:i(e.item_variant)??i(e.variant),category:Hr(e),price:r(e.price),discount:o(e.discount),quantity:s(e.quantity),coupon:i(e.coupon),position:s(e.index)!=null?s(e.index)+1:null,url:i(e.url)??i(e.item_url)??null,image_url:i(e.image_url)??i(e.image_link)??null,displayed_sku:i(e.displayed_sku)??i(e.displayed_variant_sku)}}function at(e,t=Ce){return(e??[]).map(n=>Wr(n,t)).filter(n=>n?.product_id)}var zr=1e3,Jr={shipping:2,payment:3},Xr=new Set(["password","google","apple","facebook","magic_link","sso","passkey","other"]),$=e=>{if(e==null||typeof e=="object")return null;let t=String(e).trim();return t===""?null:t},Yr=e=>{if(e==null||e===""||typeof e=="object")return null;let t=Number(e);return Number.isFinite(t)&&t>=0?t:null},Pe=e=>e!==null&&typeof e=="object";function Zr(e){return Array.isArray(e.items)?e.items.filter(Pe):null}function un(e){let t=$(e)?.toLowerCase().replace(/[\s-]+/g,"_");return t?Xr.has(t)?t:t==="email"||t==="email_password"?"password":"other":null}function Qr({ecommerce:e,items:t}){let n=s=>({promotion_id:$(s.promotion_id),name:$(s.promotion_name),creative:$(s.creative_name),position:$(s.creative_slot)}),r=new Set,o=[];for(let s of[n(e),...t.map(n)])!s.promotion_id||r.has(s.promotion_id)||(r.add(s.promotion_id),o.push(s));return o}var ln=({ecommerce:e,items:t})=>$(e.item_list_id)??$(e.item_list_name)??$(t[0]?.item_list_id)??$(t[0]?.item_list_name),se=(e,t)=>t.products?.length?{event:e,properties:t}:null,eo=new Set(["product","products","item","items"]);function to({ecommerce:e}){let t=$(e.item_id),n=$(e.content_type)?.toLowerCase();return!t||n&&!eo.has(n)?null:{event:"Product Shared",properties:{share_via:$(e.method),products:[{product_id:t}]}}}function no(e){let t=o=>se("Product List Viewed",{list_id:ln(o),products:o.products}),n=o=>{let s=$(o.ecommerce.search_term);return s?{event:"Products Searched",properties:{query:s,...o.products.length?{products:o.products}:{}}}:null},r=o=>s=>s.promotions.map(i=>({event:o,properties:i}));return{view_item:o=>se("Product Viewed",{currency:o.currency,value:o.value,products:o.products.slice(0,1)}),view_item_list:t,view_search_results:o=>n(o)??t(o),search:n,select_item:o=>se("Product Clicked",{list_id:ln(o),products:o.products.slice(0,1)}),add_to_cart:o=>se("Product Added",{cart_id:$(o.ecommerce.cart_id),products:o.products}),remove_from_cart:o=>se("Product Removed",{cart_id:$(o.ecommerce.cart_id),products:o.products}),view_cart:o=>({event:"Cart Viewed",properties:{cart_id:$(o.ecommerce.cart_id),value:o.value,currency:o.currency,coupon:$(o.ecommerce.coupon),...o.itemsListed?{products:o.products}:{}}}),begin_checkout:o=>se("Checkout Started",{value:o.value,currency:o.currency,coupon:$(o.ecommerce.coupon),products:o.products}),add_shipping_info:o=>({event:"Checkout Step Completed",properties:{step:e.shipping,step_name:"shipping",shipping_method:$(o.ecommerce.shipping_tier)}}),add_payment_info:o=>({event:"Payment Info Entered",properties:{step:e.payment,step_name:"payment",payment_method:$(o.ecommerce.payment_type)}}),view_promotion:r("Promotion Viewed"),select_promotion:r("Promotion Clicked"),add_to_wishlist:o=>se("Product Added to Wishlist",{products:o.products}),share:to,login:o=>({event:"Signed In",properties:{method:un(o.ecommerce.method)}}),sign_up:o=>({event:"Signed Up",properties:{method:un(o.ecommerce.method)}})}}var ro=e=>e.event;function oo(e,t){if(!Pe(e))return null;if(typeof e.length=="number"&&e[0]==="event"&&typeof e[1]=="string"){let r=Pe(e[2])?e[2]:{};return{name:e[1],entry:r,ecommerce:r}}let n=$(t(e));return n?{name:n,entry:e,ecommerce:Pe(e.ecommerce)?e.ecommerce:e}:null}function ct(e){return Array.isArray(e)?`[${e.map(ct).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${ct(e[t])}`).join(",")}}`:JSON.stringify(e??null)}function dn(e,t={}){if(!e||typeof e.track!="function")throw new Error("connectDataLayer: analytics must have a track(event, properties, options) method");let n=t.dataLayer??(typeof window<"u"?window.dataLayer=window.dataLayer||[]:null);if(!Array.isArray(n))return()=>{};let r=no({...Jr,...t.steps}),o=t.map??{},s=t.eventName??ro,i=t.productId??Ce,a=new Set(["purchase","refund",...t.ignore??[]]),u=t.dedupeMs??zr,d=new Map,p=!0;function f(S,y,w){if(u<=0)return!1;let L=`${S}|${w??""}|${ct(y)}`,D=Date.now();for(let[H,K]of d)D-K>u&&d.delete(H);return d.has(L)?!0:(d.set(L,D),!1)}let m=null,b=new Set;function g(S,y){if(S!=="Product Viewed")return!1;let w=typeof location>"u"?"":location.pathname;w!==m&&(m=w,b.clear());let L=y.products?.[0]?.product_id;return L?b.has(L)?!0:(b.add(L),!1):!1}function _({event:S,properties:y={}},w){if(!S||f(S,y,w)||g(S,y))return;let L=w?{context:{currency:w}}:void 0;if(S==="Signed Out"&&typeof e.signedOut=="function"){e.signedOut(L);return}e.track(S,y,L)}function x(S){let y=t.transform?t.transform(S):S,w=oo(y,s);if(!w||a.has(w.name))return;let L=Object.hasOwn(o,w.name),D=L?o[w.name]:r[w.name];if(typeof D!="function")return;let H=Zr(w.ecommerce),K=H??[],F={...w,items:K,itemsListed:H!==null,products:at(K,i),currency:Ze(w.ecommerce.currency),value:Yr(w.ecommerce.value)};F.promotions=Qr(F);let I=L?D(y,{...F,toProducts:G=>at(G,i)}):D(F);for(let G of[I].flat())G&&_(G,F.currency)}function v(S){if(p)try{x(S)}catch(y){try{t.onError?.(y,S)}catch{}}}for(let S of n.slice())v(S);let h=n.push;function E(...S){let y=h.apply(this,S);for(let w of S)v(w);return y}return n.push=E,()=>{p=!1,n.push===E&&(n.push=h)}}var so="hp_debug",io=256;function ao(){try{return new URLSearchParams(location.search).get(so)}catch{return null}}function fn(e,t){if(typeof location>"u")return!1;try{let n=ao();if(n!==null){let o=n.trim();o===""||o.length>io?sessionStorage.removeItem(t):sessionStorage.setItem(t,o)}let r=sessionStorage.getItem(t);return!!(e&&r&&Nt(r)===e)}catch{return!1}}var co=4320*60*60,uo=100,lo=1e3,fo=2e3,pn=500,Le=e=>(e?.unref?.(),e);function po(e,t){if(t?.analytics===!0&&e.analytics===!1)return"withdrawn";let n=oe.map(r=>e[r]).filter(r=>r!==null);return n.length&&n.every(Boolean)?"accept_all":n.length&&n.every(r=>!r)?"reject_all":"custom"}function mo(e,t,n=O("consent")){if(!(typeof document>"u"))try{let r=typeof location<"u"&&location.protocol==="https:"?"; Secure":"",o=t?`; Domain=${t}`:"";document.cookie=`${n}=${ot(e)}; Path=/; Max-Age=${co}; SameSite=Lax`+o+r}catch{}}function ho(e){let t=ye(U(e));return oe.some(n=>t[n]!==null)?t:null}function mn(e){let{adapter:t,timeoutMs:n=5e3,cookieDomain:r=null,cookieName:o=O("consent"),release:s,announce:i}=e,a=e.discard??(()=>{}),u="pending",d=null,p=null,f=null,m=null,b=!1,g=null,_=[],x=R=>{u=R,e.onStatus?.(R)};function v(){try{return ce(t.read())}catch{return null}}function h(){p&&clearInterval(p),f&&clearTimeout(f),p=null,f=null}let E=()=>typeof window<"u"&&typeof document<"u",S=()=>{try{return typeof t.subscribe=="function"&&t.observesChanges===!0}catch{return!1}};function y(){m&&clearInterval(m),m=null}function w(){b||m||d===null||S()||E()&&document.visibilityState==="hidden"||(m=Le(setInterval(()=>F(v()),fo)))}let L=()=>u==="pending"||u==="unknown",D=e.store??null,H=()=>{let R=_.splice(0);D?.write([]);for(let j of R)s(j)},K=()=>{let R=_.splice(0);D?.write([]);for(let j of R)a(j)};function F(R){let j=ce(R);if(!j||j.analytics===null)return;let c=d===null,l=c?ho(o):d;if(!c&&rt(l,j))return;h(),d=j,w(),mo(j,r,o);let k=!rt(l,j);if(j.analytics){x("granted"),H(),k&&i({action:po(j,l),state:j,withdrawal:!1});return}K(),l?.analytics===!0&&i({action:"withdrawn",state:j,withdrawal:!0}),x("denied")}function I(){f=null,u==="pending"&&(x("unknown"),p&&clearInterval(p),p=Le(setInterval(()=>F(v()),lo)))}function G(R){L()&&(D||R?.type!=="visibilitychange"&&!R?.persisted&&K())}let we=R=>{document.visibilityState==="hidden"?(y(),G(R)):w()},me=R=>{y(),G(R)},he=()=>w();return{get status(){return u},get state(){return d?{...d}:null},stamp(){return d?{...d,source:t.name??null,basis:t.basis??ve.visitor}:null},admits(){return u==="granted"},hold(R){return!L()||_.length>=pn?(a(R),!1):(_.push(R),D?.write(_),!0)},onHidden:G,start(){b=!1;try{g=t.subscribe?.(R=>F(R))??null}catch(R){e.debug&&console.warn("[hp] consent adapter subscribe failed",R)}E()&&(window.addEventListener("pagehide",me),window.addEventListener("pageshow",he),document.addEventListener("visibilitychange",we)),D&&_.length===0&&_.push(...D.read().slice(-pn)),F(v()),!d&&(p=Le(setInterval(()=>F(v()),uo)),f=Le(setTimeout(I,n)))},stop(){b=!0,h(),y(),typeof g=="function"&&g(),E()&&(window.removeEventListener("pagehide",me),window.removeEventListener("pageshow",he),document.removeEventListener("visibilitychange",we))}}}var go=["recommendation","search_rank","banner","offer","layout","copy","price","other"],Oe=e=>typeof e=="string"&&e!=="";function hn(e){return(Array.isArray(e?.decisions)?e.decisions:[]).filter(t=>Oe(t?.slot)&&Oe(t.decision_id)&&t.holdout!==!0).map(({decision_id:t,slot:n,arm:r,kind:o})=>({decision_id:t,slot:n,...Oe(r)&&{arm:r},...go.includes(o)&&{kind:o}}))}function gn({key:e,mayStore:t}){let n,r=()=>{if(n)return n;try{let s=JSON.parse(sessionStorage.getItem(e));n=Array.isArray(s)?s.filter(i=>Oe(i?.slot)):[]}catch{n=[]}return n},o=()=>{try{n?.length&&t()&&sessionStorage.setItem(e,JSON.stringify(n))}catch{}};return{persist:o,current:()=>r().length?n.map(s=>({...s})):null,remember(s){if(!s.length)return;let i=new Set(s.map(a=>a.slot));n=[...r().filter(a=>!i.has(a.slot)),...s],o()},forget(){n=[];try{sessionStorage.removeItem(e)}catch{}}}}function yo(e){let t=2166136261,n=String(e);for(let r=0;r<n.length;r+=1)t^=n.charCodeAt(r),t=t+((t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24))>>>0;return t^=t>>>16,t=Math.imul(t,2246822507)>>>0,t^=t>>>13,t=Math.imul(t,3266489909)>>>0,t^=t>>>16,t>>>0}function Ne(e,t,n=1e4){return yo(`${t}\0${e}`)%n}function ut(e){let t=o=>Math.min(Math.max(Number(o)||0,0),1),n=t(e.share),r=1-n;return{holdout_share:n,personalised_share:e.ramp==null?r:Math.min(t(e.ramp),r)}}function lt(e,t){if(!e||typeof e.salt!="string")return{holdout:!1,bucket:null,group:"personalised"};let n=e.ramp!=null;if(t==null||t==="")return{holdout:!1,bucket:null,group:n?"excluded":"personalised"};let{holdout_share:r,personalised_share:o}=ut(e),s=Ne(t,e.salt),i=u=>Math.round(u*1e4),a=r>0&&s<i(r)?"holdout":!n||s<i(r+o)?"personalised":"excluded";return{holdout:a==="holdout",bucket:s,group:a}}function yn(e,t){return typeof e!="string"||e===""||t==null||t===""?null:Ne(t,`bandit-vs-default:${e}`)<1e4/2?"default":"bandit"}var So="hp_preview",wo=256;function Sn(e,t=null){if(typeof location>"u")return U(e);let n=null;try{n=new URLSearchParams(location.search).get(So)}catch{n=null}if(n===null)return U(e);let r=n.trim();return r===""||r.length>wo?(Q(e,"",0,t),null):(Q(e,r,null,t),r)}var _o=40,bo=128,xo=5,Io=60,_n=e=>typeof e=="string"&&e!==""&&e.length<=bo&&!e.includes(","),wn=e=>Array.isArray(e)?e.filter(_n).slice(0,_o):[],Ao=e=>Array.isArray(e)?e.filter(t=>typeof t=="string").map(t=>t.replaceAll(","," ").trim()).filter(t=>t!=="").slice(0,xo):[],ko=e=>Object.entries(e&&typeof e=="object"?e:{}).filter(([t,n])=>_n(t)&&Number.isFinite(n)&&n>=1).sort(([,t],[,n])=>n-t).slice(0,Io).map(([t,n])=>`${t}:${Math.floor(n)}`);function dt(e){let t={},n={viewed:wn(e?.recentProducts),basket:wn(e?.basketProducts),q:Ao(e?.recentSearches),impressed:ko(e?.impressions)};for(let[r,o]of Object.entries(n))o.length>0&&(t[r]=o.join(","));return t}var vo=1800*1e3;function bn(e,t=Date.now()){try{localStorage.setItem(O("recommendationContext"),JSON.stringify({context:e,until:t+vo}))}catch{}}function xn(){try{localStorage.removeItem(O("recommendationContext"))}catch{}}var Eo="hp_sample",In=1e4;function An(e,t){return!(typeof t=="number"&&t<1)||e==null||e===""?!0:Ne(e,Eo,In)<Math.round(t*In)}function ft(e){return e.trim().toLowerCase().replace(/\s*\/\s*/g,"/").replace(/^\/+|\/+$/g,"")}var Co=2,Po=12;function Lo(e,t=Co){return e.split("/").slice(0,t).join("/")}function ue(e,t){if(typeof t!="string"||!t.trim())return null;let n=Lo(ft(t));return n?(e.unshift(n),e.length=Math.min(e.length,Po),n):null}var ie=()=>O("sessionSignals"),le=null,pt=()=>le??ie();function kn(){try{return le?localStorage:sessionStorage}catch{return}}function vn(e,t){let n=t==null?null:String(t),r=e?q(ie(),n,e):null;if(r===le)return;let o=le===null?Oo():null;if(le=r,!!le)try{let s=pt(),i=zt(ie(),n);for(let a of Object.keys(localStorage))a.startsWith(i)&&a!==s&&localStorage.removeItem(a);o&&!localStorage.getItem(s)&&(localStorage.setItem(s,JSON.stringify(o)),sessionStorage.removeItem(ie()))}catch{}}function Oo(){try{let e=sessionStorage.getItem(ie());if(!e)return null;let t=JSON.parse(e);return t&&(t.productViews>0||t.listViews>0||t.basketValue!=null||t.recentProducts?.length||t.recentBrowse?.length)?t:null}catch{return null}}function Te(){return{productViews:0,listViews:0,cartAdds:0,searches:0,recentProducts:[],recentCategories:[],recentBrowse:[],basketProducts:[],basketValue:null,recentSearches:[],impressions:{},browsingCategory:null,startedAt:0}}function mt(){let e=kn();if(!e)return Te();try{let t=e.getItem(pt());return t?{...Te(),...JSON.parse(t)}:{...Te(),startedAt:Date.now()}}catch{return{...Te(),startedAt:Date.now()}}}function ht(e){let t=kn();if(t)try{t.setItem(pt(),JSON.stringify(e))}catch{}}function En(){return mt()}function Cn(){for(let e of[()=>sessionStorage,()=>localStorage])try{let t=e();t.removeItem(ie());for(let n of Object.keys(t))n.startsWith(`${ie()}:`)&&t.removeItem(n)}catch{}}var Pn=20,Ln=200,No=new Set(["Product Viewed","Product Clicked","Product Added","Product Added to Wishlist"]),To=new Set(["Product Clicked","Product Added to Wishlist"]);function On(e,t){let n=mt();n.startedAt||(n.startedAt=Date.now());let r=Array.isArray(t?.products)?t.products:[];switch(e){case"Product Viewed":n.productViews+=1,ue(n.recentBrowse,n.browsingCategory);break;case"Product List Viewed":n.listViews+=1;{let i=t?.list_id;typeof i=="string"&&i.includes("/")&&!i.startsWith("cdp-")&&(n.browsingCategory=ue(n.recentBrowse,i)??n.browsingCategory)}break;case"Product List Filtered":{let i=t?.change,a=typeof t?.list_id=="string"?t.list_id:"",u=typeof i?.value=="string"?i.value:"";u&&i?.action!=="removed"&&(n.browsingCategory=ue(n.recentBrowse,`${a}/${u}`)??n.browsingCategory);break}case"Product Added":{n.cartAdds+=1,ue(n.recentBrowse,n.browsingCategory);for(let i of r){let a=i.product_id;typeof a!="string"||!a||(n.basketProducts=[a,...n.basketProducts.filter(u=>u!==a)].slice(0,Pn))}break}case"Product Removed":{for(let i of r){let a=i.product_id;typeof a=="string"&&a&&(n.basketProducts=n.basketProducts.filter(u=>u!==a))}break}case"Products Searched":{n.searches+=1;let i=t?.query;if(typeof i=="string"&&i.trim()){let a=i.trim().toLowerCase().slice(0,64);n.recentSearches=[a,...n.recentSearches.filter(u=>u!==a)].slice(0,5)}break}case"Product Clicked":case"Product Added to Wishlist":break;case"Cart Updated":{let i=t?.value;return n.basketValue=typeof i=="number"&&Number.isFinite(i)&&i>=0?i:null,ht(n),n}default:return n}let o=No.has(e);if(o)for(let i of r)typeof i.product_id=="string"&&i.product_id&&delete n.impressions[i.product_id];else{for(let a of r){let u=a.product_id;typeof u!="string"||!u||(n.impressions[u]=(n.impressions[u]??0)+1)}let i=Object.keys(n.impressions);if(i.length>Ln){let a={};for(let u of i.sort((d,p)=>n.impressions[p]-n.impressions[d]).slice(0,Ln))a[u]=n.impressions[u];n.impressions=a}}let s=new Set;for(let i of r){let a=i.product_id;if(o&&typeof a=="string"&&a&&(n.recentProducts=[a,...n.recentProducts.filter(u=>u!==a)].slice(0,Pn)),!To.has(e)&&typeof i.category=="string"&&i.category){let u=ft(i.category);u&&!s.has(u)&&(s.add(u),ue(n.recentCategories,u))}}return ht(n),n}var Nn=1800*1e3;function Tn({name:e,domain:t=null,mayStore:n,now:r=()=>Date.now(),mint:o=Z}){let s=null,i=0,a=()=>{s!==null&&n()&&Q(e,s,Nn/1e3,t)};return{get(){let u=r(),d=U(e);return d?s=d:(s===null||u-i>=Nn)&&(s=o()),i=u,a(),s},persist:a,forget(){Q(e,"",0,t),s=null}}}var J=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Rn=e=>Array.isArray(e)&&e.every(t=>typeof t=="string"),yt=Object.freeze({enabled:!1});function Dn(e,t){let n=e?.track;return J(n)?(Object.hasOwn(n,t)?n[t]:n.__default)?.enabled!==!1:!0}function Ro(e,t){return`${de(e)}/v1/projects/${encodeURIComponent(t)}/settings`}var Do="https://hp.thgingenuity.com";function de(e){return e==null||e===""?Do:Mn(e)}function Mn(e){if(typeof e!="string")return null;let t=e.trim().replace(/\/+$/,"");return/^https?:\/\/[^/\s]+$/i.test(t)?t:/^[a-z0-9.-]+(:\d+)?$/i.test(t)?`https://${t}`:null}function Mo(e){if(!J(e))return null;let t=Object.fromEntries(Object.entries(e).filter(([,n])=>typeof n=="boolean"));return Object.keys(t).length?t:null}var gt=e=>typeof e=="number"&&e>=0&&e<=1,Uo=/^[0-9a-f]{64}$/;function $o(e){return J(e)?{enabled:e.enabled===!0,tokenSha256:typeof e.tokenSha256=="string"&&Uo.test(e.tokenSha256)?e.tokenSha256:null}:{enabled:!1,tokenSha256:null}}function Vo(e){if(!J(e))return null;let t=Object.fromEntries(Object.entries(e).filter(([,n])=>J(n)&&gt(n.share)&&(n.ramp==null||gt(n.ramp))&&typeof n.salt=="string"&&n.salt!=="").map(([n,r])=>[n,{share:r.share,ramp:r.ramp??null,salt:r.salt,...Rn(r.readoutSlots)&&r.readoutSlots.length>0&&{readoutSlots:r.readoutSlots}}]));return Object.keys(t).length?t:null}var jo=/^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,128}$/;function Bo(e){return J(e)?e.source==="hp_cookie"?{source:"hp_cookie"}:e.source!=="site_cookie"?null:e.cookie==null?{source:"site_cookie",cookie:null}:typeof e.cookie=="string"&&jo.test(e.cookie)?{source:"site_cookie",cookie:e.cookie}:null:null}function St(e){let t=J(e)?e.hp:null;return!J(t)||t.enabled!==!0?yt:{enabled:!0,propertyId:typeof t.propertyId=="string"&&t.propertyId!==""?t.propertyId:null,environment:typeof t.environment=="string"?t.environment:null,consentPurposes:Mo(t.consent?.purposes),queryParamAllowlist:Rn(t.queryParamAllowlist)?t.queryParamAllowlist:null,anonymousId:Bo(t.anonymousId),apiHost:Mn(t.apiHost),holdouts:Vo(t.holdouts),sampleRate:gt(t.sampleRate)?t.sampleRate:1,debug:$o(t.debug),plan:J(e.plan)&&J(e.plan.track)?{track:e.plan.track}:null}}async function Un(e,t,n=globalThis.fetch){try{let r=await n(Ro(e,t),{credentials:"omit",headers:{accept:"application/json"}});return r.ok?St(await r.json()):null}catch{return null}}var Re=e=>q(O("settings"),e);function $n(e){try{let t=localStorage.getItem(Re(e));if(!t)return null;let n=JSON.parse(t);return n?.enabled===!0?n:null}catch{return null}}function Vn(e,t){try{t?.enabled===!0?localStorage.setItem(Re(e),JSON.stringify(t)):localStorage.removeItem(Re(e))}catch{}}function wt(e){try{localStorage.removeItem(Re(e))}catch{}}var qo={flushAt:20,flushIntervalMs:1e4,maxQueue:500,maxRetryAgeMs:1440*60*1e3,maxRetries:5,maxPersistedBatches:20,maxPersistedBytes:1024*1024,storageKey:re.retry,beaconSafeBytes:56*1024,maxBatchEvents:100,maxBatchBytes:500*1024},_t=()=>Date.now(),fe=e=>typeof TextEncoder<"u"?new TextEncoder().encode(e).length:Buffer.byteLength(e),bt=()=>typeof window<"u"&&typeof document<"u";function Fo(){try{return!bt()||!window.localStorage?null:(window.localStorage.getItem("thg_hp_probe"),window.localStorage)}catch{return null}}var A,xt,jn,De,It,At,Bn,Se,kt,Me,qn,vt,Fn,Ue,$e=class{constructor(t){Lt(this,A);this.o={...qo,...t},this.queue=[],this.timer=null,this.storage=Fo(),this.inflight=null,this.sending=new Set,this.replayAllowed=!this.o.deferReplay,this.draining=null,bt()&&(window.addEventListener("pagehide",()=>this.flush({beacon:!0})),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()}),N(this,A,Ue).call(this))}replayPersisted(){return this.replayAllowed=!0,N(this,A,Ue).call(this)}clearPersisted(){if(this.replayAllowed=!1,!!this.storage)try{this.storage.removeItem(this.o.storageKey)}catch{}}enqueue(t){let n;try{n=JSON.parse(JSON.stringify(t))}catch(r){this.o.onError?.(`event ${t?.messageId??""} cannot be serialised; dropped`,r);return}this.queue.push(n),this.queue.length>this.o.maxQueue&&this.queue.splice(0,this.queue.length-this.o.maxQueue),this.queue.length>=this.o.flushAt?this.flush():N(this,A,xt).call(this)}flush(t={}){if(N(this,A,jn).call(this),this.queue.length===0)return Promise.resolve();if(t.beacon&&bt()&&typeof navigator<"u"&&navigator.sendBeacon)return N(this,A,Bn).call(this),Promise.resolve();if(this.inflight)return this.inflight;let n=N(this,A,At).call(this,this.queue,this.o.maxBatchEvents,this.o.maxBatchBytes).map(r=>{let o=N(this,A,vt).call(this,r);return o&&this.sending.add(o),{batch:r,id:o}});return this.queue=[],this.inflight=(async()=>{for(let{batch:r,id:o}of n)await N(this,A,Se).call(this,r,0,o)})().finally(()=>{this.inflight=null,this.queue.length>=this.o.flushAt?this.flush():this.queue.length&&N(this,A,xt).call(this)}),this.inflight}};A=new WeakSet,xt=function(){this.timer||(this.timer=setTimeout(()=>this.flush(),this.o.flushIntervalMs),this.timer?.unref?.())},jn=function(){this.timer&&(clearTimeout(this.timer),this.timer=null)},De=function(t){return JSON.stringify({batch:t,writeKey:this.o.writeKey,sentAt:new Date().toISOString()})},It=function(){return`${this.o.apiHost.replace(/\/$/,"")}/v1/batch`},At=function(t,n,r){let o=fe(N(this,A,De).call(this,[])),s=[],i=[],a=o;for(let u of t){let d=fe(JSON.stringify(u))+1;i.length&&(i.length>=n||a+d>r)&&(s.push(i),i=[],a=o),i.push(u),a+=d}return i.length&&s.push(i),s},Bn=function(){let t=this.queue;this.queue=[];let n=N(this,A,It).call(this);for(let r of N(this,A,At).call(this,t,this.o.maxBatchEvents,this.o.beaconSafeBytes)){let o=N(this,A,De).call(this,r);if(fe(o)<=this.o.beaconSafeBytes){let s=!1;try{s=navigator.sendBeacon(n,new Blob([o],{type:"text/plain"}))}catch{s=!1}if(s)continue}N(this,A,Se).call(this,r,0,N(this,A,vt).call(this,r))}},Se=async function(t,n,r=null){r&&this.sending.add(r);let o=N(this,A,De).call(this,t);try{let s=await fetch(N(this,A,It).call(this),{method:"POST",headers:{"Content-Type":"text/plain"},body:o,keepalive:fe(o)<=this.o.beaconSafeBytes});if(s.ok)return N(this,A,kt).call(this,r),N(this,A,Ue).call(this),!0;if(!(s.status===429||s.status>=500))return N(this,A,kt).call(this,r),this.o.onError?.(`CDP ingest rejected batch (${s.status}); dropping ${t.length} events`),!0;throw new Error(`retryable ${s.status}`)}catch(s){if(n>=this.o.maxRetries){r&&this.sending.delete(r);let a=r?"kept for the next page":"dropped";return this.o.onError?.(`CDP ingest unreachable after ${n} attempts; ${a}: ${t.length} events`,s),!1}let i=Math.min(1e3*2**n,3e4)*(.5+Math.random());return await new Promise(a=>setTimeout(a,i)),N(this,A,Se).call(this,t,n+1,r)}},kt=function(t){t&&(this.sending.delete(t),N(this,A,Fn).call(this,t))},Me=function(){let t=JSON.parse(this.storage.getItem(this.o.storageKey)||"[]");return Array.isArray(t)?t:[]},qn=function(t,n){let r=i=>fe(JSON.stringify(i))+1,o=[...t,n].slice(-this.o.maxPersistedBatches),s=o.reduce((i,a)=>i+r(a),2);for(;o.length>1&&s>this.o.maxPersistedBytes;)s-=r(o[0]),o=o.slice(1);for(;o.length;)try{return this.storage.setItem(this.o.storageKey,JSON.stringify(o)),!0}catch{o=o.slice(1)}return!1},vt=function(t){if(!this.storage||this.o.mayPersist&&!this.o.mayPersist())return null;try{let n={at:_t(),id:`${_t()}-${Math.random().toString(36).slice(2)}`,batch:t};return fe(JSON.stringify(n))+3>this.o.maxPersistedBytes?null:N(this,A,qn).call(this,N(this,A,Me).call(this),n)?n.id:null}catch{return null}},Fn=function(t){if(!(!t||!this.storage))try{let n=N(this,A,Me).call(this).filter(r=>r.id!==t);n.length?this.storage.setItem(this.o.storageKey,JSON.stringify(n)):this.storage.removeItem(this.o.storageKey)}catch{}},Ue=function(){if(!this.storage||!this.replayAllowed)return this.draining??Promise.resolve();if(this.draining)return this.draining;let t;try{let r=N(this,A,Me).call(this);t=r.filter(o=>Array.isArray(o?.batch)&&_t()-o.at<this.o.maxRetryAgeMs).map(o=>o.id?o:{...o,id:`${o.at}-${Math.random().toString(36).slice(2)}`}),(t.length!==r.length||t.some((o,s)=>o!==r[s]))&&(t.length?this.storage.setItem(this.o.storageKey,JSON.stringify(t)):this.storage.removeItem(this.o.storageKey))}catch{return Promise.resolve()}let n=t.filter(r=>!this.sending.has(r.id));return n.length?(this.draining=(async()=>{for(let r of n)if(!this.replayAllowed||!await N(this,A,Se).call(this,r.batch,0,r.id))return})().finally(()=>{this.draining=null}),this.draining):Promise.resolve()};var Hn="3.18.0",Kn="@thg-agentic/hyper-personalisation-web";var Ho=new Set(["audiences","decisions","recommendations"]);var Ko=Symbol.for("@thg-agentic/hyper-personalisation-web.replay"),Ve=class extends Error{};function Go(e){let t={...e??{}},n=Array.isArray(t.products)?t.products:null;if(t.item_count==null&&n){let r=n.map(o=>o?.quantity);r.every(Number.isInteger)&&(t.item_count=r.reduce((o,s)=>o+s,0))}return t}var Wo="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";function zo(e){if(/^[0-9a-fA-F]{64}$/.test(e))return e.toLowerCase();if(!/^[A-Za-z0-9+/_-]{43}=?$/.test(e))return null;try{let t=atob(e.replace(/-/g,"+").replace(/_/g,"/").padEnd(44,"="));return t.length!==32?null:Array.from(t,n=>n.charCodeAt(0).toString(16).padStart(2,"0")).join("")}catch{return null}}function je(e){if(typeof e!="string")return null;let t=zo(e.trim());return t===Wo?null:t}var Gn=!1;function Jo(e){!e||Gn||(Gn=!0,console.warn("[hp] mintAnonymousId is ignored since 3.6: where device ids come from is set per source in the console (the site\u2019s own cookie, or Hyper-Personalisation\u2019s)."))}function Wn(e){return e&&typeof e.context=="object"&&e.context!==null?e.context:e}function zn(e){let t=de(e?.apiHost);if(!t)throw new Error("createAnalytics: apiHost, when given, must be a host or an http(s) origin");if(!e?.propertyId)throw new Error("createAnalytics: propertyId is required");let n={libraryName:Kn,libraryVersion:Hn,writeKey:"web",queryParamBlocklist:[],debug:!1,validate:null,consentTimeoutMs:5e3,...e,apiHost:t};Ie(n.storageNames),Ae();let r=!!n.consent,o=null;n.getConsent=()=>o?o.stamp():Zt();let s=()=>!r||o?.status==="granted",i=r&&n.holdAcrossPages?Jt(q(O("held"),n.propertyId)):null,a=i?.read()??{items:[],anonymousId:null};n.mintAnonymousId!==void 0&&Jo(n.debug);let u=Xt(n,{carried:a.anonymousId,mayStore:s,orOwn:!0}),d=Tn({name:O("session"),domain:n.consentCookieDomain??null,mayStore:s}),p=n.getSessionId;n.getSessionId=()=>B(M(p,null))??d.get();let f=Sn(O("preview"),n.consentCookieDomain??null),m=n.transport??new $e({apiHost:n.apiHost,writeKey:n.writeKey,storageKey:q(O("retry"),n.propertyId),mayPersist:r?()=>o?.status==="granted":void 0,deferReplay:r,onError:(c,l)=>n.debug&&console.warn("[hp]",c,l??"")}),b=q(O("previousPage"),n.propertyId),g=null,_=()=>{if(g)return g;try{return sessionStorage.getItem(b)||null}catch{return null}},x=c=>{if(g=c||null,!(r&&o.status!=="granted"))try{sessionStorage.setItem(b,c)}catch{}},v=gn({key:q(O("decisions"),n.propertyId),mayStore:s});n.getDecisions??(n.getDecisions=v.current);let h=B(M(n.getUserId,null)),E=null;function S(c,l,k=!1){try{return y(c,l,k)}catch(P){if(P instanceof Ve)throw P;return console.warn("[hp] the event was not sent",P),null}}function y(c,l,k){let P=E;E=null;let C={messageId:P?.messageId??Z(),timestamp:P?.timestamp??new Date().toISOString(),anonymousId:B(M(n.getAnonymousId,null)),userId:h??null,...c,context:Ht(n,Wn(l))};if(C.properties&&(C.properties=Xe(C.properties)),!k&&!An(C.anonymousId,n.sampleRate))return n.debug&&console.debug("[hp] not sent: outside the sample",C),null;if(n.beforeSend){let W;try{W=n.beforeSend(C)}catch(sr){return console.warn("[hp] beforeSend failed; the event was not sent",sr),null}if(!W)return null;let{type:_e,event:Ct,messageId:rr,timestamp:or}=C;C={...W,type:_e,messageId:rr,timestamp:or,...Ct?{event:Ct}:{}}}if(n.validate){let W=n.validate(C,{producer:"web"});if(!W.valid){let _e=`[hp] invalid ${C.event??C.type}:
2
- ${W.errors.join(`
3
- `)}`;if(n.throwOnInvalid)throw new Ve(_e);console.error(_e)}}return n.debug&&console.debug("[hp]",C.event??C.type,C),F(C,k),C}function w(c){if(c.type==="track"&&K(c),n.onEvent)try{n.onEvent(c)}catch(l){console.warn("[hp] onEvent observer failed",l)}}let L=()=>r&&o?.state?.personalisation===!0,D=()=>vn(M(n.getSessionId,null),n.propertyId),H=!1;function K(c){try{L()?(D(),bn(dt(On(c.event,c.properties))),H=!0):(xn(),H&&Cn(),H=!1)}catch(l){n.debug&&console.warn("[hp] the visit was not recorded",l)}}function F(c,l){!r||l||o.admits()?(w(c),m.enqueue(c)):o.hold(c)}let I=(c,l,k)=>Dn(n.plan,c)?S({type:"track",event:c,properties:l??{}},k):null,G=new Set,we=(c,l,k)=>c==="audiences"?{audiences:l?null:k.audiences??[]}:c==="decisions"?{decisions:(k.decisions??[]).map(P=>({slot:P.slot,decision_id:P.decision_id??null,arm:P.arm??null,...P.variant&&{variant:P.variant}}))}:{recommendation_request_id:k.recommendation_request_id??null};r&&(o=mn({adapter:n.consent,timeoutMs:n.consentTimeoutMs,cookieDomain:n.consentCookieDomain,debug:n.debug,store:i&&{read:()=>a.items,write:c=>i.write(c,u?.peek()??null)},release(c){c.context&&(c.context.consent=o.stamp()),w(c),m.enqueue(c)},onStatus(c){if(c==="granted"&&(u?.persist(),d.persist(),g&&x(g),v.persist(),m.replayPersisted?.()),c==="denied"){m.clearPersisted?.(),u?.forget(),d.forget();try{sessionStorage.removeItem(b)}catch{}v.forget()}try{n.onConsentStatus?.(c)}catch(l){console.warn("[hp] onConsentStatus observer failed",l)}},announce({action:c,state:l,withdrawal:k}){S({type:"track",event:"Consent Updated",properties:{action:c,state:l}},void 0,k),k&&m.flush()}}),o.start());let me=()=>n.holdout?ut(n.holdout):null,he=()=>lt(n.holdout,B(M(n.getAnonymousId,null))).group;function R(c,l){if(!Array.isArray(c?.decisions))return c;let k=n.holdout?.readoutSlots??[],P=B(M(n.getAnonymousId,null));return{...c,decisions:c.decisions.map(C=>{let W=C?.variant==="bandit"||C?.variant==="default"?C.variant:!l&&C?.holdout!==!0&&k.includes(C?.slot)?yn(C.slot,P):null;return W?W==="default"?{...C,variant:W,decision_id:null}:{...C,variant:W}:C})}}function j(c,l){if(f)return null;let k=c?.exposure,P=k?.holdout===!0,C=R(c,P);return v.remember(hn(C)),!k?.token||G.has(k.token)?null:(G.add(k.token),I("Personalisation Exposed",{exposure_token:k.token,surface:k.surface,holdout:P,holdout_share:k.holdout_share??me()?.holdout_share??null,personalised_share:k.personalised_share??me()?.personalised_share??null,...we(k.surface,P,C)},l))}return{track:I,page(c={},l){let k=n.pageType??null;c.type&&(ae.has(c.type)?(k=c.type,n.pageType=c.type):n.debug&&console.warn(`[hp] ignoring unknown page.type "${c.type}". Allowed: ${[...ae].join(", ")}`));let P=S({type:"page",properties:{name:c.name??null,navigation_trigger:c.navigation_trigger??Vt(),previous_message_id:_()}},{page:{type:k},...Wn(l)??{}});return P&&x(P.messageId),P},identify(c,l){let{userId:k,...P}=c&&typeof c=="object"?c:{};if(k!==void 0&&(h=B(k)),typeof P.email_sha256=="string"){let C=je(P.email_sha256);C?P.email_sha256=C:delete P.email_sha256}return S({type:"identify",traits:P},l)},reset(){h=null,x("")},productViewed:(c,l)=>I("Product Viewed",c,l),productListViewed:(c,l)=>I("Product List Viewed",c,l),recommendationsViewed:(c,l)=>I("Recommendations Viewed",c,l),personalisationExposed:(c,l)=>I("Personalisation Exposed",c,l),recordExposure:j,surfaceViewed({surface:c}={},l){if(!Ho.has(c)||!n.holdout||c!=="decisions"&&!L())return null;let k=B(M(n.getAnonymousId,null)),P=lt(n.holdout,k).group;if(P==="excluded")return null;let C=Mt({sessionId:be(M(n.getSessionId,null),k),surface:c});return C?j({exposure:{token:C,surface:c,holdout:P==="holdout"}},l):null},inHoldout:()=>he()!=="personalised",personalisationGroup:he,productClicked:(c,l)=>I("Product Clicked",c,l),productListFiltered:(c,l)=>I("Product List Filtered",c,l),productsSearched:(c,l)=>I("Products Searched",c,l),productAdded:(c,l)=>I("Product Added",c,l),productRemoved:(c,l)=>I("Product Removed",c,l),cartUpdated:(c,l)=>I("Cart Updated",Go(c),l),identityLinked:(c,l)=>I("Identity Linked",c,l),slotEngaged:(c,l)=>f?null:I("Slot Engaged",c,l),productVariantSelected:(c,l)=>I("Product Variant Selected",c,l),cartViewed:(c,l)=>I("Cart Viewed",c,l),productAddedToWishlist:(c,l)=>I("Product Added to Wishlist",c,l),productRemovedFromWishlist:(c,l)=>I("Product Removed from Wishlist",c,l),wishlistProductAddedToCart:(c,l)=>I("Wishlist Product Added to Cart",c,l),productShared:(c,l)=>I("Product Shared",c,l),cartShared:(c,l)=>I("Cart Shared",c,l),couponEntered:(c,l)=>I("Coupon Entered",c,l),couponApplied:(c,l)=>I("Coupon Applied",c,l),couponDenied:(c,l)=>I("Coupon Denied",c,l),couponRemoved:(c,l)=>I("Coupon Removed",c,l),checkoutStarted:(c,l)=>I("Checkout Started",c,l),promotionViewed:(c,l)=>I("Promotion Viewed",c,l),promotionClicked:(c,l)=>I("Promotion Clicked",c,l),checkoutStepViewed:(c,l)=>I("Checkout Step Viewed",c,l),checkoutStepCompleted:(c,l)=>I("Checkout Step Completed",c,l),paymentInfoEntered:(c,l)=>I("Payment Info Entered",c,l),checkoutFailed:(c,l)=>I("Checkout Failed",c,l),signedIn:(c,l)=>I("Signed In",c,l),signedUp:(c,l)=>I("Signed Up",c,l),signedOut(c){let l=I("Signed Out",{},c);return h=null,l},newsletterSubscribed:(c,l)=>I("Newsletter Subscribed",c,l),newsletterUnsubscribed:(c,l)=>I("Newsletter Unsubscribed",c,l),backInStockSubscribed:(c,l)=>I("Back In Stock Subscribed",c,l),productReviewed:(c,l)=>I("Product Reviewed",c,l),consentUpdated:(c,l)=>I("Consent Updated",c,l),flush:c=>m.flush(c),recommendationContext(){if(!L())return{};try{return D(),dt(En())}catch{return{}}},get consent(){return o?o.state:null},servingHeaders:()=>tn(o?.state??null),get lastPageMessageId(){return _()},get anonymousId(){return B(M(n.getAnonymousId,null))},[Ko](c,l,k){if(typeof this[c]=="function"){E=typeof k?.messageId=="string"&&typeof k?.timestamp=="string"?{messageId:k.messageId,timestamp:k.timestamp}:null;try{this[c](...Array.isArray(l)?l:[])}finally{E=null}}}}}var Et=Symbol.for("@thg-agentic/hyper-personalisation-web.bootstrap"),Xo=Symbol.for("@thg-agentic/hyper-personalisation-web.drain"),Yo=Symbol.for("@thg-agentic/hyper-personalisation-web.carry"),Jn=Symbol.for("@thg-agentic/hyper-personalisation-web.replay"),Zo=Symbol.for("@thg-agentic/hyper-personalisation-web.assignment"),Qo=Symbol.for("@thg-agentic/hyper-personalisation-web.recommendation-context"),Fe=e=>e?()=>U(e):()=>null,Zn={didomi:e=>nn({purposes:e.purposes}),onetrust:e=>rn({groups:e.groups,impliedConsent:e.impliedConsent}),cookie:e=>on({name:e.name,categories:e.categories,refusedValue:e.refusedValue,source:e.source,changeEvent:e.changeEvent}),forwarded:e=>sn({name:e.name}),static:e=>{if(!e.state||typeof e.state!="object")throw new Error('consent.state is required for provider "static"');return an(e.state)}},Xn=Object.keys(Zn),pe=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Be=e=>typeof e=="string"&&e!=="",es=e=>Array.isArray(e)&&e.every(t=>typeof t=="string"),Qn=e=>pe(e)&&typeof e.read=="function";function ts(e,t={}){let n=[];if(!pe(e))return["config must be { writeKey, apiHost? }"];Be(e.writeKey)||n.push("writeKey is required"),de(e.apiHost)||n.push("apiHost, when given, must be a host or an http(s) origin"),t.propertyId!==void 0&&!Be(t.propertyId)&&n.push("propertyId must be a string");let{consent:r}=t;if(!Qn(r)&&!(pe(r)&&Xn.includes(r.provider))&&n.push(`consent must be a consent adapter or { provider: ${Xn.join(" | ")} }`),t.autocapture!==void 0&&t.autocapture!==!1&&!pe(t.autocapture)&&n.push("autocapture must be false or { promotions, productLists, recommendations }"),t.beforeSend!==void 0&&typeof t.beforeSend!="function"&&n.push("beforeSend must be a function"),t.identityCookies!==void 0)if(!pe(t.identityCookies))n.push("identityCookies must be an object");else for(let o of["anonymousId","sessionId"]){let s=t.identityCookies[o];s!==void 0&&!Be(s)&&n.push(`identityCookies.${o} must be a cookie name`)}for(let o of["getCurrency","getShipToCountry","getLocale"])t[o]!==void 0&&typeof t[o]!="function"&&n.push(`${o} must be a function`);return t.dataLayer!==void 0&&(pe(t.dataLayer)?(t.dataLayer.name!==void 0&&!Be(t.dataLayer.name)&&n.push("dataLayer.name must be a string"),t.dataLayer.ignore!==void 0&&!es(t.dataLayer.ignore)&&n.push("dataLayer.ignore must be an array of strings")):n.push("dataLayer must be an object")),n}function ns(e,t,n){if(Qn(e))return st(e,t.consentPurposes);let{provider:r,...o}=e,s=n?.consent?.[r]??Zn[r];return st(s(o),t.consentPurposes)}var er=(e,t)=>`${e??""}|${t??""}`;function rs(e,t){let n=B(t?.userId),r=je(t?.emailSha256);if(!n&&!r)return null;try{let o=q(O("identified"),e);if(sessionStorage.getItem(o)===er(n,r))return null}catch{}return{...n?{userId:n}:{},...r?{email_sha256:r}:{}}}async function os({writeKey:e,apiHost:t},n){if(n.settings!==void 0)return{settings:St(n.settings),revalidated:null};let r=Un(t,e),o=$n(e);if(o)return{settings:o,revalidated:r};let s=await r,i=s??yt;return{settings:i,revalidated:Promise.resolve(i),unreachable:s===null}}function tr(e,t={}){let n=typeof window<"u"?window:null;if(n?.[Et])return n[Et];let r=Yt(e?.writeKey,()=>{let{analytics:s}=ye(U(O("consent")));return s===!0||s===null&&t.holdAcrossPages===!0}),o=is(e,t,r);if(n){let s=t.extend?.getSessionId??Fe(t.identityCookies?.sessionId),i=Fe(t.identityCookies?.anonymousId);n[Zo]=({slot:a,arm:u}={})=>Dt({sessionId:be(s(),i()),slot:a,arm:u}),n[Et]=o,n[Yo]=r,n[Xo]?.(o)}return o}function ss(){let e=globalThis.performance?.now?.();return new Date(Date.now()-(Number.isFinite(e)?e:0)).toISOString()}function Yn(e,t){if(t===!1)return()=>{};let n={promotions:!0,productLists:!0,recommendations:!0,exposures:!0,...t},r=s=>s&&typeof s=="object"?s:{},o=[];try{n.promotions&&o.push(We(e,r(n.promotions))),n.productLists&&o.push(Ge(e,r(n.productLists))),n.recommendations&&o.push(ze(e,r(n.recommendations))),n.exposures&&o.push(Ke(e,r(n.exposures)))}catch(s){console.warn("[hp] autocapture not started:",s?.message??s)}return()=>{for(let s of o)s()}}function qe(e){return e.close(),null}async function is(e,t,n){try{let r=ts(e,t);if(r.length)return console.warn(`[hp] not started, the configuration is invalid: ${r.join("; ")}`),qe(n);Ie(t.storageNames),Ae();let{writeKey:o}=e,[{settings:s,revalidated:i,unreachable:a},u]=await Promise.all([os(e,t),Promise.resolve(t.identity).catch(()=>null)]),d=i?s:null,p=!1,f=()=>p&&d&&Vn(o,d);if(i?.then(y=>{y&&(d=y,y.enabled?f():wt(o))}),a)return null;if(!s.enabled)return t.debug&&console.info("[hp] off: the settings for this write key do not enable it"),qe(n);let m=s.propertyId??t.propertyId;if(!m)return console.warn("[hp] not started: this write key covers several properties, so propertyId is required"),qe(n);let{preset:b}=t,g=rs(m,u),_=t.debug===!0||s.debug?.enabled===!0||fn(s.debug?.tokenSha256??null,O("debug")),x=B(u?.userId),v=zn({apiHost:s.apiHost??de(e.apiHost),writeKey:o,propertyId:m,pageType:ae.has(t.pageType)?t.pageType:void 0,environment:s.environment,queryParamAllowlist:s.queryParamAllowlist??void 0,plan:s.plan,sampleRate:s.sampleRate??1,holdout:s.holdouts?.[m],getCurrency:t.getCurrency,getShipToCountry:t.getShipToCountry,getLocale:t.getLocale,consent:ns(t.consent,s,b),consentCookieDomain:t.consentCookieDomain,storageNames:t.storageNames,holdAcrossPages:t.holdAcrossPages===!0,mintAnonymousId:t.mintAnonymousId,debug:_,anonymousIdSource:s.anonymousId??void 0,getAnonymousId:Fe(t.identityCookies?.anonymousId),getSessionId:t.identityCookies?.sessionId?Fe(t.identityCookies.sessionId):void 0,getUserId:()=>x,beforeSend:t.beforeSend,...t.extend,onConsentStatus(y){if(y==="granted"&&(p=!0,f()),y==="denied"){wt(o);try{sessionStorage.removeItem(q(O("identified"),m))}catch{}}t.extend?.onConsentStatus?.(y)},onEvent(y){if(y.type==="identify"){let w=er(B(y.userId),je(y.traits?.email_sha256));try{sessionStorage.setItem(q(O("identified"),m),w)}catch{}}t.extend?.onEvent?.(y)}});typeof window<"u"&&(window[Qo]=()=>v.recommendationContext());for(let[y,w,L]of n.take())try{v[Jn](y,w,L)}catch(D){console.warn(`[hp] carried ${y} failed`,D)}v[Jn]("page",[t.pageType?{type:t.pageType}:{}],{messageId:Z(),timestamp:ss()});let h=Yn(v,t.autocapture),E=v.page;v.page=(...y)=>{let w=E(...y);return h(),h=Yn(v,t.autocapture),w};let S=typeof window<"u"?window:null;if(t.dataLayer&&S){let y=t.dataLayer.name??"dataLayer";S[y]=S[y]||[],dn(v,{...b,dataLayer:S[y],ignore:[...b?.ignore??[],...t.dataLayer.ignore??[]],onError:w=>_&&console.warn("[hp] dataLayer mapping failed",w)})}return g&&v.identify(g),v}catch(r){return console.warn("[hp] not started:",r?.message??r),qe(n)}}function nr(e){e.initialized=!0,tr(e._config,e._options).then(t=>{let n=e.splice(0);if(!t){for(let r of e.methods??[])e[r]=()=>e;return}e.client=t;for(let[r,o]of Object.entries(t))typeof o=="function"&&(e[r]=o);for(let[r,...o]of n)try{t[r]?.(...o)}catch(s){console.warn(`[hp] queued ${r} failed`,s)}})}var V=window.hp=window.hp||[];V.servingHeaders??(V.servingHeaders=()=>({}));V.inHoldout??(V.inHoldout=()=>!0);V.personalisationGroup??(V.personalisationGroup=()=>"excluded");V.recommendationContext??(V.recommendationContext=()=>({}));V.initialized||(V._config?nr(V):V.load=(e,t)=>{V._config=e,V._options=t??{},nr(V)});})();
1
+ (()=>{var $t=e=>{throw TypeError(e)};var Sr=(e,t,n)=>t.has(e)||$t("Cannot "+n);var Ut=(e,t,n)=>t.has(e)?$t("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,n);var N=(e,t,n)=>(Sr(e,t,"access private method"),n);var we=[];for(let e=2;we.length<64;e++)we.every(t=>e%t!==0)&&we.push(e);var Vt=e=>(e-Math.floor(e))*2**32>>>0,wr=we.slice(0,8).map(e=>Vt(Math.sqrt(e))),_r=we.map(e=>Vt(Math.cbrt(e))),z=(e,t)=>e>>>t|e<<32-t;function Bt(e){let t=new TextEncoder().encode(String(e)),n=new Uint32Array((t.length+8>>6)*16+16);t.forEach((i,s)=>{n[s>>2]|=i<<24-s%4*8}),n[t.length>>2]|=128<<24-t.length%4*8,n[n.length-2]=Math.floor(t.length/2**29),n[n.length-1]=t.length*8;let r=Uint32Array.from(wr),o=new Uint32Array(64);for(let i=0;i<n.length;i+=16){for(let S=0;S<64;S++){if(S<16){o[S]=n[i+S];continue}let b=o[S-15],I=o[S-2],k=z(b,7)^z(b,18)^b>>>3,h=z(I,17)^z(I,19)^I>>>10;o[S]=o[S-16]+k+o[S-7]+h}let[s,a,u,d,p,f,y,x]=r;for(let S=0;S<64;S++){let b=z(p,6)^z(p,11)^z(p,25),I=x+b+(p&f^~p&y)+_r[S]+o[S]|0,h=(z(s,2)^z(s,13)^z(s,22))+(s&a^s&u^a&u)|0;[x,y,f,p,d,u,a,s]=[y,f,p,d+I|0,u,a,s,I+h|0]}[s,a,u,d,p,f,y,x].forEach((S,b)=>{r[b]+=S})}return Array.from(r,i=>i.toString(16).padStart(8,"0")).join("")}var Ye=(e,t)=>e<<t|e>>>32-t;function jt(e){let t=new Uint32Array((e.length+8>>6)*16+16);e.forEach((i,s)=>{t[s>>2]|=i<<24-s%4*8}),t[e.length>>2]|=128<<24-e.length%4*8,t[t.length-2]=Math.floor(e.length/2**29),t[t.length-1]=e.length*8;let n=Uint32Array.of(1732584193,4023233417,2562383102,271733878,3285377520),r=new Uint32Array(80);for(let i=0;i<t.length;i+=16){for(let f=0;f<80;f++)r[f]=f<16?t[i+f]:Ye(r[f-3]^r[f-8]^r[f-14]^r[f-16],1);let[s,a,u,d,p]=n;for(let f=0;f<80;f++){let y,x;f<20?(y=a&u|~a&d,x=1518500249):f<40?(y=a^u^d,x=1859775393):f<60?(y=a&u|a&d|u&d,x=2400959708):(y=a^u^d,x=3395469782);let S=Ye(s,5)+y+p+x+r[f]|0;[p,d,u,a,s]=[d,u,Ye(a,30),s,S]}[s,a,u,d,p].forEach((f,y)=>{n[y]+=f})}let o=new Uint8Array(20);return n.forEach((i,s)=>{o[s*4]=i>>>24,o[s*4+1]=i>>>16,o[s*4+2]=i>>>8,o[s*4+3]=i}),o}var br="e28e8ada-5b76-4209-bba9-79b823a0af75",xr=e=>Array.from(e,t=>t.toString(16).padStart(2,"0")).join("");function qt(e,t){let n=Uint8Array.from(e.replace(/-/g,"").match(/../g),a=>Number.parseInt(a,16)),r=new TextEncoder().encode(t),o=new Uint8Array(16+r.length);o.set(n),o.set(r,16);let i=jt(o).slice(0,16);i[6]=i[6]&15|80,i[8]=i[8]&63|128;let s=xr(i);return`${s.slice(0,8)}-${s.slice(8,12)}-${s.slice(12,16)}-${s.slice(16,20)}-${s.slice(20)}`}var re=(e,t)=>typeof e=="string"&&e!==""&&e.length<=t?e:null;function ke(e,t){return re(e,64)??re(t,256)}function Ft({sessionId:e,slot:t,arm:n}={}){let r=re(e,256);return!r||!re(t,256)||!re(n,256)?null:qt(br,`${r}:${t}:${n}`)}var Ir="4b1f2c7e-9a3d-4e58-b6c1-2d7f0a9e8c53";function Ht({sessionId:e,surface:t}={}){let n=re(e,256);return!n||!re(t,64)?null:qt(Ir,`${n}:${t}`)}var Ar=Array.from({length:11},(e,t)=>t/10);function vr(e,t){if(!e.isIntersecting)return!1;let{intersectionRect:n,boundingClientRect:r,rootBounds:o}=e;if(!n||!r)return!0;let i=n.width*n.height,s=r.width*r.height,a=o?Math.min(s,o.width*o.height):s;return a>0&&i/a>=t}function X(e){let{selector:t,onViewable:n,visibleRatio:r=.5,dwellMs:o=1e3,root:i=document}=e,s=new Map,a=new WeakSet,u=new Set,d=h=>{s.has(h)||s.set(h,setTimeout(()=>{s.delete(h),n(h)&&(f?.unobserve(h),u.delete(h))},o))},p=h=>{clearTimeout(s.get(h)),s.delete(h)},f=typeof IntersectionObserver>"u"?null:new IntersectionObserver(h=>{for(let v of h){let w=v.target;if(!vr(v,r)){u.delete(w),p(w);continue}u.add(w),y()&&d(w)}},{threshold:Ar}),y=()=>typeof document>"u"||document.visibilityState!=="hidden",x=()=>{if(y())for(let h of u)d(h);else for(let h of u)p(h)};document.addEventListener("visibilitychange",x);let S=()=>{for(let h of i.querySelectorAll(t))a.has(h)||(a.add(h),f?.observe(h))},b=typeof MutationObserver>"u"?null:new MutationObserver(()=>S()),I=()=>{S(),b?.observe(i===document?document.body:i,{childList:!0,subtree:!0})},k=i===document&&!document.body;return k?document.addEventListener("DOMContentLoaded",I,{once:!0}):I(),()=>{k&&document.removeEventListener("DOMContentLoaded",I),document.removeEventListener("visibilitychange",x),f?.disconnect(),b?.disconnect();for(let h of s.values())clearTimeout(h);s.clear(),u.clear()}}function Xe(e,t={}){if(typeof window>"u"||typeof document>"u")return()=>{};let{selector:n="[data-hp-exposure]",visibleRatio:r=.5,dwellMs:o=1e3,root:i=document}=t;return X({selector:n,root:i,visibleRatio:r,dwellMs:o,onViewable(s){let a=s.dataset?.hpExposure?.trim();return a&&e.surfaceViewed({surface:a}),!0}})}var Kt=e=>{try{return new URL(e,document.baseURI).toString()}catch{return e}};function Ze(e,t={}){if(typeof window>"u"||typeof document>"u")return()=>{};let{selector:n="[data-hp-list]",tileSelector:r="[data-hp-product]",visibleRatio:o=.5,dwellMs:i=1e3,root:s=document}=t,a=new Set,u=S=>S.closest?.(n)??null,d=S=>{let b=S.dataset.hpList?.trim();return b?{list_id:b,category:S.dataset.hpListCategory?.trim()||null}:null},p=S=>Array.from(S.querySelectorAll(r)).filter(b=>u(b)===S),f=(S,b)=>{try{let I=JSON.parse(S.dataset.hpProduct??"");return!I||typeof I!="object"||!I.product_id?null:{...I,position:I.position??b+1,...I.url?{url:Kt(I.url)}:{},...I.image_url?{image_url:Kt(I.image_url)}:{}}}catch{return null}},y=X({selector:`${n} ${r}`,root:s,visibleRatio:o,dwellMs:i,onViewable(S){let b=u(S);if(!b||b.dataset.hpListView==="manual")return!0;let I=d(b);if(!I||a.has(I.list_id))return!0;let k=new Set,h=p(b).map(f).filter(v=>!v||k.has(v.product_id)?!1:(k.add(v.product_id),!0));return h.length===0||(a.add(I.list_id),e.productListViewed({...I,products:h})),!0}}),x=S=>{let I=S.target?.closest?.("a[href]")?.closest(r);if(!I)return;let k=u(I),h=f(I,k?p(k).indexOf(I):0);h&&e.productClicked({list_id:k?d(k)?.list_id??null:null,products:[h]})};return document.addEventListener("click",x,!0),()=>{document.removeEventListener("click",x,!0),y()}}function T(e,...t){let n=e.dataset??{};for(let r of t){let o=n[r];if(typeof o=="string"&&o.trim()!=="")return o.trim()}return null}function Ce(e,...t){let n=T(e,...t);if(n===null)return null;let r=Number(n);return Number.isFinite(r)?Math.trunc(r):null}function Er(e,t){let n=T(e,"hpPromotion");return n?{promotion_id:n,name:T(e,"hpPromotionName"),creative:T(e,"hpPromotionCreative"),position:T(e,"hpPromotionSlot")??String(t+1)}:null}function Qe(e,t={}){if(typeof window>"u"||typeof document>"u")return()=>{};let{selector:n="[data-hp-promotion]",resolve:r=Er,visibleRatio:o=.5,dwellMs:i=1e3,root:s=document}=t,a=new Set,u=f=>{let y=Array.from(s.querySelectorAll(n)),x=y.indexOf(f);try{return r(f,x<0?y.length:x)}catch{return null}},d=X({selector:n,root:s,visibleRatio:o,dwellMs:i,onViewable(f){let y=u(f);return!y||a.has(y.promotion_id)?!1:(a.add(y.promotion_id),e.promotionViewed(y),!0)}}),p=f=>{let y=f.target;if(!y||typeof y.closest!="function")return;let x=y.closest(n),S=y.closest("a[href]");if(!x||!S||!x.contains(S)&&!S.contains(x))return;let b=u(x);b&&e.promotionClicked({...b,href:S.getAttribute("href")})};return document.addEventListener("click",p,!0),()=>{document.removeEventListener("click",p,!0),d()}}function kr(e){let t=T(e,"hpRecoRequest"),n=T(e,"hpRecoPlacement"),r=T(e,"hpRecoStrategy");if(!t||!n||!r)return null;let o=T(e,"hpRecoHoldout");return{recommendation_request_id:t,placement:n,strategy:r,recipe:T(e,"hpRecoRecipe"),algorithm_version:T(e,"hpRecoAlgorithmVersion"),experiment:T(e,"hpRecoExperiment"),variant:T(e,"hpRecoVariant"),holdout:o===null?null:o==="true",assignment_bucket:Ce(e,"hpRecoBucket"),assignment_unit_kind:T(e,"hpRecoUnitKind"),candidate_set_fingerprint:T(e,"hpRecoFingerprint"),candidate_set_size:Ce(e,"hpRecoCandidates"),ranked_by:T(e,"hpRecoRankedBy"),served_at:T(e,"hpRecoServedAt")}}function Cr(e,t){let n=T(e,"hpRecoProduct");if(!n)return null;let r=T(e,"hpRecoPrice");return{product_id:n,sku:T(e,"hpRecoSku"),name:T(e,"hpRecoName"),brand:T(e,"hpRecoBrand"),price:r===null||Number.isNaN(Number(r))?null:Number(r),position:Ce(e,"hpRecoPosition")??t+1}}function et(e,t={}){if(typeof window>"u"||typeof document>"u")return()=>{};let{selector:n="[data-hp-reco-request]",tileSelector:r="[data-hp-reco-product]",resolveRail:o=kr,resolveTile:i=Cr,visibleRatio:s=.5,dwellMs:a=1e3,root:u=document}=t,d=new Set,p=new Map,f=new Map,y=(h,...v)=>{try{return h(...v)}catch{return null}},x=h=>typeof h.closest=="function"?h.closest(n):null,S=h=>Array.from(h.querySelectorAll(r)),b=(h,v)=>{if(f.delete(v.recommendation_request_id),d.has(v.recommendation_request_id))return;let w=S(h).map((_,P)=>y(i,_,P)).filter(_=>_?.product_id);if(w.length===0)return;d.add(v.recommendation_request_id);let g=[...p.get(v.recommendation_request_id)?.values()??[]].sort((_,P)=>_.position-P.position);e.recommendationsViewed({...v,products:w,viewable:g})},I=X({selector:`${n} ${r}`,root:u,visibleRatio:s,dwellMs:a,onViewable(h){let v=x(h);if(!v)return!0;let w=y(o,v);if(!w)return!0;let g=w.recommendation_request_id,_=y(i,h,S(v).indexOf(h));if(_?.product_id){p.has(g)||p.set(g,new Map);let P=p.get(g);P.has(_.position)||P.set(_.position,{position:_.position,product_id:_.product_id,viewable_at:new Date().toISOString()})}return!d.has(g)&&!f.has(g)&&f.set(g,setTimeout(()=>b(v,w),0)),!0}}),k=h=>{let v=h.target;if(!v||typeof v.closest!="function")return;let w=v.closest(r);if(!w)return;let g=v.closest("[data-hp-reco-interaction]"),_=v.closest("a[href]"),P=_&&(w.contains(_)||_.contains(w));if(!(g&&w.contains(g))&&!P)return;let M=x(w);if(!M)return;let H=y(o,M);if(!H)return;let K=y(i,w,S(M).indexOf(w));if(!K?.product_id)return;let j=H.recommendation_request_id;d.has(j)||(clearTimeout(f.get(j)),b(M,H)),e.productClicked({recommendation_request_id:H.recommendation_request_id,interaction:T(g??w,"hpRecoInteraction")??"navigate",products:[K]})};return document.addEventListener("click",k,!0),()=>{document.removeEventListener("click",k,!0);for(let h of f.values())clearTimeout(h);f.clear(),I()}}var fe=new Set(["home","list","product","search","basket","checkout","order_confirmation","login","register","account","info","blog","other"]),Pr=new Set(["products"]),Gt=e=>e==null;function tt(e,t=new WeakSet){if(e instanceof Date)return e.toJSON();if(e===null||typeof e!="object"||t.has(e))return e;t.add(e);let n=Array.isArray(e)?e.map(r=>tt(r,t)):Object.fromEntries(Object.entries(e).filter(([,r])=>!Gt(r)).map(([r,o])=>[r,tt(o,t)]));return t.delete(e),n}function nt(e){if(!e)return e;let t=tt(e);for(let n of Pr)n in e&&Gt(e[n])&&(t[n]=e[n]);return t}function zt(){try{let e=performance.getEntriesByType("navigation")[0];return e?["navigate","reload","back_forward","prerender"].includes(e.type)?e.type:"navigate":null}catch{return null}}var Or=new Set(["","unknown","undefined","null","none","nil","nan","anonymous","guest","0","-1"]);function q(e){if(e==null)return null;let t=String(e).trim();return Or.has(t.toLowerCase())?null:t}function Z(){return typeof crypto<"u"&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,e=>{let t=Math.random()*16|0;return(e==="x"?t:t&3|8).toString(16)})}var Wt="1.12";function D(e){if(typeof document>"u")return null;try{let t=document.cookie.split(";").map(n=>n.trim()).find(n=>n.startsWith(`${e}=`));return t&&decodeURIComponent(t.slice(e.length+1))||null}catch{return null}}var Lr={gclid:"gclid",fbclid:"fbclid",ttclid:"ttclid"},Nr=/[a-z0-9._%+-]+@[a-z0-9-]+(\.[a-z0-9-]+)*\.[a-z]{2,}/i,Tr=/\d(?:[ -]?\d){12,}/,Rr=new Set(["gclid","fbclid","ttclid","msclkid"]);function Yt(e,t=""){if(typeof e!="string"||e==="")return!1;let n=[e];try{let o=decodeURIComponent(e.replace(/\+/g," "));o!==e&&n.push(o)}catch{}let r=!Rr.has(String(t).toLowerCase());return n.some(o=>Nr.test(o)||r&&Tr.test(o))}function Mr(e){if(!e)return null;let t=new URLSearchParams(e),n={},r={utm_source:"source",utm_medium:"medium",utm_campaign:"name",utm_term:"term",utm_content:"content"};for(let[o,i]of[...Object.entries(r),...Object.entries(Lr)]){let s=t.get(o);s&&!Yt(s,o)&&(n[i]=s)}return Object.keys(n).length?n:null}var Dr=["email","e-mail","password","pass","pwd","token","auth","code","uid","customer_id","postcode","zip","phone","applyCode"],$r=["email","password","passwd","pwd","token","secret","auth","session","phone","postcode","zip","address","birth","card","cvv","iban","ssn","customerid","userid"],Ur=["tel","uid","dob"],Vr=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","gclid","fbclid","ttclid","msclkid"],Br=new Set(Dr.map(e=>e.toLowerCase()));function jr(e){let t=String(e).toLowerCase();if(Br.has(t))return!0;let n=t.replace(/[-_.]/g,"");return $r.some(o=>n.includes(o))?!0:String(e).replace(/([a-z0-9])([A-Z])/g,"$1 $2").toLowerCase().split(/[^a-z0-9]+/).some(o=>Ur.includes(o))}function rt(e){return(e??[]).map(t=>{try{return new RegExp(`^${t}$`,"i")}catch{return{test:n=>n.toLowerCase()===String(t).toLowerCase()}}})}function Xt(e,t={}){if(!e)return null;try{let n=new URLSearchParams(e),r=t.queryParamAllowlist?rt([...t.queryParamAllowlist,...Vr]):null,o=rt(t.queryParamBlocklist),i=rt(t.sensitiveParamAllowlist);for(let a of new Set(n.keys()))(jr(a)&&!i.some(d=>d.test(a))||n.getAll(a).some(d=>Yt(d,a))||(r?!r.some(d=>d.test(a)):o.some(d=>d.test(a))))&&n.delete(a);let s=n.toString();return s?`?${s}`:""}catch{return""}}function qr(e,t,n={}){if(!e)return null;try{let r=new URL(e);return t&&r.origin===t?r.origin+r.pathname+(Xt(r.search,n)??""):r.origin+r.pathname}catch{return null}}function ot(e){if(typeof e!="string")return null;let t=e.trim().toUpperCase();return/^[A-Z]{3}$/.test(t)?t:null}function Jt(e){if(typeof e!="string")return null;let t=e.trim().toUpperCase();return/^[A-Z]{2}$/.test(t)?t:null}function Fr(e){if(typeof e!="string")return null;let t=e.trim().replace(/_/g,"-");if(!/^[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*$/.test(t))return null;try{return Intl.getCanonicalLocales(t)[0]}catch{return null}}function R(e,t){if(typeof e=="function")try{let n=e();if(n!=null)return n}catch{}return t}function Hr(e){return{property_id:String(e.propertyId),channel:e.channel??null,market:e.market??null,environment:e.environment??null}}function Kr(e){let t=typeof window<"u"?window.location:null,n=t?Xt(t.search,e):null;return{url:t?t.origin+t.pathname+(n??""):e.pageUrl??null,path:t?t.pathname:e.pagePath??null,search:n??null,referrer:typeof document<"u"?qr(document.referrer,t?.origin,e):null,title:typeof document<"u"&&document.title||null,type:e.pageType??null}}function Zt(e,t={}){let n=typeof window<"u"?window.location:null,r={library:{name:e.libraryName,version:e.libraryVersion},spec:{version:Wt},source:Hr(e),session_id:R(e.getSessionId,null),render_id:R(e.getRenderId,null),identity_trust:q(R(e.getAnonymousId,null))?"device":"none",page:Kr(e),campaign:n?Mr(n.search):null,device:{user_agent:typeof navigator<"u"?navigator.userAgent:null,type:null,viewport_width:typeof window<"u"?window.innerWidth:null,viewport_height:typeof window<"u"?window.innerHeight:null},currency:ot(R(e.getCurrency,e.currency)),locale:Fr(R(e.getLocale,e.locale)),timezone:(()=>{try{return Intl.DateTimeFormat().resolvedOptions().timeZone}catch{return null}})(),country:Jt(e.country),ship_to_country:Jt(R(e.getShipToCountry,e.shipToCountry)),ip:null,bot:null,consent:e.getConsent?.()??null,experiments:R(e.getExperiments,null),audiences:R(e.getAudiences,null),decisions:R(e.getDecisions,null),recommendations:R(e.getRecommendations,null),extensions:R(e.getExtensions,null)};return Qt(r,t)}function Qt(e,t){if(!t)return e;let n={...e};for(let[r,o]of Object.entries(t))o&&typeof o=="object"&&!Array.isArray(o)&&e[r]&&typeof e[r]=="object"&&!Array.isArray(e[r])?n[r]=Qt(e[r],o):o!==void 0&&(n[r]=o);return n}var ie=Object.freeze({consent:"thg_hp_consent",retry:"thg_hp_retry",sessionSignals:"thg_hp_session_signals",identified:"thg_hp_identified",previousPage:"thg_hp_prev_page",decisions:"thg_hp_decisions",settings:"thg_hp_settings",held:"thg_hp_held",carried:"thg_hp_carried",recommendationContext:"thg_hp_recommendation_context",anonymousId:"thg_hp_anonymous_id",session:"thg_hp_session",preview:"thg_hp_preview",debug:"thg_hp_debug"}),Gr={retry:"cdp.retry",sessionSignals:"cdp_session_signals",identified:"cdp_identified",previousPage:"cdp_prev_page_message_id"},st={...ie},en=!1;function Pe(e){let t={...ie};for(let[n,r]of Object.entries(e??{})){if(!(n in ie))throw new Error(`storageNames: unknown kind "${n}"`);if(typeof r!="string"||!/^[\w.-]+$/.test(r))throw new Error(`storageNames.${n} must be a non-empty cookie-safe name`);t[n]=r}st=t}function L(e){return st[e]}var zr="_",tn=e=>e==null||e===""?zr:String(e);function F(e,t,n){let r=`${e}:${tn(t)}`;return n==null||n===""?r:`${r}:${n}`}function nn(e,t){return`${e}:${tn(t)}:`}function Oe(){if(!en){en=!0;for(let e of[()=>localStorage,()=>sessionStorage]){let t;try{if(t=e(),!t)continue;for(let n of Object.keys(t))for(let[r,o]of Object.entries(Gr)){if(n!==o&&!n.startsWith(`${o}:`))continue;let i=st[r]+n.slice(o.length);t.getItem(i)===null&&t.setItem(i,t.getItem(n)),t.removeItem(n)}}catch{}}}}var at=1800*1e3,Le=()=>{try{return typeof sessionStorage>"u"?null:sessionStorage}catch{return null}},it=()=>{try{return typeof localStorage>"u"?null:localStorage}catch{return null}};function rn(e,t=()=>Date.now()){return{read(){try{let n=Le()?.getItem(e),r=n?JSON.parse(n):null;return!r||t()-r.savedAt>at?(r&&Le()?.removeItem(e),{items:[],anonymousId:null}):{items:Array.isArray(r.items)?r.items:[],anonymousId:typeof r.anonymousId=="string"?r.anonymousId:null}}catch{return{items:[],anonymousId:null}}},write(n,r=null){try{n.length===0?Le()?.removeItem(e):Le()?.setItem(e,JSON.stringify({savedAt:t(),items:n,anonymousId:r}))}catch{}}}}var Wr=400*24*60*60;function Q(e,t,n,r){if(typeof document>"u")return!1;try{let o=typeof location<"u"&&location.protocol==="https:"?"; Secure":"",i=r?`; Domain=${r}`:"",s=n===null?"":`; Max-Age=${n}`;return document.cookie=`${e}=${encodeURIComponent(t)}; Path=/${s}; SameSite=Lax`+i+o,!0}catch{return!1}}function Jr({name:e,domain:t=null,legacyKey:n,read:r,mint:o,mayStore:i,carried:s=null}){let a=null;try{a=it()?.getItem(n)||null}catch{a=null}let u=r(e)??a??s,d=!1,p=()=>{if(!(d||u===null||!i())&&(d=Q(e,u,Wr,t),!(!d||a===null)))try{it()?.removeItem(n),a=null}catch{}};return{get(){return u??(u=o()),p(),u},peek:()=>u,persist:p,forget(){Q(e,"",0,t),u=null,d=!1;try{it()?.removeItem(n)}catch{}a=null}}}function on(e,{carried:t=null,mayStore:n,orOwn:r=!1}){let o=e.anonymousIdSource,i=()=>Jr({name:L("anonymousId"),domain:e.consentCookieDomain??null,legacyKey:F(L("anonymousId"),e.propertyId),read:D,mint:Z,mayStore:n,carried:t});if(o?.source==="hp_cookie"){let u=i();return e.getAnonymousId=()=>u.get(),u}if(o?.source==="site_cookie"&&o.cookie){let u=o.cookie;e.getAnonymousId=()=>D(u)}if(!r)return null;let s=e.getAnonymousId,a=i();return e.getAnonymousId=()=>q(R(s,null))??a.get(),a}var Yr=200;function sn(e,t,n=()=>Date.now()){let r=()=>F(L("carried"),e),o=!1;function i(){try{let s=JSON.parse(sessionStorage.getItem(r())??"null");return!s||!(n()-s.savedAt<=at)?[]:Array.isArray(s.calls)?s.calls:[]}catch{return[]}}return{take(){let s=i();try{sessionStorage.removeItem(r())}catch{}return s},save(s){try{return o||!t()?!1:(sessionStorage.setItem(r(),JSON.stringify({savedAt:n(),calls:[...i(),...s].slice(-Yr)})),!0)}catch{return!1}},close(){o=!0}}}var si=ie.consent,Ne=Object.freeze({visitor:"visitor",host:"host"}),an=()=>({basis:Ne.host}),Xr="hp-consent",ae=["analytics","marketing","personalisation","ad_user_data","ad_personalization"],cn={analytics:"a",marketing:"m",personalisation:"p",ad_user_data:"aud",ad_personalization:"adp"},Y=()=>typeof window<"u",un=()=>Y()&&typeof window.cookieStore?.addEventListener=="function",Zr=e=>e===!0?!0:e===!1?!1:null;function pe(e){if(!e||typeof e!="object")return null;let t={};for(let n of ae)t[n]=Zr(e[n]);return t}function ut(e,t){return!e||!t?e===t:ae.every(n=>e[n]===t[n])}function lt(e){if(!e)return"";let t=[];for(let n of ae){let r=e[n];(r===!0||r===!1)&&t.push(`${cn[n]}=${r?1:0}`)}return t.join("&")}function ln(e){return{[Xr]:lt(pe(e))}}function _e(e){let t=pe({});if(e==null)return t;let n=String(e).trim();if(n.length>=2&&n.startsWith('"')&&n.endsWith('"')&&(n=n.slice(1,-1)),n.includes("%"))try{n=decodeURIComponent(n.replace(/\+/g," "))}catch{return t}let r=Object.fromEntries(Object.entries(cn).map(([i,s])=>[s,i])),o=new Set;for(let i of n.split("&")){let s=i.indexOf("=");if(s<=0)continue;let a=r[i.slice(0,s).trim()],u={1:!0,0:!1}[i.slice(s+1).trim()]??null;!a||u===null||o.has(a)||(t[a]===null?t[a]=u:t[a]!==u&&(t[a]=null,o.add(a)))}return t}function dt(e,t){if(!t)return e;let n=r=>{if(!r)return r;let o={...r};for(let i of ae)i!=="analytics"&&t[i]!==!0&&(o[i]=null);return o};return{name:e.name,basis:e.basis,read:()=>n(e.read()),subscribe:r=>e.subscribe(o=>r(n(o))),get observesChanges(){return e.observesChanges}}}function ft(e,t={}){return{...e,ad_user_data:t.ad_user_data??e.marketing,ad_personalization:t.ad_personalization??e.marketing}}function Te(e,t){let n=!0;return{notify:()=>{if(n)try{let o=e();o&&t(o)}catch{}},stop:()=>n=!1}}var Qr={analytics:["analytics","measure_content_performance","audience_measurement"],marketing:["marketing","targeting","advertising","select_personalized_ads","create_ads_profile"],personalisation:["personalisation","personalization","select_personalized_content","create_content_profile"]};function eo(e,t){if(!t?.length)return null;let n=!1;for(let[r,o]of Object.entries(e)){let i=String(o?.id??r).toLowerCase();if(t.some(a=>{let u=String(a).toLowerCase();return i===u||i.startsWith(`${u}-`)})&&(n=!0,o?.enabled===!0))return!0}return n?!1:null}function dn(e={}){let t={...Qr,...e.purposes};function n(){let r=Y()?window.Didomi:void 0;if(!r?.getCurrentUserStatus||r.shouldUserStatusBeCollected?.()!==!1||r.isUserStatusPartial?.()===!0)return null;let o=r.getCurrentUserStatus()?.purposes;if(!o||typeof o!="object")return null;let i=s=>eo(o,t[s]);return ft({analytics:i("analytics"),marketing:i("marketing"),personalisation:i("personalisation")},{ad_user_data:i("ad_user_data"),ad_personalization:i("ad_personalization")})}return{name:"didomi",read:n,observesChanges:!0,subscribe(r){if(!Y())return()=>{};let o=Te(n,r),i={event:"consent.changed",listener:o.notify};return window.didomiEventListeners=window.didomiEventListeners||[],window.didomiEventListeners.push(i),window.didomiOnReady=window.didomiOnReady||[],window.didomiOnReady.push(s=>{o.notify(),(s??window.Didomi)?.on?.("consent.changed",o.notify)}),window.Didomi?.on?.("consent.changed",o.notify),o.stop}}}function ct(e){let t={};if(!e)return t;for(let n of String(e).split("&")){let r=n.indexOf("=");if(r<=0)continue;let o=n.slice(r+1).replace(/\+/g," ");try{o=decodeURIComponent(o)}catch{}t[n.slice(0,r)]=o}return t}function fn(e={}){let t={analytics:"C0002",personalisation:"C0003",marketing:"C0004",...e.groups},n=e.impliedConsent===!0;function r(){let s=Y()?window.OnetrustActiveGroups:void 0;if(typeof s=="string"&&s.length){let d=new Set(s.split(",").map(p=>p.trim()).filter(Boolean));return p=>d.has(p)}let a=ct(D("OptanonConsent"));if(!a.groups)return null;let u={};for(let d of a.groups.split(",")){let[p,f]=d.split(":");p?.trim()&&(u[p.trim()]=f==="1"?!0:f==="0"?!1:null)}return d=>u[d]??null}function o(){if(D("OptanonAlertBoxClosed"))return!0;try{if(Y()&&window.OneTrust?.IsAlertBoxClosed?.()===!0)return!0}catch{}return Number.parseInt(ct(D("OptanonConsent")).interactionCount,10)>0?!0:n&&Y()&&(!!window.OneTrust||typeof window.OnetrustActiveGroups=="string")}function i(){if(!o())return null;let s=r();if(!s)return null;let a=f=>t[f]?s(t[f]):null,u=ft({analytics:a("analytics"),marketing:a("marketing"),personalisation:a("personalisation")},{ad_user_data:a("ad_user_data"),ad_personalization:a("ad_personalization")}),d=ct(D("OptanonConsent"));return(typeof navigator<"u"&&navigator.globalPrivacyControl===!0||d.isGpcEnabled==="1")&&(u.marketing=!1,u.ad_user_data=!1,u.ad_personalization=!1),u}return{name:"onetrust",read:i,observesChanges:!0,subscribe(s){if(!Y())return()=>{};let a=Te(i,s),u=!1,d=()=>{u||!window.OneTrust?.OnConsentChanged||(u=!0,window.OneTrust.OnConsentChanged(a.notify))};window.addEventListener("OneTrustGroupsUpdated",a.notify);let p=window.OptanonWrapper;return window.OptanonWrapper=function(...y){try{typeof p=="function"&&p.apply(this,y)}finally{d(),a.notify()}},d(),()=>{a.stop(),window.removeEventListener("OneTrustGroupsUpdated",a.notify)}}}}function to(e){let t=String(e).split(",").map(n=>n.trim()).filter(Boolean);return t.length?new Set(t):null}function pn(e){let{name:t,categories:n,parse:r=to,refusedValue:o,source:i="native",changeEvent:s="thg_hp_consent_change"}=e??{};if(typeof t!="string"||t==="")throw new Error("cookieConsent: `name` (the cookie your banner writes) is required");if(!n||typeof n.analytics!="string")throw new Error("cookieConsent: `categories.analytics` (the category id that grants analytics) is required");function a(){let u=D(t);if(u==null)return null;let d;if(o!==void 0&&u===o)d=new Set;else try{d=r(u)}catch{d=null}if(!(d instanceof Set))return null;let p=f=>n[f]?d.has(n[f]):null;return ft({analytics:p("analytics"),marketing:p("marketing"),personalisation:p("personalisation")},{ad_user_data:p("ad_user_data"),ad_personalization:p("ad_personalization")})}return{name:i,read:a,get observesChanges(){return un()},subscribe(u){if(!Y())return()=>{};let d=Te(a,u),p=f=>{[...f.changed??[],...f.deleted??[]].some(x=>x?.name===t)&&d.notify()};return window.addEventListener(s,d.notify),window.cookieStore?.addEventListener?.("change",p),()=>{d.stop(),window.removeEventListener(s,d.notify),window.cookieStore?.removeEventListener?.("change",p)}}}}function mn(e={}){let t=e.name??L("consent"),n=()=>{let r=D(t);if(r==null)return null;let o=_e(r);return o.analytics==null?null:o};return{name:"forwarded",read:n,get observesChanges(){return un()},subscribe(r){if(!Y())return()=>{};let o=Te(n,r),i=s=>{[...s.changed??[],...s.deleted??[]].some(u=>u?.name===t)&&o.notify()};return window.cookieStore?.addEventListener?.("change",i),()=>{o.stop(),window.cookieStore?.removeEventListener?.("change",i)}}}}function hn(e){let t=pe(e);return{name:"static",basis:Ne.host,read:()=>t?{...t}:null,observesChanges:!0,subscribe:()=>()=>{}}}function gn(e){if(e==null)return null;let t=String(e).split("/").map(n=>n.trim()).filter(Boolean).join("/");return t===""?null:t}function no(e){let t=e.item_category;if(t!=null&&String(t).includes("/"))return gn(t);let n=[t,e.item_category2,e.item_category3,e.item_category4,e.item_category5].map(r=>r==null?"":String(r).trim()).filter(Boolean);return gn(n.join("/"))}var ro=e=>{if(e==null||typeof e=="object")return null;let t=String(e).trim();return t===""?null:t};function oo(...e){for(let t of e){if(typeof t=="number"&&Number.isFinite(t))return String(t);if(typeof t=="string"&&t.trim()!=="")return t.trim()}return null}var Re=e=>oo(e.item_group_id,e.item_id);function so(e,t=Re){if(!e)return null;let n=a=>a==null||a===""||Number.isNaN(Number(a))?null:Number(a),r=a=>{let u=n(a);return u==null?null:Math.round(u*1e6)/1e6},o=a=>{let u=r(a);return u!=null&&u>=0?u:null},i=a=>{let u=n(a);return Number.isInteger(u)&&u>=0?u:null},s=ro;return{product_id:s(t(e)),sku:s(e.item_id),name:s(e.item_name),brand:s(e.item_brand),variant:s(e.item_variant)??s(e.variant),category:no(e),price:r(e.price),discount:o(e.discount),quantity:i(e.quantity),coupon:s(e.coupon),position:i(e.index)!=null?i(e.index)+1:null,url:s(e.url)??s(e.item_url)??null,image_url:s(e.image_url)??s(e.image_link)??null,displayed_sku:s(e.displayed_sku)??s(e.displayed_variant_sku)}}function pt(e,t=Re){return(e??[]).map(n=>so(n,t)).filter(n=>n?.product_id)}var io=1e3,ao={shipping:2,payment:3},co=new Set(["password","google","apple","facebook","magic_link","sso","passkey","other"]),$=e=>{if(e==null||typeof e=="object")return null;let t=String(e).trim();return t===""?null:t},uo=e=>{if(e==null||e===""||typeof e=="object")return null;let t=Number(e);return Number.isFinite(t)&&t>=0?t:null},Me=e=>e!==null&&typeof e=="object";function lo(e){return Array.isArray(e.items)?e.items.filter(Me):null}function yn(e){let t=$(e)?.toLowerCase().replace(/[\s-]+/g,"_");return t?co.has(t)?t:t==="email"||t==="email_password"?"password":"other":null}function fo({ecommerce:e,items:t}){let n=i=>({promotion_id:$(i.promotion_id),name:$(i.promotion_name),creative:$(i.creative_name),position:$(i.creative_slot)}),r=new Set,o=[];for(let i of[n(e),...t.map(n)])!i.promotion_id||r.has(i.promotion_id)||(r.add(i.promotion_id),o.push(i));return o}var Sn=({ecommerce:e,items:t})=>$(e.item_list_id)??$(e.item_list_name)??$(t[0]?.item_list_id)??$(t[0]?.item_list_name),ce=(e,t)=>t.products?.length?{event:e,properties:t}:null,po=new Set(["product","products","item","items"]);function mo({ecommerce:e}){let t=$(e.item_id),n=$(e.content_type)?.toLowerCase();return!t||n&&!po.has(n)?null:{event:"Product Shared",properties:{share_via:$(e.method),products:[{product_id:t}]}}}function ho(e){let t=o=>ce("Product List Viewed",{list_id:Sn(o),products:o.products}),n=o=>{let i=$(o.ecommerce.search_term);return i?{event:"Products Searched",properties:{query:i,...o.products.length?{products:o.products}:{}}}:null},r=o=>i=>i.promotions.map(s=>({event:o,properties:s}));return{view_item:o=>ce("Product Viewed",{currency:o.currency,value:o.value,products:o.products.slice(0,1)}),view_item_list:t,view_search_results:o=>n(o)??t(o),search:n,select_item:o=>ce("Product Clicked",{list_id:Sn(o),products:o.products.slice(0,1)}),add_to_cart:o=>ce("Product Added",{cart_id:$(o.ecommerce.cart_id),products:o.products}),remove_from_cart:o=>ce("Product Removed",{cart_id:$(o.ecommerce.cart_id),products:o.products}),view_cart:o=>({event:"Cart Viewed",properties:{cart_id:$(o.ecommerce.cart_id),value:o.value,currency:o.currency,coupon:$(o.ecommerce.coupon),...o.itemsListed?{products:o.products}:{}}}),begin_checkout:o=>ce("Checkout Started",{value:o.value,currency:o.currency,coupon:$(o.ecommerce.coupon),products:o.products}),add_shipping_info:o=>({event:"Checkout Step Completed",properties:{step:e.shipping,step_name:"shipping",shipping_method:$(o.ecommerce.shipping_tier)}}),add_payment_info:o=>({event:"Payment Info Entered",properties:{step:e.payment,step_name:"payment",payment_method:$(o.ecommerce.payment_type)}}),view_promotion:r("Promotion Viewed"),select_promotion:r("Promotion Clicked"),add_to_wishlist:o=>ce("Product Added to Wishlist",{products:o.products}),share:mo,login:o=>({event:"Signed In",properties:{method:yn(o.ecommerce.method)}}),sign_up:o=>({event:"Signed Up",properties:{method:yn(o.ecommerce.method)}})}}var go=e=>e.event;function yo(e,t){if(!Me(e))return null;if(typeof e.length=="number"&&e[0]==="event"&&typeof e[1]=="string"){let r=Me(e[2])?e[2]:{};return{name:e[1],entry:r,ecommerce:r}}let n=$(t(e));return n?{name:n,entry:e,ecommerce:Me(e.ecommerce)?e.ecommerce:e}:null}function mt(e){return Array.isArray(e)?`[${e.map(mt).join(",")}]`:e&&typeof e=="object"?`{${Object.keys(e).sort().map(t=>`${JSON.stringify(t)}:${mt(e[t])}`).join(",")}}`:JSON.stringify(e??null)}function wn(e,t={}){if(!e||typeof e.track!="function")throw new Error("connectDataLayer: analytics must have a track(event, properties, options) method");let n=t.dataLayer??(typeof window<"u"?window.dataLayer=window.dataLayer||[]:null);if(!Array.isArray(n))return()=>{};let r=ho({...ao,...t.steps}),o=t.map??{},i=t.eventName??go,s=t.productId??Re,a=new Set(["purchase","refund",...t.ignore??[]]),u=t.dedupeMs??io,d=new Map,p=!0;function f(w,g,_){if(u<=0)return!1;let P=`${w}|${_??""}|${mt(g)}`,M=Date.now();for(let[H,K]of d)M-K>u&&d.delete(H);return d.has(P)?!0:(d.set(P,M),!1)}let y=null,x=new Set;function S(w,g){if(w!=="Product Viewed")return!1;let _=typeof location>"u"?"":location.pathname;_!==y&&(y=_,x.clear());let P=g.products?.[0]?.product_id;return P?x.has(P)?!0:(x.add(P),!1):!1}function b({event:w,properties:g={}},_){if(!w||f(w,g,_)||S(w,g))return;let P=_?{context:{currency:_}}:void 0;if(w==="Signed Out"&&typeof e.signedOut=="function"){e.signedOut(P);return}e.track(w,g,P)}function I(w){let g=t.transform?t.transform(w):w,_=yo(g,i);if(!_||a.has(_.name))return;let P=Object.hasOwn(o,_.name),M=P?o[_.name]:r[_.name];if(typeof M!="function")return;let H=lo(_.ecommerce),K=H??[],j={..._,items:K,itemsListed:H!==null,products:pt(K,s),currency:ot(_.ecommerce.currency),value:uo(_.ecommerce.value)};j.promotions=fo(j);let te=P?M(g,{...j,toProducts:G=>pt(G,s)}):M(j);for(let G of[te].flat())G&&b(G,j.currency)}function k(w){if(p)try{I(w)}catch(g){try{t.onError?.(g,w)}catch{}}}for(let w of n.slice())k(w);let h=n.push;function v(...w){let g=h.apply(this,w);for(let _ of w)k(_);return g}return n.push=v,()=>{p=!1,n.push===v&&(n.push=h)}}var So="hp_debug",wo=256;function _o(){try{return new URLSearchParams(location.search).get(So)}catch{return null}}function _n(e,t){if(typeof location>"u")return!1;try{let n=_o();if(n!==null){let o=n.trim();o===""||o.length>wo?sessionStorage.removeItem(t):sessionStorage.setItem(t,o)}let r=sessionStorage.getItem(t);return!!(e&&r&&Bt(r)===e)}catch{return!1}}var me=class extends Error{},bo={inHoldout:()=>!0,personalisationGroup:()=>"excluded",servingHeaders:()=>({}),recommendationContext:()=>({}),flush:()=>Promise.resolve(),reset:()=>{}};function ht(e,t){let n=bo[e]??(()=>null),r=o=>(console.warn(`[hp] ${String(e)} failed; nothing was sent`,o),n());return function(...i){try{let s=t.apply(this,i);return typeof s?.then=="function"?Promise.resolve(s).catch(r):s}catch(s){if(s instanceof me)throw s;return r(s)}}}function bn(e){for(let t of Reflect.ownKeys(e)){let{value:n}=Object.getOwnPropertyDescriptor(e,t)??{};typeof n=="function"&&(e[t]=ht(t,n))}return e}var be=Symbol("hp.identityOption");var xo=4320*60*60,Io=100,Ao=1e3,vo=2e3,xn=500,De=e=>(e?.unref?.(),e);function Eo(e,t){if(t?.analytics===!0&&e.analytics===!1)return"withdrawn";let n=ae.map(r=>e[r]).filter(r=>r!==null);return n.length&&n.every(Boolean)?"accept_all":n.length&&n.every(r=>!r)?"reject_all":"custom"}function ko(e,t,n=L("consent")){if(!(typeof document>"u"))try{let r=typeof location<"u"&&location.protocol==="https:"?"; Secure":"",o=t?`; Domain=${t}`:"";document.cookie=`${n}=${lt(e)}; Path=/; Max-Age=${xo}; SameSite=Lax`+o+r}catch{}}function Co(e){let t=_e(D(e));return ae.some(n=>t[n]!==null)?t:null}function In(e){let{adapter:t,timeoutMs:n=5e3,cookieDomain:r=null,cookieName:o=L("consent"),release:i,announce:s}=e,a=e.discard??(()=>{}),u="pending",d=null,p=null,f=null,y=null,x=!1,S=null,b=[],I=m=>{u=m,e.onStatus?.(m)};function k(){try{return pe(t.read())}catch{return null}}function h(){p&&clearInterval(p),f&&clearTimeout(f),p=null,f=null}let v=()=>typeof window<"u"&&typeof document<"u",w=()=>{try{return typeof t.subscribe=="function"&&t.observesChanges===!0}catch{return!1}};function g(){y&&clearInterval(y),y=null}function _(){x||y||d===null||w()||v()&&document.visibilityState==="hidden"||(y=De(setInterval(()=>j(k()),vo)))}let P=()=>u==="pending"||u==="unknown",M=e.store??null,H=()=>{let m=b.splice(0);M?.write([]);for(let B of m)i(B)},K=()=>{let m=b.splice(0);M?.write([]);for(let B of m)a(B)};function j(m){let B=pe(m);if(!B||B.analytics===null)return;let Ae=d===null,J=Ae?Co(o):d;if(!Ae&&ut(J,B))return;h(),d=B,_(),ko(B,r,o);let ve=!ut(J,B);if(B.analytics){I("granted"),H(),ve&&s({action:Eo(B,J),state:B,previous:J,withdrawal:!1});return}K(),J?.analytics===!0&&s({action:"withdrawn",state:B,previous:J,withdrawal:!0}),I("denied")}function te(){f=null,u==="pending"&&(I("unknown"),p&&clearInterval(p),p=De(setInterval(()=>j(k()),Ao)))}function G(m){P()&&(M||m?.type!=="visibilitychange"&&!m?.persisted&&K())}let ne=m=>{document.visibilityState==="hidden"?(g(),G(m)):_()},le=m=>{g(),G(m)},Ie=()=>_();return{get status(){return u},get state(){return d?{...d}:null},stamp(){return d?{...d,source:t.name??null,basis:t.basis??Ne.visitor}:null},admits(){return u==="granted"},hold(m){return!P()||b.length>=xn?(a(m),!1):(b.push(m),M?.write(b),!0)},onHidden:G,start(){x=!1;try{S=t.subscribe?.(m=>j(m))??null}catch(m){e.debug&&console.warn("[hp] consent adapter subscribe failed",m)}v()&&(window.addEventListener("pagehide",le),window.addEventListener("pageshow",Ie),document.addEventListener("visibilitychange",ne)),M&&b.length===0&&b.push(...M.read().slice(-xn)),j(k()),!d&&(p=De(setInterval(()=>j(k()),Io)),f=De(setTimeout(te,n)))},stop(){x=!0,h(),g(),typeof S=="function"&&S(),v()&&(window.removeEventListener("pagehide",le),window.removeEventListener("pageshow",Ie),document.removeEventListener("visibilitychange",ne))}}}var Po=["recommendation","search_rank","banner","offer","layout","copy","price","other"],$e=e=>typeof e=="string"&&e!=="";function An(e){return(Array.isArray(e?.decisions)?e.decisions:[]).filter(t=>$e(t?.slot)&&$e(t.decision_id)&&t.holdout!==!0).map(({decision_id:t,slot:n,arm:r,kind:o})=>({decision_id:t,slot:n,...$e(r)&&{arm:r},...Po.includes(o)&&{kind:o}}))}function vn({key:e,mayStore:t}){let n,r=()=>{if(n)return n;try{let i=JSON.parse(sessionStorage.getItem(e));n=Array.isArray(i)?i.filter(s=>$e(s?.slot)):[]}catch{n=[]}return n},o=()=>{try{n?.length&&t()&&sessionStorage.setItem(e,JSON.stringify(n))}catch{}};return{persist:o,current:()=>r().length?n.map(i=>({...i})):null,remember(i){if(!i.length)return;let s=new Set(i.map(a=>a.slot));n=[...r().filter(a=>!s.has(a.slot)),...i],o()},forget(){n=[];try{sessionStorage.removeItem(e)}catch{}}}}function Oo(e){let t=2166136261,n=String(e);for(let r=0;r<n.length;r+=1)t^=n.charCodeAt(r),t=t+((t<<1)+(t<<4)+(t<<7)+(t<<8)+(t<<24))>>>0;return t^=t>>>16,t=Math.imul(t,2246822507)>>>0,t^=t>>>13,t=Math.imul(t,3266489909)>>>0,t^=t>>>16,t>>>0}function Ue(e,t,n=1e4){return Oo(`${t}\0${e}`)%n}function gt(e){let t=o=>Math.min(Math.max(Number(o)||0,0),1),n=t(e.share),r=1-n;return{holdout_share:n,personalised_share:e.ramp==null?r:Math.min(t(e.ramp),r)}}function yt(e,t){if(!e||typeof e.salt!="string")return{holdout:!1,bucket:null,group:"personalised"};let n=e.ramp!=null;if(t==null||t==="")return{holdout:!1,bucket:null,group:n?"excluded":"personalised"};let{holdout_share:r,personalised_share:o}=gt(e),i=Ue(t,e.salt),s=u=>Math.round(u*1e4),a=r>0&&i<s(r)?"holdout":!n||i<s(r+o)?"personalised":"excluded";return{holdout:a==="holdout",bucket:i,group:a}}function En(e,t){return typeof e!="string"||e===""||t==null||t===""?null:Ue(t,`bandit-vs-default:${e}`)<1e4/2?"default":"bandit"}var Lo="hp_preview",No=256;function kn(e,t=null){if(typeof location>"u")return D(e);let n=null;try{n=new URLSearchParams(location.search).get(Lo)}catch{n=null}if(n===null)return D(e);let r=n.trim();return r===""||r.length>No?(Q(e,"",0,t),null):(Q(e,r,null,t),r)}var To=40,Ro=128,Mo=5,Do=60,Pn=e=>typeof e=="string"&&e!==""&&e.length<=Ro&&!e.includes(","),Cn=e=>Array.isArray(e)?e.filter(Pn).slice(0,To):[],$o=e=>Array.isArray(e)?e.filter(t=>typeof t=="string").map(t=>t.replaceAll(","," ").trim()).filter(t=>t!=="").slice(0,Mo):[],Uo=e=>Object.entries(e&&typeof e=="object"?e:{}).filter(([t,n])=>Pn(t)&&Number.isFinite(n)&&n>=1).sort(([,t],[,n])=>n-t).slice(0,Do).map(([t,n])=>`${t}:${Math.floor(n)}`);function St(e){let t={},n={viewed:Cn(e?.recentProducts),basket:Cn(e?.basketProducts),q:$o(e?.recentSearches),impressed:Uo(e?.impressions)};for(let[r,o]of Object.entries(n))o.length>0&&(t[r]=o.join(","));return t}var Vo=1800*1e3;function On(e,t=Date.now()){try{localStorage.setItem(L("recommendationContext"),JSON.stringify({context:e,until:t+Vo}))}catch{}}function Ln(){try{localStorage.removeItem(L("recommendationContext"))}catch{}}var Bo="hp_sample",Nn=1e4;function Tn(e,t){return!(typeof t=="number"&&t<1)||e==null||e===""?!0:Ue(e,Bo,Nn)<Math.round(t*Nn)}function wt(e){return e.trim().toLowerCase().replace(/\s*\/\s*/g,"/").replace(/^\/+|\/+$/g,"")}var jo=2,qo=12;function Fo(e,t=jo){return e.split("/").slice(0,t).join("/")}function he(e,t){if(typeof t!="string"||!t.trim())return null;let n=Fo(wt(t));return n?(e.unshift(n),e.length=Math.min(e.length,qo),n):null}var ue=()=>L("sessionSignals"),ge=null,_t=()=>ge??ue();function Rn(){try{return ge?localStorage:sessionStorage}catch{return}}function Mn(e,t){let n=t==null?null:String(t),r=e?F(ue(),n,e):null;if(r===ge)return;let o=ge===null?Ho():null;if(ge=r,!!ge)try{let i=_t(),s=nn(ue(),n);for(let a of Object.keys(localStorage))a.startsWith(s)&&a!==i&&localStorage.removeItem(a);o&&!localStorage.getItem(i)&&(localStorage.setItem(i,JSON.stringify(o)),sessionStorage.removeItem(ue()))}catch{}}function Ho(){try{let e=sessionStorage.getItem(ue());if(!e)return null;let t=JSON.parse(e);return t&&(t.productViews>0||t.listViews>0||t.basketValue!=null||t.recentProducts?.length||t.recentBrowse?.length)?t:null}catch{return null}}function Ve(){return{productViews:0,listViews:0,cartAdds:0,searches:0,recentProducts:[],recentCategories:[],recentBrowse:[],basketProducts:[],basketValue:null,recentSearches:[],impressions:{},browsingCategory:null,startedAt:0}}function bt(){let e=Rn();if(!e)return Ve();try{let t=e.getItem(_t());return t?{...Ve(),...JSON.parse(t)}:{...Ve(),startedAt:Date.now()}}catch{return{...Ve(),startedAt:Date.now()}}}function xt(e){let t=Rn();if(t)try{t.setItem(_t(),JSON.stringify(e))}catch{}}function Dn(){return bt()}function $n(){for(let e of[()=>sessionStorage,()=>localStorage])try{let t=e();t.removeItem(ue());for(let n of Object.keys(t))n.startsWith(`${ue()}:`)&&t.removeItem(n)}catch{}}var Un=20,Vn=200,Ko=new Set(["Product Viewed","Product Clicked","Product Added","Product Added to Wishlist"]),Go=new Set(["Product Viewed","Product Added"]);function Bn(e,t){let n=bt();n.startedAt||(n.startedAt=Date.now());let r=Array.isArray(t?.products)?t.products:[];switch(e){case"Product Viewed":n.productViews+=1,he(n.recentBrowse,n.browsingCategory);break;case"Product List Viewed":{let s=t?.list_id;typeof s=="string"&&s.includes("/")&&!s.startsWith("cdp-")&&(n.listViews+=1,n.browsingCategory=he(n.recentBrowse,s)??n.browsingCategory)}break;case"Product List Filtered":{let s=t?.change,a=typeof t?.list_id=="string"?t.list_id:"",u=typeof s?.value=="string"?s.value:"";u&&s?.action!=="removed"&&(n.browsingCategory=he(n.recentBrowse,`${a}/${u}`)??n.browsingCategory);break}case"Product Added":{n.cartAdds+=1,he(n.recentBrowse,n.browsingCategory);for(let s of r){let a=s.product_id;typeof a!="string"||!a||(n.basketProducts=[a,...n.basketProducts.filter(u=>u!==a)].slice(0,Un))}break}case"Product Removed":{for(let s of r){let a=s.product_id;typeof a=="string"&&a&&(n.basketProducts=n.basketProducts.filter(u=>u!==a))}break}case"Products Searched":{n.searches+=1;let s=t?.query;if(typeof s=="string"&&s.trim()){let a=s.trim().toLowerCase().slice(0,64);n.recentSearches=[a,...n.recentSearches.filter(u=>u!==a)].slice(0,5)}break}case"Product Clicked":case"Product Added to Wishlist":break;case"Cart Updated":{let s=t?.value;return n.basketValue=typeof s=="number"&&Number.isFinite(s)&&s>=0?s:null,xt(n),n}default:return n}let o=Ko.has(e);if(o)for(let s of r)typeof s.product_id=="string"&&s.product_id&&delete n.impressions[s.product_id];else{for(let a of r){let u=a.product_id;typeof u!="string"||!u||(n.impressions[u]=(n.impressions[u]??0)+1)}let s=Object.keys(n.impressions);if(s.length>Vn){let a={};for(let u of s.sort((d,p)=>n.impressions[p]-n.impressions[d]).slice(0,Vn))a[u]=n.impressions[u];n.impressions=a}}let i=new Set;for(let s of r){let a=s.product_id;if(o&&typeof a=="string"&&a&&(n.recentProducts=[a,...n.recentProducts.filter(u=>u!==a)].slice(0,Un)),Go.has(e)&&typeof s.category=="string"&&s.category){let u=wt(s.category);u&&!i.has(u)&&(i.add(u),he(n.recentCategories,u))}}return xt(n),n}var jn=1800*1e3;function qn({name:e,domain:t=null,mayStore:n,now:r=()=>Date.now(),mint:o=Z}){let i=null,s=0,a=()=>{i!==null&&n()&&Q(e,i,jn/1e3,t)};return{get(){let u=r(),d=D(e);return d?i=d:(i===null||u-s>=jn)&&(i=o()),s=u,a(),i},persist:a,forget(){Q(e,"",0,t),i=null}}}var W=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),Fn=e=>Array.isArray(e)&&e.every(t=>typeof t=="string"),At=Object.freeze({enabled:!1});function Hn(e,t){let n=e?.track;return W(n)?(Object.hasOwn(n,t)?n[t]:n.__default)?.enabled!==!1:!0}function zo(e,t){return`${ye(e)}/v1/projects/${encodeURIComponent(t)}/settings`}var Wo="https://hp.thgingenuity.com";function ye(e){return e==null||e===""?Wo:Kn(e)}function Kn(e){if(typeof e!="string")return null;let t=e.trim().replace(/\/+$/,"");return/^https?:\/\/[^/\s]+$/i.test(t)?t:/^[a-z0-9.-]+(:\d+)?$/i.test(t)?`https://${t}`:null}function Jo(e){if(!W(e))return null;let t=Object.fromEntries(Object.entries(e).filter(([,n])=>typeof n=="boolean"));return Object.keys(t).length?t:null}var It=e=>typeof e=="number"&&e>=0&&e<=1,Yo=/^[0-9a-f]{64}$/;function Xo(e){return W(e)?{enabled:e.enabled===!0,tokenSha256:typeof e.tokenSha256=="string"&&Yo.test(e.tokenSha256)?e.tokenSha256:null}:{enabled:!1,tokenSha256:null}}function Zo(e){if(!W(e))return null;let t=Object.fromEntries(Object.entries(e).filter(([,n])=>W(n)&&It(n.share)&&(n.ramp==null||It(n.ramp))&&typeof n.salt=="string"&&n.salt!=="").map(([n,r])=>[n,{share:r.share,ramp:r.ramp??null,salt:r.salt,...Fn(r.readoutSlots)&&r.readoutSlots.length>0&&{readoutSlots:r.readoutSlots}}]));return Object.keys(t).length?t:null}var Qo=/^[A-Za-z0-9!#$%&'*+.^_`|~-]{1,128}$/;function es(e){return W(e)?e.source==="hp_cookie"?{source:"hp_cookie"}:e.source!=="site_cookie"?null:e.cookie==null?{source:"site_cookie",cookie:null}:typeof e.cookie=="string"&&Qo.test(e.cookie)?{source:"site_cookie",cookie:e.cookie}:null:null}function vt(e){let t=W(e)?e.hp:null;return!W(t)||t.enabled!==!0?At:{enabled:!0,propertyId:typeof t.propertyId=="string"&&t.propertyId!==""?t.propertyId:null,environment:typeof t.environment=="string"?t.environment:null,consentPurposes:Jo(t.consent?.purposes),queryParamAllowlist:Fn(t.queryParamAllowlist)?t.queryParamAllowlist:null,anonymousId:es(t.anonymousId),apiHost:Kn(t.apiHost),holdouts:Zo(t.holdouts),sampleRate:It(t.sampleRate)?t.sampleRate:1,debug:Xo(t.debug),plan:W(e.plan)&&W(e.plan.track)?{track:e.plan.track}:null}}async function Gn(e,t,n=globalThis.fetch){try{let r=await n(zo(e,t),{credentials:"omit",headers:{accept:"application/json"}});return r.ok?vt(await r.json()):null}catch{return null}}var Be=e=>F(L("settings"),e);function zn(e){try{let t=localStorage.getItem(Be(e));if(!t)return null;let n=JSON.parse(t);return n?.enabled===!0?n:null}catch{return null}}function Wn(e,t){try{t?.enabled===!0?localStorage.setItem(Be(e),JSON.stringify(t)):localStorage.removeItem(Be(e))}catch{}}function Et(e){try{localStorage.removeItem(Be(e))}catch{}}var ts={flushAt:20,flushIntervalMs:1e4,maxQueue:500,maxRetryAgeMs:1440*60*1e3,maxRetries:5,maxPersistedBatches:20,maxPersistedBytes:1024*1024,storageKey:ie.retry,beaconSafeBytes:56*1024,maxBatchEvents:100,maxBatchBytes:500*1024},je=()=>Date.now(),Jn=3e4,Tt=55*1024;function ns(e,t=je()){if(typeof e!="string")return 0;let n=e.trim();if(/^\d+$/.test(n))return Number(n)*1e3;if(n==="")return 0;let r=Date.parse(n);return Number.isNaN(r)?0:Math.max(0,r-t)}var ee=e=>typeof TextEncoder<"u"?new TextEncoder().encode(e).length:Buffer.byteLength(e),kt=()=>typeof window<"u"&&typeof document<"u";function rs(){try{return!kt()||!window.localStorage?null:(window.localStorage.getItem("thg_hp_probe"),window.localStorage)}catch{return null}}var A,Ct,Yn,qe,Pt,Ot,Xn,xe,Lt,Fe,Zn,Nt,Qn,He,Ke=class{constructor(t){Ut(this,A);this.o={...ts,...t},this.queue=[],this.timer=null,this.storage=rs(),this.inflight=null,this.sending=new Set,this.replayAllowed=!this.o.deferReplay,this.draining=null,kt()&&(window.addEventListener("pagehide",()=>this.flush({beacon:!0})),document.addEventListener("visibilitychange",()=>{document.visibilityState==="hidden"&&this.flush()}),N(this,A,He).call(this))}replayPersisted(){return this.replayAllowed=!0,N(this,A,He).call(this)}clearPersisted(){if(this.replayAllowed=!1,!!this.storage)try{this.storage.removeItem(this.o.storageKey)}catch{}}enqueue(t){let n;try{n=JSON.parse(JSON.stringify(t))}catch(r){this.o.onError?.(`event ${t?.messageId??""} cannot be serialised; dropped`,r);return}this.queue.push(n),this.queue.length>this.o.maxQueue&&this.queue.splice(0,this.queue.length-this.o.maxQueue),this.queue.length>=this.o.flushAt?this.flush():N(this,A,Ct).call(this)}flush(t={}){if(N(this,A,Yn).call(this),this.queue.length===0)return Promise.resolve();if(t.beacon&&kt()&&typeof navigator<"u"&&navigator.sendBeacon)return N(this,A,Xn).call(this),Promise.resolve();if(this.inflight)return this.inflight;let n=N(this,A,Ot).call(this,this.queue,this.o.maxBatchEvents,this.o.maxBatchBytes).map(r=>{let o=N(this,A,Nt).call(this,r);return o&&this.sending.add(o),{batch:r,id:o}});return this.queue=[],this.inflight=(async()=>{for(let{batch:r,id:o}of n)await N(this,A,xe).call(this,r,0,o)})().finally(()=>{this.inflight=null,this.queue.length>=this.o.flushAt?this.flush():this.queue.length&&N(this,A,Ct).call(this)}),this.inflight}};A=new WeakSet,Ct=function(){this.timer||(this.timer=setTimeout(()=>this.flush(),this.o.flushIntervalMs),this.timer?.unref?.())},Yn=function(){this.timer&&(clearTimeout(this.timer),this.timer=null)},qe=function(t){return JSON.stringify({batch:t,writeKey:this.o.writeKey,sentAt:new Date().toISOString()})},Pt=function(){return`${this.o.apiHost.replace(/\/$/,"")}/v1/batch`},Ot=function(t,n,r){let o=ee(N(this,A,qe).call(this,[])),i=[],s=[],a=o;for(let u of t){let d=ee(JSON.stringify(u))+1;s.length&&(s.length>=n||a+d>r)&&(i.push(s),s=[],a=o),s.push(u),a+=d}return s.length&&i.push(s),i},Xn=function(){let t=this.queue;this.queue=[];let n=N(this,A,Pt).call(this);for(let r of N(this,A,Ot).call(this,t,this.o.maxBatchEvents,this.o.beaconSafeBytes)){let o=N(this,A,qe).call(this,r);if(ee(o)<=this.o.beaconSafeBytes){let i=!1;try{i=navigator.sendBeacon(n,new Blob([o],{type:"text/plain"}))}catch{i=!1}if(i)continue}N(this,A,xe).call(this,r,0,N(this,A,Nt).call(this,r))}},xe=async function(t,n,r=null){r&&this.sending.add(r);let o=N(this,A,qe).call(this,t),i=0;try{let s=await fetch(N(this,A,Pt).call(this),{method:"POST",headers:{"Content-Type":"text/plain"},body:o,keepalive:ee(o)<=this.o.beaconSafeBytes});if(s.ok)return N(this,A,Lt).call(this,r),N(this,A,He).call(this),!0;let a=s.status===429||s.status>=500;if(a&&(i=ns(s.headers?.get?.("retry-after"))),!a)return N(this,A,Lt).call(this,r),this.o.onError?.(`CDP ingest rejected batch (${s.status}); dropping ${t.length} events`),!0;throw new Error(`retryable ${s.status}`)}catch(s){if(n>=this.o.maxRetries){r&&this.sending.delete(r);let u=r?"kept for the next page":"dropped";return this.o.onError?.(`CDP ingest unreachable after ${n} attempts; ${u}: ${t.length} events`,s),!1}let a=Math.max(Math.min(1e3*2**n,Jn)*(.5+Math.random()),Math.min(i,Jn));return await new Promise(u=>setTimeout(u,a)),N(this,A,xe).call(this,t,n+1,r)}},Lt=function(t){t&&(this.sending.delete(t),N(this,A,Qn).call(this,t))},Fe=function(){let t=JSON.parse(this.storage.getItem(this.o.storageKey)||"[]");return Array.isArray(t)?t:[]},Zn=function(t,n){let r=s=>ee(JSON.stringify(s))+1,o=[...t,n].slice(-this.o.maxPersistedBatches),i=o.reduce((s,a)=>s+r(a),2);for(;o.length>1&&i>this.o.maxPersistedBytes;)i-=r(o[0]),o=o.slice(1);for(;o.length;)try{return this.storage.setItem(this.o.storageKey,JSON.stringify(o)),!0}catch{o=o.slice(1)}return!1},Nt=function(t){if(!this.storage||this.o.mayPersist&&!this.o.mayPersist())return null;try{let n={at:je(),id:`${je()}-${Math.random().toString(36).slice(2)}`,batch:t};return ee(JSON.stringify(n))+3>this.o.maxPersistedBytes?null:N(this,A,Zn).call(this,N(this,A,Fe).call(this),n)?n.id:null}catch{return null}},Qn=function(t){if(!(!t||!this.storage))try{let n=N(this,A,Fe).call(this).filter(r=>r.id!==t);n.length?this.storage.setItem(this.o.storageKey,JSON.stringify(n)):this.storage.removeItem(this.o.storageKey)}catch{}},He=function(){if(!this.storage||!this.replayAllowed)return this.draining??Promise.resolve();if(this.draining)return this.draining;let t;try{let r=N(this,A,Fe).call(this);t=r.filter(o=>Array.isArray(o?.batch)&&je()-o.at<this.o.maxRetryAgeMs).map(o=>o.id?o:{...o,id:`${o.at}-${Math.random().toString(36).slice(2)}`}),(t.length!==r.length||t.some((o,i)=>o!==r[i]))&&(t.length?this.storage.setItem(this.o.storageKey,JSON.stringify(t)):this.storage.removeItem(this.o.storageKey))}catch{return Promise.resolve()}let n=t.filter(r=>!this.sending.has(r.id));return n.length?(this.draining=(async()=>{for(let r of n)if(!this.replayAllowed||!await N(this,A,xe).call(this,r.batch,0,r.id))return})().finally(()=>{this.draining=null}),this.draining):Promise.resolve()};var er="3.19.0",tr="@thg-agentic/hyper-personalisation-web";var os=new Set(["audiences","decisions","recommendations"]);var ss=Symbol.for("@thg-agentic/hyper-personalisation-web.replay");function is(e){let t={...e??{}},n=Array.isArray(t.products)?t.products:null;if(t.item_count==null&&n){let r=n.map(o=>o?.quantity);r.every(Number.isInteger)&&(t.item_count=r.reduce((o,i)=>o+i,0))}return t}var as="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";function cs(e){if(/^[0-9a-fA-F]{64}$/.test(e))return e.toLowerCase();if(!/^[A-Za-z0-9+/_-]{43}=?$/.test(e))return null;try{let t=atob(e.replace(/-/g,"+").replace(/_/g,"/").padEnd(44,"="));return t.length!==32?null:Array.from(t,n=>n.charCodeAt(0).toString(16).padStart(2,"0")).join("")}catch{return null}}function Ge(e){if(typeof e!="string")return null;let t=cs(e.trim());return t===as?null:t}var nr=!1,rr=!1;function us(){rr||(rr=!0,console.warn("[hp] no consent adapter: events are sent under the host's lawful basis (basis 'host'). Pass `consent` to send only once the visitor allows analytics."))}function ls(e){!e||nr||(nr=!0,console.warn("[hp] mintAnonymousId is ignored since 3.6: where device ids come from is set per source in the console (the site\u2019s own cookie, or Hyper-Personalisation\u2019s)."))}function ds(e){try{return ee(JSON.stringify(e))}catch{return 0}}function or(e){return e&&typeof e.context=="object"&&e.context!==null?e.context:e}function sr(e){let t=ye(e?.apiHost);if(!t)throw new Error("createAnalytics: apiHost, when given, must be a host or an http(s) origin");if(!e?.propertyId)throw new Error("createAnalytics: propertyId is required");let n={libraryName:tr,libraryVersion:er,writeKey:"web",queryParamBlocklist:[],debug:!1,validate:null,consentTimeoutMs:5e3,...e,apiHost:t};Pe(n.storageNames),Oe();let r=!!n.consent;r||us();let o=null;n.getConsent=()=>o?o.stamp():an();let i=()=>!r||o?.status==="granted",s=r&&n.holdAcrossPages?rn(F(L("held"),n.propertyId)):null,a=s?.read()??{items:[],anonymousId:null};n.mintAnonymousId!==void 0&&ls(n.debug);let u=on(n,{carried:a.anonymousId,mayStore:i,orOwn:!0}),d=qn({name:L("session"),domain:n.consentCookieDomain??null,mayStore:i}),p=n.getSessionId;n.getSessionId=()=>q(R(p,null))??d.get();let f=kn(L("preview"),n.consentCookieDomain??null),y=(c,l)=>n.debug&&console.warn("[hp]",c,l??""),x=n.transport??new Ke({apiHost:n.apiHost,writeKey:n.writeKey,storageKey:F(L("retry"),n.propertyId),mayPersist:r?()=>o?.status==="granted":void 0,deferReplay:r,onError:y}),S=F(L("previousPage"),n.propertyId),b=null,I=()=>{if(b)return b;try{return sessionStorage.getItem(S)||null}catch{return null}},k=c=>{if(b=c||null,!(r&&o.status!=="granted"))try{sessionStorage.setItem(S,c)}catch{}},h=vn({key:F(L("decisions"),n.propertyId),mayStore:i});n.getDecisions??(n.getDecisions=h.current);let v=q(R(n.getUserId,null)),w=null,g=null,_=null;function P(c,l,C=!1){try{return M(c,l,C)}catch(O){if(O instanceof me)throw O;return console.warn("[hp] the event was not sent",O),null}}function M(c,l,C){let O=_;_=null;let E={messageId:O?.messageId??Z(),timestamp:O?.timestamp??new Date().toISOString(),anonymousId:q(R(n.getAnonymousId,null)),userId:v??null,...c,context:Zt(n,or(l))};if(E.properties&&(E.properties=nt(E.properties)),!C&&!Tn(E.anonymousId,n.sampleRate))return n.debug&&console.debug("[hp] not sent: outside the sample",E),null;if(n.beforeSend){let de;try{de=n.beforeSend(E)}catch(yr){return console.warn("[hp] beforeSend failed; the event was not sent",yr),null}if(!de)return null;let{type:Ee,event:Dt,messageId:hr,timestamp:gr}=E;E={...de,type:Ee,messageId:hr,timestamp:gr,...Dt?{event:Dt}:{}}}if(n.validate){let de=n.validate(E,{producer:"web"});if(!de.valid){let Ee=`[hp] invalid ${E.event??E.type}:
2
+ ${de.errors.join(`
3
+ `)}`;if(n.throwOnInvalid)throw new me(Ee);console.error(Ee)}}n.debug&&console.debug("[hp]",E.event??E.type,E);let U=ds(E);return U>Tt?(y(`${E.event??E.type} not sent: ${U} bytes, over the ${Tt}-byte limit for one event`),null):(Ie(E,C),E)}function H(c){if(c.type==="track"&&G(c),n.onEvent)try{n.onEvent(c)}catch(l){console.warn("[hp] onEvent observer failed",l)}}let K=()=>r&&o?.state?.personalisation===!0,j=()=>Mn(R(n.getSessionId,null),n.propertyId),te=!1;function G(c){try{K()?(j(),On(St(Bn(c.event,c.properties))),te=!0):ne()}catch(l){n.debug&&console.warn("[hp] the visit was not recorded",l)}}function ne(c=!1){Ln(),(c||te)&&$n(),te=!1}function le(){d.forget(),h.forget(),B.clear(),u?.forget(),ne(!0)}function Ie(c,l){!r||l||o.admits()?(H(c),x.enqueue(c)):o.hold(c)}let m=(c,l,C)=>Hn(n.plan,c)?P({type:"track",event:c,properties:l??{}},C):null,B=new Set,Ae=(c,l,C)=>c==="audiences"?{audiences:l?null:C.audiences??[]}:c==="decisions"?{decisions:(C.decisions??[]).map(O=>({slot:O.slot,decision_id:O.decision_id??null,arm:O.arm??null,...O.variant&&{variant:O.variant}}))}:{recommendation_request_id:C.recommendation_request_id??null};r&&(o=In({adapter:n.consent,timeoutMs:n.consentTimeoutMs,cookieDomain:n.consentCookieDomain,debug:n.debug,store:s&&{read:()=>a.items,write:c=>s.write(c,u?.peek()??null)},release(c){c.context&&(c.context.consent=o.stamp()),H(c),x.enqueue(c)},onStatus(c){if(c==="granted"&&(u?.persist(),d.persist(),b&&k(b),h.persist(),x.replayPersisted?.()),c==="denied"){x.clearPersisted?.(),u?.forget(),d.forget();try{sessionStorage.removeItem(S)}catch{}h.forget(),ne(!0)}try{n.onConsentStatus?.(c)}catch(l){console.warn("[hp] onConsentStatus observer failed",l)}},announce({action:c,state:l,previous:C,withdrawal:O}){l?.personalisation!==!0&&ne(C?.personalisation===!0),P({type:"track",event:"Consent Updated",properties:{action:c,state:l}},void 0,O),O&&x.flush()}}),o.start());let J=()=>n.holdout?gt(n.holdout):null,ve=()=>yt(n.holdout,q(R(n.getAnonymousId,null))).group;function mr(c,l){if(!Array.isArray(c?.decisions))return c;let C=n.holdout?.readoutSlots??[],O=q(R(n.getAnonymousId,null));return{...c,decisions:c.decisions.map(E=>{let U=E?.variant==="bandit"||E?.variant==="default"?E.variant:!l&&E?.holdout!==!0&&C.includes(E?.slot)?En(E.slot,O):null;return U?U==="default"?{...E,variant:U,decision_id:null}:{...E,variant:U}:E})}}function Mt(c,l){if(f)return null;let C=c?.exposure,O=C?.holdout===!0,E=mr(c,O);return h.remember(An(E)),!C?.token||B.has(C.token)?null:(B.add(C.token),m("Personalisation Exposed",{exposure_token:C.token,surface:C.surface,holdout:O,holdout_share:C.holdout_share??J()?.holdout_share??null,personalised_share:C.personalised_share??J()?.personalised_share??null,...Ae(C.surface,O,E)},l))}return bn({track:m,page(c,l){c??(c={});let C=n.pageType??null;c.type&&(fe.has(c.type)?(C=c.type,n.pageType=c.type):n.debug&&console.warn(`[hp] ignoring unknown page.type "${c.type}". Allowed: ${[...fe].join(", ")}`));let O=P({type:"page",properties:{name:c.name??null,navigation_trigger:c.navigation_trigger??zt(),previous_message_id:I()}},{page:{type:C},...or(l)??{}});return O&&k(O.messageId),O},identify(c,l,C){let{userId:O,...E}=c&&typeof c=="object"?c:{};if(O!==void 0){let U=q(O);C===be?g=U:w&&U&&U!==w?le():n.debug&&g&&U&&U!==g&&console.warn("[hp] identify names another userId than the identity option; using the new one"),v=U,U&&C!==be&&(w=U)}if(typeof E.email_sha256=="string"){let U=Ge(E.email_sha256);U?E.email_sha256=U:delete E.email_sha256}return P({type:"identify",traits:E},l)},reset(){v&&le(),v=null,w=null,g=null,k("")},productViewed:(c,l)=>m("Product Viewed",c,l),productListViewed:(c,l)=>m("Product List Viewed",c,l),recommendationsViewed:(c,l)=>m("Recommendations Viewed",c,l),personalisationExposed:(c,l)=>m("Personalisation Exposed",c,l),recordExposure:Mt,surfaceViewed({surface:c}={},l){if(!os.has(c)||!n.holdout||c!=="decisions"&&!K())return null;let C=q(R(n.getAnonymousId,null)),O=yt(n.holdout,C).group;if(O==="excluded")return null;let E=Ht({sessionId:ke(R(n.getSessionId,null),C),surface:c});return E?Mt({exposure:{token:E,surface:c,holdout:O==="holdout"}},l):null},inHoldout:()=>ve()!=="personalised",personalisationGroup:ve,productClicked:(c,l)=>m("Product Clicked",c,l),productListFiltered:(c,l)=>m("Product List Filtered",c,l),productsSearched:(c,l)=>m("Products Searched",c,l),searchSuggestionsViewed:(c,l)=>m("Search Suggestions Viewed",c,l),productAdded:(c,l)=>m("Product Added",c,l),productRemoved:(c,l)=>m("Product Removed",c,l),cartUpdated:(c,l)=>m("Cart Updated",is(c),l),identityLinked:(c,l)=>m("Identity Linked",c,l),slotEngaged:(c,l)=>f?null:m("Slot Engaged",c,l),productVariantSelected:(c,l)=>m("Product Variant Selected",c,l),cartViewed:(c,l)=>m("Cart Viewed",c,l),productAddedToWishlist:(c,l)=>m("Product Added to Wishlist",c,l),productRemovedFromWishlist:(c,l)=>m("Product Removed from Wishlist",c,l),wishlistProductAddedToCart:(c,l)=>m("Wishlist Product Added to Cart",c,l),productShared:(c,l)=>m("Product Shared",c,l),cartShared:(c,l)=>m("Cart Shared",c,l),couponEntered:(c,l)=>m("Coupon Entered",c,l),couponApplied:(c,l)=>m("Coupon Applied",c,l),couponDenied:(c,l)=>m("Coupon Denied",c,l),couponRemoved:(c,l)=>m("Coupon Removed",c,l),checkoutStarted:(c,l)=>m("Checkout Started",c,l),promotionViewed:(c,l)=>m("Promotion Viewed",c,l),promotionClicked:(c,l)=>m("Promotion Clicked",c,l),checkoutStepViewed:(c,l)=>m("Checkout Step Viewed",c,l),checkoutStepCompleted:(c,l)=>m("Checkout Step Completed",c,l),paymentInfoEntered:(c,l)=>m("Payment Info Entered",c,l),checkoutFailed:(c,l)=>m("Checkout Failed",c,l),signedIn:(c,l)=>m("Signed In",c,l),signedUp:(c,l)=>m("Signed Up",c,l),signedOut(c){let l=m("Signed Out",{},c);return v&&le(),v=null,w=null,g=null,l},newsletterSubscribed:(c,l)=>m("Newsletter Subscribed",c,l),newsletterUnsubscribed:(c,l)=>m("Newsletter Unsubscribed",c,l),backInStockSubscribed:(c,l)=>m("Back In Stock Subscribed",c,l),productReviewed:(c,l)=>m("Product Reviewed",c,l),consentUpdated:(c,l)=>m("Consent Updated",c,l),flush:c=>x.flush(c),recommendationContext(){if(!K())return{};try{return j(),St(Dn())}catch{return{}}},get consent(){return o?o.state:null},servingHeaders:()=>ln(o?.state??null),get lastPageMessageId(){return I()},get anonymousId(){return q(R(n.getAnonymousId,null))},[ss](c,l,C){if(typeof this[c]=="function"){_=typeof C?.messageId=="string"&&typeof C?.timestamp=="string"?{messageId:C.messageId,timestamp:C.timestamp}:null;try{this[c](...Array.isArray(l)?l:[])}finally{_=null}}}})}var Rt=Symbol.for("@thg-agentic/hyper-personalisation-web.bootstrap"),fs=Symbol.for("@thg-agentic/hyper-personalisation-web.drain"),ps=Symbol.for("@thg-agentic/hyper-personalisation-web.carry"),ir=Symbol.for("@thg-agentic/hyper-personalisation-web.replay"),ms=Symbol.for("@thg-agentic/hyper-personalisation-web.assignment"),hs=Symbol.for("@thg-agentic/hyper-personalisation-web.recommendation-context"),Je=e=>e?()=>D(e):()=>null,ur={didomi:e=>dn({purposes:e.purposes}),onetrust:e=>fn({groups:e.groups,impliedConsent:e.impliedConsent}),cookie:e=>pn({name:e.name,categories:e.categories,refusedValue:e.refusedValue,source:e.source,changeEvent:e.changeEvent}),forwarded:e=>mn({name:e.name}),static:e=>{if(!e.state||typeof e.state!="object")throw new Error('consent.state is required for provider "static"');return hn(e.state)}},ar=Object.keys(ur),Se=e=>e!==null&&typeof e=="object"&&!Array.isArray(e),ze=e=>typeof e=="string"&&e!=="",gs=e=>Array.isArray(e)&&e.every(t=>typeof t=="string"),lr=e=>Se(e)&&typeof e.read=="function";function ys(e,t={}){let n=[];if(!Se(e))return["config must be { writeKey, apiHost? }"];ze(e.writeKey)||n.push("writeKey is required"),ye(e.apiHost)||n.push("apiHost, when given, must be a host or an http(s) origin"),t.propertyId!==void 0&&!ze(t.propertyId)&&n.push("propertyId must be a string");let{consent:r}=t;if(!lr(r)&&!(Se(r)&&ar.includes(r.provider))&&n.push(`consent must be a consent adapter or { provider: ${ar.join(" | ")} }`),t.autocapture!==void 0&&t.autocapture!==!1&&!Se(t.autocapture)&&n.push("autocapture must be false or { promotions, productLists, recommendations }"),t.beforeSend!==void 0&&typeof t.beforeSend!="function"&&n.push("beforeSend must be a function"),t.identityCookies!==void 0)if(!Se(t.identityCookies))n.push("identityCookies must be an object");else for(let o of["anonymousId","sessionId"]){let i=t.identityCookies[o];i!==void 0&&!ze(i)&&n.push(`identityCookies.${o} must be a cookie name`)}for(let o of["getCurrency","getShipToCountry","getLocale"])t[o]!==void 0&&typeof t[o]!="function"&&n.push(`${o} must be a function`);return t.dataLayer!==void 0&&(Se(t.dataLayer)?(t.dataLayer.name!==void 0&&!ze(t.dataLayer.name)&&n.push("dataLayer.name must be a string"),t.dataLayer.ignore!==void 0&&!gs(t.dataLayer.ignore)&&n.push("dataLayer.ignore must be an array of strings")):n.push("dataLayer must be an object")),n}function Ss(e,t,n){if(lr(e))return dt(e,t.consentPurposes);let{provider:r,...o}=e,i=n?.consent?.[r]??ur[r];return dt(i(o),t.consentPurposes)}var dr=(e,t)=>`${e??""}|${t??""}`;function ws(e,t){let n=q(t?.userId),r=Ge(t?.emailSha256);if(!n&&!r)return null;try{let o=F(L("identified"),e);if(sessionStorage.getItem(o)===dr(n,r))return null}catch{}return{...n?{userId:n}:{},...r?{email_sha256:r}:{}}}async function _s({writeKey:e,apiHost:t},n){if(n.settings!==void 0)return{settings:vt(n.settings),revalidated:null};let r=Gn(t,e),o=zn(e);if(o)return{settings:o,revalidated:r};let i=await r,s=i??At;return{settings:s,revalidated:Promise.resolve(s),unreachable:i===null}}function fr(e,t){let n=t??{},r=typeof window<"u"?window:null;if(r?.[Rt])return r[Rt];let o=sn(e?.writeKey,()=>{let{analytics:s}=_e(D(L("consent")));return s===!0||s===null&&n.holdAcrossPages===!0}),i=xs(e,n,o);if(r){let s=n.extend?.getSessionId??Je(n.identityCookies?.sessionId),a=Je(n.identityCookies?.anonymousId);r[ms]=({slot:u,arm:d}={})=>Ft({sessionId:ke(s(),a()),slot:u,arm:d}),r[Rt]=i,r[ps]=o,r[fs]?.(i)}return i}function bs(){let e=globalThis.performance?.now?.();return new Date(Date.now()-(Number.isFinite(e)?e:0)).toISOString()}function cr(e,t){if(t===!1)return()=>{};let n={promotions:!0,productLists:!0,recommendations:!0,exposures:!0,...t},r=i=>i&&typeof i=="object"?i:{},o=[];try{n.promotions&&o.push(Qe(e,r(n.promotions))),n.productLists&&o.push(Ze(e,r(n.productLists))),n.recommendations&&o.push(et(e,r(n.recommendations))),n.exposures&&o.push(Xe(e,r(n.exposures)))}catch(i){console.warn("[hp] autocapture not started:",i?.message??i)}return()=>{for(let i of o)i()}}function We(e){return e.close(),null}async function xs(e,t,n){try{let r=ys(e,t);if(r.length)return console.warn(`[hp] not started, the configuration is invalid: ${r.join("; ")}`),We(n);Pe(t.storageNames),Oe();let{writeKey:o}=e,[{settings:i,revalidated:s,unreachable:a},u]=await Promise.all([_s(e,t),Promise.resolve(t.identity).catch(()=>null)]),d=s?i:null,p=!1,f=()=>p&&d&&Wn(o,d);if(s?.then(g=>{g&&(d=g,g.enabled?f():Et(o))}),a)return null;if(!i.enabled)return t.debug&&console.info("[hp] off: the settings for this write key do not enable it"),We(n);let y=i.propertyId??t.propertyId;if(!y)return console.warn("[hp] not started: this write key covers several properties, so propertyId is required"),We(n);let{preset:x}=t,S=ws(y,u),b=t.debug===!0||i.debug?.enabled===!0||_n(i.debug?.tokenSha256??null,L("debug")),I=q(u?.userId),k=sr({apiHost:i.apiHost??ye(e.apiHost),writeKey:o,propertyId:y,pageType:fe.has(t.pageType)?t.pageType:void 0,environment:i.environment,queryParamAllowlist:i.queryParamAllowlist??void 0,plan:i.plan,sampleRate:i.sampleRate??1,holdout:i.holdouts?.[y],getCurrency:t.getCurrency,getShipToCountry:t.getShipToCountry,getLocale:t.getLocale,consent:Ss(t.consent,i,x),consentCookieDomain:t.consentCookieDomain,storageNames:t.storageNames,holdAcrossPages:t.holdAcrossPages===!0,mintAnonymousId:t.mintAnonymousId,debug:b,anonymousIdSource:i.anonymousId??void 0,getAnonymousId:Je(t.identityCookies?.anonymousId),getSessionId:t.identityCookies?.sessionId?Je(t.identityCookies.sessionId):void 0,getUserId:()=>I,beforeSend:t.beforeSend,...t.extend,onConsentStatus(g){if(g==="granted"&&(p=!0,f()),g==="denied"){Et(o);try{sessionStorage.removeItem(F(L("identified"),y))}catch{}}t.extend?.onConsentStatus?.(g)},onEvent(g){if(g.type==="identify"){let _=dr(q(g.userId),Ge(g.traits?.email_sha256));try{sessionStorage.setItem(F(L("identified"),y),_)}catch{}}t.extend?.onEvent?.(g)}});typeof window<"u"&&(window[hs]=()=>k.recommendationContext());for(let[g,_,P]of n.take())try{k[ir](g,_,P)}catch(M){console.warn(`[hp] carried ${g} failed`,M)}k[ir]("page",[t.pageType?{type:t.pageType}:{}],{messageId:Z(),timestamp:bs()});let h=cr(k,t.autocapture),v=k.page;k.page=ht("page",(...g)=>{let _=v(...g);return h(),h=cr(k,t.autocapture),_});let w=typeof window<"u"?window:null;if(t.dataLayer&&w){let g=t.dataLayer.name??"dataLayer";w[g]=w[g]||[],wn(k,{...x,dataLayer:w[g],ignore:[...x?.ignore??[],...t.dataLayer.ignore??[]],onError:_=>b&&console.warn("[hp] dataLayer mapping failed",_)})}return S&&k.identify(S,void 0,be),k}catch(r){return console.warn("[hp] not started:",r?.message??r),We(n)}}function pr(e){e.initialized=!0,fr(e._config,e._options).then(t=>{let n=e.splice(0);if(!t){for(let r of e.methods??[])e[r]=()=>e;return}e.client=t;for(let[r,o]of Object.entries(t))typeof o=="function"&&(e[r]=o);for(let[r,...o]of n)try{t[r]?.(...o)}catch(i){console.warn(`[hp] queued ${r} failed`,i)}})}var V=window.hp=window.hp||[];V.servingHeaders??(V.servingHeaders=()=>({}));V.inHoldout??(V.inHoldout=()=>!0);V.personalisationGroup??(V.personalisationGroup=()=>"excluded");V.recommendationContext??(V.recommendationContext=()=>({}));V.initialized||(V._config?pr(V):V.load=(e,t)=>{V._config=e,V._options=t??{},pr(V)});})();
@@ -1 +1 @@
1
- (()=>{(()=>{let e=window.hp=window.hp||[];if(!(e.initialized||e.invoked)){e.invoked=!0,e.methods=["track","page","identify","reset","flush","productViewed","productListViewed","recommendationsViewed","personalisationExposed","recordExposure","surfaceViewed","productClicked","productListFiltered","productsSearched","productAdded","productRemoved","cartUpdated","identityLinked","slotEngaged","productVariantSelected","cartViewed","productAddedToWishlist","productRemovedFromWishlist","wishlistProductAddedToCart","productShared","cartShared","couponEntered","couponApplied","couponDenied","couponRemoved","checkoutStarted","promotionViewed","promotionClicked","checkoutStepViewed","checkoutStepCompleted","paymentInfoEntered","checkoutFailed","signedIn","signedUp","signedOut","newsletterSubscribed","newsletterUnsubscribed","backInStockSubscribed","productReviewed","consentUpdated"],e.factory=t=>(...o)=>(e.push([t,...o]),e);for(let t of e.methods)e[t]=e.factory(t);e.servingHeaders=()=>({}),e.inHoldout=()=>!0,e.personalisationGroup=()=>"excluded",e.recommendationContext=()=>({}),e.load=(t,o,r)=>{e._config=t,e._options=o||{};let d=document.createElement("script");d.async=!0,d.src=r;let i=document.getElementsByTagName("script")[0];i.parentNode.insertBefore(d,i)},e.SNIPPET_VERSION="1",e.load({writeKey:"hp_pk_live_REPLACE_ME",apiHost:"https://api.example.com"},{consent:{provider:"onetrust"},identityCookies:{sessionId:"session_id"}},"https://cdn.jsdelivr.net/npm/@thg-agentic/hyper-personalisation-web@3/dist/hp.min.js")}})();})();
1
+ (()=>{(()=>{let e=window.hp=window.hp||[];if(!(e.initialized||e.invoked)){e.invoked=!0,e.methods=["track","page","identify","reset","flush","productViewed","productListViewed","recommendationsViewed","personalisationExposed","recordExposure","surfaceViewed","productClicked","productListFiltered","productsSearched","searchSuggestionsViewed","productAdded","productRemoved","cartUpdated","identityLinked","slotEngaged","productVariantSelected","cartViewed","productAddedToWishlist","productRemovedFromWishlist","wishlistProductAddedToCart","productShared","cartShared","couponEntered","couponApplied","couponDenied","couponRemoved","checkoutStarted","promotionViewed","promotionClicked","checkoutStepViewed","checkoutStepCompleted","paymentInfoEntered","checkoutFailed","signedIn","signedUp","signedOut","newsletterSubscribed","newsletterUnsubscribed","backInStockSubscribed","productReviewed","consentUpdated"],e.factory=t=>(...o)=>(e.push([t,...o]),e);for(let t of e.methods)e[t]=e.factory(t);e.servingHeaders=()=>({}),e.inHoldout=()=>!0,e.personalisationGroup=()=>"excluded",e.recommendationContext=()=>({}),e.load=(t,o,r)=>{e._config=t,e._options=o||{};let d=document.createElement("script");d.async=!0,d.src=r;let i=document.getElementsByTagName("script")[0];i.parentNode.insertBefore(d,i)},e.SNIPPET_VERSION="1",e.load({writeKey:"hp_pk_live_REPLACE_ME",apiHost:"https://api.example.com"},{consent:{provider:"onetrust"},identityCookies:{sessionId:"session_id"}},"https://cdn.jsdelivr.net/npm/@thg-agentic/hyper-personalisation-web@3/dist/hp.min.js")}})();})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thg-agentic/hyper-personalisation-web",
3
- "version": "3.18.0",
3
+ "version": "3.19.0",
4
4
  "description": "Browser SDK for the THG Hyper-Personalisation event specification: event tracking from a write key and remote settings, strict CMP-agnostic consent, a GA4 dataLayer bridge and in-session audience evaluation. Dependency-free ESM on the Segment wire protocol, a CDN script (dist/hp.min.js), and an optional @segment/analytics-next plugin path.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
@@ -74,7 +74,7 @@
74
74
  "esbuild": "^0.28.2",
75
75
  "jsdom": "^30.1.1",
76
76
  "typescript": "^7.0.2",
77
- "@thg-agentic/hyper-personalisation-spec": "1.10.0"
77
+ "@thg-agentic/hyper-personalisation-spec": "1.12.0"
78
78
  },
79
79
  "scripts": {
80
80
  "test": "node --test tests/*.test.mjs",
package/src/bootstrap.js CHANGED
@@ -41,6 +41,8 @@ import {
41
41
  import { getCookie } from './context.js';
42
42
  import { connectDataLayer } from './datalayer.js';
43
43
  import { sessionDebug } from './debug.js';
44
+ import { guard } from './guard.js';
45
+ import { IDENTITY_OPTION } from './identity-option.js';
44
46
  import { createAnalytics, normaliseSha256, PAGE_TYPES } from './index.js';
45
47
  import {
46
48
  cachedSettings,
@@ -229,7 +231,8 @@ async function resolveSettings({ writeKey, apiHost }, options) {
229
231
  * @param {import('../types/bootstrap').BootstrapOptions} options See types/bootstrap.d.ts.
230
232
  * @returns {Promise<object|null>} the client, or null when it is off.
231
233
  */
232
- export function bootstrap(config, options = {}) {
234
+ export function bootstrap(config, maybeOptions) {
235
+ const options = maybeOptions ?? {};
233
236
  const host = typeof window !== 'undefined' ? window : null;
234
237
  if (host?.[INSTANCE]) return host[INSTANCE];
235
238
  // What `hp` queued on an earlier page that navigated before its client started. Carried only as
@@ -472,12 +475,12 @@ async function start(config, options, carry) {
472
475
  let stopAutocapture = startAutocapture(client, options.autocapture);
473
476
  // Impressions count once per page view, so a single-page app's next route reports afresh.
474
477
  const page = client.page;
475
- client.page = (...args) => {
478
+ client.page = guard('page', (...args) => {
476
479
  const event = page(...args);
477
480
  stopAutocapture();
478
481
  stopAutocapture = startAutocapture(client, options.autocapture);
479
482
  return event;
480
- };
483
+ });
481
484
 
482
485
  const host = typeof window !== 'undefined' ? window : null;
483
486
  if (options.dataLayer && host) {
@@ -495,7 +498,7 @@ async function start(config, options, carry) {
495
498
  });
496
499
  }
497
500
 
498
- if (identified) client.identify(identified);
501
+ if (identified) client.identify(identified, undefined, IDENTITY_OPTION);
499
502
  return client;
500
503
  } catch (err) {
501
504
  console.warn('[hp] not started:', err?.message ?? err);
@@ -92,7 +92,7 @@ function previousFromCookie(name) {
92
92
  * @param {string} [options.cookieName] Defaults to storageName('consent'), `thg_hp_consent`.
93
93
  * @param {(item: any) => void} options.release A held item may now be sent.
94
94
  * @param {(item: any) => void} [options.discard] A held item will never be sent.
95
- * @param {(update: { action: string, state: object, withdrawal: boolean }) => void} options.announce
95
+ * @param {(update: { action: string, state: object, previous: object|null, withdrawal: boolean }) => void} options.announce
96
96
  * Record a Consent Updated. Called only when analytics is granted, or for the withdrawal itself.
97
97
  * @param {(status: string) => void} [options.onStatus]
98
98
  * @param {boolean} [options.debug]
@@ -201,6 +201,7 @@ export function createConsentGate(options) {
201
201
  announce({
202
202
  action: consentAction(next, previous),
203
203
  state: next,
204
+ previous,
204
205
  withdrawal: false,
205
206
  });
206
207
  return;
@@ -210,7 +211,12 @@ export function createConsentGate(options) {
210
211
  // A withdrawal is recorded, then nothing more: the raw table is where a refusal is proven to
211
212
  // have been received, and the warehouse's consent filter keeps it out of every model.
212
213
  if (previous?.analytics === true)
213
- announce({ action: 'withdrawn', state: next, withdrawal: true });
214
+ announce({
215
+ action: 'withdrawn',
216
+ state: next,
217
+ previous,
218
+ withdrawal: true,
219
+ });
214
220
  setStatus('denied');
215
221
  }
216
222
 
@@ -154,8 +154,9 @@ export function createDeviceCookie({
154
154
  /** Consent was granted: write what was minted before it. */
155
155
  persist,
156
156
  /**
157
- * Consent was refused or withdrawn: the id must not outlive the permission it was kept under,
158
- * so a later grant starts a new device, as `analytics.reset()` does.
157
+ * Consent was refused or withdrawn, or the user signed out: the id must not outlive the
158
+ * permission or the person it was kept under, so the next event starts a new device, as
159
+ * Segment's `analytics.reset()` does.
159
160
  */
160
161
  forget() {
161
162
  writeCookie(name, '', 0, domain);
package/src/guard.js ADDED
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Tracking never throws into the host. Every public method runs behind `guard`: a failure is logged
3
+ * and the method answers what a client that could not decide answers. That fails closed, as the CDN
4
+ * stub does before the client starts: no personalisation, no consent headers, nothing sent.
5
+ */
6
+
7
+ /** `throwOnInvalid`: the one failure a caller asked to have thrown at them. */
8
+ export class InvalidEventError extends Error {}
9
+
10
+ const FALLBACKS = {
11
+ inHoldout: () => true,
12
+ personalisationGroup: () => 'excluded',
13
+ servingHeaders: () => ({}),
14
+ recommendationContext: () => ({}),
15
+ flush: () => Promise.resolve(),
16
+ reset: () => undefined,
17
+ };
18
+
19
+ /** `method` behind the guard, answering `name`'s fallback when it throws. */
20
+ export function guard(name, method) {
21
+ const fallback = FALLBACKS[name] ?? (() => null);
22
+ const failed = (err) => {
23
+ console.warn(`[hp] ${String(name)} failed; nothing was sent`, err);
24
+ return fallback();
25
+ };
26
+ return function guarded(...args) {
27
+ try {
28
+ const result = method.apply(this, args);
29
+ return typeof result?.then === 'function'
30
+ ? Promise.resolve(result).catch(failed)
31
+ : result;
32
+ } catch (err) {
33
+ if (err instanceof InvalidEventError) throw err;
34
+ return failed(err);
35
+ }
36
+ };
37
+ }
38
+
39
+ /** Every method of `client` behind the guard, in place. Getters are left as they are. */
40
+ export function guardMethods(client) {
41
+ for (const key of Reflect.ownKeys(client)) {
42
+ const { value } = Object.getOwnPropertyDescriptor(client, key) ?? {};
43
+ if (typeof value === 'function') client[key] = guard(key, value);
44
+ }
45
+ return client;
46
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Marks bootstrap's own identify, from its `identity` option, so it never counts as the earlier
3
+ * user a later host `identify` would be switching away from: the host may name the same person
4
+ * in another form.
5
+ */
6
+ export const IDENTITY_OPTION = Symbol('hp.identityOption');
package/src/index.js CHANGED
@@ -53,7 +53,9 @@ import {
53
53
  holdoutAssignment,
54
54
  holdoutShares,
55
55
  } from './experiments.js';
56
+ import { guardMethods, InvalidEventError } from './guard.js';
56
57
  import { emailSha256, sha256Hex } from './hash.js';
58
+ import { IDENTITY_OPTION } from './identity-option.js';
57
59
  import { applyPreview } from './preview.js';
58
60
  import {
59
61
  forgetVisit,
@@ -82,7 +84,11 @@ import {
82
84
  storageKey,
83
85
  storageName,
84
86
  } from './storage-scope.js';
85
- import { Transport } from './transport.js';
87
+ import {
88
+ MAX_EVENT_BYTES,
89
+ Transport,
90
+ byteLength as utf8Bytes,
91
+ } from './transport.js';
86
92
  import { LIB, VERSION } from './version.js';
87
93
 
88
94
  /** The surfaces a `Personalisation Exposed` names, as the public API's exposures do. */
@@ -104,9 +110,6 @@ export { emailSha256, getCookie };
104
110
  /** How a call made before the SDK started is replayed with the messageId and time it was made at. */
105
111
  const REPLAY = Symbol.for('@thg-agentic/hyper-personalisation-web.replay');
106
112
 
107
- /** `throwOnInvalid`: the one failure a caller asked to have thrown at them. */
108
- class InvalidEventError extends Error {}
109
-
110
113
  /** SHA-256 of a string as lowercase hex. Synchronous since 3.9 (sha256Hex); kept async for callers. */
111
114
  export async function sha256(value) {
112
115
  return sha256Hex(String(value));
@@ -195,6 +198,19 @@ export { createSocialProof } from './social-proof.js';
195
198
  export { VERSION };
196
199
 
197
200
  let warnedMintAnonymousId = false;
201
+ let warnedHostBasis = false;
202
+
203
+ /**
204
+ * Without a consent adapter the SDK sends under the host's lawful basis: fail-open, so say it once.
205
+ * Requiring `consent` is a breaking change, planned for 4.0.
206
+ */
207
+ function warnHostBasis() {
208
+ if (warnedHostBasis) return;
209
+ warnedHostBasis = true;
210
+ console.warn(
211
+ "[hp] no consent adapter: events are sent under the host's lawful basis (basis 'host'). Pass `consent` to send only once the visitor allows analytics.",
212
+ );
213
+ }
198
214
 
199
215
  /** 3.5's `mintAnonymousId` does nothing since 3.6: where device ids come from is the source's. */
200
216
  function warnMintAnonymousId(debug) {
@@ -205,6 +221,15 @@ function warnMintAnonymousId(debug) {
205
221
  );
206
222
  }
207
223
 
224
+ /** UTF-8 bytes of the event as sent; 0 for one JSON cannot serialise, which delivery drops itself. */
225
+ function serialisedBytes(event) {
226
+ try {
227
+ return utf8Bytes(JSON.stringify(event));
228
+ } catch {
229
+ return 0;
230
+ }
231
+ }
232
+
208
233
  /** Per-call context overrides, given directly or analytics-next style as `{ context }`. */
209
234
  function contextOverrides(options) {
210
235
  if (
@@ -243,6 +268,7 @@ export function createAnalytics(options) {
243
268
  // A consent adapter makes the SDK strict: nothing is sent or stored until analytics is granted.
244
269
  // Without one the host has decided the lawful basis itself, and every event says so (spec 1.5).
245
270
  const strict = Boolean(cfg.consent);
271
+ if (!strict) warnHostBasis();
246
272
  let gate = null;
247
273
  cfg.getConsent = () => (gate ? gate.stamp() : hostConsent());
248
274
  const granted = () => !strict || gate?.status === 'granted';
@@ -276,6 +302,10 @@ export function createAnalytics(options) {
276
302
  cfg.consentCookieDomain ?? null,
277
303
  );
278
304
 
305
+ /** Delivery problems, logged with `debug`. */
306
+ const reportError = (message, err) =>
307
+ cfg.debug && console.warn('[hp]', message, err ?? '');
308
+
279
309
  const transport =
280
310
  cfg.transport ??
281
311
  new Transport({
@@ -288,7 +318,7 @@ export function createAnalytics(options) {
288
318
  // grant (see onStatus below, which replays on a grant and clears on a refusal).
289
319
  mayPersist: strict ? () => gate?.status === 'granted' : undefined,
290
320
  deferReplay: strict,
291
- onError: (m, e) => cfg.debug && console.warn('[hp]', m, e ?? ''),
321
+ onError: reportError,
292
322
  });
293
323
 
294
324
  // The previous page view's messageId, carried across navigations in sessionStorage, per tenant.
@@ -323,6 +353,10 @@ export function createAnalytics(options) {
323
353
  cfg.getDecisions ??= decisions.current;
324
354
 
325
355
  let userId = realId(perRequest(cfg.getUserId, null));
356
+ /** The user the host's last `identify` named: a different one later on this page is a switch. */
357
+ let identifiedUserId = null;
358
+ /** The user bootstrap's `identity` option named, only to warn when the host names another form. */
359
+ let optionsUserId = null;
326
360
 
327
361
  /** The call being replayed: its first event keeps the messageId and time it was made with. */
328
362
  let replaying = null;
@@ -391,6 +425,16 @@ export function createAnalytics(options) {
391
425
 
392
426
  if (cfg.debug) console.debug('[hp]', event.event ?? event.type, event);
393
427
 
428
+ // Too big to travel in a keepalive request on its own: it could never go at unload, and once
429
+ // persisted it would push every other batch out of the retry buffer.
430
+ const bytes = serialisedBytes(event);
431
+ if (bytes > MAX_EVENT_BYTES) {
432
+ reportError(
433
+ `${event.event ?? event.type} not sent: ${bytes} bytes, over the ${MAX_EVENT_BYTES}-byte limit for one event`,
434
+ );
435
+ return null;
436
+ }
437
+
394
438
  deliver(event, force);
395
439
  return event;
396
440
  }
@@ -429,15 +473,36 @@ export function createAnalytics(options) {
429
473
  visitKept = true;
430
474
  } else {
431
475
  // Not allowed, or withdrawn: what was kept under the permission goes with it.
432
- forgetVisit();
433
- if (visitKept) clearSessionSignals();
434
- visitKept = false;
476
+ forgetKeptVisit();
435
477
  }
436
478
  } catch (err) {
437
479
  if (cfg.debug) console.warn('[hp] the visit was not recorded', err);
438
480
  }
439
481
  }
440
482
 
483
+ /**
484
+ * What the visit built up for recommendations, kept only under personalisation consent. The
485
+ * session signals are cleared only when this SDK built them, unless `all`: a refusal or a sign-out.
486
+ */
487
+ function forgetKeptVisit(all = false) {
488
+ forgetVisit();
489
+ if (all || visitKept) clearSessionSignals();
490
+ visitKept = false;
491
+ }
492
+
493
+ /**
494
+ * A sign-out, or another user signing in: the SDK's session, the decisions in force, the visit
495
+ * and the SDK's own device id go with the user, as Segment's `reset()` starts a new anonymousId.
496
+ * A device id read from the site's own cookie stays: the SDK does not own it.
497
+ */
498
+ function forgetVisitor() {
499
+ session.forget();
500
+ decisions.forget();
501
+ exposedTokens.clear();
502
+ device?.forget();
503
+ forgetKeptVisit(true);
504
+ }
505
+
441
506
  function deliver(event, force) {
442
507
  // The observer only sees what will be sent, never an event the visitor has not allowed.
443
508
  if (!strict || force || gate.admits()) {
@@ -509,6 +574,7 @@ export function createAnalytics(options) {
509
574
  /* nothing was stored */
510
575
  }
511
576
  decisions.forget();
577
+ forgetKeptVisit(true);
512
578
  }
513
579
  try {
514
580
  cfg.onConsentStatus?.(status);
@@ -516,7 +582,12 @@ export function createAnalytics(options) {
516
582
  console.warn('[hp] onConsentStatus observer failed', err);
517
583
  }
518
584
  },
519
- announce({ action, state, withdrawal }) {
585
+ announce({ action, state, previous, withdrawal }) {
586
+ // Withdrawn here, not only when the next event reaches recordVisit: a beforeSend that drops
587
+ // it must not keep the visit. A withdrawal on a later page, before it recorded anything,
588
+ // forgets the signals an earlier page kept under the permission.
589
+ if (state?.personalisation !== true)
590
+ forgetKeptVisit(previous?.personalisation === true);
520
591
  emit(
521
592
  {
522
593
  type: 'track',
@@ -611,11 +682,12 @@ export function createAnalytics(options) {
611
682
  );
612
683
  }
613
684
 
614
- return {
685
+ return guardMethods({
615
686
  /** Escape hatch for events not yet in the core spec. Prefer a named method. */
616
687
  track,
617
688
 
618
- page(props = {}, ctx) {
689
+ page(props, ctx) {
690
+ props ??= {};
619
691
  // page.type is inherited by every later event on the page, so an unknown value would get
620
692
  // them all rejected. Keep the last good type instead: stale beats invalid.
621
693
  let pageType = cfg.pageType ?? null;
@@ -647,10 +719,31 @@ export function createAnalytics(options) {
647
719
  return ev;
648
720
  },
649
721
 
650
- identify(traits, ctx) {
722
+ identify(traits, ctx, source) {
651
723
  const { userId: id, ...rest } =
652
724
  traits && typeof traits === 'object' ? traits : {};
653
- if (id !== undefined) userId = realId(id);
725
+ if (id !== undefined) {
726
+ const next = realId(id);
727
+ if (source === IDENTITY_OPTION) {
728
+ optionsUserId = next;
729
+ } else if (identifiedUserId && next && next !== identifiedUserId) {
730
+ // The host named another user after an earlier one on this page: nothing the last one
731
+ // built up carries over. A host cookie or bootstrap's identity option never counts as
732
+ // the earlier user: either may name the same person in another form.
733
+ forgetVisitor();
734
+ } else if (
735
+ cfg.debug &&
736
+ optionsUserId &&
737
+ next &&
738
+ next !== optionsUserId
739
+ ) {
740
+ console.warn(
741
+ '[hp] identify names another userId than the identity option; using the new one',
742
+ );
743
+ }
744
+ userId = next;
745
+ if (next && source !== IDENTITY_OPTION) identifiedUserId = next;
746
+ }
654
747
  // A digest in hex or base64 is sent as hex; anything that is not one is not sent.
655
748
  if (typeof rest.email_sha256 === 'string') {
656
749
  const email = normaliseSha256(rest.email_sha256);
@@ -660,9 +753,16 @@ export function createAnalytics(options) {
660
753
  return emit({ type: 'identify', traits: rest }, ctx);
661
754
  },
662
755
 
663
- /** Clear identity on sign-out so the next events are not attributed to the old user. */
756
+ /**
757
+ * Clear the signed-in user. At a real sign-out (a user was signed in), also forget what the visit
758
+ * built up and the SDK's own device id, so the next person on a shared device inherits none of
759
+ * it. A device id from the site's own cookie stays: the site owns it.
760
+ */
664
761
  reset() {
762
+ if (userId) forgetVisitor();
665
763
  userId = null;
764
+ identifiedUserId = null;
765
+ optionsUserId = null;
666
766
  writeLastPageMessageId('');
667
767
  },
668
768
 
@@ -728,7 +828,14 @@ export function createAnalytics(options) {
728
828
  personalisationGroup,
729
829
  productClicked: (p, c) => track('Product Clicked', p, c),
730
830
  productListFiltered: (p, c) => track('Product List Filtered', p, c),
831
+ /** A submitted search and the results page it produced: the query, its results and `pagination`. */
731
832
  productsSearched: (p, c) => track('Products Searched', p, c),
833
+ /**
834
+ * The search box's suggested products for a partly typed query, once seen: never
835
+ * productsSearched, which is a submitted search's results page. A click on one carries this
836
+ * set's `search_request_id`.
837
+ */
838
+ searchSuggestionsViewed: (p, c) => track('Search Suggestions Viewed', p, c),
732
839
  productAdded: (p, c) => track('Product Added', p, c),
733
840
  productRemoved: (p, c) => track('Product Removed', p, c),
734
841
  /**
@@ -780,7 +887,10 @@ export function createAnalytics(options) {
780
887
  /** Sent under the signed-in identity, which is then cleared for whoever uses the device next. */
781
888
  signedOut(c) {
782
889
  const event = track('Signed Out', {}, c);
890
+ if (userId) forgetVisitor();
783
891
  userId = null;
892
+ identifiedUserId = null;
893
+ optionsUserId = null;
784
894
  return event;
785
895
  },
786
896
  newsletterSubscribed: (p, c) => track('Newsletter Subscribed', p, c),
@@ -836,5 +946,5 @@ export function createAnalytics(options) {
836
946
  replaying = null;
837
947
  }
838
948
  },
839
- };
949
+ });
840
950
  }
@@ -7,6 +7,8 @@
7
7
  * `new URLSearchParams(context)` is the query string to append, and an empty visit appends nothing.
8
8
  */
9
9
 
10
+ import { parseConsentCookie } from './consent.js';
11
+ import { getCookie } from './context.js';
10
12
  import { storageName } from './storage-scope.js';
11
13
 
12
14
  /** packages/cdp/src/recommendations/request.ts: MAX_IDS, MAX_ID_LENGTH, five queries, 60 pairs. */
@@ -86,9 +88,16 @@ export function forgetVisit() {
86
88
  }
87
89
  }
88
90
 
89
- /** The visit an earlier page kept, or `{}` once it has lapsed. */
91
+ /**
92
+ * The visit an earlier page kept, or `{}` once it has lapsed, or when the consent cookie does not
93
+ * grant personalisation: a withdrawal on a page this SDK never ran on still stops it being read.
94
+ */
90
95
  export function keptVisit(now = Date.now()) {
91
96
  try {
97
+ const { personalisation } = parseConsentCookie(
98
+ getCookie(storageName('consent')),
99
+ );
100
+ if (personalisation !== true) return {};
92
101
  const kept = JSON.parse(
93
102
  localStorage.getItem(storageName('recommendationContext')) ?? 'null',
94
103
  );
@@ -13,11 +13,12 @@ const ENGAGEMENT_EVENTS = new Set([
13
13
  'Product Added to Wishlist',
14
14
  ]);
15
15
 
16
- /** Engagement the warehouse's category affinity does not count, so a session measure must not. */
17
- const UNCOUNTED_ENGAGEMENT = new Set([
18
- 'Product Clicked',
19
- 'Product Added to Wishlist',
20
- ]);
16
+ /**
17
+ * The browsing events the warehouse's category affinity counts (identifier_affinity_daily), so a
18
+ * session measure counts the same: a product shown in a listing, removed or found in search is not
19
+ * interest. The warehouse also counts orders, which a visit's own measure leaves out.
20
+ */
21
+ const CATEGORY_EVENTS = new Set(['Product Viewed', 'Product Added']);
21
22
 
22
23
  /**
23
24
  * Fold one CDP event into the session signals.
@@ -39,9 +40,8 @@ export function recordSessionEvent(eventName, properties) {
39
40
  bump(s.recentBrowse, s.browsingCategory);
40
41
  break;
41
42
  case 'Product List Viewed':
42
- s.listViews += 1;
43
- // Only a category list is browsing. A rail or carousel names its widget (no slash, or a
44
- // `cdp-` id), and counting it would let personalisation measure itself.
43
+ // Only a category list is browsing, or a category page viewed. A rail or carousel names its
44
+ // widget (no slash, or a `cdp-` id), and counting it would let personalisation measure itself.
45
45
  {
46
46
  const list = properties?.list_id;
47
47
  const isCategoryPath =
@@ -49,6 +49,7 @@ export function recordSessionEvent(eventName, properties) {
49
49
  list.includes('/') &&
50
50
  !list.startsWith('cdp-');
51
51
  if (isCategoryPath) {
52
+ s.listViews += 1;
52
53
  s.browsingCategory = bump(s.recentBrowse, list) ?? s.browsingCategory;
53
54
  }
54
55
  }
@@ -159,7 +160,7 @@ export function recordSessionEvent(eventName, properties) {
159
160
  ].slice(0, RECENT_LIMIT);
160
161
  }
161
162
  if (
162
- !UNCOUNTED_ENGAGEMENT.has(eventName) &&
163
+ CATEGORY_EVENTS.has(eventName) &&
163
164
  typeof p.category === 'string' &&
164
165
  p.category
165
166
  ) {
@@ -1,4 +1,4 @@
1
1
  // Generated from the spec by scripts/build-types.mjs. DO NOT EDIT: run `pnpm run build:types`.
2
2
 
3
3
  /** The event specification this SDK emits, stamped on every event as context.spec.version. */
4
- export const SPEC_VERSION = '1.10';
4
+ export const SPEC_VERSION = '1.12';
package/src/transport.js CHANGED
@@ -32,12 +32,30 @@ const DEFAULTS = {
32
32
  };
33
33
 
34
34
  const now = () => Date.now();
35
+ const MAX_BACKOFF_MS = 30_000;
36
+
37
+ /** The largest event the SDK sends: with its batch envelope it still fits one keepalive request. */
38
+ export const MAX_EVENT_BYTES = 55 * 1024;
39
+
40
+ /**
41
+ * Retry-After as milliseconds from now: delay-seconds or an HTTP date (RFC 9110 §10.2.3); 0 when
42
+ * absent or unreadable. Cross-origin, the browser exposes it only if the ingest lists it in
43
+ * Access-Control-Expose-Headers; a first-party proxy always can.
44
+ */
45
+ export function retryAfterMs(value, at = now()) {
46
+ if (typeof value !== 'string') return 0;
47
+ const text = value.trim();
48
+ if (/^\d+$/.test(text)) return Number(text) * 1000;
49
+ if (text === '') return 0;
50
+ const date = Date.parse(text);
51
+ return Number.isNaN(date) ? 0 : Math.max(0, date - at);
52
+ }
35
53
 
36
54
  /**
37
55
  * UTF-8 bytes, the unit of sendBeacon's ceiling and the keepalive budget. String length counts
38
56
  * UTF-16 code units and undercounts any non-ASCII catalogue.
39
57
  */
40
- const byteLength = (s) =>
58
+ export const byteLength = (s) =>
41
59
  typeof TextEncoder !== 'undefined'
42
60
  ? new TextEncoder().encode(s).length
43
61
  : Buffer.byteLength(s);
@@ -282,6 +300,7 @@ export class Transport {
282
300
  async #send(batch, attempt, persisted = null) {
283
301
  if (persisted) this.sending.add(persisted);
284
302
  const body = this.#body(batch);
303
+ let wait = 0;
285
304
  try {
286
305
  const res = await fetch(this.#url(), {
287
306
  method: 'POST',
@@ -300,6 +319,7 @@ export class Transport {
300
319
  // 4xx (except 429) means the payload is wrong; retrying cannot fix it and
301
320
  // would loop forever. Drop and surface.
302
321
  const retryable = res.status === 429 || res.status >= 500;
322
+ if (retryable) wait = retryAfterMs(res.headers?.get?.('retry-after'));
303
323
  if (!retryable) {
304
324
  this.#settle(persisted);
305
325
  this.o.onError?.(
@@ -318,8 +338,11 @@ export class Transport {
318
338
  );
319
339
  return false;
320
340
  }
321
- const delay =
322
- Math.min(1000 * 2 ** attempt, 30_000) * (0.5 + Math.random());
341
+ // The ingest's Retry-After is a floor on the backoff, within the same cap.
342
+ const delay = Math.max(
343
+ Math.min(1000 * 2 ** attempt, MAX_BACKOFF_MS) * (0.5 + Math.random()),
344
+ Math.min(wait, MAX_BACKOFF_MS),
345
+ );
323
346
  await new Promise((r) => setTimeout(r, delay));
324
347
  return this.#send(batch, attempt + 1, persisted);
325
348
  }
package/src/version.js CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '3.18.0';
1
+ export const VERSION = '3.19.0';
2
2
  export const LIB = '@thg-agentic/hyper-personalisation-web';
@@ -1,9 +1,7 @@
1
- export type ConsentPurpose =
2
- | 'analytics'
3
- | 'marketing'
4
- | 'personalisation'
5
- | 'ad_user_data'
6
- | 'ad_personalization';
1
+ import type { CdpConsentPurpose } from './events';
2
+
3
+ /** The purposes a visitor answers for: the spec's, generated. */
4
+ export type ConsentPurpose = CdpConsentPurpose;
7
5
 
8
6
  /** schemas/common/consent-state.json: true granted, false refused, null never asked. */
9
7
  export type ConsentState = Record<ConsentPurpose, boolean | null>;
package/types/events.d.ts CHANGED
@@ -1,7 +1,39 @@
1
1
  // Generated from spec/registry.json by scripts/generate-sdk-models.mjs.
2
2
  // DO NOT EDIT. Change the schema and regenerate — the schema is the contract,
3
3
  // and a hand-edit here is an SDK that disagrees with the ingest.
4
- // Spec version: 1.10
4
+ // Spec version: 1.12
5
+
6
+ /** context.source.environment. Lets non-production traffic be filtered out of the warehouse without a separate writeKey. */
7
+ export type CdpSourceEnvironment = 'production' | 'staging' | 'development';
8
+ export declare const CDP_SOURCE_ENVIRONMENT_VALUES: readonly CdpSourceEnvironment[];
9
+
10
+ /** context.identity_trust. Whether anonymousId on THIS event identifies a single real visitor. */
11
+ export type CdpIdentityTrust = 'device' | 'shared_render' | 'none';
12
+ export declare const CDP_IDENTITY_TRUST_VALUES: readonly CdpIdentityTrust[];
13
+
14
+ /** context.page.type. Normalised page taxonomy. */
15
+ export type CdpPageType = 'home' | 'list' | 'product' | 'search' | 'basket' | 'checkout' | 'order_confirmation' | 'login' | 'register' | 'account' | 'info' | 'blog' | 'other';
16
+ export declare const CDP_PAGE_TYPE_VALUES: readonly CdpPageType[];
17
+
18
+ /** context.device.type. The kind of thing this is. */
19
+ export type CdpDeviceType = 'desktop' | 'mobile' | 'tablet' | 'bot' | 'app';
20
+ export declare const CDP_DEVICE_TYPE_VALUES: readonly CdpDeviceType[];
21
+
22
+ /** context.device.network. How the device was connected when the event happened, as a native app reports it. */
23
+ export type CdpDeviceNetwork = 'wifi' | 'cellular' | 'wired' | 'offline' | 'other';
24
+ export declare const CDP_DEVICE_NETWORK_VALUES: readonly CdpDeviceNetwork[];
25
+
26
+ /** context.consent.basis. Who decided whether this event may be collected (spec 1.5). */
27
+ export type CdpConsentBasis = 'visitor' | 'host';
28
+ export declare const CDP_CONSENT_BASIS_VALUES: readonly CdpConsentBasis[];
29
+
30
+ /** context.decisions[].kind. The KIND of decision this was. */
31
+ export type CdpDecisionKind = 'recommendation' | 'search_rank' | 'banner' | 'offer' | 'layout' | 'copy' | 'price' | 'other';
32
+ export declare const CDP_DECISION_KIND_VALUES: readonly CdpDecisionKind[];
33
+
34
+ /** context.consent. The purposes a visitor answers for: context.consent and Consent Updated `state`. */
35
+ export type CdpConsentPurpose = 'analytics' | 'marketing' | 'personalisation' | 'ad_user_data' | 'ad_personalization';
36
+ export declare const CDP_CONSENT_PURPOSE_VALUES: readonly CdpConsentPurpose[];
5
37
 
6
38
  /** The product shape shared by every commerce event. */
7
39
  export interface CdpProduct {
@@ -138,6 +170,13 @@ export interface ProductsSearched {
138
170
  search_request_id?: string | null;
139
171
  }
140
172
 
173
+ /** A search box suggested products for a partly typed query, and they were seen. */
174
+ export interface SearchSuggestionsViewed {
175
+ query: string;
176
+ products: CdpProduct[];
177
+ search_request_id?: string | null;
178
+ }
179
+
141
180
  /** A product was added to the basket. */
142
181
  export interface ProductAdded {
143
182
  products: CdpProduct[];
@@ -261,6 +300,7 @@ export interface OrderUpdated {
261
300
  taxes_included?: boolean | null;
262
301
  discount?: number | null;
263
302
  coupon?: string | null;
303
+ payment_method?: string | null;
264
304
  products?: CdpProduct[] | null;
265
305
  reason?: 'line_added' | 'line_removed' | 'quantity_changed' | 'address_changed' | 'delivery_changed' | 'payment_changed' | 'price_adjusted' | 'partial_cancellation' | 'other' | 'status_changed' | null;
266
306
  status?: 'processing' | 'despatched' | 'partially_despatched' | 'delivered' | 'collected' | 'held' | 'payment_problem' | null;
@@ -591,7 +631,7 @@ export interface ApplicationUpdated {
591
631
  }
592
632
 
593
633
  /** The spec version these models implement. SDKs stamp it on context.spec.version. */
594
- export const SPEC_VERSION = '1.10';
634
+ export const SPEC_VERSION = '1.12';
595
635
 
596
636
  /** Every event name the registry accepts. Anything else is rejected at the ingest. */
597
637
  export type CdpEventName =
@@ -605,6 +645,7 @@ export type CdpEventName =
605
645
  | 'Product Clicked'
606
646
  | 'Product List Filtered'
607
647
  | 'Products Searched'
648
+ | 'Search Suggestions Viewed'
608
649
  | 'Product Added'
609
650
  | 'Product Removed'
610
651
  | 'Cart Viewed'
@@ -663,6 +704,7 @@ export interface CdpEventProperties {
663
704
  'Product Clicked': ProductClicked;
664
705
  'Product List Filtered': ProductListFiltered;
665
706
  'Products Searched': ProductsSearched;
707
+ 'Search Suggestions Viewed': SearchSuggestionsViewed;
666
708
  'Product Added': ProductAdded;
667
709
  'Product Removed': ProductRemoved;
668
710
  'Cart Viewed': CartViewed;
package/types/index.d.ts CHANGED
@@ -6,6 +6,7 @@ import type {
6
6
  CartViewed,
7
7
  CdpEventName,
8
8
  CdpEventProperties,
9
+ CdpPageType,
9
10
  CdpProduct,
10
11
  CheckoutFailed,
11
12
  CheckoutStarted,
@@ -36,6 +37,7 @@ import type {
36
37
  PromotionClicked,
37
38
  PromotionViewed,
38
39
  RecommendationsViewed,
40
+ SearchSuggestionsViewed,
39
41
  SignedIn,
40
42
  SignedUp,
41
43
  SlotEngaged,
@@ -45,20 +47,8 @@ import type {
45
47
  export type { ConsentAdapter, ConsentBasis, ConsentState } from './consent';
46
48
  export type * from './events';
47
49
 
48
- export type PageType =
49
- | 'home'
50
- | 'list'
51
- | 'product'
52
- | 'search'
53
- | 'basket'
54
- | 'checkout'
55
- | 'order_confirmation'
56
- | 'login'
57
- | 'register'
58
- | 'account'
59
- | 'info'
60
- | 'blog'
61
- | 'other';
50
+ /** context.page.type: the spec's page taxonomy, generated. */
51
+ export type PageType = CdpPageType;
62
52
 
63
53
  export interface PageProperties {
64
54
  /** Stored as context.page.type and inherited by later events on the page. */
@@ -252,9 +242,13 @@ export interface AnalyticsClient {
252
242
  properties?: Record<string, unknown>,
253
243
  options?: CallOptions,
254
244
  ): CdpEvent | null;
255
- page(properties?: PageProperties, options?: CallOptions): CdpEvent;
256
- identify(traits?: IdentifyTraits, options?: CallOptions): CdpEvent;
257
- /** Clear identity and the page chain. */
245
+ /** Null when the event is not sent: outside the sample, dropped by `beforeSend`, or failed. */
246
+ page(properties?: PageProperties, options?: CallOptions): CdpEvent | null;
247
+ identify(traits?: IdentifyTraits, options?: CallOptions): CdpEvent | null;
248
+ /**
249
+ * Sign-out on a shared device: forget the user, the page chain, the SDK's session, the decisions
250
+ * in force and the recommendation context. The device id stays.
251
+ */
258
252
  reset(): void;
259
253
 
260
254
  productViewed: Emit<ProductViewed>;
@@ -293,7 +287,13 @@ export interface AnalyticsClient {
293
287
  personalisationGroup(): PersonalisationGroup;
294
288
  productClicked: Emit<ProductClicked>;
295
289
  productListFiltered: Emit<ProductListFiltered>;
290
+ /** A submitted search and the results page it produced. */
296
291
  productsSearched: Emit<ProductsSearched>;
292
+ /**
293
+ * The search box's suggested products for a partly typed query, once seen. Never productsSearched,
294
+ * which is a submitted search's results page.
295
+ */
296
+ searchSuggestionsViewed: Emit<SearchSuggestionsViewed>;
297
297
  productAdded: Emit<ProductAdded>;
298
298
  productRemoved: Emit<ProductRemoved>;
299
299
  /**