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.9 KB · 46 lines typescript
Raw Blame History
1/**2 * Capture live PriceCharting / SportsCardsPro card pages as compact fixtures (sales trimmed to 40 rows).3 * Usage: pnpm tsx connectors/api/_lib/capture-pc.ts4 */5import { createCrawlContext, createRouter, loadConnector } from '@rareindex/connectors';6import { saveFixture } from '@rareindex/connectors/testing';7import { childLogger } from '@rareindex/shared';8import type { ProductPayload } from './pc-core.js';910const router = createRouter({ firecrawlApiKey: process.env.FIRECRAWL_API_KEY, scrapflyApiKey: process.env.SCRAPFLY_API_KEY });11const targets: Array<{ connector: string; url: string; name: string; expect: Record<string, unknown> }> = [12  { connector: 'pricecharting', url: 'https://www.pricecharting.com/game/pokemon-base-set/charizard-4', name: 'pokemon-base-set__charizard-4', expect: { minCount: 10, kinds: ['catalog_item', 'price_observation', 'sale'], requiredFields: ['attributes.setCode', 'attributes.number'] } },13  { connector: 'pricecharting', url: 'https://www.pricecharting.com/game/pokemon-base-set/charizard-1st-edition-4', name: 'pokemon-base-set__charizard-1st-edition-4', expect: { minCount: 5, kinds: ['catalog_item', 'price_observation', 'sale'] } },14  { connector: 'pricecharting', url: 'https://www.pricecharting.com/game/magic-alpha/black-lotus', name: 'magic-alpha__black-lotus', expect: { minCount: 3, kinds: ['catalog_item', 'price_observation', 'sale'] } },15  { connector: 'pricecharting', url: 'https://www.pricecharting.com/game/yugioh-legend-of-blue-eyes-white-dragon/blue-eyes-white-dragon-1st-edition-lob-001', name: 'yugioh-lob__blue-eyes-1st-edition', expect: { minCount: 3, kinds: ['catalog_item', 'price_observation', 'sale'] } },16  { connector: 'sportscardspro', url: 'https://www.sportscardspro.com/game/basketball-cards-1986-fleer/michael-jordan-57', name: 'basketball-1986-fleer__michael-jordan-57', expect: { minCount: 10, kinds: ['catalog_item', 'price_observation', 'sale'], requiredFields: ['attributes.set', 'attributes.number'] } },17];1819for (const t of targets) {20  const connector = await loadConnector(t.connector);21  const ctx = createCrawlContext({ router, meta: connector.meta, options: { mode: 'probe', limit: 1 }, log: childLogger({ connector: t.connector, level: 'warn' }) });22  const [rec] = (await connector.lookup?.(t.url, ctx)) ?? [];23  if (!rec) {24    console.error(`✗ ${t.name}: no record (${ctx.anomalies.join('; ')})`);25    continue;26  }27  const p = rec.payload as ProductPayload;28  // keep at most 3 rows per tab to stay small29  const perTab = new Map<string, number>();30  p.sales = p.sales.filter((s) => {31    const n = (perTab.get(s.tab) ?? 0) + 1;32    perTab.set(s.tab, n);33    return n <= 3;34  });35  saveFixture(t.connector, t.name, {36    raw: { url: rec.url, externalId: rec.externalId ?? null, kind: rec.kind, engine: rec.engine, fetchedAt: rec.fetchedAt ?? new Date(), payload: p },37    expect: t.expect,38    note: `Live capture of ${t.url} (sales trimmed to 3 rows per grade tab).`,39  });40  const out = await connector.normalize({ ...rec, externalId: rec.externalId ?? null, fetchedAt: rec.fetchedAt ?? new Date() });41  const cat = out.find((r) => r.kind === 'catalog_item');42  console.log(`✓ ${t.name}: ${out.length} records; setRef=${JSON.stringify(p.setRef)} cardRef=${JSON.stringify(p.cardRef)} attrs=${cat && 'attributes' in cat ? JSON.stringify({ set: cat.attributes.set, setCode: cat.attributes.setCode, number: cat.attributes.number, name: cat.attributes.name, variant: cat.attributes.variant, ids: cat.attributes.identifiers }) : ''}`);43  for (const r of out.filter((x) => x.kind === 'sale').slice(0, 3)) if (r.kind === 'sale') console.log(`    sale ${r.saleDate.toISOString().slice(0, 10)} ${r.price} ${r.currency} grader=${r.grade.grader} grade=${r.grade.grade} :: ${r.rawTitle.slice(0, 70)}`);44  for (const r of out.filter((x) => x.kind === 'price_observation').slice(0, 4)) if (r.kind === 'price_observation') console.log(`    guide ${r.condition.conditionRaw} = ${r.price}`);45}46