← back to Tk10630 Sku Suffix Canary

apply-507.mjs

28 lines

// Apply the 507 clean-code recovery: set global/dwc/custom dw_sku = real code
// (from recover-proposals.json). Metafield-only (write_products). Resumable.
import { gql } from './shopify.mjs';
import { readFileSync, appendFileSync, existsSync } from 'node:fs';
const APPLY = process.argv.includes('--apply');
const DONE = 'done-507.jsonl';
const props = JSON.parse(readFileSync('recover-proposals.json', 'utf8'));
const done = new Set();
if (existsSync(DONE)) for (const l of readFileSync(DONE, 'utf8').split('\n')) if (l.trim()) done.add(JSON.parse(l).id);
const work = props.filter(p => !done.has(p.id) && /^[A-Z]{2,6}-\d/i.test(p.code) && !/^DW/i.test(p.code));
console.log(`[507] mode=${APPLY ? 'LIVE' : 'DRY'} total=${props.length} todo=${work.length}`);
const sleep = ms => new Promise(r => setTimeout(r, ms));
const MFS = `mutation($m:[MetafieldsSetInput!]!){ metafieldsSet(metafields:$m){ userErrors{ field message } } }`;
let idx = 0, ok = 0, err = 0;
async function worker() {
  while (idx < work.length) {
    const p = work[idx++];
    const m = ['global', 'dwc', 'custom'].map(ns => ({ ownerId: p.id, namespace: ns, key: 'dw_sku', type: 'single_line_text_field', value: p.code }));
    if (APPLY) {
      try { for (let a = 0; ; a++) { try { const { data } = await gql(MFS, { m }); const ue = data.metafieldsSet.userErrors; if (ue.length) { err++; console.log('✗', p.handle, JSON.stringify(ue)); } else { ok++; appendFileSync(DONE, JSON.stringify({ id: p.id, code: p.code }) + '\n'); } break; } catch (e) { if (/THROTTLED/i.test(e.message) && a < 8) { await sleep(1500 * (a + 1)); continue; } throw e; } } }
      catch (e) { err++; console.log('✗', p.handle, e.message.slice(0, 90)); }
    } else ok++;
    if ((ok + err) % 100 === 0) process.stderr.write(`  ${ok + err}/${work.length}\n`);
  }
}
await Promise.all(Array.from({ length: Math.min(5, work.length) }, () => worker()));
console.log(`[507] DONE ok=${ok} err=${err} ${APPLY ? '(written)' : '(dry)'}`);