PageSourceSearch

https://www.lyleandscott.com/cdn/shop/t/458/assets/algolia-product-fetcher.js?v=7089899288454109641788364385

js lyleandscott.com collected 2026-09-24 22:59:50 UTC 8,382 bytes, 2 lines download raw bytes

1import{metafieldValue,numericId}from"@lyle/algolia-product-utils";import{getLocalisedRoute}from"@lyle/weglot";const ALGOLIA_QUERY_ENDPOINT=appId=>`https://${appId}-dsn.algolia.net/1/indexes/*/queries`,RELATED_PRODUCT_LIMIT=1e3,ALGOLIA_PRODUCT_ATTRIBUTES=["available","featuredCollection","handle","id","images","metafields.colour.colour_family_size","metafields.colour.colour_swatch_code","metafields.colour.colour_swatch_name","metafields.pricing.member_discount_percentage","metafields.product.members_only_purchase","objectID","sizeStock","sizes","sizesAvailable","tags","title","variants"];export function algoliaProductAttributes(marketHandle){const market=String(marketHandle||"").trim();return[...ALGOLIA_PRODUCT_ATTRIBUTES,market?`compareAtPrice.${market}`:"compareAtPrice",market?`price.${market}`:"price"]}export function formatAlgoliaProductHit(hit,config,options={}){const metafields=[{name:"colour_family_size",value:hit.metafields?.["colour.colour_family_size"]},{name:"swatch_code",value:hit.metafields?.["colour.colour_swatch_code"]},{name:"swatch_name",value:hit.metafields?.["colour.colour_swatch_name"]},{name:"member_discount_percentage",value:hit.metafields?.["pricing.member_discount_percentage"]},{name:"members_only_purchase",value:hit.metafields?.["product.members_only_purchase"]==="true"}];return{available:resolveAvailable(hit),compareAtPrice:marketPrice(hit,"compareAtPrice",config),featuredCollection:hit.featuredCollection,handle:hit.handle,id:hit.id,images:(hit.images||[]).map(image=>({alt:image.alt||image.altText,height:image.height,id:image.id,src:image.src||image.url,width:image.width})),isComingSoon:(hit.tags||[]).includes("coming-soon"),metafields,objectID:hit.objectID||String(hit.id),price:marketPrice(hit,"price",config),sizeStock:hit.sizeStock,sizes:hit.sizes,sizesAvailable:hit.sizesAvailable,tags:hit.tags||[],title:hit.title,url:productUrl(hit,config,options),variantId:hit.objectID?numericId(hit.objectID):null,variants:hit.variants||[],__indexName:options.indexName||hit.__indexName,__position:options.position||hit.__position,__queryID:options.queryID||hit.__queryID}}function resolveAvailable(hit){if(hit?.available===!1||hit?.available==="false"||hit?.available===0)return!1;const sizeStock=hit?.sizeStock;if(sizeStock&&typeof sizeStock=="object"){const stocks=Object.values(sizeStock);if(stocks.length>0&&stocks.every(stock=>Number(stock)===0))return!1}return Array.isArray(hit?.sizesAvailable)&&hit.sizesAvailable.length===0&&(Array.isArray(hit?.sizes)&&hit.sizes.length>0||sizeStock&&Object.keys(sizeStock).length>0)?!1:hit?.available!==!1}function marketPrice(product,type,config){const value=product?.[type]?.[config.market.handle]||product?.[type];return value?{amount:Number(value.amount||value.min||0),currencyCode:value.currencyCode||config.market.currencyCode,max:Number(value.max||value.amount||0),min:Number(value.min||value.amount||0),varies:value.max!==value.min}:null}function productUrl(hit,config,options){return typeof options.productUrl=="function"?options.productUrl(hit):getLocalisedRoute(`${config.market.rootUrl||"/"}products/${hit.handle}`)}export class AlgoliaProductFetcher{#config;#signal=null;#cache=new Map;#requests=new Map;constructor({config,signal=null}){this.#config=config,this.#signal=signal}familyTag(product){return product.tags?.find(tag=>tag.startsWith("ProductColourFamily-"))||product.tags?.find(tag=>tag.startsWith("ProductSizeFamily-"))||""}filterColourProducts(product,relatedProducts){const currentSize=this.#currentSize(product),colourFamily=product.tags?.find(tag=>tag.startsWith("ProductColourFamily-"));return relatedProducts.filter(related=>{const sameColourFamily=!colourFamily||related.tags?.includes(colourFamily),sameSize=!currentSize||related.tags?.includes(`ProductSize-${currentSize}`),hasSwatch=!!metafieldValue(related,"swatch_code");return sameColourFamily&&sameSize&&hasSwatch&&(related.available||related.handle===product.handle)})}formatProduct(hit,options={}){return formatAlgoliaProductHit(hit,this.#config,options)}async fetchRelatedProductsBatch(tags){const uniqueTags=[...new Set(tags)].filter(
1Boolean).sort();if(!this.#config.algolia?.indexName)return new Map(uniqueTags.map(tag=>[tag,[]]));const missingTags=uniqueTags.filter(tag=>!this.#cache.has(tag)&&!this.#requests.has(tag));if(missingTags.length){const batchRequest=this.#requestRelatedProductMap(missingTags);missingTags.forEach(tag=>{this.#requests.set(tag,batchRequest.then(productsByTag=>productsByTag.get(tag)||[]))})}return await Promise.all(uniqueTags.map(async tag=>{if(this.#cache.has(tag))return;const request=this.#requests.get(tag);if(!request){this.#cache.set(tag,[]);return}try{this.#cache.set(tag,await request)}catch{this.#cache.set(tag,[])}finally{this.#requests.get(tag)===request&&this.#requests.delete(tag)}})),new Map(uniqueTags.map(tag=>[tag,this.#cache.get(tag)||[]]))}async fetchProductsByHandles(handles){const uniqueHandles=[...new Set(handles)].filter(Boolean);if(!uniqueHandles.length||!this.#config.algolia?.indexName)return new Map;const filters=uniqueHandles.map(handle=>`handle:"${this.#escapeFilterValue(handle)}"`).join(" OR "),params=new URLSearchParams;params.set("query",""),params.set("hitsPerPage",String(uniqueHandles.length)),params.set("distinct","true"),params.set("filters",`(${filters}) AND ${this.#config.algolia.defaultFilters}`),params.set("attributesToRetrieve",JSON.stringify(algoliaProductAttributes(this.#config.market.handle))),params.set("attributesToHighlight","[]");try{const response=await fetch(ALGOLIA_QUERY_ENDPOINT(this.#config.algolia.appId),{method:"POST",headers:{"Content-Type":"application/json","X-Algolia-API-Key":this.#config.algolia.apiKey,"X-Algolia-Application-Id":this.#config.algolia.appId},body:JSON.stringify({requests:[{indexName:this.#config.algolia.indexName,params:params.toString()}]}),signal:this.#signal});if(!response.ok)return new Map;const hits=(await response.json())?.results?.[0]?.hits||[];return new Map(hits.map(hit=>[hit.handle,this.formatProduct(hit)]))}catch{return new Map}}async fetchProductJson(handle){if(!handle)return null;try{const response=await fetch(`${this.#config.market.rootUrl??"/"}products/${handle}.js`,{headers:{Accept:"application/json"},signal:this.#signal});if(!response.ok)return null;const product=await response.json();return this.#formatProductJson(product)}catch{return null}}async#requestRelatedProductMap(tags){const response=await fetch(ALGOLIA_QUERY_ENDPOINT(this.#config.algolia.appId),{method:"POST",headers:{"Content-Type":"application/json","X-Algolia-API-Key":this.#config.algolia.apiKey,"X-Algolia-Application-Id":this.#config.algolia.appId},body:JSON.stringify({requests:[{indexName:this.#config.algolia.indexName,params:this.#swatchParams(tags).toString()}]}),signal:this.#signal});if(!response.ok)return new Map(tags.map(tag=>[tag,[]]));const payload=await response.json(),productsByTag=new Map(tags.map(tag=>[tag,[]]));return(payload?.results?.[0]?.hits||[]).forEach(hit=>{const product=this.formatProduct(hit);(hit.tags||[]).forEach(tag=>{productsByTag.get(tag)?.push(product)})}),productsByTag}#swatchParams(tags){const params=new URLSearchParams;return params.set("query",""),params.set("hitsPerPage",String(RELATED_PRODUCT_LIMIT)),params.set("page","0"),params.set("filters",`(${tags.map(tag=>`tags:"${this.#escapeFilterValue(tag)}"`).join(" OR ")}) AND ${this.#config.algolia.defaultFilters}`),params.set("clickAnalytics","false"),params.set("distinct","true"),params.set("attributesToRetrieve",JSON.stringify(algoliaProductAttributes(this.#config.market.handle))),params.set("attributesToHighlight","[]"),params}#formatProductJson(product){const options=product.options||[];return{handle:product.handle,id:product.id,title:product.title,url:getLocalisedRoute(product.url||`${this.#config.market.rootUrl??"/"}products/${product.handle}`),variants:(product.variants||[]).map(variant=>({id:variant.id,available:variant.available,title:variant.title,selectedOptions:options.map((name,index)=>({name,value:variant.options?.[index]||variant[`option${index+1}`]||""}))}))}}#currentSize(product){const tag=product.tags?.find(item=>item.startsWith("ProductSize-"));return tag?tag.split("-")[1]:""}#escapeFilterValue(value){return String(value).replace(/"/g,'\\"')}}
2//# sourceMappingURL=/cdn/shop/t/458/assets/algolia-product-fetcher.js.map?v=7089899288454109641788364385

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.