diff --git a/src/stores/willowpark.js b/src/stores/willowpark.js
index 15aa658..0cccd6c 100644
--- a/src/stores/willowpark.js
+++ b/src/stores/willowpark.js
@@ -3,6 +3,7 @@
const { decodeHtml, stripTags, extractFirstImgUrl, cleanText } = require("../utils/html");
const { makePageUrlShopifyQueryPage } = require("../utils/url");
+const { needsSkuDetail, pickBetterSku, normalizeCspc } = require("../utils/sku");
function extractSkuFromUrlOrHref(hrefOrUrl) {
const s = String(hrefOrUrl || "");
@@ -66,8 +67,8 @@ function parseProductsWillowPark(html, ctx, finalUrl) {
const base = `https://${(ctx && ctx.store && ctx.store.host) || "www.willowpark.net"}/`;
const starts = [...s.matchAll(/
]*class=["'][^"']*\bgrid-item\b[^"']*\bgrid-product\b[^"']*["'][^>]*>/gi)]
- .map(m => m.index)
- .filter(i => typeof i === "number");
+ .map((m) => m.index)
+ .filter((i) => typeof i === "number");
const blocks = [];
for (let i = 0; i < starts.length; i++) {
@@ -91,7 +92,8 @@ function parseProductsWillowPark(html, ctx, finalUrl) {
url = canonicalizeWillowUrl(url);
const titleHtml =
- block.match(/
]*class=["'][^"']*\bgrid-product__title\b[^"']*["'][^>]*>([\s\S]*?)<\/div>/i)?.[1] || "";
+ block.match(/
]*class=["'][^"']*\bgrid-product__title\b[^"']*["'][^>]*>([\s\S]*?)<\/div>/i)?.[1] ||
+ "";
const name = cleanText(decodeHtml(stripTags(titleHtml)));
if (!name) continue;
@@ -120,6 +122,147 @@ function willowIsEmptyListingPage(html) {
return false;
}
+/* ---------------- Storefront GraphQL (token extracted from HTML) ---------------- */
+
+const WILLOW_STOREFRONT_GQL_URL = "https://willow-park-wines.myshopify.com/api/2025-07/graphql.json";
+
+const PRODUCT_BY_ID_QUERY = `
+query ($id: ID!) @inContext(country: CA) {
+ product(id: $id) {
+ variants(first: 50) {
+ nodes { sku availableForSale quantityAvailable }
+ }
+ }
+}
+`;
+
+function pickBestVariantSku(product) {
+ const vs = Array.isArray(product?.variants?.nodes) ? product.variants.nodes : [];
+ if (!vs.length) return "";
+
+ const inStock = vs.find((v) => Number(v?.quantityAvailable) > 0 && String(v?.sku || "").trim());
+ if (inStock) return String(inStock.sku).trim();
+
+ const forSale = vs.find((v) => Boolean(v?.availableForSale) && String(v?.sku || "").trim());
+ if (forSale) return String(forSale.sku).trim();
+
+ const any = vs.find((v) => String(v?.sku || "").trim());
+ return any ? String(any.sku).trim() : "";
+}
+
+function extractStorefrontTokenFromHtml(html) {
+ const s = String(html || "");
+
+ // 1) script#shopify-features JSON: {"accessToken":"..."}
+ const j = s.match(/