/** * Router parserów kolekcji tkanin (wzorzec Strategii). * Wybiera adapter na podstawie hosta strony kolekcji: * sic.com.pl → import/sic.ts, davis.pl → import/fabricDavis.ts. * Wspólny kontrakt wyjściowy konsumuje domain/fabricImport.ts. */ import { parseSicCollectionPage, SicParseError } from "./sic.js"; import { parseDavisCollectionPage } from "./fabricDavis.js"; import { parseLechCollectionPage } from "./fabricLech.js"; export class FabricParseError extends Error { constructor(public readonly code: string, message: string) { super(message); this.name = "FabricParseError"; } } export interface ParsedFabricShade { /** kod w konwencji Vilmax (np. "01", "14", "100") */ code: string; /** kod/numer u producenta — do nazw plików i provenance */ vendorCode: string; /** nazwa handlowa odcienia, jeśli dostępna na stronie */ name: string | null; /** URL pełnowymiarowej próbki */ imageUrl: string; } export interface ParsedFabricCollection { /** slug kolekcji → fabrics.code */ code: string; /** nazwa handlowa (np. "TILIA", "Nova") */ name: string; producer: string; /** parametry techniczne → fabrics.params_json (klucze kanoniczne PARAM_LABELS) */ params: Record; cardUrl: string | null; shades: ParsedFabricShade[]; } export type FabricVendor = "sic" | "davis" | "lech"; export function detectFabricVendor(pageUrl: string): FabricVendor { let host: string; try { host = new URL(pageUrl).hostname.toLowerCase(); } catch { throw new FabricParseError("unsupported_host", "Nieprawidłowy URL strony kolekcji tkaniny."); } if (/(^|\.)sic\.com\.pl$/i.test(host)) return "sic"; if (/(^|\.)davis\.pl$/i.test(host)) return "davis"; if (/(^|\.)lechfabrics\.com$/i.test(host)) return "lech"; throw new FabricParseError("unsupported_host", `Brak adaptera kolekcji tkaniny dla hosta ${host}. Obsługiwane: sic.com.pl, davis.pl, lechfabrics.com.`); } const slugify = (text: string) => text.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase() .replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "tkanina"; export function parseFabricCollectionPage(html: string, pageUrl: string): ParsedFabricCollection { const vendor = detectFabricVendor(pageUrl); if (vendor === "sic") { try { const sic = parseSicCollectionPage(html, pageUrl); return { code: slugify(sic.collection), name: sic.collection, producer: sic.producer, params: {}, cardUrl: sic.cardUrl, shades: sic.shades.map((s) => ({ code: s.code, vendorCode: s.vendorCode, name: s.name, imageUrl: s.swatchUrl })), }; } catch (err) { if (err instanceof SicParseError) throw new FabricParseError(err.code, err.message); throw err; } } if (vendor === "lech") return parseLechCollectionPage(html, pageUrl); return parseDavisCollectionPage(html, pageUrl); }