"""The dependency graph is CycloneDX's `dependencies` array: one entry per component that has
outgoing edges, `{"ref": <bom-ref>, "dependsOn": [<bom-ref>, ...]}`. A leaf component still gets
an entry with an empty `dependsOn` — the spec recommends this so a *missing* entry means "not
scanned", not "no dependencies".
"""


def build_dependency_graph(sbom: dict) -> dict[str, set[str]]:
    """Return `{ref: set(dependsOn)}` for every entry in `sbom["dependencies"]`, same shape —
    a ref whose `dependsOn` is `[]` is still a key, mapped to `set()`, not dropped.
    """
    ...


def reachable_from(graph: dict[str, set[str]], root: str) -> set[str]:
    """Every ref reachable from `root` by following edges any number of hops, **excluding**
    `root` itself — `requests -> mycorp-billing` is two hops from the application root and still
    belongs in the result. A ref with no outgoing edges, or missing from `graph` entirely
    (CycloneDX does not require a `dependencies` entry for a component nobody depends on further),
    simply contributes nothing more to the walk.
    """
    ...
