Bound npmremote.FlightRegistry retention with an idle sweep and its caller

Context

npmremote.FlightRegistry caches one remote.SingleFlight per (namespace, repository). The shared instance is required: remote.SingleFlight's contract is one instance per remote repository, and a per-handler flight would multiply max_concurrent_fills_per_repo by the number of wired read routes.

Until the sweep below lands, an entry is retained for the process lifetime. Once it lands, an entry idle beyond the TTL is dropped and rebuilt as a new flight on its next request. Growth tracks the number of distinct remote npm repositories read since boot, not request volume.

What to build

  • An idle sweep on FlightRegistry that drops entries with no request inside the TTL window and reports how many it dropped.
  • Its caller: an in-process ticker with a lifecycle tied to the server. A River periodic job cannot serve this, because the map is per process and a distributed schedule sweeps one node.
  • A TTL configuration knob, with config.example.yaml and docs/dev/configuration-reference.md updated in the same MR.

The sweep and its caller land in the same MR, so neither side ships without the other.

Notes

Eviction is not required for credential or URL correctness. npmremote.LiveUpstreamConfigSource reads the row on every call, so a long-lived flight honours a URL change or a token rotation on the next request it builds.

A revived entry is a new flight, which costs one uncoalesced round of fills for that repository.