← back to Dw Activation Debug TK11314

scripts/verify-live-boundary.js

64 lines

#!/usr/bin/env node
'use strict';
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const assert = require('node:assert/strict');
const { completeLiveProduct, canonicalProduct, widthFromLive } = require('../lib/live-product-fields');
const env = fs.readFileSync(path.join(os.homedir(), 'Projects/secrets-manager/.env'), 'utf8');
const token = (env.match(/^SHOPIFY_ADMIN_TOKEN=(.*)$/m) || [])[1]?.trim().replace(/^['"]|['"]$/g, '');
const source = fs.readFileSync(path.join(__dirname, '..', 'rotate-activate.js'), 'utf8');
const query = source.match(/const STATUS_Q = `([\s\S]+?)`;/)?.[1];
if (!query) throw new Error('Actual runtime query not found');
const snapshot = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
const validator = require(path.resolve(process.argv[3]));
const output = process.argv[4];
let requests = 0;
const costs = [];
async function read(query, variables) {
  assert.match(query, /^query\(/); assert.doesNotMatch(query, /\bmutation\b/);
  requests++;
  const response = await fetch('https://designer-laboratory-sandbox.myshopify.com/admin/api/2026-07/graphql.json', {
    method: 'POST', headers: { 'X-Shopify-Access-Token': token, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, variables }), signal: AbortSignal.timeout(30000),
  });
  const json = await response.json();
  if (json.extensions?.cost) costs.push(json.extensions.cost.requestedQueryCost);
  return { status: response.status, json };
}
async function main() {
  const special = snapshot.products.filter(p => p.variants.nodes.length > 20 || p.metafields.nodes.length > 80 ||
    ['7867560886323','7923565461555','7896454332467','7944491794483'].some(id => p.id.endsWith('/'+id)));
  const ids = [...new Set([...special, ...snapshot.products].map(p => p.id))].slice(0, 50);
  const first = await read(query, { ids });
  assert.equal(first.status, 200); assert.equal(first.json.errors, undefined, JSON.stringify(first.json.errors));
  assert.equal(first.json.data.nodes.length, ids.length);
  const products = [];
  for (const initial of first.json.data.nodes) {
    const complete = await completeLiveProduct(initial, read);
    assert.equal(complete.variants.pageInfo.hasNextPage, false);
    assert.equal(complete.metafields.pageInfo.hasNextPage, false);
    const result = validator.validateBeforeActivate(canonicalProduct(complete, '', complete.vendor));
    products.push({ id: complete.id, status: complete.status, vendor: complete.vendor,
      variants_before: initial.variants.nodes.length, variants_after: complete.variants.nodes.length,
      metafields_before: initial.metafields.nodes.length, metafields_after: complete.metafields.nodes.length,
      width: widthFromLive(complete), reasons: result.reasons });
  }
  const designtex = products.find(p => p.id.endsWith('/7867560886323'));
  assert.ok(!designtex.reasons.some(r => /legal\/disclaimer/.test(r)));
  const carnegie = products.find(p => p.id.endsWith('/7923565461555'));
  assert.equal(carnegie.width.source, 'dwc.width');
  const fischbacher = products.find(p => p.id.endsWith('/7896454332467'));
  assert.ok(fischbacher.reasons.includes('empty description'));
  assert.ok(products.every(p => p.status === 'DRAFT'));
  const report = { timestamp: new Date().toISOString(), verdict: 'PASS',
    scope: 'Actual runtime Shopify batch query + real pagination + corrected validator; no activation or vision call',
    requests, requested_query_costs: costs, graphql_mutations: 0, vision_calls: 0, products };
  fs.writeFileSync(output, JSON.stringify(report, null, 2) + '\n');
  console.log(JSON.stringify({ verdict: report.verdict, products: products.length, requests,
    paginated_variants: products.filter(p => p.variants_after > p.variants_before).length,
    paginated_metafields: products.filter(p => p.metafields_after > p.metafields_before).length,
    max_query_cost: Math.max(...costs), output }));
}
main().catch(error => { console.error(error.message); process.exitCode = 1; });