/** * Live smoke + fixture capture for the g8 auction connectors (plain HTTP, no paid engines unless the * connector's meta says so). Runs the real router/context in probe mode and saves the first raw records. * Usage: pnpm tsx connectors/api/_g8-auctions-eu-apac-lib/capture.ts [--save] [--limit N] [--name prefix] */ import { readFileSync } from 'node:fs'; import path from 'node:path'; import { ConnectorMetaSchema, DomainsFileSchema, DOMAINS_PATH, createCrawlContext, createRouter, setDomains, type RareIndexConnector } from '@rareindex/connectors'; import { saveFixture } from '@rareindex/connectors/testing'; import { childLogger } from '@rareindex/shared'; // Load domains.json + only this group's fragment (other groups' work-in-progress fragments may be invalid while agents run in parallel). { const base = DomainsFileSchema.parse(JSON.parse(readFileSync(DOMAINS_PATH, 'utf8'))); const frag = JSON.parse(readFileSync(path.resolve(process.cwd(), 'connectors/domains.d/g8-auctions-eu-apac.json'), 'utf8')) as { domains: Record }; setDomains({ ...base, domains: { ...base.domains, ...(frag.domains as typeof base.domains) } }); } const [id, ...rest] = process.argv.slice(2); if (!id) throw new Error('usage: capture.ts [--save] [--limit N] [--name prefix]'); const save = rest.includes('--save'); const limit = rest.includes('--limit') ? Number(rest[rest.indexOf('--limit') + 1]) : 2; const prefix = rest.includes('--name') ? rest[rest.indexOf('--name') + 1]! : 'sale'; const dir = path.resolve(process.cwd(), 'connectors/api', id); const meta = ConnectorMetaSchema.parse(JSON.parse(readFileSync(path.join(dir, 'meta.json'), 'utf8'))); const mod = (await import(path.join(dir, 'index.ts'))) as { default: (m: typeof meta) => RareIndexConnector }; const connector = mod.default(meta); 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: id, level: 'warn' }) }); let i = 0; const started = Date.now(); for await (const raw of connector.crawl(ctx)) { const out = await connector.normalize({ ...raw, externalId: raw.externalId ?? null, fetchedAt: raw.fetchedAt ?? new Date() }); const sales = out.filter((r) => r.kind === 'sale'); const lots = out.filter((r) => r.kind === 'auction_lot'); const currencies = [...new Set(sales.map((r) => (r.kind === 'sale' ? r.currency : '')))]; console.log(`raw ${raw.externalId} (${raw.engine}, ${raw.httpStatus}) → ${out.length} records: ${sales.length} sales ${JSON.stringify(currencies)}, ${lots.length} auction_lot`); for (const r of out.slice(0, 2)) console.log(' ', JSON.stringify(r).slice(0, 360)); if (save) { const name = `${prefix}-${String(raw.externalId ?? i).replace(/[^a-z0-9]+/gi, '-').toLowerCase()}`; saveFixture(meta.id, name, { raw: { url: raw.url, externalId: raw.externalId ?? null, kind: raw.kind, engine: raw.engine, fetchedAt: raw.fetchedAt ?? new Date(), payload: raw.payload }, expect: { minCount: Math.max(1, Math.min(5, out.length)), kinds: ['sale', 'auction_lot'], requiredFields: ['rawTitle', 'attributes.categorySlug'] }, note: `Live capture via the real router (engine ${raw.engine}) from ${raw.url} on ${new Date().toISOString().slice(0, 10)}; payload trimmed by the connector's own parser.`, }); console.log(` saved fixture ${name}`); } i++; } console.log(`done raw=${i} ms=${Date.now() - started} engineStats=${JSON.stringify(ctx.engineStats)} anomalies=${JSON.stringify(ctx.anomalies)}`);