/** * Explore Jane API to understand data structure * Usage: npx ts-node scripts/test-jane-api-explore.ts */ import puppeteer from 'puppeteer-extra'; import StealthPlugin from 'puppeteer-extra-plugin-stealth'; puppeteer.use(StealthPlugin()); async function main() { console.log('Exploring Jane API from browser context...\n'); const browser = await puppeteer.launch({ headless: 'new', args: ['--no-sandbox', '--disable-setuid-sandbox'], }); const page = await browser.newPage(); // Intercept network requests to find store data API calls const capturedResponses: Array<{ url: string; data: any }> = []; await page.setRequestInterception(true); page.on('request', (req) => req.continue()); page.on('response', async (response) => { const url = response.url(); if (url.includes('iheartjane.com') && (url.includes('/stores') || url.includes('/search') || url.includes('algolia'))) { try { const text = await response.text(); if (text.startsWith('{') || text.startsWith('[')) { const data = JSON.parse(text); capturedResponses.push({ url, data }); console.log(`Captured: ${url.substring(0, 100)}...`); } } catch { // Not JSON } } }); // Visit Jane to establish session console.log('Visiting Jane stores page to capture network requests...'); await page.goto('https://www.iheartjane.com/stores', { waitUntil: 'networkidle2', timeout: 60000, }); console.log(`\nCaptured ${capturedResponses.length} API responses`); for (const resp of capturedResponses) { console.log(`\n--- ${resp.url.substring(0, 80)} ---`); const keys = Object.keys(resp.data); console.log('Keys:', keys); // Check for stores array if (resp.data.stores && Array.isArray(resp.data.stores)) { console.log(`Stores count: ${resp.data.stores.length}`); const firstStore = resp.data.stores[0]; if (firstStore) { console.log('First store keys:', Object.keys(firstStore)); console.log('Sample:', JSON.stringify(firstStore, null, 2).substring(0, 500)); } } // Check for hits (Algolia) if (resp.data.hits && Array.isArray(resp.data.hits)) { console.log(`Hits count: ${resp.data.hits.length}`); const firstHit = resp.data.hits[0]; if (firstHit) { console.log('First hit keys:', Object.keys(firstHit)); } } } // Look for __NEXT_DATA__ or similar embedded data console.log('\n--- Checking for embedded page data ---'); const pageData = await page.evaluate(() => { // Check for Next.js data const nextData = (window as any).__NEXT_DATA__; if (nextData?.props?.pageProps?.stores) { return { source: '__NEXT_DATA__', storeCount: nextData.props.pageProps.stores.length, firstStore: nextData.props.pageProps.stores[0], }; } // Check for any global store data const win = window as any; if (win.stores) return { source: 'window.stores', data: win.stores }; if (win.__stores) return { source: 'window.__stores', data: win.__stores }; return null; }); if (pageData) { console.log('Found embedded data:', pageData.source); console.log('Store count:', pageData.storeCount); if (pageData.firstStore) { console.log('First store keys:', Object.keys(pageData.firstStore)); console.log('Sample:', JSON.stringify({ id: pageData.firstStore.id, name: pageData.firstStore.name, city: pageData.firstStore.city, state: pageData.firstStore.state, }, null, 2)); } } else { console.log('No embedded page data found'); } // Try alternative API endpoints from browser context console.log('\n--- Testing alternative API endpoints ---'); // Try the map endpoint const mapData = await page.evaluate(async () => { try { const res = await fetch('https://api.iheartjane.com/v1/stores/map?per_page=100'); if (res.ok) return await res.json(); } catch {} return null; }); if (mapData) { console.log('\n/v1/stores/map response:'); console.log('Keys:', Object.keys(mapData)); if (mapData.stores?.[0]) { console.log('First store keys:', Object.keys(mapData.stores[0])); } } // Try index endpoint const indexData = await page.evaluate(async () => { try { const res = await fetch('https://api.iheartjane.com/v1/stores/index?per_page=10'); if (res.ok) return await res.json(); } catch {} return null; }); if (indexData) { console.log('\n/v1/stores/index response:'); console.log('Keys:', Object.keys(indexData)); if (indexData.stores?.[0]) { console.log('First store keys:', Object.keys(indexData.stores[0])); } } // Try with state parameter const stateData = await page.evaluate(async () => { try { const res = await fetch('https://api.iheartjane.com/v1/stores?state=AZ&per_page=10'); if (res.ok) return await res.json(); } catch {} return null; }); if (stateData) { console.log('\n/v1/stores?state=AZ response:'); console.log('Keys:', Object.keys(stateData)); console.log('Stores count:', stateData.stores?.length); if (stateData.stores?.[0]) { console.log('First store keys:', Object.keys(stateData.stores[0])); console.log('Sample:', JSON.stringify(stateData.stores[0], null, 2).substring(0, 300)); } } // Try Algolia directly for stores console.log('\n--- Testing Algolia for stores ---'); const algoliaStores = await page.evaluate(async () => { try { // Common Algolia search pattern const res = await fetch('https://search.iheartjane.com/1/indexes/stores-production/query', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Algolia-Application-Id': 'HKXSXRD7RA', 'X-Algolia-API-Key': 'YjZhYjQxZjU4ZTNjMTRhYzExZTk2YjU2MzliMGE4ZTE5YjJkMmZkZTI2ODllYTY2MThlMzQ3Y2QxOTFkMjI5Y3RhZ0ZpbHRlcnM9', }, body: JSON.stringify({ query: 'Arizona', hitsPerPage: 20, }), }); if (res.ok) return await res.json(); } catch {} return null; }); if (algoliaStores) { console.log('Algolia stores-production response:'); console.log('Keys:', Object.keys(algoliaStores)); console.log('Hits count:', algoliaStores.hits?.length); if (algoliaStores.hits?.[0]) { console.log('First hit keys:', Object.keys(algoliaStores.hits[0])); console.log('Sample:', JSON.stringify(algoliaStores.hits[0], null, 2).substring(0, 500)); } } // Check if there's a /v2 endpoint const v2Data = await page.evaluate(async () => { try { const res = await fetch('https://api.iheartjane.com/v2/stores?per_page=10'); if (res.ok) return await res.json(); } catch {} return null; }); if (v2Data) { console.log('\n/v2/stores response:'); console.log('Keys:', Object.keys(v2Data)); if (v2Data.stores?.[0]) { console.log('First store keys:', Object.keys(v2Data.stores[0])); } } await browser.close(); console.log('\nDone!'); } main().catch(console.error);