← back to Designer Wallcoverings
Guard cadence-import against {type,value} blob: unwrap row.material at parse so live importer can't leak JSON into custom.material + product description
ef5c99cb7c274af2ad3bcaffe947c8294c7f26bf · 2026-06-24 16:31:59 -0700 · Steve Abrams
Files touched
M shopify/scripts/cadence/cadence-import.js
Diff
commit ef5c99cb7c274af2ad3bcaffe947c8294c7f26bf
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Wed Jun 24 16:31:59 2026 -0700
Guard cadence-import against {type,value} blob: unwrap row.material at parse so live importer can't leak JSON into custom.material + product description
---
shopify/scripts/cadence/cadence-import.js | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/shopify/scripts/cadence/cadence-import.js b/shopify/scripts/cadence/cadence-import.js
index f505b4a8..4ca61306 100644
--- a/shopify/scripts/cadence/cadence-import.js
+++ b/shopify/scripts/cadence/cadence-import.js
@@ -114,6 +114,19 @@ function titleCase(s) {
return ws.map((w,i) => tcWord(w, i===0, i===ws.length-1)).join(' ');
}
const esc = s => String(s==null?'':s).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
+// Defensive unwrap for double-encoded metafield values. If a catalog spec column
+// (e.g. material) holds a whole Shopify metafield object — {"type":...,"value":"X"} —
+// stored as its JSON string, writing it raw leaks the blob into BOTH custom.material
+// and the product description spec list on every new product. Return the inner value.
+// (DWQW/Malibu blob-leak root cause, 2026-06-24.)
+const mfVal = v => {
+ if (typeof v !== 'string') return v;
+ const s = v.trim();
+ if (s.startsWith('{') && s.includes('"value"')) {
+ try { const o = JSON.parse(s); if (o && typeof o.value === 'string') return o.value; } catch (_) {}
+ }
+ return v;
+};
// ---- image-size guard (Steve 2026-06-12) ----
// Shopify rejects images >20MB (INVALID_IMAGE_FILE_SIZE) → the product lands
@@ -294,7 +307,7 @@ function selectSkus(cfg, limit) {
const out = psql(sql);
if (!out) return [];
const keys = [...STD, 'vendor_desc', ...(hasAllImg ? ['all_images'] : [])];
- return out.split('\n').map(line => { const v=line.split('\t'); const o={}; keys.forEach((k,i)=>o[k]=v[i]); o.cost=0; o.sampleOnly=true; return o; });
+ return out.split('\n').map(line => { const v=line.split('\t'); const o={}; keys.forEach((k,i)=>o[k]=v[i]); o.material=mfVal(o.material); o.cost=0; o.sampleOnly=true; return o; });
}
const sql = `SELECT ${cols}, (${cfg.costExpr})::numeric AS cost${sellSel}${allImgSel}
FROM ${cfg.table}
@@ -303,7 +316,7 @@ function selectSkus(cfg, limit) {
const out = psql(sql);
if (!out) return [];
const keys = [...STD, 'cost', ...(cfg.sellExpr ? ['sell'] : []), ...(hasAllImg ? ['all_images'] : [])];
- return out.split('\n').map(line => { const v=line.split('\t'); const o={}; keys.forEach((k,i)=>o[k]=v[i]); o.cost=parseFloat(o.cost); if(cfg.sellExpr) o.sell=parseFloat(o.sell); return o; });
+ return out.split('\n').map(line => { const v=line.split('\t'); const o={}; keys.forEach((k,i)=>o[k]=v[i]); o.material=mfVal(o.material); o.cost=parseFloat(o.cost); if(cfg.sellExpr) o.sell=parseFloat(o.sell); return o; });
}
// ---- SAMPLE-ONLY product input (Quadrille-house, no cost — Steve AUTHORIZED 2026-06-23) ----
← 98eea4a6 Harden backfill-to-catalog extractFromMetafields: unwrap {ty
·
back to Designer Wallcoverings
·
Repair tool: collapse newlines for single-line metafield typ e4ee0772 →