[object Object]

← back to Designer Wallcoverings

TK-11786: scraper fixes — anna-french null-byte sentinel, slack no-op resolve() (Promise hang), kravet GID normalize

008592bf55e7b6aadf876843f698f8259770e2c0 · 2026-09-22 13:35:27 -0700 · Steve

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3ChReG53fwpNgUESv4SY7

Files touched

Diff

commit 008592bf55e7b6aadf876843f698f8259770e2c0
Author: Steve <steve@designerwallcoverings.com>
Date:   Tue Sep 22 13:35:27 2026 -0700

    TK-11786: scraper fixes — anna-french null-byte sentinel, slack no-op resolve() (Promise hang), kravet GID normalize
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
    Claude-Session: https://claude.ai/code/session_01G3ChReG53fwpNgUESv4SY7
---
 shopify/scripts/anna-french-scraper.js            |  2 +-
 shopify/scripts/fix-sample-inventory-only.js      |  2 +-
 shopify/scripts/gemini-tag-engine/index.js        |  2 +-
 shopify/scripts/kravet-map-reprice.js             | 13 ++++++++++++-
 shopify/scripts/push-pattern-repeat-metafields.js |  2 +-
 shopify/scripts/resume-sample-variant-creation.js |  2 +-
 shopify/scripts/texture-repeat-fixer.js           |  2 +-
 shopify/scripts/thibaut-repeat-scraper.js         |  2 +-
 8 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/shopify/scripts/anna-french-scraper.js b/shopify/scripts/anna-french-scraper.js
index ab11554c..13db53ae 100644
--- a/shopify/scripts/anna-french-scraper.js
+++ b/shopify/scripts/anna-french-scraper.js
@@ -84,7 +84,7 @@ function cleanWallpaper(s) {
   // "Wallpaper", skip wallpapered/wallpapering, case-preserving generic swap.
   const WALLPAPER_BRANDS = ['Apartment','Boråstapeter','China Seas','DW Exclusive','Edge','Fentucci','Grasscloth','Laura Ashley','Malibu','MC Escher','Missoni','Nicolette Mayer','PS Removable','Ralph Lauren','Roberto Cavalli','Scalamandre','Schumacher'];
   const _brandRe = new RegExp('(' + WALLPAPER_BRANDS.map(function(b){return b.replace(/[.*+?^${}()|[\]\\]/g,'\\$&');}).join('|') + ')\\s+Wallpaper','gi');
-  const _hold = []; const _S = ' ';
+  const _hold = []; const _S = '\u0000'; // null-byte sentinel; a plain space collided with numeric repeat/width values and corrupted descriptions (matches anna-french-enrich.js)
   let out = String(s).replace(_brandRe, function(m){ _hold.push(m); return _S + (_hold.length-1) + _S; });
   out = out.replace(/\bWallpaper(s?)\b(?!ed|ing)/gi, function(m, plural){
     const base = plural ? 'wallcoverings' : 'wallcovering';
diff --git a/shopify/scripts/fix-sample-inventory-only.js b/shopify/scripts/fix-sample-inventory-only.js
index 0a3ab8ad..c5759c08 100644
--- a/shopify/scripts/fix-sample-inventory-only.js
+++ b/shopify/scripts/fix-sample-inventory-only.js
@@ -47,7 +47,7 @@ function graphql(query, variables = {}) {
 function sendSlack(text) {
   return new Promise((resolve) => {
     const body = JSON.stringify({ text });
-    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return; // slack: no-op when unset
+    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return resolve(); // slack: no-op when unset
     const url = new URL(SLACK_WEBHOOK);
     const req = https.request({
       hostname: url.hostname, path: url.pathname, method: 'POST',
diff --git a/shopify/scripts/gemini-tag-engine/index.js b/shopify/scripts/gemini-tag-engine/index.js
index 7536161f..b1eef24e 100644
--- a/shopify/scripts/gemini-tag-engine/index.js
+++ b/shopify/scripts/gemini-tag-engine/index.js
@@ -120,7 +120,7 @@ function log(msg) {
 
 async function sendSlack(text) {
   return new Promise((resolve) => {
-    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return; // slack: no-op when unset
+    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return resolve(); // slack: no-op when unset
     const url = new URL(SLACK_WEBHOOK);
     const data = JSON.stringify({ text });
     const req = https.request({
diff --git a/shopify/scripts/kravet-map-reprice.js b/shopify/scripts/kravet-map-reprice.js
index 262ea6bc..4de77a1b 100644
--- a/shopify/scripts/kravet-map-reprice.js
+++ b/shopify/scripts/kravet-map-reprice.js
@@ -106,7 +106,12 @@ const logFail = o => fs.appendFileSync(FAILLOG, JSON.stringify(o) + '\n');
   let i = 0;
   for (const row of rows) {
     i++;
-    const pid = `gid://shopify/Product/${row.shopify_id}`;
+    // shopify_id may be a bare numeric id (frozen CSV) OR already a full GID (the
+    // FTP-regenerated worklist-latest.csv). Normalize so we never double-prefix into
+    // gid://shopify/Product/gid://shopify/Product/… which resolves to nothing.
+    const pid = String(row.shopify_id).startsWith('gid://')
+      ? row.shopify_id
+      : `gid://shopify/Product/${row.shopify_id}`;
     const map = parseFloat(row.map), cost = parseFloat(row.cost_price);
     if (!(map > 0)) { skipped++; continue; }
 
@@ -151,4 +156,10 @@ const logFail = o => fs.appendFileSync(FAILLOG, JSON.stringify(o) + '\n');
   }
   console.log(`\nDone. repriced=${repriced} costSet=${costSet} already=${already} skipped=${skipped} failed=${failed}`);
   if (failed) console.log(`failures → ${FAILLOG}`);
+  // Fail loud: if EVERY row failed to resolve, this is a broken run (e.g. malformed ids),
+  // not a clean "nothing to do" — never let a total failure masquerade as success.
+  if (rows.length > 0 && failed === rows.length) {
+    console.error(`\n❌ ALL ${rows.length} rows failed — aborting as a broken run (check id/GID shape and ${FAILLOG}).`);
+    process.exitCode = 1;
+  }
 })();
diff --git a/shopify/scripts/push-pattern-repeat-metafields.js b/shopify/scripts/push-pattern-repeat-metafields.js
index 039d0ea1..05e52bae 100644
--- a/shopify/scripts/push-pattern-repeat-metafields.js
+++ b/shopify/scripts/push-pattern-repeat-metafields.js
@@ -149,7 +149,7 @@ function normalizeGid(shopifyId) {
 async function sendSlack(text) {
   return new Promise((resolve) => {
     const body = JSON.stringify({ text });
-    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return; // slack: no-op when unset
+    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return resolve(); // slack: no-op when unset
     const url = new URL(SLACK_WEBHOOK);
     const req = https.request({
       hostname: url.hostname,
diff --git a/shopify/scripts/resume-sample-variant-creation.js b/shopify/scripts/resume-sample-variant-creation.js
index ad289ab2..ea581c8f 100644
--- a/shopify/scripts/resume-sample-variant-creation.js
+++ b/shopify/scripts/resume-sample-variant-creation.js
@@ -88,7 +88,7 @@ function restApi(method, path, body = null) {
 function sendSlack(text) {
   return new Promise(resolve => {
     const body = JSON.stringify({ text });
-    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return; // slack: no-op when unset
+    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return resolve(); // slack: no-op when unset
     const url = new URL(SLACK_WEBHOOK);
     const req = https.request({
       hostname: url.hostname, path: url.pathname, method: 'POST',
diff --git a/shopify/scripts/texture-repeat-fixer.js b/shopify/scripts/texture-repeat-fixer.js
index 9aa14e25..914b5e0b 100644
--- a/shopify/scripts/texture-repeat-fixer.js
+++ b/shopify/scripts/texture-repeat-fixer.js
@@ -205,7 +205,7 @@ async function processVendor(vendorConfig) {
 async function sendSlackReport(report) {
   return new Promise((resolve) => {
     const body = JSON.stringify({ text: report });
-    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return; // slack: no-op when unset
+    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return resolve(); // slack: no-op when unset
     const url = new URL(SLACK_WEBHOOK);
     const req = https.request({
       hostname: url.hostname,
diff --git a/shopify/scripts/thibaut-repeat-scraper.js b/shopify/scripts/thibaut-repeat-scraper.js
index df7502d3..a160b33c 100644
--- a/shopify/scripts/thibaut-repeat-scraper.js
+++ b/shopify/scripts/thibaut-repeat-scraper.js
@@ -107,7 +107,7 @@ async function sendSlackNotification(stats, duration) {
 
   return new Promise((resolve) => {
     const payload = JSON.stringify({ text: msg });
-    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return; // slack: no-op when unset
+    if (!/^https?:\/\//.test(SLACK_WEBHOOK)) return resolve(); // slack: no-op when unset
     const url = new URL(SLACK_WEBHOOK);
     const req = https.request({
       hostname: url.hostname, path: url.pathname, method: 'POST',

← a939ff3d TK-11786: fail-loud --apply/--dry-run guards on push + singl  ·  back to Designer Wallcoverings  ·  TK-11786: remove superseded one-off scripts (romo-drilldown, 83214c10 →