← back to Hollywood Import
scrubMomentum(): Momentum→Hollywood private-label scrub + verifier (0 leaks / 8,043 rows)
6e3a6a3472788a20d6a227153f41dfbf231321af · 2026-06-10 06:54:42 -0700 · SteveStudio2
hollywood vendor_code = DW private label for Momentum Textiles & Walls. scrub maps
to PL fields (pl_brand 'Hollywood Wallcoverings' / pl_city_name CA-cities / DWHD sku),
suppresses source-brand leaks (real_vendor=Momentum, installation_pdf momentum URL,
grade_collection 'VERSA …', Versa's Second-Look program), and keeps the momentum
image URL only as a transient push src (Shopify rehosts → cdn.shopify.com).
verify.js: 8,043 momentum/versa-carrying rows scrubbed → 0 surviving leaks.
Files touched
A .gitignoreA scrub-momentum.jsA verify.js
Diff
commit 6e3a6a3472788a20d6a227153f41dfbf231321af
Author: SteveStudio2 <steve@designerwallcoverings.com>
Date: Wed Jun 10 06:54:42 2026 -0700
scrubMomentum(): Momentum→Hollywood private-label scrub + verifier (0 leaks / 8,043 rows)
hollywood vendor_code = DW private label for Momentum Textiles & Walls. scrub maps
to PL fields (pl_brand 'Hollywood Wallcoverings' / pl_city_name CA-cities / DWHD sku),
suppresses source-brand leaks (real_vendor=Momentum, installation_pdf momentum URL,
grade_collection 'VERSA …', Versa's Second-Look program), and keeps the momentum
image URL only as a transient push src (Shopify rehosts → cdn.shopify.com).
verify.js: 8,043 momentum/versa-carrying rows scrubbed → 0 surviving leaks.
---
.gitignore | 4 ++++
scrub-momentum.js | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
verify.js | 36 +++++++++++++++++++++++++++++++++
3 files changed, 100 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1bcdbdc
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules/
+.env*
+*.log
+.DS_Store
diff --git a/scrub-momentum.js b/scrub-momentum.js
new file mode 100644
index 0000000..26f693a
--- /dev/null
+++ b/scrub-momentum.js
@@ -0,0 +1,60 @@
+'use strict';
+// Momentum → Hollywood private-label scrub. "hollywood" vendor_code in dw_unified
+// is the DW private label for Momentum Textiles & Walls. NEVER expose "Momentum"
+// (or its sub-brand "Versa") in any customer-facing field. The catalog already
+// carries the PL mapping in specs (pl_brand / pl_city_name); this turns a
+// vendor_catalog hollywood row into a public-safe product object + a leak check.
+//
+// Public mapping: vendor = specs.pl_brand ("Hollywood Wallcoverings")
+// collection = specs.pl_city_name (CA cities)
+// sku = dw_sku (DWHD-)
+// Suppress: real_vendor, installation_pdf (momentum URL), grade_collection
+// ("VERSA …"), hw/list/cost prices; scrub "Versa's Second-Look".
+// Image: image_url passed only as transient push src → Shopify rehosts
+// to cdn.shopify.com. Never stored in a text field.
+
+const SOURCE_URL = /https?:\/\/[^\s"']*momentum[^\s"']*/gi;
+const VERSA_PROGRAM = /Versa'?s\s+Second[-\s]?Look\s*®?\s*Program/gi;
+const SOURCE_BRANDS = /\b(momentum(?:\s+textiles(?:\s+and\s+walls)?)?|versa(?:'s)?(?:\s+designed\s+surfaces)?)\b/gi;
+// Spec keys that are internal-only or carry a source-brand value — never push.
+const DROP_KEYS = new Set(['real_vendor','installation_pdf','grade_collection','hw_price','list_price','cost']);
+
+function scrubText(s) {
+ if (s == null) return s;
+ return String(s)
+ .replace(SOURCE_URL, '')
+ .replace(VERSA_PROGRAM, "the manufacturer's take-back program")
+ .replace(SOURCE_BRANDS, (m) => /momentum/i.test(m) ? 'Hollywood' : '')
+ .replace(/\s{2,}/g, ' ').replace(/\s+,/g, ',').trim();
+}
+
+function scrubMomentum(row) {
+ const specs = typeof row.specs === 'string' ? JSON.parse(row.specs || '{}') : (row.specs || {});
+ const pubSpecs = {};
+ for (const [k, v] of Object.entries(specs)) {
+ if (DROP_KEYS.has(k)) continue;
+ pubSpecs[k] = (typeof v === 'string') ? scrubText(v) : v;
+ }
+ const tags = scrubText(specs.tags || '').split(',').map(t => t.trim()).filter(Boolean);
+ return {
+ vendor: specs.pl_brand || 'Hollywood Wallcoverings',
+ title: scrubText(row.pattern_name) + (row.color_name ? ', ' + scrubText(row.color_name) : ''),
+ collection: specs.pl_city_name ? scrubText(specs.pl_city_name) : (row.collection ? scrubText(row.collection) : null),
+ sku: row.dw_sku,
+ mfr_sku: row.mfr_sku,
+ imageSrc: row.image_url, // TRANSIENT push src only — Shopify rehosts; excluded from leak check
+ tags,
+ specs: pubSpecs,
+ };
+}
+
+// Leak check: scan every customer-facing field EXCEPT imageSrc (its momentum URL
+// is the sanctioned transient source Shopify launders). Returns array of leaks.
+function leakCheck(pub) {
+ const { imageSrc, ...rest } = pub;
+ const hay = JSON.stringify(rest);
+ const hits = hay.match(/momentum|versa/gi) || [];
+ return hits;
+}
+
+module.exports = { scrubMomentum, scrubText, leakCheck };
diff --git a/verify.js b/verify.js
new file mode 100644
index 0000000..aa100a7
--- /dev/null
+++ b/verify.js
@@ -0,0 +1,36 @@
+'use strict';
+const { spawnSync } = require('child_process');
+const { scrubMomentum, leakCheck } = require('./scrub-momentum');
+const PSQL = '/opt/homebrew/opt/postgresql@14/bin/psql';
+// Pull every hollywood row that carries 'momentum' anywhere (specs or image_url)
+const sql = `SELECT json_agg(t) FROM (
+ SELECT id, pattern_name, color_name, collection, dw_sku, mfr_sku, image_url, specs
+ FROM vendor_catalog
+ WHERE vendor_code='hollywood' AND (specs::text ILIKE '%momentum%' OR specs::text ILIKE '%versa%')
+) t`;
+const r = spawnSync(PSQL, ['-U','stevestudio2','-d','dw_unified','-tA','-c',sql], {encoding:'utf8', maxBuffer:1<<30});
+const rows = JSON.parse(r.stdout || '[]') || [];
+let leaky = 0; const samples = [];
+for (const row of rows) {
+ const pub = scrubMomentum(row);
+ const leaks = leakCheck(pub);
+ if (leaks.length) { leaky++; if (samples.length < 5) samples.push({ id: row.id, leaks, vendor: pub.vendor, title: pub.title }); }
+}
+console.log(`rows scrubbed: ${rows.length}`);
+console.log(`rows with surviving momentum/versa leak (target 0): ${leaky}`);
+if (samples.length) { console.log('LEAK SAMPLES:'); samples.forEach(s=>console.log(' ', JSON.stringify(s))); }
+// show one full before/after so we can eyeball the scrub
+if (rows.length) {
+ const ex = rows.find(x => (x.specs && JSON.stringify(x.specs).match(/versa/i))) || rows[0];
+ const pub = scrubMomentum(ex);
+ console.log('\nSAMPLE SCRUB (id '+ex.id+'):');
+ console.log(' vendor :', pub.vendor);
+ console.log(' title :', pub.title);
+ console.log(' collection :', pub.collection);
+ console.log(' sku :', pub.sku);
+ console.log(' imageSrc :', (pub.imageSrc||'').slice(0,55), '(transient → Shopify rehosts)');
+ console.log(' specs.grade_collection kept?:', pub.specs.grade_collection, '(should be undefined)');
+ console.log(' specs.real_vendor kept? :', pub.specs.real_vendor, '(should be undefined)');
+ console.log(' specs.sustainability :', (pub.specs.sustainability||'').slice(0,70));
+ console.log(' tags :', pub.tags.join(' | ').slice(0,90));
+}
(oldest)
·
back to Hollywood Import
·
scrub: emit ALL images (all_images array) + completeness gat 402ee5e →