[object Object]

← back to Designer Wallcoverings

auto-save: 2026-06-25T10:33:35 (6 files) — vendor-scrapers/china-seas-refresh _incr.mjs pending-approval/boost-filter-consolidation-2026-06-25/ shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170825Z.json shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170839Z.json

b10eebc109bfc1829f99b05643a336b69ab5196e · 2026-06-25 10:33:41 -0700 · Steve Abrams

Files touched

Diff

commit b10eebc109bfc1829f99b05643a336b69ab5196e
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Thu Jun 25 10:33:41 2026 -0700

    auto-save: 2026-06-25T10:33:35 (6 files) — vendor-scrapers/china-seas-refresh _incr.mjs pending-approval/boost-filter-consolidation-2026-06-25/ shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170825Z.json shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170839Z.json
---
 _incr.mjs                                          |  20 ++++
 .../boost-filter-consolidation-2026-06-25          |   1 +
 ...na-french-phase0-assign-2026-06-25T170825Z.json |   0
 ...na-french-phase0-assign-2026-06-25T170839Z.json |   1 +
 .../data/anna-french/install-af-redirects.mjs      | 102 +++++++++++++++++++++
 5 files changed, 124 insertions(+)

diff --git a/_incr.mjs b/_incr.mjs
new file mode 100644
index 00000000..2beae7dc
--- /dev/null
+++ b/_incr.mjs
@@ -0,0 +1,20 @@
+import { chromium } from 'playwright';
+const URL='https://www.designerwallcoverings.com/collections/new-arrivals?preview_theme_id=143953264691';
+const b=await chromium.launch(); const p=await (await b.newContext({viewport:{width:1440,height:1000}})).newPage();
+await p.goto(URL,{waitUntil:'domcontentloaded',timeout:60000});
+await p.waitForSelector('.boost-sd__product-list',{timeout:30000}); await p.waitForTimeout(3000);
+const count=()=>p.evaluate(()=>document.querySelectorAll('.boost-sd__product-item').length);
+let log=[await count()];
+// incremental human-like scroll
+for(let i=0;i<14;i++){
+  await p.evaluate(()=>window.scrollBy(0, Math.round(window.innerHeight*0.85)));
+  await p.waitForTimeout(1200);
+  log.push(await count());
+}
+// also try clicking the load-more button if present
+const btn=await p.$('.boost-sd__pagination-infinite-scroll-container-button, [class*="load-more"] button, [class*="loadmore"]');
+let clicked=false;
+if(btn){ try{ await btn.click(); clicked=true; await p.waitForTimeout(2000);}catch(e){} }
+log.push('afterClick='+(clicked?await count():'noBtn'));
+console.log('product counts over scroll:', JSON.stringify(log));
+await b.close();
diff --git a/pending-approval/boost-filter-consolidation-2026-06-25 b/pending-approval/boost-filter-consolidation-2026-06-25
new file mode 160000
index 00000000..0f3a9924
--- /dev/null
+++ b/pending-approval/boost-filter-consolidation-2026-06-25
@@ -0,0 +1 @@
+Subproject commit 0f3a992460694a6e7b00e48fade526261875d39a
diff --git a/shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170825Z.json b/shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170825Z.json
new file mode 100644
index 00000000..e69de29b
diff --git a/shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170839Z.json b/shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170839Z.json
new file mode 100644
index 00000000..d2fd605f
--- /dev/null
+++ b/shopify/scripts/cadence/data/anna-french/anna-french-phase0-assign-2026-06-25T170839Z.json
@@ -0,0 +1 @@
+{"phase" : "anna-french-phase0-staging-dwsku", "generated" : "2026-06-25T170839Z", "dry_run" : 1, "dtd_verdict" : "A (reuse live DWTT numeric as DWAF-<num>); Claude+Codex 2/0, Qwen non-vote", "transforms" : {"reprefix_dwta_to_dwat" : 591, "fabric_reused_existing_dwaf" : 0, "fabric_minted_new_dwaf" : 143, "dwaf_number_range" : "74664-75006"}, "readiness" : {"promotable_total" : 734, "null_dw_sku" : 0, "remaining_dwta" : 0, "wallcovering_wrong_prefix" : 0, "fabric_wrong_prefix" : 0, "banned_word_wallpaper_in_title" : 0, "unknown_in_title" : 0, "dwat_count" : 591, "dwaf_count" : 143}, "collisions" : {"reg_diff_product" : 0, "shop_diff_product" : 0, "intra_staging_dup" : 0}}
diff --git a/shopify/scripts/cadence/data/anna-french/install-af-redirects.mjs b/shopify/scripts/cadence/data/anna-french/install-af-redirects.mjs
new file mode 100644
index 00000000..7fcf4dfa
--- /dev/null
+++ b/shopify/scripts/cadence/data/anna-french/install-af-redirects.mjs
@@ -0,0 +1,102 @@
+#!/usr/bin/env node
+// Phase 1 — install Anna French 301 redirects on the LIVE store.
+// IDEMPOTENT: checks redirects.json?path=<from> before creating; skips/updates if present.
+// Rate-limit aware: serial, ~3 req/s ceiling with header-driven backoff + 429 retry.
+import fs from 'node:fs';
+import path from 'node:path';
+
+const SHOP = 'designer-laboratory-sandbox.myshopify.com';
+const VER = '2024-10';
+const TOKEN = process.env.AF_REDIRECT_TOKEN;
+if (!TOKEN) { console.error('AF_REDIRECT_TOKEN env not set'); process.exit(1); }
+
+const CSV = path.resolve(process.argv[2] ||
+  '/Users/stevestudio2/Projects/Designer-Wallcoverings/shopify/scripts/cadence/data/anna-french/anna-french-301-redirects.csv');
+const DRY = process.argv.includes('--dry');
+const OUT = '/Users/stevestudio2/Projects/Designer-Wallcoverings/shopify/scripts/cadence/data/anna-french/af-redirects-install-audit.json';
+
+const base = `https://${SHOP}/admin/api/${VER}`;
+const H = { 'X-Shopify-Access-Token': TOKEN, 'Content-Type': 'application/json' };
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+
+// parse CSV (simple — no embedded commas in these paths)
+const rows = fs.readFileSync(CSV, 'utf8').trim().split(/\r?\n/).slice(1)
+  .map(l => { const i = l.indexOf(','); return { from: l.slice(0, i).trim(), to: l.slice(i + 1).trim() }; })
+  .filter(r => r.from && r.to);
+
+let MIN_GAP = 340; // ms between calls (~2.9/s, under the 4/s burst bucket)
+
+async function api(method, url, body) {
+  for (let attempt = 0; attempt < 6; attempt++) {
+    const res = await fetch(url, { method, headers: H, body: body ? JSON.stringify(body) : undefined });
+    const limit = res.headers.get('X-Shopify-Shop-Api-Call-Limit');
+    if (limit) {
+      const [used, cap] = limit.split('/').map(Number);
+      if (used / cap > 0.7) await sleep(900); // ease off near bucket cap
+    }
+    if (res.status === 429) {
+      const ra = Number(res.headers.get('Retry-After') || 2);
+      await sleep((ra + 0.5) * 1000);
+      continue;
+    }
+    const text = await res.text();
+    let json = null; try { json = text ? JSON.parse(text) : null; } catch {}
+    return { status: res.status, json, text };
+  }
+  return { status: 0, json: null, text: 'retries exhausted' };
+}
+
+const audit = { startedAt: new Date().toISOString(), shop: SHOP, total: rows.length,
+  created: [], preExisting: [], updated: [], failed: [] };
+
+(async () => {
+  let n = 0;
+  for (const r of rows) {
+    n++;
+    // 1. idempotency check — exact path match
+    const chk = await api('GET', `${base}/redirects.json?path=${encodeURIComponent(r.from)}&limit=1`);
+    await sleep(MIN_GAP);
+    if (chk.status !== 200) {
+      audit.failed.push({ ...r, stage: 'check', status: chk.status, body: chk.text?.slice(0, 300) });
+      console.error(`[${n}/${rows.length}] CHECK FAIL ${chk.status} ${r.from}`);
+      continue;
+    }
+    const existing = chk.json?.redirects?.[0];
+    if (existing) {
+      if (existing.target === r.to) {
+        audit.preExisting.push({ ...r, id: existing.id });
+      } else if (!DRY) {
+        // exists but points elsewhere -> update to correct target (idempotent convergence)
+        const upd = await api('PUT', `${base}/redirects/${existing.id}.json`,
+          { redirect: { id: existing.id, path: r.from, target: r.to } });
+        await sleep(MIN_GAP);
+        if (upd.status === 200) audit.updated.push({ ...r, id: existing.id, oldTarget: existing.target });
+        else audit.failed.push({ ...r, stage: 'update', status: upd.status, body: upd.text?.slice(0, 300) });
+      } else {
+        audit.updated.push({ ...r, id: existing.id, oldTarget: existing.target, dry: true });
+      }
+      if (n % 50 === 0) console.log(`[${n}/${rows.length}] created=${audit.created.length} pre=${audit.preExisting.length} upd=${audit.updated.length} fail=${audit.failed.length}`);
+      continue;
+    }
+    // 2. create
+    if (DRY) { audit.created.push({ ...r, dry: true }); continue; }
+    const cr = await api('POST', `${base}/redirects.json`, { redirect: { path: r.from, target: r.to } });
+    await sleep(MIN_GAP);
+    if (cr.status === 201) audit.created.push({ ...r, id: cr.json?.redirect?.id });
+    else if (cr.status === 422) {
+      // 422 = likely already exists (race / unicode-normalized dupe) -> treat as pre-existing
+      audit.preExisting.push({ ...r, note: '422-create', body: cr.text?.slice(0, 200) });
+    } else {
+      audit.failed.push({ ...r, stage: 'create', status: cr.status, body: cr.text?.slice(0, 300) });
+      console.error(`[${n}/${rows.length}] CREATE FAIL ${cr.status} ${r.from}`);
+    }
+    if (n % 50 === 0) console.log(`[${n}/${rows.length}] created=${audit.created.length} pre=${audit.preExisting.length} upd=${audit.updated.length} fail=${audit.failed.length}`);
+  }
+  audit.finishedAt = new Date().toISOString();
+  audit.summary = { created: audit.created.length, preExisting: audit.preExisting.length,
+    updated: audit.updated.length, failed: audit.failed.length };
+  fs.writeFileSync(OUT, JSON.stringify(audit, null, 2));
+  console.log('\n=== DONE ===');
+  console.log(JSON.stringify(audit.summary, null, 2));
+  console.log('audit ->', OUT);
+})();

← 2cd03b9d China Seas mailer: inline all pill styles (client-proof rend  ·  back to Designer Wallcoverings  ·  Anna French Phase 1: install 789 dormant 301 redirects on li 163fa1fb →