/** * The discovery orchestrator: one domain in, an editable {@link BrandProfile} * out (M4). * * It is the only stateful piece of M4 - it fetches (M2), extracts (pure), * makes the one guarded competitor judge call, merges with any persisted * profile (user edits win), and writes `profile.json`. Everything I/O is * injected so the whole flow unit-tests with no network and no disk. */ import { CostGuard } from '../costs.js'; import { type Fetcher } from '../fetcher/index.js'; import type { BrandProfile, JudgeClient } from '../types.js'; import { type ProfileFs } from './persist.js'; /** Injected collaborators. Only `fetcher` is required. */ export interface DiscoverDeps { fetcher: Fetcher; /** Judge for the competitor call; omit to skip competitor discovery. */ judge?: JudgeClient; /** Cost guard for the setup budget; defaults to an uncapped guard. */ guard?: CostGuard; /** Profile persistence; defaults to the real node fs. */ fs?: ProfileFs; /** Clock, injected for deterministic `generatedAt`. */ now?: () => string; } export interface DiscoverOptions { /** Where `profile.json` lives (from `resolveStateDir`). */ stateDir: string; /** Re-discover even if a profile exists; user fields still win. */ refresh?: boolean; /** `--brand`: take the no-fetch degraded path. */ brand?: string; /** `--category`: no-fetch degraded path (with or without `--brand`). */ category?: string; /** Max sitemap pages to sample in addition to the homepage (default 5). */ samplePages?: number; /** Persist the result to disk (default true). */ persist?: boolean; } export interface DiscoverResult { profile: BrandProfile; /** Path the profile was written to, if persisted. */ path?: string; /** True when an existing profile was returned without re-discovery. */ fromCache?: boolean; /** * Why this discovery could not deliver everything it normally does - a failed * fetch, or a skipped/failed competitor call. Absent when nothing needs * saying. * * EVERY note here carries an origin prefix, because a SEPARATE guarded judge * call - query generation's, in `resolveQueries` - produces notes from the * same templates (`judge error: ${message}`, `no judge configured`) and can * fail identically against the same outage. Unprefixed, two independent * failures read as one ambiguous line, and `dedupeNotes` collapses the * byte-identical ones into a single line outright. * * The prefix names the phase that actually failed: `Competitor discovery: ` * for the judge call, `Discovery: ` for a fetch failure, which happens before * (and instead of) that call and is not competitor-specific. */ competitorNote?: string; } /** Discover (or reuse) a brand profile for `domain`. */ export declare function discover(domain: string, deps: DiscoverDeps, opts: DiscoverOptions): Promise;