From 2e626ce1f3a6d91f5f9b5d77d384ab8f67252c6f Mon Sep 17 00:00:00 2001 From: Victor Noguera Date: Wed, 8 Apr 2026 00:35:32 -0400 Subject: [PATCH] feat: refactor token management and enhance logging in fetchKeepaDataBatch function --- src/keepa.ts | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/src/keepa.ts b/src/keepa.ts index 60cf022..e086cde 100644 --- a/src/keepa.ts +++ b/src/keepa.ts @@ -22,15 +22,20 @@ async function waitForToken(): Promise { } // Wait until we regenerate at least 1 token - const waitMs = Math.ceil((1 / refillRate) * 60_000) - (Date.now() - lastRequestTime); + const waitMs = + Math.ceil((1 / refillRate) * 60_000) - (Date.now() - lastRequestTime); if (waitMs > 0) { - console.log(`Keepa tokens exhausted. Waiting ${Math.ceil(waitMs / 1000)}s for token regeneration...`); + console.log( + `Keepa tokens exhausted. Waiting ${Math.ceil(waitMs / 1000)}s for token regeneration...`, + ); await new Promise((r) => setTimeout(r, waitMs)); } tokensLeft = 1; } -export async function fetchKeepaDataBatch(asins: string[]): Promise> { +export async function fetchKeepaDataBatch( + asins: string[], +): Promise> { const results = new Map(); // Split into chunks of MAX_ASINS_PER_REQUEST @@ -41,7 +46,9 @@ export async function fetchKeepaDataBatch(asins: string[]): Promise): KeepaData { const stats = product.stats; const csv = product.csv; + const salesRankDrops30 = pickKeepaNumber( + product.salesRankDrops30, + stats?.salesRankDrops30, + ); + const salesRankDrops90 = + pickKeepaNumber(product.salesRankDrops90, stats?.salesRankDrops90) ?? + (salesRankDrops30 != null ? salesRankDrops30 * 3 : null); + const monthlySold = + pickKeepaNumber(product.monthlySold, stats?.monthlySold) ?? + salesRankDrops30; return { currentPrice: extractCurrentPrice(csv), @@ -86,16 +105,27 @@ function parseKeepaProduct(product: Record): KeepaData { maxPrice90: stats?.max?.[0] != null ? stats.max[0] / 100 : null, salesRank: stats?.current?.[3] ?? null, salesRankAvg90: stats?.avg?.[3] ?? null, - salesRankDrops30: product.salesRankDrops30 ?? null, - salesRankDrops90: product.salesRankDrops90 ?? null, + salesRankDrops30, + salesRankDrops90, sellerCount: stats?.current?.[11] ?? null, buyBoxSeller: product.buyBoxSellerId ?? null, buyBoxPrice: stats?.current?.[18] != null ? stats.current[18] / 100 : null, - monthlySold: product.monthlySold ?? null, - categoryTree: product.categoryTree?.map((c: { name: string }) => c.name) ?? [], + monthlySold, + categoryTree: + product.categoryTree?.map((c: { name: string }) => c.name) ?? [], }; } +function pickKeepaNumber(...values: unknown[]): number | null { + for (const value of values) { + if (typeof value !== "number" || !Number.isFinite(value)) continue; + // Keepa often uses -1 as "not available". + if (value < 0) continue; + return value; + } + return null; +} + function extractCurrentPrice(csv: number[][] | undefined): number | null { if (!csv) return null;