{"version":3,"file":"product-complementary-section.min.js","mappings":"wZAEAA,eAAeC,IACb,MAAMC,EAAgBC,SAASC,cAC7B,2CACF,IAAKF,EACH,OAGF,MAAMG,EAAYF,SAASC,cAAc,mCACpCC,IAILA,EAAUC,gBC8CLN,eAA2CO,GAChD,IAAK,QAAQC,KAAKD,EAAME,YAAa,CACnC,MAAMC,EAAa,IAAIC,MAAM,+CAE7B,MADAD,EAAMH,MAAQA,EACRG,CACR,CAOA,MAAME,EAAM,IAAIC,IAAIN,EAAMK,IAAKE,SAASC,QAWxC,IAAIC,EAoBAC,EA9BJL,EAAIM,aAAaC,IAAI,aAAcZ,EAAME,YAErCF,EAAMa,YACRR,EAAIM,aAAaC,IAAI,aAAcZ,EAAMa,YAGvCb,EAAMc,QACRT,EAAIM,aAAaC,IAAI,SAAUZ,EAAMc,QAIvC,IACEL,QAAiBM,MAAMV,EAAIW,KAC7B,CAAE,MAAOC,GACP,MAAMd,EAAa,IAAIC,MAAM,0CAA2C,CAAEa,UAG1E,MAFAd,EAAMH,MAAQA,EACdG,EAAME,IAAMA,EAAIW,KACVb,CACR,CAEA,IAAKM,EAASS,GAAI,CAGhB,MAAMf,EAAa,IAAIC,MAAM,mBAAmBK,EAASU,mCAIzD,MAHAhB,EAAMiB,KAAOX,EAASU,OACtBhB,EAAMH,MAAQA,EACdG,EAAME,IAAMI,EAASJ,IACfF,CACR,CAGA,IACEO,QAAqBD,EAASY,MAChC,CAAE,MAAOJ,GACP,MAAMd,EAAa,IAAIC,MAAM,wDAC3B,CAAEa,UAIJ,MAHAd,EAAMH,MAAQA,EACdG,EAAMgB,OAASV,EAASU,OACxBhB,EAAME,IAAMI,EAASJ,IACfF,CACR,CAEA,OAAOO,CACT,CDtG8BY,CAA4B,CACtDR,OAAQ,gBACRZ,WAAYP,EAAc4B,QAAQC,UAClCX,WAAY,wBACZR,IAAKV,EAAc4B,QAAQE,4BAE/B,CAEA,SAASC,EAAiBC,GACxB,KAAMA,EAAMC,yBAAyBC,aACnC,OAGF,MAAMC,EAAa,IAAIC,MAAM,6BAA8B,CACzDC,SAAS,EACTC,UAAU,IAEZN,EAAMC,cAAcM,cAAcJ,GAElC,MAGMK,EAYR,SAA2BC,GACzB,MAAMD,EAAmB,CAAC,EAC1BA,EAAQE,kBAAoBD,EAAeb,QAAQe,gBACnDH,EAAQI,UAAYH,EAAeb,QAAQiB,SAC3CL,EAAQM,UAAiD,SAArCL,EAAeb,QAAQkB,UAC3CN,EAAQO,WAA+BN,EAAeb,QAAQoB,UAC9DR,EAAQS,yBAA2BR,EAAeb,QAAQsB,aAC1DV,EAAQW,+BAAiCV,EAAeb,QAAQwB,mBAC9DC,WAAWZ,EAAeb,QAAQwB,oBAClC,EACFZ,EAAQc,6BAA+Bb,EAAeb,QAAQ2B,iBAC9Df,EAAQgB,GAAKC,SAAShB,EAAeb,QAAQ4B,GAAI,IACjDhB,EAAQkB,SAA+C,SAApCjB,EAAeb,QAAQ8B,SAC1ClB,EAAQmB,WAAalB,EAAeb,QAAQgC,UAC5CpB,EAAQqB,iBAAmBpB,EAAeb,QAAQkC,gBAClDtB,EAAQuB,oBAAsBtB,EAAeb,QAAQoC,mBACrDxB,EAAQyB,gBAAkBxB,EAAeb,QAAQsC,eAC/Cb,WAAWZ,EAAeb,QAAQsC,gBAAkB,EACtD1B,EAAQ2B,cAAgB1B,EAAeb,QAAQwC,aAC7CX,SAAShB,EAAeb,QAAQwC,aAAc,IAAM,EACtD5B,EAAQ6B,KAAO5B,EAAeb,QAAQyC,KACtC7B,EAAQ8B,MAAQ7B,EAAeb,QAAQ0C,MACvC9B,EAAQ+B,KAAO9B,EAAeb,QAAQ2C,KACtC/B,EAAQ9B,IAAM+B,EAAeb,QAAQlB,IACrC8B,EAAQgC,oBAAsBf,SAAShB,EAAeb,QAAQ6C,kBAAmB,IAEjFjC,EAAQkC,cAA0C,GAElD,MAAMC,EAAsBlC,EAAemC,iBACzC,yDACF,IAAK,MAAMC,KAAsBF,EAAqB,CACpD,MAAMG,EAA2C,CAAC,EAClDA,EAAYC,KAAOF,EAAmBjD,QAAQmD,KAC9CD,EAAYE,gBAAkBH,EAAmBjD,QAAQqD,eACrDJ,EAAmBjD,QAAQsD,WAC7BJ,EAAYI,SAAWzB,SAASoB,EAAmBjD,QAAQsD,SAAU,KAGnEL,EAAmBjD,QAAQuD,sBAC7BL,EAAYM,uBAAyBP,EAAmBjD,QAAQuD,qBAGlEL,EAAYO,QAAU,GAGtB,MAAMC,EAAwBT,EAAmBD,iBAC/C,8BAEF,IAAK,MAAMW,KAAwBD,EAAuB,CACxD,MAAME,EAA2D,CAAC,EAClEA,EAAcC,MAAQF,EAAqB3D,QAAQ6D,MAC/CF,EAAqB3D,QAAQ8D,QAC/BF,EAAcE,MAAQH,EAAqB3D,QAAQ8D,OAGrDF,EAAcG,SAAW,GAEzB,MAAMC,EAAkBL,EAAqBX,iBACD,4BAE5C,IAAK,MAAMiB,KAAkBD,EAAiB,CAC5C,MAAME,EAA0B,CAAC,EACjCA,EAAQhD,UAAiD,SAArC+C,EAAejE,QAAQkB,UAC3CgD,EAAQC,UAAYF,EAAejE,QAAQmE,UAC3CD,EAAQE,iBAAmBH,EAAejE,QAAQqE,eAChDxC,SAASoC,EAAejE,QAAQqE,eAAgB,IAAM,KACxDH,EAAQtC,GAAKC,SAASoC,EAAejE,QAAQ4B,GAAI,IACjDsC,EAAQI,MAAQzC,SAASoC,EAAejE,QAAQsE,MAAO,IACvDJ,EAAQK,SAA+C,SAApCN,EAAejE,QAAQuE,SAC1CL,EAAQM,IAAMP,EAAejE,QAAQwE,IACrCN,EAAQxB,MAAQuB,EAAejE,QAAQ0C,OAAS7B,EAAeb,QAAQ0C,OAAS,GAChFwB,EAAQO,gBAAkBR,EAAejE,QAAQyE,iBAAmB,GACpEP,EAAQQ,QAAUT,EAAejE,QAAQ0E,SAAW,GACpDR,EAAQS,QAAUV,EAAejE,QAAQ2E,SAAW,GACpDT,EAAQU,QAAUX,EAAejE,QAAQ4E,SAAW,GAEpDhB,EAAcG,SAASc,KAAKX,EAC9B,CAEAhB,EAAYO,QAAQoB,KAAKjB,EAC3B,CAEAhD,EAAQkC,cAAc+B,KAAK3B,EAC7B,CAEAtC,EAAQmD,SAAW,GAKnB,MAAMC,EAAkBnD,EAAemC,iBACrC,qCAEF,IAAK,MAAMiB,KAAkBD,EAAiB,CAC5C,MAAME,EAA0B,CAAC,EACjCA,EAAQhD,UAAiD,SAArC+C,EAAejE,QAAQkB,UAC3CgD,EAAQC,UAAYF,EAAejE,QAAQmE,UAC3CD,EAAQE,iBAAmBH,EAAejE,QAAQqE,eAChDxC,SAASoC,EAAejE,QAAQqE,eAAgB,IAAM,KACxDH,EAAQY,cAAgBb,EAAejE,QAAQ+E,aAC/Cb,EAAQc,6BAA+Bf,EAAejE,QAAQiF,0BAC9Df,EAAQtC,GAAKC,SAASoC,EAAejE,QAAQ4B,GAAI,IACjDsC,EAAQgB,UAAYjB,EAAejE,QAAQmF,UAAY,GACvDjB,EAAQkB,yCACNnB,EAAejE,QAAQqF,4BAA8B,GACvDnB,EAAQQ,QAAUT,EAAejE,QAAQ0E,QACzCR,EAAQS,QAAUV,EAAejE,QAAQ2E,QACzCT,EAAQU,QAAUX,EAAejE,QAAQ4E,QACzCV,EAAQI,MAAQzC,SAASoC,EAAejE,QAAQsE,MAAO,IACvDJ,EAAQK,SAA+C,SAApCN,EAAejE,QAAQuE,SAC1CL,EAAQM,IAAMP,EAAejE,QAAQwE,IACrCN,EAAQxB,MAAQuB,EAAejE,QAAQ0C,OAAS7B,EAAeb,QAAQ0C,OAAS,GAChF9B,EAAQmD,SAASc,KAAKX,EACxB,CAEA,OAAOtD,CACT,CAhIkB0E,CAHJlF,EAAMC,cAAckF,QAAQ,QACbjH,cACzB,oBAIIkH,EAAe,IAAIC,YAAoB,0BAA2B,CACtEC,OAAQ,CACNC,QAASvF,EAAMC,cAAcL,QAAQ4F,YACrChF,QAASA,KAGbD,cAAc6E,EAChB,CAwHA,SAASK,IACP,MAAMtH,EAAYF,SAASC,cAAc,mCACzC,IAAKC,EACH,OAGF,MAAMuH,EAAUvH,EAAUyE,iBAAoC,mCAC9D,IAAK,MAAM+C,KAAUD,EACnBC,EAAOC,iBAAiB,QAAS7F,EAErC,CAGEhC,IACG8H,KAAKJ,GACLK,MAAMC,QAAQC","sources":["webpack://lange/./sections/product-complementary/index.ts","webpack://lange/./lib/shopify/product-recommendations.ts"],"sourcesContent":["import { fetchProductRecommendations } from '../../lib/shopify/product-recommendations.js';\n\nasync function loadRecommendations() {\n  const staticContent = document.querySelector<HTMLElement>(\n    '.complementary-products .static-content');\n  if (!staticContent) {\n    return;\n  }\n\n  const container = document.querySelector('.complementary-products .result');\n  if (!container) {\n    return;\n  }\n\n  container.innerHTML = await fetchProductRecommendations({\n    intent: 'complementary',\n    product_id: staticContent.dataset.productId,\n    section_id: 'product-complementary',\n    url: staticContent.dataset.productRecommendationsUrl\n  });\n}\n\nfunction onCtaButtonClick(event: MouseEvent) {\n  if (!(event.currentTarget instanceof HTMLElement)) {\n    return;\n  }\n\n  const focusEvent = new Event('focus-stack-push-requested', {\n    bubbles: true,\n    composed: true\n  });\n  event.currentTarget.dispatchEvent(focusEvent);\n\n  const cta = event.currentTarget.closest('.cta');\n  const productElement = cta.querySelector<HTMLElementTagNameMap['product-element']>(\n    'product-element');\n  const product = scrapeProductInfo(productElement);\n\n  type Detail = WindowEventMap['select-option-requested']['detail'];\n  const requestEvent = new CustomEvent<Detail>('select-option-requested', {\n    detail: {\n      country: event.currentTarget.dataset.countryCode,\n      product: product\n    }\n  });\n  dispatchEvent(requestEvent);\n}\n\nfunction scrapeProductInfo(productElement: HTMLElementTagNameMap['product-element']) {\n  const product = <Product>{};\n  product.award_badge_image = productElement.dataset.awardBadgeImage;\n  product.award_tag = productElement.dataset.awardTag;\n  product.available = productElement.dataset.available === 'true';\n  product.badge_type = <BadgeTypeOptions>productElement.dataset.badgeType;\n  product.metafields_discount_code = productElement.dataset.discountCode;\n  product.metafields_discount_percentage = productElement.dataset.discountPercentage ?\n    parseFloat(productElement.dataset.discountPercentage) :\n    0;\n  product.metafields_discount_override = productElement.dataset.discountOverride;\n  product.id = parseInt(productElement.dataset.id, 10);\n  product.lazyload = productElement.dataset.lazyload === 'true';\n  product.main_image = productElement.dataset.mainImage;\n  product.metafields_title = productElement.dataset.metafieldsTitle;\n  product.metafields_subtitle = productElement.dataset.metafieldsSubtitle;\n  product.reviews_average = productElement.dataset.reviewsAverage ?\n    parseFloat(productElement.dataset.reviewsAverage) : 0;\n  product.reviews_count = productElement.dataset.reviewsCount ?\n    parseInt(productElement.dataset.reviewsCount, 10) : 0;\n  product.tags = productElement.dataset.tags;\n  product.title = productElement.dataset.title;\n  product.type = productElement.dataset.type;\n  product.url = productElement.dataset.url;\n  product.selected_variant_id = parseInt(productElement.dataset.selectedVariantId, 10);\n\n  product.option_groups = <Product['option_groups']>[];\n  type ProductOptionGroup = HTMLElementTagNameMap['product-option-group'];\n  const optionGroupElements = productElement.querySelectorAll<ProductOptionGroup>(\n    'product-option-groups product-option-group[data-name]');\n  for (const optionGroupElement of optionGroupElements) {\n    const optionGroup = <Product['option_groups'][0]>{};\n    optionGroup.name = optionGroupElement.dataset.name;\n    optionGroup.selected_option = optionGroupElement.dataset.selectedOption;\n    if (optionGroupElement.dataset.position) {\n      optionGroup.position = parseInt(optionGroupElement.dataset.position, 10);\n    }\n\n    if (optionGroupElement.dataset.useSwatchesForColor) {\n      optionGroup.use_swatches_for_color = optionGroupElement.dataset.useSwatchesForColor;\n    }\n\n    optionGroup.options = [];\n\n    type ProductOption = HTMLElementTagNameMap['product-option'];\n    const productOptionElements = optionGroupElement.querySelectorAll<ProductOption>(\n      'product-option[data-value]');\n\n    for (const productOptionElement of productOptionElements) {\n      const productOption = <Product['option_groups'][0]['options'][0]>{};\n      productOption.value = productOptionElement.dataset.value;\n      if (productOptionElement.dataset.color) {\n        productOption.color = productOptionElement.dataset.color;\n      }\n\n      productOption.variants = [];\n\n      const variantElements = productOptionElement.querySelectorAll<\n        HTMLElementTagNameMap['variant-element']>('variant-element[data-id]');\n\n      for (const variantElement of variantElements) {\n        const variant = <ProductVariant>{};\n        variant.available = variantElement.dataset.available === 'true';\n        variant.backorder = variantElement.dataset.backorder;\n        variant.compare_at_price = variantElement.dataset.compareAtPrice ?\n          parseInt(variantElement.dataset.compareAtPrice, 10) : null;\n        variant.id = parseInt(variantElement.dataset.id, 10);\n        variant.price = parseInt(variantElement.dataset.price, 10);\n        variant.selected = variantElement.dataset.selected === 'true';\n        variant.sku = variantElement.dataset.sku;\n        variant.title = variantElement.dataset.title || productElement.dataset.title || '';\n        variant.sizeDescription = variantElement.dataset.sizeDescription || '';\n        variant.option1 = variantElement.dataset.option1 || '';\n        variant.option2 = variantElement.dataset.option2 || '';\n        variant.option3 = variantElement.dataset.option3 || '';\n\n        productOption.variants.push(variant);\n      }\n\n      optionGroup.options.push(productOption);\n    }\n\n    product.option_groups.push(optionGroup);\n  }\n\n  product.variants = [];\n\n  // We must isolate the query selector to children of the parent <product-element> custom element\n  // to ensure we exclude any <variant-element> child instances in the <product-option-groups>\n  // custom element.\n  const variantElements = productElement.querySelectorAll<HTMLElementTagNameMap['variant-element']>(\n    ':scope > variant-element[data-id]');\n\n  for (const variantElement of variantElements) {\n    const variant = <ProductVariant>{};\n    variant.available = variantElement.dataset.available === 'true';\n    variant.backorder = variantElement.dataset.backorder;\n    variant.compare_at_price = variantElement.dataset.compareAtPrice ?\n      parseInt(variantElement.dataset.compareAtPrice, 10) : null;\n    variant.currency_code = variantElement.dataset.currencyCode;\n    variant.hide_backorder_in_collection = variantElement.dataset.hideBackorderInCollection;\n    variant.id = parseInt(variantElement.dataset.id, 10);\n    variant.image_src = variantElement.dataset.imageSrc || '';\n    variant.metafield_compare_at_price_modal_content =\n      variantElement.dataset.compareAtPriceModalContent || '';\n    variant.option1 = variantElement.dataset.option1;\n    variant.option2 = variantElement.dataset.option2;\n    variant.option3 = variantElement.dataset.option3;\n    variant.price = parseInt(variantElement.dataset.price, 10);\n    variant.selected = variantElement.dataset.selected === 'true';\n    variant.sku = variantElement.dataset.sku;\n    variant.title = variantElement.dataset.title || productElement.dataset.title || '';\n    product.variants.push(variant);\n  }\n\n  return product;\n}\n\nfunction setupCtaButtons() {\n  const container = document.querySelector('.complementary-products .result');\n  if (!container) {\n    return;\n  }\n\n  const buttons = container.querySelectorAll<HTMLButtonElement>('.small-product-card .cta button');\n  for (const button of buttons) {\n    button.addEventListener('click', onCtaButtonClick);\n  }\n}\n\nfunction main() {\n  loadRecommendations()\n    .then(setupCtaButtons)\n    .catch(console.warn);\n}\n\n// we don't need to wait for DOMContentLoaded as this script is loaded after the relevant HTML and\n// we want to start fetching the content as soon as possible\nmain();\n\n/**\n * Represents a Shopify product variant\n */\ninterface ProductVariant {\n  /**\n   * Whether the variant is in stock\n   */\n  available: boolean;\n\n  /**\n   * Whether the variant is on backorder\n   */\n  backorder: string;\n\n  /**\n   * The \"Pre-Order\" deferred payment selling plan ID associated with a backordered variant.\n   */\n  backorder_selling_plan_id: number;\n\n  /**\n   * The MSRP, or original sales price before discounts, per unit\n   */\n  compare_at_price: number | null;\n\n  /**\n   * The currency code associated with the price\n   */\n  currency_code: string;\n\n  /**\n   * Boolean as a string to indicate whether to hide any backorder message if the product card is\n   * being viewed in a collection context.\n   */\n  hide_backorder_in_collection: string;\n\n  /**\n   * Variant id\n   *\n   * @example 12345568943\n   */\n  id: number;\n\n  /**\n   * The URL for the variant image\n   */\n  image_src: string;\n\n  /**\n   * Content to be displayed in the compare at price modal for a particular variant\n   */\n  metafield_compare_at_price_modal_content: string;\n\n  /**\n   * The value of the variant for the first product option. If there's no first product option, then\n   * nil is returned.\n   */\n  option1: string;\n\n  /**\n   * The value of the variant for the second product option. If there's no second product option,\n   * then nil is returned.\n   */\n  option2: string;\n\n  /**\n   * The value of the variant for the third product option. If there's no third product option, then\n   * nil is returned.\n   */\n  option3: string;\n\n  /**\n   * The price of the variant represented in cents\n   */\n  price: number;\n\n  /**\n   * Whether this variant is the currently selected variant out of all the variants for the product\n   */\n  selected: boolean;\n\n  /**\n   * The short description associated with the variant size option.\n   */\n  sizeDescription: string;\n\n  /**\n   * The Stock Keeping Unit associated with the variant. This is another kind of product id.\n   */\n  sku: string;\n\n  /**\n   * The title of the variant.\n   */\n  title: string;\n}\n\ntype BadgeTypeOptions = '' | 'best-seller' | 'favorite' | 'new' | 'travel-friendly';\ninterface Product {\n  /**\n   * Product inventory availability\n   */\n  available: boolean;\n\n  /**\n   * Product award image\n   */\n  award_badge_image: string;\n\n  /**\n   * Product award tag\n   */\n  award_tag: string;\n\n  /**\n   * Product badge type\n   */\n  badge_type: BadgeTypeOptions;\n\n  /**\n   * Bundles created using the Shopify app use an abstraction product created by the application to\n   * represent the bundle. If a product's variant options are being presented in the context of a\n   * product in a bundle this attribute identifies the product ID for the abstraction product\n   * created by the Shopify Bundle App to represent that bundle.\n   */\n  bundle_product_id: number;\n\n  /**\n   * Shopify product id\n   */\n  id: number;\n\n  /**\n   * Product main image loading method\n   */\n  lazyload: boolean;\n\n  /**\n   * Product main image\n   */\n  main_image: string;\n\n  /**\n   * Product metafields discount code (used to promote public discount codes)\n   */\n  metafields_discount_code: string;\n\n  /**\n   * Product metafields discount override to use one-off messages instead of generated text\n   */\n  metafields_discount_override: string;\n\n  /**\n   * Product metafields discount percentage (used to render public discount code prices)\n   */\n  metafields_discount_percentage: number;\n\n  /**\n   * Product metafields subtitle\n   */\n  metafields_subtitle: string;\n\n  /**\n   * Product metafields title\n   */\n  metafields_title: string;\n\n  option_groups: {\n    name: string;\n    options: {\n      color?: string;\n      value: string;\n      variants: Partial<{\n        /**\n         * Whether the variant has inventory in stock and is available for sale.\n         */\n        available: boolean;\n\n        backorder_message: string;\n\n        compare_at_price: number;\n\n        customAttributes: {\n          /**\n           * Key or name of the attribute.\n           */\n          key: string;\n\n          /**\n           * Value of the attribute.\n           */\n          value: string;\n        }[];\n\n        hideBackorderInCollection: string;\n\n        /**\n         * Variant id\n         */\n        id: number;\n\n        price: number;\n\n        sku: string;\n\n        title: string;\n      }>[];\n    }[];\n    position?: number;\n    selected_option: string;\n    use_swatches_for_color?: 'false' | 'true';\n  }[];\n\n  /**\n   * Product yotpo reviews average value\n   */\n  reviews_average: number;\n\n  /**\n   * Product yotpo reviews count\n   */\n  reviews_count: number;\n\n  /**\n   * Selected variant id\n   */\n  selected_variant_id: number;\n\n  /**\n   * Product sku\n   */\n  sku: string;\n\n  /**\n   * Product tags\n   */\n  tags: string;\n\n  /**\n   * Product title\n   */\n  title: string;\n\n  /**\n   * Product type\n   *\n   * @example \"hair-care\"\n   */\n  type: string;\n\n  /**\n   * Shopify product url\n   */\n  url: string;\n\n  /**\n   * A list of the product's variants with some information about variant\n   */\n  variants: ProductVariant[];\n}\n","/**\n * Input to fetching a rendered recommendations section.\n */\nexport interface Input {\n  /**\n   * Whether we want related products or complementary products\n   *\n   * Shopify defaults to related when we leave this out.\n   */\n  intent?: 'complementary' | 'related';\n\n  /**\n   * Shopify id of the product the recommendations are for, as a string of digits\n   *\n   * Shopify documents this as required and answers with a 422 when it is left out, so we throw\n   * before sending a request without a valid one.\n   */\n  product_id: string;\n\n  /**\n   * Id of the section Shopify renders the recommendations into\n   *\n   * Shopify documents this as required for the html version of the endpoint and answers with a 422\n   * when it is left out.\n   */\n  section_id?: string;\n\n  /**\n   * Url of the recommendations endpoint, which is best passed as an absolute url but may be\n   * relative\n   *\n   * This is meant to be the value of `routes.product_recommendations_url` from Liquid with the\n   * store's own domain in front of it. The section renders it into a data attribute, and the\n   * section's script reads that attribute and passes it here. The route on its own is a path such\n   * as `/recommendations/products`, with the locale prefix in front of it when another locale is\n   * active.\n   *\n   * We take this instead of building it ourselves. Shopify only puts the site root on the page for\n   * javascript to read, not the recommendations route, so building it here would mean hardcoding\n   * the route and hoping Shopify never moves it.\n   */\n  url: string;\n}\n\n/**\n * Fetch the html of a recommendations section that Shopify rendered on the server.\n *\n * Shopify serves recommendations in two versions. The url ending in `.json` answers with the\n * recommended products as json and takes no section id. The url without `.json` renders the section\n * named by the section id with the recommendations filled in and answers with its html. This uses\n * the html version, since the url callers pass is the route Liquid gives us, which has no `.json`\n * on the end.\n *\n * Callers have their own preferences about how to parse the html, or whether to parse it at all, so\n * we hand back the raw text and let them decide.\n *\n * @param input which product and section to ask for\n *\n * @see https://shopify.dev/docs/api/ajax/reference/product-recommendations\n */\nexport async function fetchProductRecommendations(input: Input) {\n  if (!/^\\d+$/.test(input.product_id)) {\n    const error: any = new Error('Invalid product id fetching recommendations');\n    error.input = input;\n    throw error;\n  }\n\n  // Callers pass an absolute url, and when the url has its own host the base below is ignored. We\n  // keep the base so a relative url still works. A relative url picks up whatever host the page\n  // reports, though, and a page is not always running on the store's own domain. For example, a\n  // tool can load our html while reporting some other site as the page url, and then the request\n  // goes to that site instead of the store. That is why callers pass an absolute url.\n  const url = new URL(input.url, location.origin);\n  url.searchParams.set('product_id', input.product_id);\n\n  if (input.section_id) {\n    url.searchParams.set('section_id', input.section_id);\n  }\n\n  if (input.intent) {\n    url.searchParams.set('intent', input.intent);\n  }\n\n  let response;\n  try {\n    response = await fetch(url.href);\n  } catch (cause) {\n    const error: any = new Error('Failed to fetch product recommendations', { cause });\n    error.input = input;\n    error.url = url.href;\n    throw error;\n  }\n\n  if (!response.ok) {\n    // Some of these statuses are routine enough that Sentry filters them out by message, so take\n    // care when rewording this.\n    const error: any = new Error(`Received status ${response.status} fetching recommendations`);\n    error.code = response.status;\n    error.input = input;\n    error.url = response.url;\n    throw error;\n  }\n\n  let responseText;\n  try {\n    responseText = await response.text();\n  } catch (cause) {\n    const error: any = new Error('Failed to fetch product recommendations response body',\n      { cause });\n    error.input = input;\n    error.status = response.status;\n    error.url = response.url;\n    throw error;\n  }\n\n  return responseText;\n}\n"],"names":["async","loadRecommendations","staticContent","document","querySelector","container","innerHTML","input","test","product_id","error","Error","url","URL","location","origin","response","responseText","searchParams","set","section_id","intent","fetch","href","cause","ok","status","code","text","fetchProductRecommendations","dataset","productId","productRecommendationsUrl","onCtaButtonClick","event","currentTarget","HTMLElement","focusEvent","Event","bubbles","composed","dispatchEvent","product","productElement","award_badge_image","awardBadgeImage","award_tag","awardTag","available","badge_type","badgeType","metafields_discount_code","discountCode","metafields_discount_percentage","discountPercentage","parseFloat","metafields_discount_override","discountOverride","id","parseInt","lazyload","main_image","mainImage","metafields_title","metafieldsTitle","metafields_subtitle","metafieldsSubtitle","reviews_average","reviewsAverage","reviews_count","reviewsCount","tags","title","type","selected_variant_id","selectedVariantId","option_groups","optionGroupElements","querySelectorAll","optionGroupElement","optionGroup","name","selected_option","selectedOption","position","useSwatchesForColor","use_swatches_for_color","options","productOptionElements","productOptionElement","productOption","value","color","variants","variantElements","variantElement","variant","backorder","compare_at_price","compareAtPrice","price","selected","sku","sizeDescription","option1","option2","option3","push","currency_code","currencyCode","hide_backorder_in_collection","hideBackorderInCollection","image_src","imageSrc","metafield_compare_at_price_modal_content","compareAtPriceModalContent","scrapeProductInfo","closest","requestEvent","CustomEvent","detail","country","countryCode","setupCtaButtons","buttons","button","addEventListener","then","catch","console","warn"],"sourceRoot":""}