← back to Hollywood Mfr Trim

trim.mjs

50 lines

#!/usr/bin/env node
// Trim whitespace on Hollywood (HWC) manufacturer_sku metafields (custom + dwc).
// Target: LIVE designer-laboratory-sandbox store. Dry-run unless --apply.
import fs from 'node:fs';

const APPLY = process.argv.includes('--apply');
const SHOP = 'designer-laboratory-sandbox.myshopify.com';
const API = '2024-10';

// pull token from the secrets .env
const envTxt = fs.readFileSync('/Users/macstudio3/Projects/secrets-manager/.env', 'utf8');
const TOKEN = (envTxt.match(/^SHOPIFY_ADMIN_TOKEN=(.+)$/m) || [])[1]?.trim();
if (!TOKEN) { console.error('No SHOPIFY_ADMIN_TOKEN'); process.exit(1); }

const work = JSON.parse(fs.readFileSync(new URL('./worklist.json', import.meta.url), 'utf8'));
console.log(`Worklist: ${work.length} metafield writes across ${new Set(work.map(w=>w.ownerId)).size} products`);
console.log(APPLY ? '>>> APPLY MODE — writing to LIVE Shopify' : '>>> DRY RUN (no writes). Pass --apply to execute.');

if (!APPLY) {
  for (const w of work) console.log(`  ${w.ownerId.split('/').pop()}  ${w.namespace}.${w.key} -> "${w.value}"`);
  console.log(`\nDry run complete. ${work.length} writes staged.`);
  process.exit(0);
}

const MUT = `mutation($mf:[MetafieldsSetInput!]!){metafieldsSet(metafields:$mf){metafields{id namespace key value} userErrors{field message}}}`;

async function gql(variables){
  const r = await fetch(`https://${SHOP}/admin/api/${API}/graphql.json`, {
    method:'POST',
    headers:{'Content-Type':'application/json','X-Shopify-Access-Token':TOKEN},
    body: JSON.stringify({query:MUT, variables})
  });
  return r.json();
}

let ok=0, errs=[];
for (let i=0; i<work.length; i+=25){
  const batch = work.slice(i, i+25);
  const res = await gql({mf: batch});
  const ue = res?.data?.metafieldsSet?.userErrors || [];
  const set = res?.data?.metafieldsSet?.metafields || [];
  if (res.errors) { errs.push(JSON.stringify(res.errors)); console.error('GraphQL error', res.errors); }
  if (ue.length) { errs.push(...ue.map(e=>JSON.stringify(e))); console.error('userErrors', ue); }
  ok += set.length;
  console.log(`  batch ${i/25+1}: set ${set.length}/${batch.length}`);
  await new Promise(r=>setTimeout(r, 600)); // gentle rate limit
}
console.log(`\nDone. ${ok}/${work.length} metafields set. Errors: ${errs.length}`);
if (errs.length) process.exit(1);