← back to Dw Yolo Loop
scripts/orphan-collections/orphan-collection-livecount.mjs
51 lines
// orphan-collection-livecount (c41) — READ-ONLY. c40 proved collections.json
// products_count is STALE (Nicolette Mayer showed 214, live=0), so c39's empty
// list undercounted. This re-scans every live collection using the LIVE
// /collections/<handle>/products.json count (limit=4 → classify 0 / 1-3 / 4+),
// with retry/backoff to avoid the c38 UNKNOWN problem. $0.
import fs from 'node:fs';
const BASE='https://www.designerwallcoverings.com';
const OUT=`${process.env.HOME}/.claude/yolo-queue/orphan-collections-LIVE-2026-06-16.json`;
const MD =`${process.env.HOME}/.claude/yolo-queue/orphan-collections-LIVE-2026-06-16.md`;
const sleep=ms=>new Promise(r=>setTimeout(r,ms));
async function getj(url){
for(let a=0;a<5;a++){
try{ const r=await fetch(url,{signal:AbortSignal.timeout(15000)});
if(r.status===200) return await r.json();
if(r.status===404) return {__404:true};
if(r.status===429){await sleep(1500*(a+1));continue;} }
catch(e){ await sleep(1200*(a+1)); }
}
return null;
}
// 1) collection handles from feed
const cols=[];
for(let p=1;p<=20;p++){ const j=await getj(`${BASE}/collections.json?limit=250&page=${p}`); if(!j||!(j.collections||[]).length)break; cols.push(...j.collections.map(c=>({handle:c.handle,title:c.title,feed_count:c.products_count}))); await sleep(150); }
// 2) live count each
const empty=[], near=[], unknown=[]; let ok=0;
for(const c of cols){
const j=await getj(`${BASE}/collections/${encodeURIComponent(c.handle)}/products.json?limit=4`);
if(j===null){ unknown.push(c.handle); await sleep(150); continue; }
const n = j.__404 ? 0 : (j.products||[]).length;
const is404 = !!j.__404;
if(n===0) empty.push({...c, live:0, http404:is404});
else if(n<4) near.push({...c, live:n});
else ok++;
await sleep(150);
}
const report={generated_at:new Date().toISOString(), scanned:cols.length, empty:empty.length, near_lt4:near.length, ok_4plus:ok, unknown:unknown.length,
empty_list:empty, near_list:near, unknown_handles:unknown};
fs.writeFileSync(OUT,JSON.stringify(report,null,2));
let md=`# Orphan-collection scan — LIVE counts (c41) — ${new Date().toISOString().slice(0,16)}\n\n`;
md+=`**READ-ONLY (live per-collection /products.json, retry/backoff), \$0.** Corrects c39 (which used the STALE feed products_count, c40). ${cols.length} collections scanned.\n\n`;
md+=`Empty (0 live) **${empty.length}** · near-empty (<4) **${near.length}** · ok (4+) **${ok}** · unknown **${unknown.length}**\n\n`;
md+=`## EMPTY (0 live products) — ${empty.length}\n| handle | feed_count(stale) | http404 |\n|---|--:|:--:|\n`;
for(const c of empty) md+=`| ${c.handle} | ${c.feed_count} | ${c.http404?'Y':''} |\n`;
md+=`\n## NEAR-EMPTY (<4) — ${near.length}\n| handle | live |\n|---|--:|\n`;
for(const c of near.slice(0,60)) md+=`| ${c.handle} | ${c.live} |\n`;
fs.writeFileSync(MD,md);
console.log(`[orphan-live] scanned=${cols.length} EMPTY=${empty.length} near(<4)=${near.length} ok=${ok} unknown=${unknown.length}`);
console.log(' c39 said 4 empty (feed-count); live empties:');
for(const c of empty) console.log(` ${c.handle} (feed said ${c.feed_count})`);
console.log(`Report: ${MD}`);