← back to Designer Wallcoverings
cron-create-sample-variants: pass --apply (script is now dry-by-default)
ff460585c16d2b0368ccf084616d31e1fb1a5559 · 2026-09-14 15:24:58 -0700 · Steve
create-missing-sample-variants.js flipped to dry-by-default on 2026-09-14
(dryRun = !args.includes('--apply')). This cron ran it bare, so it would
silently create zero variants, exit 0, and never satisfy its REMAINING==0
self-removal guard. Add --apply so the wrapper writes live as intended.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X2onw3VVrDkPYwaoLj7jms
Files touched
A DW-Programming/lib/versa-colorway-extraction.jsM shopify/scripts/cron-create-sample-variants.sh
Diff
commit ff460585c16d2b0368ccf084616d31e1fb1a5559
Author: Steve <steve@designerwallcoverings.com>
Date: Mon Sep 14 15:24:58 2026 -0700
cron-create-sample-variants: pass --apply (script is now dry-by-default)
create-missing-sample-variants.js flipped to dry-by-default on 2026-09-14
(dryRun = !args.includes('--apply')). This cron ran it bare, so it would
silently create zero variants, exit 0, and never satisfy its REMAINING==0
self-removal guard. Add --apply so the wrapper writes live as intended.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X2onw3VVrDkPYwaoLj7jms
---
DW-Programming/lib/versa-colorway-extraction.js | 69 +++++++++++++++++++++++++
shopify/scripts/cron-create-sample-variants.sh | 5 +-
2 files changed, 73 insertions(+), 1 deletion(-)
diff --git a/DW-Programming/lib/versa-colorway-extraction.js b/DW-Programming/lib/versa-colorway-extraction.js
new file mode 100644
index 00000000..c63cd918
--- /dev/null
+++ b/DW-Programming/lib/versa-colorway-extraction.js
@@ -0,0 +1,69 @@
+'use strict';
+
+// Passed directly to Playwright.evaluate: keep every dependency inside this function.
+// TK-11241: only semantic swatch nodes can supply SKU images; featured photos
+// are pattern-level and must never enter image_url or all_images.
+function extractVersaColorways({ pageUrl }) {
+ const base = new URL(pageUrl);
+ const absolute = value => {
+ if (typeof value !== 'string' || !value.trim() || /data:|;base64,|[\\\u0000-\u001f]/i.test(value)) return null;
+ try {
+ const url = new URL(value.trim(), value.trim().startsWith('fileadmin/') ? base.origin + '/' : base.href);
+ return /^https?:$/.test(url.protocol) && !url.username && !url.password ? url.href : null;
+ } catch { return null; }
+ };
+ const identity = value => {
+ const url = absolute(value);
+ const match = url && new URL(url).pathname.match(/\/product-detail\/(.+)\/([a-z0-9]+(?:-[a-z0-9]+)+)\/?$/i);
+ return match && /\d/.test(match[2]) ? { pattern: match[1].toLowerCase(), sku: match[2].toUpperCase(), url } : null;
+ };
+ const current = identity(pageUrl);
+ const root = document.querySelector('.product-detail');
+ if (!root || !current) throw new Error('Versa detail identity/markup missing; refusing catalog update');
+ const color = node => {
+ // Strip the "PVC-free" marketing suffix the swatch titles carry (e.g. "Horizon PVC-free")
+ // so it never lands in the customer-facing Color tag / custom.color metafield / body copy.
+ const value = node?.textContent?.replace(/\s+/g, ' ').trim().replace(/\s*PVC-free\s*/gi, '').trim();
+ return value && !/^#[0-9a-f]{3,8}$/i.test(value) ? value : null;
+ };
+ const imageFor = (img, sku, zoom) => {
+ if (!img) return null;
+ const srcset = (img.getAttribute('data-srcset') || img.getAttribute('srcset') || '')
+ .split(',').map(item => item.trim().split(/\s+/))
+ .sort((a, b) => (parseFloat(b[1]) || 0) - (parseFloat(a[1]) || 0)).map(item => item[0]);
+ const candidates = [zoom, img.getAttribute('data-full-size'), img.getAttribute('data-src'), ...srcset, img.getAttribute('src')];
+ // Vendor filenames omit legacy collection suffixes; pattern+color core is
+ // AVF29-128 in both AVF29-128 and AVF29-128-90. Require token boundaries.
+ const combined = sku.match(/^(?:INV-)?(M[A-Z]{2})(\d{4})-\d+$/i);
+ const core = combined ? '(?:INV[-_])?' + combined[1] + '[-_]?' + combined[2]
+ : sku.split('-').slice(0, sku.startsWith('VU-') ? 3 : 2).join('[-_]');
+ const skuToken = new RegExp('(?:^|[^a-z0-9])' + core + '(?=[^a-z0-9]|$)', 'i');
+ for (const candidate of candidates) {
+ const url = absolute(candidate);
+ if (!url) continue;
+ let path;
+ try { path = decodeURIComponent(new URL(url).pathname); } catch { continue; }
+ if (!/\/fileadmin\//i.test(path) || !/\.(?:jpe?g|png|webp|avif)$/i.test(path)) continue;
+ if (/(?:^|[\/_.\s-])(?:install(?:ation)?|room|featured|lifestyle|mockup|logo|icon|imagenotfound)(?=$|[\/_.\s-])/i.test(path)) continue;
+ if (skuToken.test(path)) return url;
+ }
+ return null;
+ };
+ const mainImage = imageFor(root.querySelector('img.product-detail__colorway-image'), current.sku,
+ root.querySelector('.product-detail__colorway-image-wrap .product-detail__zoom-trigger')?.getAttribute('href'));
+ const main = { sku: current.sku, href: current.url, url: current.url,
+ colorName: color(root.querySelector('.product-detail__colorway-image-title')), imgSrc: mainImage };
+ const colorways = [main];
+ const seen = new Set([current.sku]);
+ for (const link of root.querySelectorAll('a.product-detail__colorway-link')) {
+ const id = identity(link.getAttribute('href'));
+ if (!id || id.pattern !== current.pattern || new URL(id.url).origin !== base.origin || seen.has(id.sku)) continue;
+ seen.add(id.sku);
+ colorways.push({ sku: id.sku, href: id.url, url: id.url,
+ colorName: color(link.querySelector('.product-detail__colorway-title')),
+ imgSrc: imageFor(link.querySelector('img.product-detail__colorway-thumbnail'), id.sku) });
+ }
+ return { main, colorways };
+}
+
+module.exports = { extractVersaColorways };
diff --git a/shopify/scripts/cron-create-sample-variants.sh b/shopify/scripts/cron-create-sample-variants.sh
index 96b04370..a3cf06bc 100755
--- a/shopify/scripts/cron-create-sample-variants.sh
+++ b/shopify/scripts/cron-create-sample-variants.sh
@@ -29,6 +29,9 @@ if [ -f "$PROGRESS_FILE" ]; then
fi
cd /root/Projects/Designer-Wallcoverings
-node "$SCRIPT" >> "$LOG_FILE" 2>&1
+# --apply REQUIRED: as of 2026-09-14 create-missing-sample-variants.js is DRY BY DEFAULT
+# (dryRun = !args.includes('--apply')). Without --apply this cron would run dry, create zero
+# variants, exit 0, and its REMAINING>0 guard would never let it self-remove — a silent no-op.
+node "$SCRIPT" --apply >> "$LOG_FILE" 2>&1
echo "=== $(date '+%Y-%m-%d %H:%M:%S PT') === Finished ===" >> "$LOG_FILE"
← ac9e69dc auto-data-snapshot: 2026-09-14T15:08:48 (3 data files) — sho
·
back to Designer Wallcoverings
·
fix(TK-11503): validate Shopify token via read_products prob 25dadd6f →