SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
4.1 KB · 101 lines typescript
Raw Blame History
1import type { ConnectorMeta } from '@rareindex/connectors';2import { CertLookupConnector, type PageStatus, type ParsedCert } from '../_g2-grading-lib/cert-base.js';3import { ccgClassify, ccgParse, ccgTrim, pick } from '../_g2-grading-lib/ccg.js';4import { cardCategoryFromText, clean, parseCardGrade, yearOf } from '../_g2-grading-lib/text.js';56/**7 * CGC Cards certificate verification — https://www.cgccards.com/certlookup/<cert>/ (public). TCG, sports8 * and non-sports cards graded by CGC Cards (formerly CGC Trading Cards + CSG). Same CCG template as CGC9 * Comics; behind a Cloudflare managed challenge, rendered by Firecrawl.10 */11const PARSER_VERSION = '1.0.0';12const PATTERNS = [/^https?:\/\/(?:www\.)?cgccards\.com\/certlookup\/(\d{6,12})(?:\/[^/?#]*)?\/?(?:[?#].*)?$/i];1314export function certUrl(cert: string): string {15  return `https://www.cgccards.com/certlookup/${clean(cert).replace(/\D/g, '')}/`;16}1718export function certFromUrl(url: string): string | null {19  const m = url.match(PATTERNS[0]!);20  return m ? m[1]! : null;21}2223export class CgcCardsCertConnector extends CertLookupConnector {24  readonly version = '1.0.0';25  readonly parserVersion = PARSER_VERSION;26  readonly grader = 'cgc';27  readonly idKey = 'cgc_cert';28  readonly format = 'html' as const;29  override readonly urlPatterns = PATTERNS;3031  certUrl(cert: string): string {32    return certUrl(cert);33  }34  certFromUrl(url: string): string | null {35    return certFromUrl(url);36  }37  classify(doc: string): PageStatus {38    return ccgClassify(doc);39  }40  trim(doc: string): string {41    return ccgTrim(doc);42  }4344  parse(snapshot: string, info: { cert: string; url: string }): ParsedCert | null {45    const page = ccgParse(snapshot);46    const f = page.fields;47    const cert = pick(f, 'Cert #', 'CGC Cert #', 'Cert') ?? info.cert;48    const name = pick(f, 'Card Name', 'Player', 'Name', 'Title');49    if (!name) return null;50    const game = pick(f, 'Game', 'Sport', 'Category');51    const set = pick(f, 'Card Set', 'Set');52    const number = pick(f, 'Card Number', 'Card #');53    const language = pick(f, 'Language');54    const gradeRaw = pick(f, 'Grade');55    const g = parseCardGrade(gradeRaw);56    const auto = pick(f, 'Autograph Grade', 'Auto Grade');57    const pedigree = pick(f, 'Pedigree', 'Label');58    const variant = pick(f, 'Variant', 'Variety', 'Parallel', 'Subset') ?? null;59    const text = `${game ?? ''} ${set ?? ''} ${name} ${variant ?? ''}`;60    const cat = cardCategoryFromText(text, /baseball|basketball|football|hockey|soccer|racing|golf|tennis|boxing|wrestling|ufc|mma/i.test(game ?? '') ? 'sports_cards' : 'trading_cards');61    const isPokemon = cat.slug === 'pokemon';62    const year = yearOf(pick(f, 'Year'));63    const rawTitle = `${year ?? ''} ${set ?? game ?? ''} ${name}${number ? ` #${number}` : ''}${gradeRaw ? ` CGC ${gradeRaw}` : ''}`.replace(/\s+/g, ' ').trim();64    return {65      title: rawTitle,66      attributes: {67        categorySlug: cat.slug,68        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,69        brand: isPokemon ? 'The Pokémon Company' : null,70        set,71        name,72        number: number ? number.split('/')[0]!.trim() : null,73        year,74        variant,75        language,76        identifiers: {},77        metadata: {78          cgc_cert: cert,79          game,80          card_number_raw: number,81          total_in_set: number?.includes('/') ? number.split('/')[1]!.trim() : null,82          grade_date: pick(f, 'Grade Date'),83          autograph_grade: auto,84          pedigree,85          population_url: page.populationUrl,86          population_note: page.stats.find((s) => /coming soon/i.test(s)) ?? null,87          fields: f,88        },89      },90      grade: g.grade,91      qualifier: g.qualifier,92      gradeLabel: g.label || null,93      images: page.images,94      population: page.atGrade !== null ? { gradeKey: g.grade ?? page.popGradeKey ?? g.label, atGrade: page.atGrade, higher: page.higher, url: page.populationUrl } : null,95      confidence: cat.confident ? 0.9 : 0.78,96    };97  }98}99100export default (meta: ConnectorMeta) => new CgcCardsCertConnector(meta);101