import { discoverHarListing } from "../../web-automation/scripts/har-discover.js"; import { discoverZillowListing } from "../../web-automation/scripts/zillow-discover.js"; import { TimeoutError, withTimeout } from "./async-timeout.js"; export interface ListingDiscoveryResult { attempts: string[]; zillowUrl: string | null; harUrl: string | null; } interface ListingDiscoveryDeps { timeoutMs?: number; zillowTimeoutMs?: number; harTimeoutMs?: number; discoverZillowListingFn?: typeof discoverZillowListing; discoverHarListingFn?: typeof discoverHarListing; } const DEFAULT_DISCOVERY_TIMEOUT_MS = Number( process.env.PROPERTY_ASSESSOR_DISCOVERY_TIMEOUT_MS || 20_000 ); const DEFAULT_ZILLOW_DISCOVERY_TIMEOUT_MS = Number( process.env.PROPERTY_ASSESSOR_ZILLOW_DISCOVERY_TIMEOUT_MS || 60_000 ); const DEFAULT_HAR_DISCOVERY_TIMEOUT_MS = Number( process.env.PROPERTY_ASSESSOR_HAR_DISCOVERY_TIMEOUT_MS || DEFAULT_DISCOVERY_TIMEOUT_MS ); interface SourceDiscoveryOutcome { source: "zillow" | "har"; url: string | null; attempts: string[]; } export async function discoverListingSources( address: string, deps: ListingDiscoveryDeps = {} ): Promise { const timeoutMs = deps.timeoutMs ?? DEFAULT_DISCOVERY_TIMEOUT_MS; const zillowTimeoutMs = deps.zillowTimeoutMs ?? (deps.timeoutMs != null ? timeoutMs : DEFAULT_ZILLOW_DISCOVERY_TIMEOUT_MS); const harTimeoutMs = deps.harTimeoutMs ?? (deps.timeoutMs != null ? timeoutMs : DEFAULT_HAR_DISCOVERY_TIMEOUT_MS); const discoverZillowListingFn = deps.discoverZillowListingFn || discoverZillowListing; const discoverHarListingFn = deps.discoverHarListingFn || discoverHarListing; const runSource = async ( source: "zillow" | "har", timeoutForSourceMs: number, operation: () => Promise<{ listingUrl: string | null; attempts: string[] }> ): Promise => { try { const result = await withTimeout(operation, { operationName: `${source === "zillow" ? "Zillow" : "HAR"} discovery`, timeoutMs: timeoutForSourceMs }); return { source, url: result.listingUrl, attempts: result.attempts }; } catch (error) { if (error instanceof TimeoutError) { return { source, url: null, attempts: [ `${source === "zillow" ? "Zillow" : "HAR"} discovery timed out after ${timeoutForSourceMs}ms.` ] }; } return { source, url: null, attempts: [ `${source === "zillow" ? "Zillow" : "HAR"} discovery failed: ${error instanceof Error ? error.message : String(error)}` ] }; } }; const zillowPromise = runSource("zillow", zillowTimeoutMs, () => discoverZillowListingFn(address, { timeoutMs: zillowTimeoutMs }) ); const harPromise = runSource("har", harTimeoutMs, () => discoverHarListingFn(address, { timeoutMs: harTimeoutMs }) ); const [zillowResult, harResult] = await Promise.all([zillowPromise, harPromise]); const attempts = [...zillowResult.attempts, ...harResult.attempts]; return { attempts, zillowUrl: zillowResult.url, harUrl: harResult.url }; }