import type { ConnectorMeta, CrawlContext } from '@rareindex/connectors'; import { parseBidsquareEvents, popCultureCategory } from '../_memorabilia-lib/index.js'; import { BidsquareHouseConnector } from '../scp-auctions/index.js'; /** * Hake's Auctions (Bidsquare platform, same HTML as SCP). Hake's hides its past-auction list * (/auctions/past returns "No Auction Available"), so this connector works in two phases: * upcoming catalogs are crawled for auction lots and remembered in the cursor; once an event's * catalog reports status 'past' the same pages yield realized "Sold for" prices. */ export class HakesConnector extends BidsquareHouseConnector { protected override async listEvents(ctx: CrawlContext): Promise> { const known = (Array.isArray(ctx.options.cursor?.knownEvents) ? (ctx.options.cursor!.knownEvents as Array<{ id: string; name: string; url: string }>) : []).filter((e) => e && e.id && e.url); const up = await this.fetchHtml(ctx, `${this.opts.base}/auctions`); const upcoming = up.html ? parseBidsquareEvents(up.html) : []; const upcomingIds = new Set(upcoming.map((e) => e.id)); // Events seen before that are no longer upcoming are revisited as past (their catalog reports the status). const past = known.filter((e) => !upcomingIds.has(e.id)).map((e) => ({ ...e, status: 'past' })); const merged = [...known.filter((e) => upcomingIds.has(e.id)), ...upcoming.filter((e) => !known.some((k) => k.id === e.id)).map((e) => ({ id: e.id, name: e.name, url: e.url }))]; await ctx.setCursor({ ...(ctx.options.cursor ?? {}), knownEvents: merged.slice(-60) }); return [...past, ...upcoming.map((e) => ({ id: e.id, name: e.name, url: e.url, status: 'upcoming' }))]; } } export default (meta: ConnectorMeta) => new HakesConnector(meta, { base: 'https://www.hakes.com', house: "Hake's Auctions", category: popCultureCategory, buyerPremiumIncluded: null, idKey: 'hakes_item_id', location: 'US', });