import puppeteer from 'puppeteer'; async function sleep(ms: number): Promise { return new Promise(resolve => setTimeout(resolve, ms)); } async function main() { const browser = await puppeteer.launch({ headless: true, args: ['--no-sandbox', '--disable-setuid-sandbox'], }); const page = await browser.newPage(); await page.setViewport({ width: 1920, height: 1080 }); // Go to a product detail page await page.goto('https://shop.bestdispensary.com/brand/dime', { waitUntil: 'networkidle2', timeout: 60000 }); await sleep(3000); // Bypass age gate const ageGate = await page.$('[data-testid="age-gate-modal"]'); if (ageGate) { const btn = await page.$('[data-testid="age-gate-submit-button"]'); if (btn) await btn.click(); await sleep(2000); } // Get first product URL const productUrl = await page.evaluate(() => { const a = document.querySelector('a[href*="/product/"]'); return a ? 'https://shop.bestdispensary.com' + a.getAttribute('href') : null; }); if (!productUrl) { console.log('No product found'); await browser.close(); return; } console.log('Checking product: ' + productUrl + '\n'); await page.goto(productUrl, { waitUntil: 'networkidle2', timeout: 30000 }); await sleep(2000); // Look for inventory/stock info const inventoryData = await page.evaluate(() => { const data: any = {}; // Check for stock/inventory elements const stockSelectors = [ '[class*="stock"]', '[class*="Stock"]', '[class*="inventory"]', '[class*="Inventory"]', '[class*="quantity"]', '[class*="Quantity"]', '[class*="available"]', '[class*="Available"]', '[class*="in-stock"]', '[class*="out-of-stock"]', '[data-stock]', '[data-quantity]', '[data-inventory]', ]; data.stockElements = []; stockSelectors.forEach(sel => { document.querySelectorAll(sel).forEach(el => { data.stockElements.push({ selector: sel, text: el.textContent?.trim().slice(0, 100), dataAttrs: Object.keys((el as HTMLElement).dataset || {}), }); }); }); // Check for "Add to cart" button state (disabled = out of stock) const addToCartBtn = document.querySelector('button[class*="add"], button[class*="cart"]'); data.addToCartBtn = { found: !!addToCartBtn, disabled: (addToCartBtn as HTMLButtonElement)?.disabled, text: addToCartBtn?.textContent?.trim(), }; // Check page source for inventory keywords const bodyText = document.body.innerText; data.hasStockText = bodyText.includes('stock') || bodyText.includes('Stock'); data.hasInventoryText = bodyText.includes('inventory') || bodyText.includes('Inventory'); data.hasQuantityText = bodyText.includes('quantity') || bodyText.includes('Quantity'); data.hasAvailableText = bodyText.includes('available') || bodyText.includes('Available'); // Get all data attributes on the page data.allDataAttrs = []; document.querySelectorAll('[data-product-id], [data-sku], [data-variant]').forEach(el => { const attrs: any = {}; Object.entries((el as HTMLElement).dataset).forEach(([k, v]) => { attrs[k] = v; }); if (Object.keys(attrs).length > 0) { data.allDataAttrs.push(attrs); } }); // Check for JSON-LD or schema data const scripts = document.querySelectorAll('script[type="application/ld+json"]'); data.jsonLd = []; scripts.forEach(s => { try { const json = JSON.parse(s.textContent || ''); data.jsonLd.push(json); } catch {} }); // Check Next.js data const nextData = document.getElementById('__NEXT_DATA__'); if (nextData) { try { const json = JSON.parse(nextData.textContent || ''); data.hasNextData = true; data.nextDataKeys = Object.keys(json); // Look for product data in props if (json.props?.pageProps?.product) { data.productFromNext = json.props.pageProps.product; } if (json.props?.pageProps) { data.pagePropsKeys = Object.keys(json.props.pageProps); } } catch {} } return data; }); console.log('Inventory Analysis:\n'); console.log('Stock elements found: ' + inventoryData.stockElements.length); inventoryData.stockElements.forEach((s: any) => { console.log(' - ' + s.selector + ': "' + s.text + '"'); }); console.log('\nAdd to Cart button: ' + JSON.stringify(inventoryData.addToCartBtn)); console.log('\nText checks:'); console.log(' Has "stock": ' + inventoryData.hasStockText); console.log(' Has "inventory": ' + inventoryData.hasInventoryText); console.log(' Has "quantity": ' + inventoryData.hasQuantityText); console.log(' Has "available": ' + inventoryData.hasAvailableText); console.log('\nData attributes: ' + JSON.stringify(inventoryData.allDataAttrs)); console.log('\nJSON-LD: ' + JSON.stringify(inventoryData.jsonLd, null, 2)); if (inventoryData.hasNextData) { console.log('\nNext.js data found!'); console.log(' Keys: ' + inventoryData.nextDataKeys); console.log(' Page props keys: ' + inventoryData.pagePropsKeys); if (inventoryData.productFromNext) { console.log('\n Product data from Next.js:'); console.log(JSON.stringify(inventoryData.productFromNext, null, 2)); } } await browser.close(); } main().catch(console.error);