← back to Dw Sku Integrity
match-helpers.mjs
47 lines
// match-helpers.mjs — PURE, side-effect-free helpers for the content-match recovery matcher.
// Extracted from content-match-gen.mjs so they can be unit-tested in isolation (the generator itself
// runs DB queries at import time and cannot be imported by a test). Mirrors classify.mjs's pure design.
// NO imports, NO I/O, NO global state. TK-10900.
export const CODE_SHAPE = /^[A-Za-z0-9][A-Za-z0-9._/-]{0,39}$/;
export const GREENFIELD_MINT = /^DW(AG|AX|CX|ST|SC|DX|WG)/i;
// Vendors whose catalog dw_sku is reverted greenfield-mint residue -> recover from mfr_sku instead.
export const MINT_CATALOG = new Set(['carnegie', 'maharam', 'cmo paris', 'cmo_paris', 'stout', 'stout textiles']);
export const norm = (s) => (s || '').trim().toLowerCase().replace(/\s+/g, ' ');
// Identity-key scrub applied to BOTH the catalog key and the vendor-stripped Shopify title, so
// storefront-only title noise ("... , <color> Wallcoverings") matches the catalog's pattern+color.
// Removes standalone "wallcovering(s)" and treats commas as spaces. Does NOT strip meaningful pattern
// words (e.g. "Wide Width" is part of Thibaut's pattern_name and is preserved).
export const scrubTitle = (s) => norm(s).replace(/\bwallcoverings?\b/g, ' ').replace(/,/g, ' ').replace(/\s+/g, ' ').trim();
// Base code = strip trailing DW type-suffix groups ('-panels', '-panels-museums', '-dividers', ...).
// Strips ONLY trailing '-<alpha>' tokens so a genuinely hyphenated real code with a numeric segment
// ('91026-10') is preserved rather than truncated. Only applied to mint-catalog mfr_sku (dw_sku verbatim).
export const baseCode = (c) => (c || '').trim().replace(/(-[A-Za-z][A-Za-z]*)+$/, '').toUpperCase();
// product_type semantic bucket so Shopify 'Upholstery' matches catalog 'Upholstery'/'Fabric' etc.
export const bucket = (pt) => {
const s = norm(pt);
if (/wallcover|wallpaper|mural|panel|museum|window|privacy|imo/.test(s)) return 'wall';
if (/upholst|fabric|textile|drapery|seat/.test(s)) return 'fabric';
return 'other';
};
// Strip the vendor name off EITHER end of a Shopify title: leading "Vendor ..." OR trailing "... | Vendor"
// / "... - Vendor". Handles Carnegie (prefix) and Maharam (" | Maharam" suffix).
export function stripVendor(title, vendor) {
let t = norm(title); const v = norm(vendor);
const esc = v.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
t = t.replace(new RegExp('\\s*[|\\-–]\\s*' + esc + '\\s*$', 'i'), ''); // trailing " | Vendor" / " - Vendor"
if (t.startsWith(v + ' ')) t = t.slice(v.length + 1); // leading "Vendor "
return t.trim();
}
// True when the Shopify row's bucket is incompatible with the sole catalog candidate's buckets
// (the Abbey-61 cross-class guard). 'other' on either side is permissive.
export function isCrossClass(shopBucket, catBuckets) {
return shopBucket !== 'other' && !catBuckets.has(shopBucket) && !catBuckets.has('other');
}