TypeScript 61.9%
HTML 37.2%
SQL 0.7%
1/**2 * Live fixture capture + smoke for the Auctionet connector (public JSON API, no key).3 * Usage: pnpm tsx connectors/api/auctionet/_capture.ts [--save] [--smoke]4 */5import { ConnectorMetaSchema, createCrawlContext, createRouter } from '@rareindex/connectors';6import { saveFixture } from '@rareindex/connectors/testing';7import metaJson from './meta.json' with { type: 'json' };8import createConnector, { parseItemsPage } from './index.js';910const UA = { 'user-agent': 'RareIndexBot/0.1 (+https://www.rareindex.io/about/data)', accept: 'application/json' };11const save = process.argv.includes('--save');12const smoke = process.argv.includes('--smoke');13const meta = ConnectorMetaSchema.parse(metaJson);14const connector = createConnector(meta);1516const captures: Array<{ name: string; url: string; slice: { categoryId: number | null; countryCode: string | null }; page: number; note: string; expect: Record<string, unknown> }> = [17 { name: 'ended-page-1', url: 'https://auctionet.com/api/v2/items.json?is=ended&per_page=25&page=1', slice: { categoryId: null, countryCode: null }, page: 1, note: 'Live capture of the public items.json endpoint (is=ended, 25 most recently closed lots, mixed houses/currencies). Trimmed with trimItem().', expect: { minCount: 10, kinds: ['sale', 'auction_lot'], requiredFields: ['attributes.categorySlug', 'attributes.identifiers.auctionet_item_id'] } },18 { name: 'wristwatches-de-eur', url: 'https://auctionet.com/api/v2/items.json?is=ended&per_page=25&page=1&category_id=15&country_code=DE', slice: { categoryId: 15, countryCode: 'DE' }, page: 1, note: 'Live capture: backfill slice Wristwatches (category 15) × Germany (EUR, German titles) — exercises watch brand/reference parsing and Konvolut bundle detection.', expect: { minCount: 5, kinds: ['sale', 'auction_lot'] } },19 { name: 'search-with-unsold', url: 'https://auctionet.com/api/v2/items.json?is=ended&per_page=25&q=1955+SEK', slice: { categoryId: null, countryCode: null }, page: 1, note: 'Live capture of an ended search page containing unsold lots (state=unsold, no bids) → auction_lot records with status ended; edge case for the sold/unsold split.', expect: { minCount: 3, kinds: ['sale', 'auction_lot'] } },20];2122for (const c of captures) {23 const res = await fetch(c.url, { headers: UA });24 if (!res.ok) throw new Error(`${c.url}: HTTP ${res.status}`);25 const json = await res.json();26 const payload = parseItemsPage(json, c.url, c.page, c.slice);27 if (!payload) throw new Error(`parse failed ${c.url}`);28 const raw = { url: c.url, externalId: `capture:${c.name}`, kind: 'sale' as const, engine: 'api' as const, fetchedAt: new Date(), payload };29 const out = await connector.normalize(raw);30 const sales = out.filter((r) => r.kind === 'sale');31 const lots = out.filter((r) => r.kind === 'auction_lot');32 console.log(`${c.name}: ${payload.items.length} items → ${out.length} records (${sales.length} sales, ${lots.length} auction_lot); states=${JSON.stringify([...new Set(payload.items.map((i) => i.state))])} currencies=${JSON.stringify([...new Set(payload.items.map((i) => i.currency))])}`);33 for (const r of out.slice(0, 2)) console.log(' ', JSON.stringify(r).slice(0, 300));34 if (save) saveFixture(meta.id, c.name, { raw, expect: c.expect as never, note: c.note });35 await new Promise((r) => setTimeout(r, 1500));36}3738if (smoke) {39 const router = createRouter({});40 const ctx = createCrawlContext({ router, meta, options: { mode: 'probe', limit: 2 } });41 let raws = 0;42 let recs = 0;43 for await (const raw of connector.crawl(ctx)) {44 raws++;45 const out = await connector.normalize({ ...raw, externalId: raw.externalId ?? null, fetchedAt: raw.fetchedAt ?? new Date() });46 recs += out.length;47 }48 console.log(`smoke: raw=${raws} normalized=${recs} engineStats=${JSON.stringify(ctx.engineStats)} anomalies=${JSON.stringify(ctx.anomalies)}`);49}50