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%
3.7 KB · 98 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 { clean, parseNoteGrade, yearOf } from '../_g2-grading-lib/text.js';56/**7 * PMG banknote certificate verification — https://www.pmgnotes.com/certlookup/<cert>/<grade>/ (public).8 * Like NGC, PMG requires the holder grade as the second path segment (1–70 or "NonNumeric"); a URL9 * without it renders the empty lookup form. Seeds: "8067968-054/67".10 */11const PARSER_VERSION = '1.0.0';12const PATTERNS = [/^https?:\/\/(?:www\.)?pmgnotes\.(?:com|uk|de|hk|in|cn)\/(?:[a-z]{2}-[a-z]{2}\/)?certlookup\/([\d-]{5,20})(?:\/([^/?#]+))?\/?(?:[?#].*)?$/i];1314export function splitCert(cert: string): { number: string; grade: string | null } {15  const m = clean(cert).match(/^([\d-]+)(?:[\s/:]+(.+))?$/);16  if (!m) return { number: clean(cert), grade: null };17  return { number: m[1]!, grade: m[2] ? clean(m[2]).replace(/\s+/g, '') : null };18}1920export function certUrl(cert: string): string {21  const { number, grade } = splitCert(cert);22  return `https://www.pmgnotes.com/certlookup/${number}/${grade ? `${encodeURIComponent(grade)}/` : ''}`;23}2425export function certFromUrl(url: string): string | null {26  const m = url.match(PATTERNS[0]!);27  if (!m) return null;28  return m[2] ? `${m[1]}/${decodeURIComponent(m[2])}` : m[1]!;29}3031export class PmgCertConnector extends CertLookupConnector {32  readonly version = '1.0.0';33  readonly parserVersion = PARSER_VERSION;34  readonly grader = 'pmg';35  readonly idKey = 'pmg_cert';36  readonly format = 'html' as const;37  override readonly urlPatterns = PATTERNS;3839  certUrl(cert: string): string {40    return certUrl(cert);41  }42  certFromUrl(url: string): string | null {43    return certFromUrl(url);44  }45  override certIdentifier(cert: string): string {46    return splitCert(cert).number;47  }48  classify(doc: string): PageStatus {49    return ccgClassify(doc);50  }51  trim(doc: string): string {52    return ccgTrim(doc);53  }5455  parse(snapshot: string, info: { cert: string; url: string }): ParsedCert | null {56    const page = ccgParse(snapshot);57    const f = page.fields;58    const cert = pick(f, 'PMG Cert', 'Cert #', 'Cert') ?? splitCert(info.cert).number;59    const description = pick(f, 'Note Description', 'Description');60    if (!description) return null;61    const gradeRaw = pick(f, 'Grade');62    const g = parseNoteGrade(gradeRaw);63    const pickNo = pick(f, 'Note #', 'Pick #', 'Catalog #');64    const region = pick(f, 'Region');65    const identifiers: Record<string, string> = {};66    if (pickNo) identifiers.pick_number = pickNo;67    const countryName = description.split(',')[0]?.trim() || null;68    return {69      title: `${description} PMG ${gradeRaw ?? ''}`.trim(),70      attributes: {71        categorySlug: 'banknotes',72        name: description,73        year: yearOf(description),74        country: countryName,75        region,76        identifiers,77        metadata: {78          pmg_cert: cert,79          serial_number: pick(f, 'Serial #'),80          grade_date: pick(f, 'Grade Date'),81          signatures_vignettes: pick(f, 'Signatures/Vignettes'),82          comments: pick(f, 'Comments'),83          population_url: page.populationUrl,84          fields: f,85        },86      },87      grade: g.grade,88      qualifier: g.qualifier,89      gradeLabel: g.label || null,90      images: page.images,91      population: page.atGrade !== null ? { gradeKey: [g.grade, g.qualifier].filter(Boolean).join(' ') || page.popGradeKey || g.label, atGrade: page.atGrade, higher: page.higher, url: page.populationUrl } : null,92      confidence: pickNo ? 0.93 : 0.85,93    };94  }95}9697export default (meta: ConnectorMeta) => new PmgCertConnector(meta);98