import type { ConnectorMeta } from '@rareindex/connectors'; import { CertLookupConnector, type PageStatus, type ParsedCert } from '../_g2-grading-lib/cert-base.js'; import { ccgClassify, ccgParse, ccgTrim, pick } from '../_g2-grading-lib/ccg.js'; import { cardCategoryFromText, clean, parseCardGrade, yearOf } from '../_g2-grading-lib/text.js'; /** * CGC Cards certificate verification — https://www.cgccards.com/certlookup// (public). TCG, sports * and non-sports cards graded by CGC Cards (formerly CGC Trading Cards + CSG). Same CCG template as CGC * Comics; behind a Cloudflare managed challenge, rendered by Firecrawl. */ const PARSER_VERSION = '1.0.0'; const PATTERNS = [/^https?:\/\/(?:www\.)?cgccards\.com\/certlookup\/(\d{6,12})(?:\/[^/?#]*)?\/?(?:[?#].*)?$/i]; export function certUrl(cert: string): string { return `https://www.cgccards.com/certlookup/${clean(cert).replace(/\D/g, '')}/`; } export function certFromUrl(url: string): string | null { const m = url.match(PATTERNS[0]!); return m ? m[1]! : null; } export class CgcCardsCertConnector extends CertLookupConnector { readonly version = '1.0.0'; readonly parserVersion = PARSER_VERSION; readonly grader = 'cgc'; readonly idKey = 'cgc_cert'; readonly format = 'html' as const; override readonly urlPatterns = PATTERNS; certUrl(cert: string): string { return certUrl(cert); } certFromUrl(url: string): string | null { return certFromUrl(url); } classify(doc: string): PageStatus { return ccgClassify(doc); } trim(doc: string): string { return ccgTrim(doc); } parse(snapshot: string, info: { cert: string; url: string }): ParsedCert | null { const page = ccgParse(snapshot); const f = page.fields; const cert = pick(f, 'Cert #', 'CGC Cert #', 'Cert') ?? info.cert; const name = pick(f, 'Card Name', 'Player', 'Name', 'Title'); if (!name) return null; const game = pick(f, 'Game', 'Sport', 'Category'); const set = pick(f, 'Card Set', 'Set'); const number = pick(f, 'Card Number', 'Card #'); const language = pick(f, 'Language'); const gradeRaw = pick(f, 'Grade'); const g = parseCardGrade(gradeRaw); const auto = pick(f, 'Autograph Grade', 'Auto Grade'); const pedigree = pick(f, 'Pedigree', 'Label'); const variant = pick(f, 'Variant', 'Variety', 'Parallel', 'Subset') ?? null; const text = `${game ?? ''} ${set ?? ''} ${name} ${variant ?? ''}`; const cat = cardCategoryFromText(text, /baseball|basketball|football|hockey|soccer|racing|golf|tennis|boxing|wrestling|ufc|mma/i.test(game ?? '') ? 'sports_cards' : 'trading_cards'); const isPokemon = cat.slug === 'pokemon'; const year = yearOf(pick(f, 'Year')); const rawTitle = `${year ?? ''} ${set ?? game ?? ''} ${name}${number ? ` #${number}` : ''}${gradeRaw ? ` CGC ${gradeRaw}` : ''}`.replace(/\s+/g, ' ').trim(); return { title: rawTitle, attributes: { categorySlug: cat.slug, franchise: isPokemon ? 'Pokémon' : game && /pok[eé]mon|magic|yu-?gi-?oh|one piece|lorcana|dragon ball|digimon|flesh and blood/i.test(game) ? game : null, brand: isPokemon ? 'The Pokémon Company' : null, set, name, number: number ? number.split('/')[0]!.trim() : null, year, variant, language, identifiers: {}, metadata: { cgc_cert: cert, game, card_number_raw: number, total_in_set: number?.includes('/') ? number.split('/')[1]!.trim() : null, grade_date: pick(f, 'Grade Date'), autograph_grade: auto, pedigree, population_url: page.populationUrl, population_note: page.stats.find((s) => /coming soon/i.test(s)) ?? null, fields: f, }, }, grade: g.grade, qualifier: g.qualifier, gradeLabel: g.label || null, images: page.images, population: page.atGrade !== null ? { gradeKey: g.grade ?? page.popGradeKey ?? g.label, atGrade: page.atGrade, higher: page.higher, url: page.populationUrl } : null, confidence: cat.confident ? 0.9 : 0.78, }; } } export default (meta: ConnectorMeta) => new CgcCardsCertConnector(meta);