/** * Shared live smoke/capture runner for the auction-house connectors. Loads meta.json + index.ts from * the connector folder directly (works before the registry is rebuilt). * Usage: set -a; . ./.env; set +a; pnpm tsx connectors///_smoke.ts [limit] */ import { readFileSync } from 'node:fs'; import path from 'node:path'; import { ConnectorMetaSchema, createCrawlContext, createRouter, type ConnectorMeta, type RareIndexConnector } from '@rareindex/connectors'; import { saveFixture } from '@rareindex/connectors/testing'; import { childLogger } from '@rareindex/shared'; export async function loadLocal(dir: string): Promise<{ meta: ConnectorMeta; connector: RareIndexConnector }> { const raw = JSON.parse(readFileSync(path.join(dir, 'meta.json'), 'utf8')); const meta = ConnectorMetaSchema.parse(raw); const mod = (await import(path.join(dir, 'index.ts'))) as { default: (m: ConnectorMeta) => RareIndexConnector }; return { meta, connector: mod.default(meta) }; } export async function runSmoke(dir: string, limit = Number(process.argv[2] ?? 2)): Promise { const { meta, connector } = await loadLocal(dir); const router = createRouter({ firecrawlApiKey: process.env.FIRECRAWL_API_KEY, scrapflyApiKey: process.env.SCRAPFLY_API_KEY }); const ctx = createCrawlContext({ router, meta, options: { mode: 'probe', limit }, log: childLogger({ connector: meta.id, smoke: true }) }); let raws = 0; let total = 0; const samples: unknown[] = []; for await (const raw of connector.crawl(ctx)) { raws++; const records = await connector.normalize({ ...raw, externalId: raw.externalId ?? null, fetchedAt: raw.fetchedAt ?? new Date() }); total += records.length; console.log(`raw ${raw.url} → ${records.length} records`); for (const r of records.slice(0, 3)) samples.push(r); if (raws >= limit) break; } for (const s of samples.slice(0, 3)) console.log(JSON.stringify(s, null, 1)); console.log(JSON.stringify({ raws, totalNormalized: total, engineStats: ctx.engineStats, anomalies: ctx.anomalies })); } /** Capture the first probe raw record as a fixture (item lists trimmed to `keep`). */ export async function captureFixture(dir: string, name: string, keep = 4): Promise { const { meta, connector } = await loadLocal(dir); const router = createRouter({ firecrawlApiKey: process.env.FIRECRAWL_API_KEY, scrapflyApiKey: process.env.SCRAPFLY_API_KEY }); const ctx = createCrawlContext({ router, meta, options: { mode: 'probe', limit: 1 }, log: childLogger({ connector: meta.id, capture: true }) }); for await (const raw of connector.crawl(ctx)) { const payload = raw.payload as Record; for (const k of ['items', 'lots', 'cards', 'listings']) { if (Array.isArray(payload[k])) payload[k] = (payload[k] as unknown[]).slice(0, keep); } const fetchedAt = raw.fetchedAt ?? new Date(); const records = await connector.normalize({ ...raw, externalId: raw.externalId ?? null, fetchedAt }); saveFixture(meta.id, name, { raw: { url: raw.url, externalId: raw.externalId ?? null, kind: raw.kind, engine: raw.engine, fetchedAt, payload }, expect: { count: records.length, kinds: [...new Set(records.map((r) => r.kind))] }, note: `Captured live from ${raw.url} (lists trimmed to ${keep}).`, }); console.log(`saved data/fixtures/${meta.id}/${name}.json (${records.length} records)`); return; } throw new Error('crawl yielded nothing'); }