← back to Hollywood Import
momentum-feed/prep-golive.mjs
61 lines
#!/usr/bin/env node
// Go-live prep for the Hollywood line (blockers #2–4), STAGING ONLY, reversible.
// Adds non-destructive columns to momentum_colorways (pattern_name never mutated):
// hw_product_type — "Acoustic Panel" (category=Acoustic) else "Wallcovering" [#2 taxonomy]
// hw_title — faithful title; only de-SHOUTs ALLCAPS names [#3 name-clean, non-merging]
// panel_spec — dimension/thickness tokens SURFACED (copied, not stripped) [#3 spec field]
// #4 leak scrub: re-asserts 0 Momentum/Versa across public-bound text (already verified clean).
// Run: NODE_PATH=.../AbramsOS/node_modules node prep-golive.mjs [--commit]
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const { Pool } = require('pg');
const COMMIT = process.argv.includes('--commit');
const pool = new Pool({ connectionString: 'postgresql://dw_admin:DW2024!@127.0.0.1:5432/dw_unified' });
const DIM = /(\d+(?:\.\d+)?\s*H\s*[x×]\s*\d+(?:\.\d+)?\s*L|\b\d+(?:\.\d+)?\s?(?:IN|MM)\b)/gi;
function titleClean(name) {
let t = name.replace(/\s+\/\s+/g, ' ').replace(/\s{2,}/g, ' ').trim();
if (t === t.toUpperCase() && /[A-Z]{4,}/.test(t)) t = t.toLowerCase().replace(/\b\w/g, c => c.toUpperCase());
return t;
}
const specOf = name => { const m = name.match(DIM); return m ? [...new Set(m.map(s => s.replace(/\s+/g, ' ').trim()))].join(' · ') : null; };
async function main() {
const leak = (await pool.query(
`SELECT count(*) n FROM momentum_colorways WHERE pattern_name ~* 'momentum|versa' OR description ~* 'momentum|versa' OR tags ~* 'momentum|versa' OR ai_color_name ~* 'momentum|versa'`)).rows[0].n;
console.log(`#4 leak scan (Momentum/Versa in public text): ${leak} hits ${leak == 0 ? '✅ CLEAN' : '⚠️ MUST SCRUB'}`);
const { rows } = await pool.query(`SELECT id, pattern_name, category FROM momentum_colorways`);
const plan = rows.map(r => ({ id: r.id,
pt: r.category === 'Acoustic' ? 'Acoustic Panel' : 'Wallcovering',
title: titleClean(r.pattern_name),
spec: r.category === 'Acoustic' ? specOf(r.pattern_name) : null }));
const changedTitles = plan.filter((p, i) => p.title !== rows[i].pattern_name).length;
const withSpec = plan.filter(p => p.spec).length;
console.log(`#2 product_type: ${plan.filter(p=>p.pt==='Acoustic Panel').length} Acoustic Panel / ${plan.filter(p=>p.pt==='Wallcovering').length} Wallcovering`);
console.log(`#3 hw_title: ${changedTitles} de-SHOUTed (rest faithful) | panel_spec surfaced on ${withSpec} acoustic rows`);
console.log(' samples:'); plan.filter(p=>p.spec).slice(0,3).forEach(p=>console.log(` "${rows.find(r=>r.id===p.id).pattern_name}" → title "${p.title}" · spec "${p.spec}"`));
if (!COMMIT) { console.log('DRY-RUN — re-run with --commit.'); await pool.end(); return; }
if (leak != 0) { console.error('ABORT: leak hits > 0 — scrub required before prep.'); await pool.end(); process.exit(1); }
const c = await pool.connect(); let done = 0;
try {
// momentum_colorways is owned by legacy role stevestudio2 → no DDL. Use a dw_admin-owned
// companion table keyed on the colorway id (drop it to fully reverse).
await c.query(`CREATE TABLE IF NOT EXISTS momentum_golive_prep (
colorway_id int PRIMARY KEY, dw_sku text, hw_product_type text, hw_title text, panel_spec text, updated_at timestamptz DEFAULT now())`);
await c.query('BEGIN');
for (const p of plan) {
await c.query(`INSERT INTO momentum_golive_prep (colorway_id, dw_sku, hw_product_type, hw_title, panel_spec, updated_at)
SELECT $1, dw_sku, $2, $3, $4, now() FROM momentum_colorways WHERE id=$1
ON CONFLICT (colorway_id) DO UPDATE SET hw_product_type=EXCLUDED.hw_product_type,
hw_title=EXCLUDED.hw_title, panel_spec=EXCLUDED.panel_spec, dw_sku=EXCLUDED.dw_sku, updated_at=now()`,
[p.id, p.pt, p.title, p.spec]); done++;
}
await c.query('COMMIT');
console.log(`COMMITTED — momentum_golive_prep populated for ${done} rows. (companion table; momentum_colorways untouched)`);
} catch (e) { await c.query('ROLLBACK'); console.error('ROLLBACK:', e.message); process.exitCode = 1; }
finally { c.release(); await pool.end(); }
}
main().catch(e => { console.error(e); process.exit(1); });