← back to Nationalrealestate
src/ingest/listings/types.ts
40 lines
/**
* M-B3 firm↔listing pilot types.
*
* HARD LEGAL RAIL: a ListingFacts row carries FACTS ONLY. No photo URLs, no agent
* remarks, no marketing description — those are copyrighted and must never be stored.
* Every row keeps `url` (the broker's own listing page) so we link out, never present
* a listing as ours.
*/
export interface ListingFacts {
sourceId: string; // stable per-listing id from the source (URL token / MLS#)
url: string; // canonical listing URL on the broker's own site
address: string | null;
city: string | null;
state: string | null; // 2-letter, used for firm + region crosswalk
price: number | null;
beds: number | null;
baths: number | null;
sqft: number | null;
lat: number | null;
lng: number | null;
status: string | null; // 'active' | 'sold' | 'pending' | null
}
export interface ListingAdapter {
/** stored in listing.source and ingest_runs.source */
source: string;
/** host used to resolve firm_id via firm_site.url domain match */
host: string;
/** robots.txt path the adapter reads listings under — used for the honor check */
listingsPathHint: string;
/**
* Yield up to `cap` listing PDP URLs from the site's public sitemap/index.
* Each URL must be robots-allowed (the engine re-checks before fetch).
*/
discover(cap: number): Promise<string[]>;
/** Fetch one listing URL and extract FACTS ONLY (JSON-LD). null = unparseable, skip. */
parse(url: string, html: string): ListingFacts | null;
}