101 lines
3.2 KiB
TypeScript
101 lines
3.2 KiB
TypeScript
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<ListingDiscoveryResult> {
|
|
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<SourceDiscoveryOutcome> => {
|
|
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
|
|
};
|
|
}
|