# `discover/discover.ts` _(no submodules)_ ## Purpose Implements the filesystem walker that produces a validated `SuavePlanManifest` from a populated `packages////package.json` tree. Both the synchronous `discoverPackages` and the async `discoverPackagesIter` live here. The module is the only server-tier surface in `@suaveplan/overview`; it ships under the `./discover` sub-path export so universal consumers can avoid the `node:fs` / `node:path` imports entirely. Discovery is straightforward: walk three directory levels deep, read each `package.json`, infer its tier, collect workspace deps, validate against `PackageRecordSchema`, then aggregate the resulting rows into a manifest. ## Features - Synchronous `discoverPackages` returning a fully-validated manifest - Async `discoverPackagesIter` honouring `AbortSignal` - `onError: "throw" | "skip"` for graceful handling of malformed `package.json` files - Optional `tierHints` extending `SERVER_DEP_HINTS` - Emits `overview.packages_discovered_total`, `overview.records_rejected_total{reason}`, and `overview.discover_duration_ms` - Built-in roll-up so the manifest carries `plugins`, `categories`, and `tierCounts` ready for consumption ## Usage: Basic ```ts import { discoverPackages } from "@suaveplan/overview/discover"; const manifest = discoverPackages({ root: process.cwd() }); console.log(`Found ${manifest.packages.length} packages.`); ``` ## Usage: Advanced ```ts import { discoverPackagesIter } from "@suaveplan/overview/discover"; const controller = new AbortController(); setTimeout(() => controller.abort(), 5_000); try { for await (const r of discoverPackagesIter({ root: "/repo", signal: controller.signal, onError: "skip", tierHints: new Set(["custom-native-binding"]), })) { process.stdout.write(`${r.name}\n`); } } catch (err) { // OverviewError with code OVERVIEW.DISCOVERY_ABORTED when the signal fired. } ``` `onError: "skip"` is the right mode for tooling that should never abort on a single bad package.json (e.g. a long-running watcher); `"throw"` is right for one-shot CLIs that prefer a hard fail on corruption. ## API Reference - `discoverPackages(options: DiscoverOptions): SuavePlanManifest` - `discoverPackagesIter(options: AsyncDiscoverOptions): AsyncGenerator` - `interface DiscoverOptions { root, onError?, tierHints? }` - `interface AsyncDiscoverOptions extends DiscoverOptions { signal? }` - `type DiscoveryErrorMode = "throw" | "skip"` ## Implementation Notes The walker enforces exactly three directory levels (category → plugin → package). Anything shallower or deeper is silently ignored — partial-migration trees are common and a permissive walker keeps the tooling robust. `existsSync` + `statSync` are used to filter non-directories and packages without a `package.json`; both are cheap on a populated tree because the OS will have cached the directory listings. The histogram observation uses `performance.now()` when available (it always is in Node 20+/Bun) and falls back to `Date.now()` otherwise.