← back to Greenland Onboard
greenland cork go-live: 100% color coverage (lift from pattern_name), localize image_url column, add 2-leaf cork discovery script
9ae7f092c3d2413a85cefaf3d8b4e22526947d42 · 2026-08-10 16:12:26 -0700 · Steve Abrams
Files touched
A backups/runs/color-backfill-before-20260810-161121.csvA backups/runs/image-localize-before-20260810-161207.csvA scripts/expand-diff.mjsA scripts/expand-discover.mjs
Diff
commit 9ae7f092c3d2413a85cefaf3d8b4e22526947d42
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Mon Aug 10 16:12:26 2026 -0700
greenland cork go-live: 100% color coverage (lift from pattern_name), localize image_url column, add 2-leaf cork discovery script
---
.../runs/color-backfill-before-20260810-161121.csv | 87 +++++++++
.../runs/image-localize-before-20260810-161207.csv | 214 +++++++++++++++++++++
scripts/expand-diff.mjs | 128 ++++++++++++
scripts/expand-discover.mjs | 142 ++++++++++++++
4 files changed, 571 insertions(+)
diff --git a/backups/runs/color-backfill-before-20260810-161121.csv b/backups/runs/color-backfill-before-20260810-161121.csv
new file mode 100644
index 0000000..91a60ec
--- /dev/null
+++ b/backups/runs/color-backfill-before-20260810-161121.csv
@@ -0,0 +1,87 @@
+dw_sku,mfr_sku,pattern_name,color_name
+Cork-501770,S300NQ8319,Warm White,
+Cork-501970,S300NQ8375,Oyster White,
+Cork-502120,S300NQ8618,Ivory Grandeur,
+Cork-502100,S300NQ8616,Slate Blue,
+Cork-502080,S300NQ8614,Cocoa Brown,
+Cork-501950,S300NQ8372,Iced Coffee,
+Cork-502070,S300NQ8611,Chalk,
+Cork-502050,S300NQ8392,Carbon Teal,
+Cork-501940,S300NQ8368,Olive,
+Cork-501350,S176NQ8319,Warm White,
+Cork-502020,S300NQ8381,Ivory,
+Cork-501930,S300NQ8364,Sweet Grape,
+Cork-502000,S300NQ8379,Silver Birch,
+Cork-501910,S300NQ8361,Chanterelle,
+Cork-501610,S300NQ8283,Rich Gold,
+Cork-502140,S300NQ8373,Antique Gold,
+Cork-501330,S176NQ8313,Rifle Green,
+Cork-501920,S300NQ8363,Horizon Blue,
+Cork-501720,S300NQ8314,Whisper White,
+Cork-501900,S300NQ8360,Pelican,
+Cork-501510,S300NQ8241,Light Gold,
+Cork-501810,S300NQ8323,Insignia Blue,
+Cork-501880,S300NQ8332,Clean White,
+Cork-501800,S300NQ8322,Satellite,
+Cork-501780,S300NQ8320,Cement,
+Cork-501850,S300NQ8328,Calliste Green,
+Cork-501830,S300NQ8326,Raw Sienna,
+Cork-501500,S300NQ8240,Brown Gold,
+Cork-501820,S300NQ8325,Mauve Wine,
+Cork-501290,H192NQ8317,Black coffee,
+Cork-501870,S300NQ8330,Concrete,
+Cork-501320,S176NQ8311,Silvery Grey,
+Cork-501280,H192NQ8310,Blanc de blanc,
+Cork-501480,S176NQ8380,Jadesheen,
+Cork-501440,S176NQ8353,Ethereal Sage,
+Cork-501310,S176NQ8269,Charcoal Gray,
+Cork-501420,S176NQ8344,Turkish Tile,
+Cork-501400,S176NQ8342,Bubbly Blue,
+Cork-501460,S176NQ8363,Colonial Blue,
+Cork-501390,S176NQ8341,Fascinating Fir,
+Cork-501760,S300NQ8318,Black,
+Cork-501590,S300NQ8269,Charcoal Gray,
+Cork-501700,S300NQ8312,Silver Lining,
+Cork-501640,S300NQ8306,Bright White,
+Cork-501710,S300NQ8313,Rifle Green,
+Cork-501550,S300NQ8248,Monk's Robe,
+Cork-501530,S300NQ8243,Black Gold,
+Cork-501520,S300NQ8242,Gold Silver,
+Cork-501600,S300NQ8282,Stardust,
+Cork-501680,S300NQ8310,Snow White,
+Cork-501570,S300NQ8250,Carafe,
+Cork-501670,S300NQ8309,Woodsmoke,
+Cork-501540,S300NQ8247,Black Silver,
+Cork-501650,S300NQ8307,Woodash,
+Cork-501620,S300NQ8288,Bison Gold,
+Cork-501730,S300NQ8315,Brilliant White,
+Cork-501300,H192NQ8321,Sliver gray,
+Cork-501380,S176NQ8328,Calliste Green,
+Cork-501370,S176NQ8324,Bistro Green,
+Cork-501580,S300NQ8258,Caribou,
+Cork-501660,S300NQ8308,Rosewood,
+Cork-501360,S176NQ8323,Insignia Blue,
+Cork-502090,S300NQ8615,Espresso Moss,
+Cork-501840,S300NQ8327,Brown Sugar,
+Cork-501860,S300NQ8329,Lark,
+Cork-502130,S300NQ8383,Bark,
+Cork-501740,S300NQ8316,Chanterelle,
+Cork-501790,S300NQ8321,Gardenia,
+Cork-501340,S176NQ8318,Black,
+Cork-501450,S176NQ8355,Captivating Night,
+Cork-501490,S300NQ8238,Gilded White,
+Cork-501430,S176NQ8352,Steel Sorcerer,
+Cork-501560,S300NQ8249,Black Coffee,
+Cork-501630,S300NQ8305,Cosmic Sky,
+Cork-501690,S300NQ8311,Silver,
+Cork-501750,S300NQ8317,Emperador,
+Cork-501890,S300NQ8357,Sunny Lime,
+Cork-501980,S300NQ8377,Sepia Tint,
+Cork-502010,S300NQ8380,Leaf Green,
+Cork-501990,S300NQ8378,Desert Sage,
+Cork-502060,S300NQ8601,Terracotta,
+Cork-502040,S300NQ8391,Deep Teal,
+Cork-502110,S300NQ8617,Moss Stone,
+Cork-501270,H192NQ8309,Lced coffee,
+Cork-501470,S176NQ8368,Shadow,
+Cork-501410,S176NQ8343,Dazzling Junper,
diff --git a/backups/runs/image-localize-before-20260810-161207.csv b/backups/runs/image-localize-before-20260810-161207.csv
new file mode 100644
index 0000000..ac53db0
--- /dev/null
+++ b/backups/runs/image-localize-before-20260810-161207.csv
@@ -0,0 +1,214 @@
+dw_sku,image_url
+Cork-500000,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0110NQ8248 _20230527075337.jpg
+Cork-500010,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0110NQ8249 _20230527075339.jpg
+Cork-500020,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0110NQ8250 _20230527075339.jpg
+Cork-500030,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0110NQ8254 _20230527075337.jpg
+Cork-500040,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0110NQ8258 _20230527075338.jpg
+Cork-500050,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0110NQ8276 _20230527075338.jpg
+Cork-500060,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0110NQ8277 _20230527075338.jpg
+Cork-500070,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0110NQ8288 _20230527075339.jpg
+Cork-500080,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0111NQ8238_20230527074449.jpg
+Cork-500090,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0111NQ8240_20230527074448.jpg
+Cork-500100,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0111NQ8241_20230527074448.jpg
+Cork-500110,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0111NQ8242 _20230527074448.jpg
+Cork-500120,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0111NQ8243_20230527074447.jpg
+Cork-500130,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0111NQ8247_20230527074448.jpg
+Cork-500140,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0111NQ8282_20230527074448.jpg
+Cork-500150,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0111NQ8283_20230527074448.jpg
+Cork-500160,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0148NQ8101_20230524065253.jpg
+Cork-500170,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0148NQ8102_20230524065254.jpg
+Cork-500180,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0148NQ8103_20230524065254.jpg
+Cork-500190,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0148NQ8104_20230524065253.jpg
+Cork-500200,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0148NQ8105_20230524065253.jpg
+Cork-500210,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0154NQ8290_20230524062118.jpg
+Cork-500220,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0154NQ8291_20230524062120.jpg
+Cork-500230,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0154NQ8292_20230524062124.jpg
+Cork-500240,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0154NQ8293_20230524062121.jpg
+Cork-500250,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0154NQ8294_20230524062121.jpg
+Cork-500260,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0154NQ8295_20230524062123.jpg
+Cork-500270,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8296_20230524061641.jpg
+Cork-500280,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8297_20230524061641.jpg
+Cork-500290,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8298_20230524061643.jpg
+Cork-500300,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8299_20230524061644.jpg
+Cork-500310,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8300_20230524061644.jpg
+Cork-500320,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8301_20230524061644.jpg
+Cork-500330,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8302_20230524061646.jpg
+Cork-500340,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8303_20230524061645.jpg
+Cork-500350,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0155NQ8304_20230524061642.jpg
+Cork-500360,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8269_20230523091704.jpg
+Cork-500370,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8305_20230523091704.jpg
+Cork-500380,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8306_20230523091703.jpg
+Cork-500390,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8307_20230523091712.jpg
+Cork-500400,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8308_20230523091712.jpg
+Cork-500410,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8309_20230523091712.jpg
+Cork-500420,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8310_20230523091704.jpg
+Cork-500430,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8311_20230523091705.jpg
+Cork-500440,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8312_20230523091704.jpg
+Cork-500450,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8313_20230523091703.jpg
+Cork-500460,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8314_20230523091704.jpg
+Cork-500470,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8315_20230523091704.jpg
+Cork-500480,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8316_20230523091703.jpg
+Cork-500490,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8317_20230523091703.jpg
+Cork-500500,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8318_20230523091703.jpg
+Cork-500510,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8319_20230523091704.jpg
+Cork-500520,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8320_20230523091705.jpg
+Cork-500530,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8321_20230523091711.jpg
+Cork-500540,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8322_20230523091712.jpg
+Cork-500550,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8323_20230523091712.jpg
+Cork-500560,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8324_20230523091706.jpg
+Cork-500570,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8325_20230523091707.jpg
+Cork-500580,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8326_20230523091705.jpg
+Cork-500590,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8327_20230523091707.jpg
+Cork-500600,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8328_20230523091708.jpg
+Cork-500610,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8329_20230523091712.jpg
+Cork-500620,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=G0174NQ8330_20230523091712.jpg
+Cork-500630,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8339_20240305082505.jpg
+Cork-500640,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8340_20240305082507.jpg
+Cork-500650,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8341_20240305082510.jpg
+Cork-500660,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8342_20240305082510.jpg
+Cork-500670,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8343_20240305082512.jpg
+Cork-500680,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8344_20240305082512.jpg
+Cork-500690,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8345_20240305082511.jpg
+Cork-500700,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8346 (1)_20240305082511.jpg
+Cork-500710,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8347_20240305082513.jpg
+Cork-500720,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8348_20240305082511.jpg
+Cork-500730,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8349_20240307064425.jpg
+Cork-500740,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8350_20240307064426.jpg
+Cork-500750,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8351_20240307064426.jpg
+Cork-500760,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8352_20240307064427.jpg
+Cork-500770,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8353_20240307064426.jpg
+Cork-500780,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8354_20240307064425.jpg
+Cork-500790,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8355_20240307064427.jpg
+Cork-500800,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8356_20240307064426.jpg
+Cork-500810,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=DP1172_20240614093710.jpg
+Cork-500820,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=DP1173_20240614093709.jpg
+Cork-500830,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=LA3001_20240614094856.jpg
+Cork-500840,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=LA3002_20240614094854.jpg
+Cork-500850,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=LA3003_20240614095328.jpg
+Cork-500860,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=LA3004_20240614095326.jpg
+Cork-500870,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=LA3005_20240614095720.jpg
+Cork-500880,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8101_20240614102140.jpg
+Cork-500890,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8103_20240614102140.jpg
+Cork-500900,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8269_20240614103333.jpg
+Cork-500910,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8305_20240614103333.jpg
+Cork-500920,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8306_20240614103333.jpg
+Cork-500930,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8307_20240614103339.jpg
+Cork-500940,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8308_20240614103337.jpg
+Cork-500950,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8309_20240614103337.jpg
+Cork-500960,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8311_20240614103333.jpg
+Cork-500970,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8312_20240614103333.jpg
+Cork-500980,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8313_20240614103333.jpg
+Cork-500990,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8314_20240614103334.jpg
+Cork-501000,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8315_20240614103335.jpg
+Cork-501010,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8316_20240614103334.jpg
+Cork-501020,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8317_20240614103335.jpg
+Cork-501030,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8318_20240614103334.jpg
+Cork-501040,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8319_20240614103335.jpg
+Cork-501050,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8322_20240614103337.jpg
+Cork-501060,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8323_20240614103337.jpg
+Cork-501070,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8324_20240614103335.jpg
+Cork-501080,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8325_20240614103335.jpg
+Cork-501090,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8326_20240614103335.jpg
+Cork-501100,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8327_20240614103336.jpg
+Cork-501110,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8328_20240614103336.jpg
+Cork-501120,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8329_20240614103337.jpg
+Cork-501130,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8330_20240614103336.jpg
+Cork-501140,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8332_20240614103340.jpg
+Cork-501150,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8357_20240614103402.jpg
+Cork-501160,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8360_20240614103347.jpg
+Cork-501170,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8361_20240614103358.jpg
+Cork-501180,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8363_20240614103355.jpg
+Cork-501190,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8364_20240614103353.jpg
+Cork-501200,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=nq8368_20240614103338.jpg
+Cork-501210,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8373_20240614103352.jpg
+Cork-501220,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8375_20240614103344.jpg
+Cork-501230,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8377_20240614103359.jpg
+Cork-501240,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8378_20240614103408.jpg
+Cork-501250,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8379_20240614103405.jpg
+Cork-501260,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8380_20240614103354.jpg
+Cork-501270,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=H192NQ8309_20250426085134.jpg
+Cork-501280,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=H192NQ8310_20250426091355.jpg
+Cork-501290,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=H192NQ8317_20250426084121.jpg
+Cork-501300,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=H192NQ8321(1)_20250426085445.jpg
+Cork-501310,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8269_20260609021346.jpg
+Cork-501320,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8311_20260609015627.jpg
+Cork-501330,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8313_20260608083507.jpg
+Cork-501340,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8318_20260609012847.jpg
+Cork-501350,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8319_20260608054537.jpg
+Cork-501360,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8323_20260609005838.jpg
+Cork-501370,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8324_20260608090147.jpg
+Cork-501380,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8328_20260608082837.jpg
+Cork-501390,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8341_20260609015435.jpg
+Cork-501400,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8342_20260609004700.jpg
+Cork-501410,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8343_20260608085757.jpg
+Cork-501420,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8344_20260608085955.jpg
+Cork-501430,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8352_20260609024100.jpg
+Cork-501440,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8353_20260608083050.jpg
+Cork-501450,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8355_20260609011052.jpg
+Cork-501460,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8363_20260608094456.jpg
+Cork-501470,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=nq8368_20260608082238.jpg
+Cork-501480,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8380_20260608091626.jpg
+Cork-501490,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8238_20260612025615.jpg
+Cork-501500,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8240_20260612030800.jpg
+Cork-501510,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8241_20260612025259.jpg
+Cork-501520,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8242_20260612031713.jpg
+Cork-501530,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8243_20260612030429.jpg
+Cork-501540,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8247_20260612031856.jpg
+Cork-501550,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8248_20260612033403.jpg
+Cork-501560,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8249_20260612032338.jpg
+Cork-501570,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8250_20260612032632.jpg
+Cork-501580,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8258_20260612033104.jpg
+Cork-501590,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8269_20260612032014.jpg
+Cork-501600,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8282_20260612031540.jpg
+Cork-501610,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8283_20260612034124.jpg
+Cork-501620,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8288_20260612032937.jpg
+Cork-501630,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8305_20260612051122.jpg
+Cork-501640,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8306_20260612031104.jpg
+Cork-501650,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8307_20260612033828.jpg
+Cork-501660,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8308_20260612033556.jpg
+Cork-501670,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8309_20260612032806.jpg
+Cork-501680,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8310_20260612024741.jpg
+Cork-501690,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8311_20260612031409.jpg
+Cork-501700,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8312_20260612030602.jpg
+Cork-501710,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8313_20260612051443.jpg
+Cork-501720,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8314_20260612024926.jpg
+Cork-501730,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8315_20260612025437.jpg
+Cork-501740,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8316_20260612035328.jpg
+Cork-501750,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8317_20260612032157.jpg
+Cork-501760,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8318_20260612032502.jpg
+Cork-501770,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8319_20260612021953.jpg
+Cork-501780,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8320_20260612025737.jpg
+Cork-501790,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8321_20260612025907.jpg
+Cork-501800,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8322_20260612030040.jpg
+Cork-501810,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8323_20260612050950.jpg
+Cork-501820,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8325_20260612050813.jpg
+Cork-501830,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8326_20260612050511.jpg
+Cork-501840,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8327_20260612050642.jpg
+Cork-501850,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8328_20260612051813.jpg
+Cork-501860,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8329_20260612034325.jpg
+Cork-501870,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8330_20260612025108.jpg
+Cork-501880,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8332_20260612024438.jpg
+Cork-501890,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8357_20260612034542.jpg
+Cork-501900,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8360_20260612030205.jpg
+Cork-501910,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8361_20260612033230.jpg
+Cork-501920,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8363_20260612052629.jpg
+Cork-501930,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8364_20260612051246.jpg
+Cork-501940,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8368_20260612054040.jpg
+Cork-501950,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8372_20260612055112.jpg
+Cork-501970,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8375_20260612054435.jpg
+Cork-501980,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8377_20260612055459.jpg
+Cork-501990,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8378_20260612053108.jpg
+Cork-502000,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8379_20260612031238.jpg
+Cork-502010,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8380_20260612055910.jpg
+Cork-502020,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8381_20260612054246.jpg
+Cork-502040,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8391_20260612052502.jpg
+Cork-502050,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8392_20260612052838.jpg
+Cork-502060,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8601_20260612061251.jpg
+Cork-502070,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8611_20260612061504.jpg
+Cork-502080,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8614_20260612060642.jpg
+Cork-502090,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8615_20260612060854.jpg
+Cork-502100,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8616_20260612060421.jpg
+Cork-502110,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8617_20260612060206.jpg
+Cork-502120,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8618_20260612061057.jpg
+Cork-502130,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8383_20260612055658.jpg
+Cork-502140,https://api.greenlandwallcoverings.com/api/downloadFile?fileName=NQ8373_20260612054803.jpg
diff --git a/scripts/expand-diff.mjs b/scripts/expand-diff.mjs
new file mode 100644
index 0000000..f733602
--- /dev/null
+++ b/scripts/expand-diff.mjs
@@ -0,0 +1,128 @@
+#!/usr/bin/env node
+// EXPAND step: re-scrape the live Cork(40) line, explode detail.skus[] colorways
+// (same logic as scrape.mjs), then DIFF against the mfr_skus already in
+// greenland_catalog. Emits data/expand-new.ndjson (rows NOT yet in DB) + prints
+// counts. NO DB WRITE — pure discovery. $0 (open API).
+import { writeFileSync, readFileSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname, join } from 'node:path';
+
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
+const API = process.env.GL_API || 'https://api.greenlandwallcoverings.com/api';
+const CATEGORY_ID = 1, SUBCAT_ID = 40, PAGE_SIZE = 200;
+
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+async function j(url, tries = 4) {
+ for (let i = 0; i < tries; i++) {
+ try {
+ const r = await fetch(url, { headers: { accept: 'application/json' } });
+ if (!r.ok) throw new Error('HTTP ' + r.status);
+ return await r.json();
+ } catch (e) { if (i === tries - 1) throw e; await sleep(600 * (i + 1)); }
+ }
+}
+const SKU_RE = /^[A-Z]{1,4}\d{2,}[A-Z0-9-]*$/;
+function splitNameCode(name, code) {
+ const n = (name || '').trim(), c = (code || '').trim();
+ const nSku = SKU_RE.test(n.replace(/\s+/g, '')), cSku = SKU_RE.test(c.replace(/\s+/g, ''));
+ if (nSku && !cSku) return { sku: n, label: c };
+ if (cSku && !nSku) return { sku: c, label: n };
+ return { sku: n || c, label: (n && c && n !== c) ? c : '' };
+}
+function widthInches(w) {
+ if (!w) return null;
+ let m = w.match(/([\d.]+)\s*(?:''|"|in\b|inch)/i); if (m) return parseFloat(m[1]);
+ m = w.match(/([\d.]+)\s*cm/i); if (m) return +(parseFloat(m[1]) / 2.54).toFixed(2);
+ return null;
+}
+const norm = s => (s || '').trim().toUpperCase().replace(/\s+/g, '');
+
+async function main() {
+ // existing skus from DB (via stdin file)
+ const existRaw = readFileSync('/tmp/gl_existing_skus.txt', 'utf8').split('\n').map(x => x.trim()).filter(Boolean);
+ const existNorm = new Set(existRaw.map(norm));
+ console.log(`existing DB mfr_skus: ${existRaw.length} (${existNorm.size} normalized)`);
+
+ const first = await j(`${API}/product/page?categoryId=${CATEGORY_ID}&subCategoryIds=${SUBCAT_ID}&page=1&pageSize=${PAGE_SIZE}`);
+ const total = first.data.total; let all = [...(first.data.list || [])];
+ for (let p = 2; all.length < total; p++) {
+ const d = await j(`${API}/product/page?categoryId=${CATEGORY_ID}&subCategoryIds=${SUBCAT_ID}&page=${p}&pageSize=${PAGE_SIZE}`);
+ const l = d.data.list || []; if (!l.length) break; all.push(...l);
+ }
+ console.log(`live cork groups: ${all.length} (total=${total})`);
+
+ const rows = []; const seen = new Set();
+ let parents = 0, exploded = 0;
+ for (let i = 0; i < all.length; i++) {
+ const li = all[i];
+ let det = {};
+ try { det = (await j(`${API}/product/${li.ID}`)).data || {}; }
+ catch (e) { console.warn(` ! detail ${li.ID} failed: ${e.message}`); }
+ const { sku: parentSku, label: parentLabel } = splitNameCode(det.name ?? li.name, det.code ?? li.code);
+ const patternName = parentLabel || parentSku;
+ const subs = (det.subCategories || []).filter(s => s.category_name && s.category_name !== '是');
+ const collection = subs.find(s => s.category_type === 'wallType')?.category_name || null;
+ const subcats = subs.map(s => s.category_name).join(', ') || null;
+ const compName = (det.compositions || []).map(c => c.compositionName).filter(Boolean).join(', ');
+ const compPct = (det.compositions?.[0]?.percentage) || null;
+ const material = compName || 'Cork';
+ const composition = compName ? [compName, compPct && `${compPct}%`].filter(Boolean).join(' ')
+ : (compPct ? `Cork ${compPct}%` : 'Cork');
+ const shared = {
+ collection, category_name: det.categoryName || li.categoryName || 'Wallcovering',
+ subcategory_names: subcats, product_type: 'Wallcovering', use: det.use || null,
+ width: det.width || null, width_inches: widthInches(det.width),
+ minimum_order: det.minimumOrder || null, pattern_repeat: det.repeat || null,
+ fire_rating_us: det.fireRating || null, backing: det.backing || null,
+ weight: det.weight != null ? String(det.weight) : null,
+ material, composition,
+ is_publish: det.isPublish ?? li.isPublish ?? 0,
+ specs: { width: det.width, minimumOrder: det.minimumOrder, fireRating: det.fireRating, backing: det.backing, weight: det.weight, use: det.use, unitPriceCode: det.unitPrice },
+ };
+ const push = (r) => { if (r.mfr_sku && !seen.has(norm(r.mfr_sku))) { seen.add(norm(r.mfr_sku)); rows.push(r); } };
+ const kids = Array.isArray(det.skus) ? det.skus.filter(k => k && k.model) : [];
+ if (kids.length) {
+ exploded++;
+ for (const k of kids) {
+ const img = k.thumbnail || det.coverImg || li.coverImg || null;
+ push({
+ gl_id: 10000000 + (k.id || 0), mfr_sku: k.model, pattern_name: patternName,
+ color_name: k.colorDescription || null, ...shared, image_url: img,
+ all_images: [img].filter(Boolean).join('|') || null,
+ product_url: `https://m.greenlandwallcoverings.com/#/pages/detail/index?id=${li.ID}`,
+ raw: { parent_id: li.ID, parent_sku: parentSku, sku: k },
+ });
+ }
+ } else {
+ parents++;
+ push({
+ gl_id: li.ID, mfr_sku: parentSku, pattern_name: patternName, color_name: null, ...shared,
+ image_url: det.coverImg || li.coverImg || null,
+ all_images: [det.coverImg || li.coverImg, det.rotograph].filter(Boolean).join('|') || null,
+ product_url: `https://m.greenlandwallcoverings.com/#/pages/detail/index?id=${li.ID}`,
+ raw: det && det.id ? det : li,
+ });
+ }
+ await sleep(100);
+ }
+ console.log(`live scrape: ${parents} leaf + ${exploded} groups exploded → ${rows.length} total colorways`);
+
+ // diff
+ const newRows = rows.filter(r => !existNorm.has(norm(r.mfr_sku)));
+ console.log(`\n>>> NEW (not in DB): ${newRows.length}`);
+ console.log(`>>> already in DB : ${rows.length - newRows.length}`);
+ // also: which existing DB skus are NOT in the live scrape (removed upstream)?
+ const liveNorm = new Set(rows.map(r => norm(r.mfr_sku)));
+ const goneFromLive = existRaw.filter(s => !liveNorm.has(norm(s)));
+ console.log(`>>> in DB but NOT in live scrape: ${goneFromLive.length}`);
+ if (goneFromLive.length) console.log(' (kept, not deleted):', goneFromLive.slice(0, 20).join(', '), goneFromLive.length > 20 ? '…' : '');
+
+ writeFileSync(join(ROOT, 'data', 'expand-new.ndjson'), newRows.map(r => JSON.stringify(r)).join('\n') + (newRows.length ? '\n' : ''));
+ writeFileSync(join(ROOT, 'data', 'expand-live-all.ndjson'), rows.map(r => JSON.stringify(r)).join('\n') + '\n');
+ if (newRows.length) {
+ console.log('\nNEW sample:');
+ for (const r of newRows.slice(0, 15)) console.log(` ${r.mfr_sku} | ${r.pattern_name} | color=${r.color_name || '—'} | ${r.collection || '—'}`);
+ }
+ console.log(`\nwrote data/expand-new.ndjson (${newRows.length}) + data/expand-live-all.ndjson (${rows.length})`);
+}
+main().catch(e => { console.error(e); process.exit(1); });
diff --git a/scripts/expand-discover.mjs b/scripts/expand-discover.mjs
new file mode 100644
index 0000000..447e320
--- /dev/null
+++ b/scripts/expand-discover.mjs
@@ -0,0 +1,142 @@
+#!/usr/bin/env node
+// EXPAND DISCOVERY: scrape BOTH cork leaves (40 Handmade Cork + 292 Top100 Cork&Wood),
+// explode detail.skus[] colorways, capture wallClass, FILTER to Cork only (exclude Wood),
+// dedup by mfr_sku, then DIFF against greenland_catalog. $0 open API. NO DB WRITE.
+// Emits data/expand-new.ndjson (cork rows NOT yet in DB) + data/expand-live-all.ndjson.
+import { writeFileSync, readFileSync } from 'node:fs';
+import { fileURLToPath } from 'node:url';
+import { dirname, join } from 'node:path';
+
+const ROOT = join(dirname(fileURLToPath(import.meta.url)), '..');
+const API = process.env.GL_API || 'https://api.greenlandwallcoverings.com/api';
+const LEAVES = [{ cat: 1, sub: 40 }, { cat: 1, sub: 292 }];
+const PAGE_SIZE = 200;
+
+const sleep = ms => new Promise(r => setTimeout(r, ms));
+async function j(url, tries = 4) {
+ for (let i = 0; i < tries; i++) {
+ try {
+ const r = await fetch(url, { headers: { accept: 'application/json' } });
+ if (!r.ok) throw new Error('HTTP ' + r.status);
+ return await r.json();
+ } catch (e) { if (i === tries - 1) throw e; await sleep(600 * (i + 1)); }
+ }
+}
+const SKU_RE = /^[A-Z]{1,4}\d{2,}[A-Z0-9-]*$/;
+function splitNameCode(name, code) {
+ const n = (name || '').trim(), c = (code || '').trim();
+ const nSku = SKU_RE.test(n.replace(/\s+/g, '')), cSku = SKU_RE.test(c.replace(/\s+/g, ''));
+ if (nSku && !cSku) return { sku: n, label: c };
+ if (cSku && !nSku) return { sku: c, label: n };
+ return { sku: n || c, label: (n && c && n !== c) ? c : '' };
+}
+function widthInches(w) {
+ if (!w) return null;
+ let m = w.match(/([\d.]+)\s*(?:''|"|in\b|inch)/i); if (m) return parseFloat(m[1]);
+ m = w.match(/([\d.]+)\s*cm/i); if (m) return +(parseFloat(m[1]) / 2.54).toFixed(2);
+ return null;
+}
+const norm = s => (s || '').trim().toUpperCase().replace(/\s+/g, '');
+
+async function collectLeaf(cat, sub) {
+ const first = await j(`${API}/product/page?categoryId=${cat}&subCategoryIds=${sub}&page=1&pageSize=${PAGE_SIZE}`);
+ const total = first.data.total; let all = [...(first.data.list || [])];
+ for (let p = 2; all.length < total; p++) {
+ const d = await j(`${API}/product/page?categoryId=${cat}&subCategoryIds=${sub}&page=${p}&pageSize=${PAGE_SIZE}`);
+ const l = d.data.list || []; if (!l.length) break; all.push(...l);
+ }
+ console.log(` leaf ${sub}: ${all.length} groups (total=${total})`);
+ return all;
+}
+
+async function main() {
+ const existRaw = readFileSync('/tmp/gl_existing_skus.txt', 'utf8').split('\n').map(x => x.trim()).filter(Boolean);
+ const existNorm = new Set(existRaw.map(norm));
+ console.log(`existing DB mfr_skus: ${existRaw.length} (${existNorm.size} normalized)`);
+
+ // gather groups across both leaves, dedup by group ID
+ const groupsById = new Map();
+ for (const { cat, sub } of LEAVES) {
+ const gs = await collectLeaf(cat, sub);
+ for (const g of gs) if (!groupsById.has(g.ID)) groupsById.set(g.ID, g);
+ }
+ const all = [...groupsById.values()];
+ console.log(`unique groups across cork leaves: ${all.length}`);
+
+ const rows = []; const seen = new Set();
+ let parents = 0, exploded = 0, woodSkipped = 0;
+ for (let i = 0; i < all.length; i++) {
+ const li = all[i];
+ let det = {};
+ try { det = (await j(`${API}/product/${li.ID}`)).data || {}; }
+ catch (e) { console.warn(` ! detail ${li.ID} failed: ${e.message}`); }
+ const subs = (det.subCategories || []).filter(s => s.category_name && s.category_name !== '是');
+ const wallClass = subs.find(s => s.category_type === 'wallClass')?.category_name || null;
+ // CORK-ONLY GATE: exclude Wood (and anything not Cork) — leaf 292 mixes Cork & Wood
+ if (wallClass && wallClass.toLowerCase() !== 'cork') { woodSkipped++; continue; }
+ const { sku: parentSku, label: parentLabel } = splitNameCode(det.name ?? li.name, det.code ?? li.code);
+ const patternName = parentLabel || parentSku;
+ const collection = subs.find(s => s.category_type === 'wallType')?.category_name || null;
+ const subcats = subs.map(s => s.category_name).join(', ') || null;
+ const compName = (det.compositions || []).map(c => c.compositionName).filter(Boolean).join(', ');
+ const compPct = (det.compositions?.[0]?.percentage) || null;
+ const material = compName || 'Cork';
+ const composition = compName ? [compName, compPct && `${compPct}%`].filter(Boolean).join(' ')
+ : (compPct ? `Cork ${compPct}%` : 'Cork');
+ const shared = {
+ collection, category_name: det.categoryName || li.categoryName || 'Wallcovering',
+ subcategory_names: subcats, product_type: 'Wallcovering', use: det.use || null,
+ width: det.width || null, width_inches: widthInches(det.width),
+ minimum_order: det.minimumOrder || null, pattern_repeat: det.repeat || null,
+ fire_rating_us: det.fireRating || null, backing: det.backing || null,
+ weight: det.weight != null ? String(det.weight) : null,
+ material, composition,
+ is_publish: det.isPublish ?? li.isPublish ?? 0,
+ specs: { width: det.width, minimumOrder: det.minimumOrder, fireRating: det.fireRating, backing: det.backing, weight: det.weight, use: det.use, unitPriceCode: det.unitPrice, wallClass },
+ };
+ const push = (r) => { if (r.mfr_sku && !seen.has(norm(r.mfr_sku))) { seen.add(norm(r.mfr_sku)); rows.push(r); } };
+ const kids = Array.isArray(det.skus) ? det.skus.filter(k => k && k.model) : [];
+ if (kids.length) {
+ exploded++;
+ for (const k of kids) {
+ const img = k.thumbnail || det.coverImg || li.coverImg || null;
+ push({
+ gl_id: 10000000 + (k.id || 0), mfr_sku: k.model, pattern_name: patternName,
+ color_name: k.colorDescription || null, ...shared, image_url: img,
+ all_images: [img].filter(Boolean).join('|') || null,
+ product_url: `https://m.greenlandwallcoverings.com/#/pages/detail/index?id=${li.ID}`,
+ raw: { parent_id: li.ID, parent_sku: parentSku, sku: k },
+ });
+ }
+ } else {
+ parents++;
+ push({
+ gl_id: li.ID, mfr_sku: parentSku, pattern_name: patternName, color_name: null, ...shared,
+ image_url: det.coverImg || li.coverImg || null,
+ all_images: [det.coverImg || li.coverImg, det.rotograph].filter(Boolean).join('|') || null,
+ product_url: `https://m.greenlandwallcoverings.com/#/pages/detail/index?id=${li.ID}`,
+ raw: det && det.id ? det : li,
+ });
+ }
+ if ((i + 1) % 25 === 0) console.log(` …${i + 1}/${all.length} groups (${rows.length} cork SKUs, ${woodSkipped} wood-skipped)`);
+ await sleep(100);
+ }
+ console.log(`cork scrape: ${parents} leaf + ${exploded} groups exploded → ${rows.length} cork colorways (${woodSkipped} non-cork groups skipped)`);
+
+ const newRows = rows.filter(r => !existNorm.has(norm(r.mfr_sku)));
+ console.log(`\n>>> NEW cork (not in DB): ${newRows.length}`);
+ console.log(`>>> already in DB : ${rows.length - newRows.length}`);
+ const liveNorm = new Set(rows.map(r => norm(r.mfr_sku)));
+ const goneFromLive = existRaw.filter(s => !liveNorm.has(norm(s)));
+ console.log(`>>> in DB but NOT in live cork scrape: ${goneFromLive.length} (kept, not deleted)`);
+ if (goneFromLive.length) console.log(' ', goneFromLive.slice(0, 25).join(', '), goneFromLive.length > 25 ? '…' : '');
+
+ writeFileSync(join(ROOT, 'data', 'expand-new.ndjson'), newRows.map(r => JSON.stringify(r)).join('\n') + (newRows.length ? '\n' : ''));
+ writeFileSync(join(ROOT, 'data', 'expand-live-all.ndjson'), rows.map(r => JSON.stringify(r)).join('\n') + '\n');
+ if (newRows.length) {
+ console.log('\nNEW sample:');
+ for (const r of newRows.slice(0, 20)) console.log(` ${r.mfr_sku} | ${r.pattern_name} | color=${r.color_name || '—'} | ${r.collection || '—'} | wc=${r.specs.wallClass}`);
+ }
+ console.log(`\nwrote data/expand-new.ndjson (${newRows.length}) + data/expand-live-all.ndjson (${rows.length})`);
+}
+main().catch(e => { console.error(e); process.exit(1); });
← 3bb9eb3 auto-data-snapshot: 2026-08-10T16:09:17 (4 data files) — bac
·
back to Greenland Onboard
·
greenland cork go-live step 6: reactivate+publish+image-atta 4b24288 →