← back to Dwjs Consolidation 2026 04 23
yb_vendor_audit.js
88 lines
#!/usr/bin/env node
// READ-ONLY authoritative audit BY VENDOR (not SKU glob) — catches in-scope products
// whose variant SKU falls outside DWJS*/DWQW* (blank/TRF/GGA/CCA prefixes seen in dw_sku).
// Confirmed York/Brewster identity = Jeffrey Stevens + Malibu Wallpaper.
// Lineage (report-only, NOT gated) = LA Walls, York, York Contract.
const https=require('https'), fs=require('fs');
const STORE='designer-laboratory-sandbox.myshopify.com';
const os=require('os');
function loadToken(){const k=process.env.SHOPIFY_ADMIN_TOKEN;if(k)return k;try{const e=fs.readFileSync(os.homedir()+'/Projects/secrets-manager/.env','utf8');const m=e.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m);if(m)return m[1].trim();}catch{}throw new Error('SHOPIFY_ADMIN_TOKEN not found (export it or set in secrets-manager/.env)');}
let TOKEN; // set below via loadToken()
TOKEN=loadToken();
const DIR='/Users/macstudio3/Projects/dwjs-consolidation-2026-04-23/yb-audit';
fs.mkdirSync(DIR,{recursive:true});
function gql(b,retry=0){return new Promise((res,rej)=>{const d=JSON.stringify(b);const r=https.request({hostname:STORE,path:'/admin/api/2024-10/graphql.json',method:'POST',headers:{'X-Shopify-Access-Token':TOKEN,'Content-Type':'application/json','Content-Length':Buffer.byteLength(d)}},R=>{let c='';R.on('data',x=>c+=x);R.on('end',()=>{try{res(JSON.parse(c))}catch{if(retry<3)setTimeout(()=>res(gql(b,retry+1)),2000);else res({error:c.slice(0,300)})}})});r.on('error',e=>{if(retry<3)setTimeout(()=>res(gql(b,retry+1)),2000);else rej(e)});r.setTimeout(60000,()=>{r.destroy();if(retry<3)res(gql(b,retry+1));else rej(new Error('t'))});r.write(d);r.end()});}
async function bulk(filter){
const q=`{ products(query:${JSON.stringify(filter)}) { edges { node { id title vendor productType tags status
m1: metafield(namespace:"global", key:"v_prods_quantity_order_min") { value }
m2: metafield(namespace:"global", key:"v_prods_quantity_order_units") { value }
m3: metafield(namespace:"global", key:"unit_of_measure") { value }
collections { edges { node { handle title } } }
variants { edges { node { id sku title } } }
} } } }`;
for(let i=0;i<10;i++){const r=await gql({query:'{ currentBulkOperation { status } }'});const st=r?.data?.currentBulkOperation?.status;if(!st||['COMPLETED','FAILED','CANCELED','EXPIRED'].includes(st))break;await new Promise(r=>setTimeout(r,3000));}
const s=await gql({query:`mutation{ bulkOperationRunQuery(query: ${JSON.stringify(q)}) { bulkOperation{id} userErrors{message} } }`});
const ue=s?.data?.bulkOperationRunQuery?.userErrors; if(ue&&ue.length)throw new Error(JSON.stringify(ue));
for(;;){await new Promise(r=>setTimeout(r,4000));const p=await gql({query:'{ currentBulkOperation { status objectCount url } }'});const op=p?.data?.currentBulkOperation;console.log(` ${filter} ${op?.status} obj=${op?.objectCount||0}`);if(op?.status==='COMPLETED')return op.url;if(['FAILED','CANCELED','EXPIRED'].includes(op?.status))throw new Error(op.status);}
}
function dl(u,f){return new Promise((r,j)=>{const o=fs.createWriteStream(f);https.get(u,res=>{res.pipe(o);o.on('finish',()=>o.close(()=>r(f)))}).on('error',j)})}
const MURAL_TAGS=new Set(['mural','murals','wall mural','wall murals','mural collection']);
function muralReason(p){
if(/\bmurals?\b/i.test(p.title))return 'title';
if(/\bmurals?\b/i.test(p.productType))return 'type';
for(const t of (Array.isArray(p.tags)?p.tags:String(p.tags||'').split(','))) if(MURAL_TAGS.has(String(t).trim().toLowerCase()))return 'tag:'+String(t).trim();
for(const c of p.collections) if(/\bmurals?\b/i.test(`${c.handle} ${c.title}`))return 'collection:'+c.handle;
return null;
}
function classify(products){
const r={total:0,byStatus:{},nonRoll:[],mural:[],ok:0,drifted:[]};
for(const p of products.values()){
r.total++; r.byStatus[p.status]=(r.byStatus[p.status]||0)+1;
const main=p.variants.find(v=>!/sample/i.test(v.sku+v.title))||p.variants[0]||{sku:''};
const dw=main.sku;
const unit=(p.m3?.value||'').toLowerCase();
if(unit && !/roll/.test(unit)){r.nonRoll.push({dw,vendor:p.vendor,unit:p.m3,status:p.status});continue;}
const mr=muralReason(p);
if(mr){r.mural.push({dw,vendor:p.vendor,reason:mr,status:p.status,m1:p.m1?.value??null});continue;}
if(p.m1?.value==='2'&&p.m2?.value==='2'){r.ok++;continue;}
r.drifted.push({dw,vendor:p.vendor,status:p.status,m1:p.m1?.value??null,m2:p.m2?.value??null,unit:p.m3?.value||'(unset)',product_id:p.id});
}
return r;
}
(async()=>{
const groups={
confirmed:["vendor:'Jeffrey Stevens'","vendor:'Malibu Wallpaper'"],
lineage:["vendor:'LA Walls'","vendor:'York'","vendor:'York Contract'"],
};
const out={};
for(const [grp,filters] of Object.entries(groups)){
const products=new Map();
for(const filter of filters){
const file=`${DIR}/vraw_${filter.replace(/\W/g,'')}.jsonl`;
if(!(process.env.SKIP_PULL && fs.existsSync(file))){ const url=await bulk(filter); await dl(url,file); }
else console.log(` (cached) ${filter}`);
for(const line of fs.readFileSync(file,'utf8').split('\n').filter(Boolean)){
const o=JSON.parse(line);
if(o.id?.includes('/Product/'))products.set(o.id,{id:o.id,title:o.title||'',vendor:o.vendor||'',productType:o.productType||'',tags:o.tags||'',status:o.status||'',m1:o.m1,m2:o.m2,m3:o.m3,collections:[],variants:[]});
else if(o.id?.includes('/Collection/')&&o.__parentId){const p=products.get(o.__parentId);if(p)p.collections.push({handle:o.handle||'',title:o.title||''});}
else if(o.id?.includes('/ProductVariant/')&&o.__parentId){const p=products.get(o.__parentId);if(p)p.variants.push({id:o.id,sku:o.sku||'',title:o.title||''});}
}
}
out[grp]=classify(products);
}
const c=out.confirmed, l=out.lineage;
fs.writeFileSync(`${DIR}/v_drifted_confirmed.json`,JSON.stringify(c.drifted,null,2));
fs.writeFileSync(`${DIR}/v_drifted_lineage.json`,JSON.stringify(l.drifted,null,2));
fs.writeFileSync(`${DIR}/v_mural_confirmed.json`,JSON.stringify(c.mural,null,2));
fs.writeFileSync(`${DIR}/v_nonroll_confirmed.json`,JSON.stringify(c.nonRoll,null,2));
const pr=(t,r)=>{console.log(`\n===== ${t} =====`);console.log(' total:',r.total,' byStatus:',JSON.stringify(r.byStatus));console.log(' non-ROLL exempt:',r.nonRoll.length,' mural exempt:',r.mural.length,' OK 2/2:',r.ok,' DRIFTED:',r.drifted.length);const ad=r.drifted.filter(d=>d.status==='ACTIVE');console.log(' ACTIVE drifted:',ad.length, ad.length?JSON.stringify(ad.slice(0,6).map(d=>({dw:d.dw,m1:d.m1,m2:d.m2,unit:d.unit}))):'');const dd=r.drifted.filter(d=>d.status==='DRAFT');console.log(' DRAFT drifted:',dd.length);const muralMin1=r.mural.filter(m=>m.m1!=='2');console.log(' mural-exempt currently min!=2 (left alone per DTD):',muralMin1.length);};
pr('CONFIRMED York/Brewster (Jeffrey Stevens + Malibu Wallpaper) — BY VENDOR',c);
pr('LINEAGE-only (LA Walls + York + York Contract) — report, NOT gated',l);
console.log('\nfiles ->',DIR);
})().catch(e=>{console.error(e);process.exit(1)});