/** * Live fixture capture for the g10 connectors (real router + crawl context, trimmed payloads, trimmed HTML * snapshots for parser tests). Usage: pnpm tsx connectors/api/_g10-lib/capture.ts [id…] * Gated connectors (ebay-browse, etsy, trademe) are captured only when their env vars are present. */ import { readFileSync } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { createCrawlContext, createRouter, type ConnectorMeta, type RareIndexConnector, type RawRecordInput } from '@rareindex/connectors'; import { saveFixture, type Fixture } from '@rareindex/connectors/testing'; import { childLogger } from '@rareindex/shared'; import { localMeta } from '../_lib/local-meta.js'; const here = path.dirname(fileURLToPath(import.meta.url)); const apiDir = path.resolve(here, '..'); const router = createRouter({}); const ids = process.argv.slice(2).length ? process.argv.slice(2) : ['whisky-auction', 'chairish', 'pamono', 'peyton-street-pens', '1stdibs', 'chatterley-luxuries']; async function load(id: string): Promise<{ connector: RareIndexConnector; meta: ConnectorMeta; mod: Record }> { const meta = localMeta(JSON.parse(readFileSync(path.join(apiDir, id, 'meta.json'), 'utf8'))); const mod = (await import(path.join(apiDir, id, 'index.ts'))) as Record & { default: (m: ConnectorMeta) => RareIndexConnector }; return { connector: mod.default(meta), meta, mod }; } function ctxFor(meta: ConnectorMeta, seeds?: string[], limit = 2) { return createCrawlContext({ router, meta, options: { mode: 'probe', limit, ...(seeds ? { seeds } : {}) }, log: childLogger({ connector: meta.id, level: 'warn' }) }); } async function firstRaw(connector: RareIndexConnector, meta: ConnectorMeta, seeds?: string[]): Promise { for await (const raw of connector.crawl(ctxFor(meta, seeds, 1))) return raw; return null; } function fixture(raw: RawRecordInput, expect: Fixture['expect'], note: string): Fixture { return { raw: { url: raw.url, externalId: raw.externalId ?? null, kind: raw.kind, engine: raw.engine, payload: raw.payload, fetchedAt: raw.fetchedAt ?? new Date() }, expect, note }; } const today = new Date().toISOString().slice(0, 10); for (const id of ids) { const { connector, meta, mod } = await load(id); console.log(`[capture] ${id}`); try { if (id === 'whisky-auction') { const parseAuctionList = mod.parseAuctionList as (h: string) => Array<{ id: string }>; const parseLotPage = mod.parseLotPage as (h: string) => { lots: unknown[]; total: number | null; auctionTitle: string | null }; const trimLotHtml = mod.trimLotHtml as (h: string, n?: number) => string; const pageUrl = mod.pageUrl as (a: string, s: { category: string; slug: 'whisky' | 'rum' | 'cognac' | 'wine' }, i: number, n: number) => string; const ctx = ctxFor(meta); const list = await ctx.fetch('https://whisky.auction/auctions', { engines: ['api'], responseType: 'text', minQuality: 0 }); const entries = parseAuctionList(list.html!); // keep the first 4 auction entries of the real list page as the HTML snapshot const m = list.html!.match(/