# onym

Check whether a name is available on package registries (crates.io, PyPI, npm), GitHub, and common TLDs — with a qualitative classification of the taken results so you can tell real conflicts from squatters.

```
$ onym check tokio
┌───────────┬─────────────────┬──────────────────────────────────────────────────┐
│ source    │ status          │ detail                                           │
├───────────┼─────────────────┼──────────────────────────────────────────────────┤
│ crates.io ┆ TAKEN · ACTIVE  ┆ v1.49.0 · 312M downloads · async runtime…       │
│ pypi      ┆ TAKEN · DEAD    ┆ v0.2.0 · released=2017-07-23 · …                │
│ npm       ┆ TAKEN · DEAD    ┆ v0.1.2 · released=2022-06-27 · …                │
│ github    ┆ TAKEN · UNKNOWN ┆ user or organization name is registered         │
│ .io       ┆ TAKEN · UNKNOWN ┆ NameCheap, Inc. · tokio.io is registered        │
└───────────┴─────────────────┴──────────────────────────────────────────────────┘
0/5 free · CONTESTED — active project(s) hold this name
```

## What makes it different

Most availability checkers tell you "taken" and stop. `onym` classifies each taken result:

- **active** — recently downloaded, real project
- **placeholder** — `0.0.x` versioning, near-zero downloads (squat or stub)
- **dead** — abandoned (no release in 2+ years, near-zero recent traffic)
- **unknown** — not enough signal

A `crates.io` hit on a placeholder is materially different from a hit on `tokio`. `onym` reflects that in both the output and the exit code.

## Install

```sh
cargo install onym
```

Or from a checkout:

```sh
cargo install --path .
```

## Usage

```sh
onym check <name>              # auto: table on a TTY, JSON when piped
onym check <name> -o table
onym check <name> -o markdown
onym check <name> --json
onym check <name> --tld io --tld dev   # override default TLDs
onym check foo bar baz                 # multiple names in one run
onym schema                            # machine-readable CLI description
```

### Output

| format     | when                            |
|------------|---------------------------------|
| `auto`     | table on TTY, JSON when piped   |
| `json`     | `{"reports":[…]}` to stdout     |
| `table`    | comfy-table rendering           |
| `markdown` | GitHub-flavored table           |

Diagnostics go to stderr as a JSON envelope:
`{"error":{"kind":"...","message":"...","retryable":bool}}`.

### Exit codes

| code | meaning                                                |
|------|--------------------------------------------------------|
| 0    | all sources free                                       |
| 1    | at least one taken, none classified active             |
| 2    | at least one ACTIVE project holds the name             |
| 3    | invalid input                                          |
| 4    | network / timeout / rate-limit / upstream              |
| 5    | parse / internal                                       |

## Sources

- **crates.io** — `404` = free; `200` returns full crate metadata used for classification.
- **PyPI** — `404` = free; `200` returns release metadata.
- **npm** — `404` = free; `200` returns dist-tags + modification time.
- **GitHub** — `HEAD /<name>` to avoid burning API quota.
- **TLDs** — authoritative RDAP per registry, with `rdap.org` as a last-resort fallback. Default TLDs: `.io`, `.dev`, `.com`, `.app`.

## Input rules

Names must match `[A-Za-z0-9._-]`, be 1–100 characters, and may not start with `.` or `-`. Anything else exits with code 3 and a structured error.

## Library

`onym` ships as a library too. `check_name` (batch) and `check_name_stream` (callback per source) are generic over a `Transport` trait, so the same source adapters can run in different async runtimes.

## License

MIT — see [LICENSE](LICENSE).
