← back to Imageless Rescue
material-default-widths: Steve-approved material→width defaults for clear-material width-violators (grasscloth/cork/silk/textile/metallic=36in, vinyl=54in); skips ambiguous standard+murals; additive, never overwrites
900b1400648a3365b67f66513c645ddf125e6487 · 2026-06-21 13:57:45 -0700 · Steve
Files touched
A scripts/material-default-widths.js
Diff
commit 900b1400648a3365b67f66513c645ddf125e6487
Author: Steve <steve@designerwallcoverings.com>
Date: Sun Jun 21 13:57:45 2026 -0700
material-default-widths: Steve-approved material→width defaults for clear-material width-violators (grasscloth/cork/silk/textile/metallic=36in, vinyl=54in); skips ambiguous standard+murals; additive, never overwrites
---
scripts/material-default-widths.js | 40 ++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)
diff --git a/scripts/material-default-widths.js b/scripts/material-default-widths.js
new file mode 100644
index 0000000..4c61a76
--- /dev/null
+++ b/scripts/material-default-widths.js
@@ -0,0 +1,40 @@
+'use strict';
+// Set global.width from a MATERIAL DEFAULT for the clear-material width-violators
+// (Steve-approved 2026-06-21). Skips ambiguous "standard" + murals. Additive: checks
+// live width first and never overwrites. Idempotent, resumable, dry-run.
+const fs=require('fs'),os=require('os'),path=require('path'),https=require('https');
+const DRY=process.argv.includes('--dry-run');
+const LIMIT=process.argv.includes('--limit')?parseInt(process.argv[process.argv.indexOf('--limit')+1],10):Infinity;
+function tok(){if(process.env.SHOPIFY_ADMIN_TOKEN)return process.env.SHOPIFY_ADMIN_TOKEN.replace(/['"]/g,'').trim();try{return fs.readFileSync(path.join(os.homedir(),'Projects/secrets-manager/.env'),'utf8').match(/^SHOPIFY_ADMIN_TOKEN=(.*)$/m)[1].replace(/['"]/g,'').trim()}catch{return null}}
+const T=tok(),STORE='designer-laboratory-sandbox.myshopify.com',API='2024-10',sleep=ms=>new Promise(r=>setTimeout(r,ms));
+function gql(q,v){return new Promise(res=>{const b=JSON.stringify({query:q,variables:v});const r=https.request({hostname:STORE,path:`/admin/api/${API}/graphql.json`,method:'POST',headers:{'X-Shopify-Access-Token':T,'Content-Type':'application/json','Content-Length':Buffer.byteLength(b)}},x=>{let d='';x.on('data',c=>d+=c);x.on('end',()=>{try{res(JSON.parse(d))}catch{res({})}})});r.on('error',()=>res({}));r.write(b);r.end()})}
+async function G(q,v){for(let a=0;a<6;a++){const r=await gql(q,v);if(r.errors&&JSON.stringify(r.errors).toUpperCase().includes('THROTTL')){await sleep(2000*(a+1));continue}return r}return{}}
+const PROD=`query($id:ID!){product(id:$id){id title wG:metafield(namespace:"global",key:"width"){value} wC:metafield(namespace:"custom",key:"width"){value}}}`;
+const SET=`mutation($mf:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mf){userErrors{message}}}`;
+function material(t){t=(t||'').toLowerCase();
+ if(/grasscloth|grass cloth|sisal|jute|hemp|paperweave|raffia|abaca|arrowroot|sea ?grass|rush ?cloth|reed/.test(t))return['natural-grasscloth','36 Inches'];
+ if(/cork/.test(t))return['cork','36 Inches']; if(/silk/.test(t))return['silk','36 Inches'];
+ if(/mica|metallic|foil|leaf|metal/.test(t))return['mica-metallic','36 Inches'];
+ if(/type ?ii|type ?2|type ?iii|commercial|contract|vinyl/.test(t))return['commercial-vinyl','54 Inches'];
+ if(/linen|weave|cloth|textile|fabric/.test(t))return['textile-weave','36 Inches'];
+ return [null,null]; // standard / mural / unknown -> SKIP (held, not guessed)
+}
+(async()=>{
+ const d=require('../data/active-gate-violations-2026-06-21.json');
+ const vio=(d.violations||[]).filter(x=>(x.types||[]).includes('specs'));
+ const LED=path.join(__dirname,'..','data','material-width-ledger.jsonl');const done=new Set();
+ if(fs.existsSync(LED))for(const l of fs.readFileSync(LED,'utf8').split('\n')){if(l.trim())try{done.add(JSON.parse(l).id)}catch{}}
+ const queue=vio.filter(x=>{const [m]=material(x.title);return m&&!done.has(x.id)}).slice(0,LIMIT===Infinity?undefined:LIMIT);
+ console.log(`material-default-widths ${DRY?'[DRY] ':''}clear-material-violators(remaining)=${queue.length}`);
+ const st={filled:0,hadWidth:0,err:0,byMat:{}};
+ for(const x of queue){
+ const [mat,w]=material(x.title);
+ const pr=await G(PROD,{id:x.id});const p=pr.data&&pr.data.product;if(!p){st.err++;continue}
+ const live=((p.wG&&p.wG.value)||(p.wC&&p.wC.value)||'').trim();
+ if(live){st.hadWidth++;fs.appendFileSync(LED,JSON.stringify({id:x.id,result:'already-has-width',live})+'\n');continue}
+ if(DRY){st.filled++;st.byMat[mat]=(st.byMat[mat]||0)+1;if(st.filled<=10)console.log(` • ${p.title.slice(0,46)} [${mat}] → ${w}`);continue}
+ const mr=await G(SET,{mf:[{ownerId:x.id,namespace:'global',key:'width',type:'single_line_text_field',value:w}]});
+ if(mr.data&&mr.data.metafieldsSet&&!mr.data.metafieldsSet.userErrors.length){st.filled++;st.byMat[mat]=(st.byMat[mat]||0)+1;fs.appendFileSync(LED,JSON.stringify({id:x.id,width:w,material:mat,result:'filled'})+'\n');if(st.filled%50===0)console.log(` ✓ ${st.filled} set`)}else st.err++;
+ }
+ console.log(`\n=== ${DRY?'DRY ':''}SUMMARY ===`,JSON.stringify(st));
+})().catch(e=>{console.error('FATAL',e);process.exit(1)});
← 5722e55 width-from-catalog: DB-lookup width fill from per-vendor *_c
·
back to Imageless Rescue
·
tag-needs-width: add Needs-Width tag to width-violators stil ff2a7c1 →