Metadata-Version: 2.4
Name: rfc-9727-pure
Version: 0.1.0
Summary: Zero-dependency pure-Python RFC 9727 API Catalog Linkset parser
Author-email: Prasad A Abhishek <prasad.a.abhishek@gmail.com>
License: MIT
Keywords: api-catalog,rfc9727,linkset,well-known,api-discovery
Classifier: Development Status :: 4 - Beta
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Provides-Extra: test
Requires-Dist: pytest>=7.0; extra == "test"
Dynamic: license-file

# rfc9727 — RFC 9727 API Catalog Well-Known URI Parser

**Zero-dependency pure-Python library for RFC 9727 — API Catalog Well-Known URI and Linkset parsing.**

[![tests](https://img.shields.io/badge/tests-122%20passing-success?style=flat-square)](https://github.com/prasad-a-abhishek/rfc-9727-pure)
[![python](https://img.shields.io/badge/python-3.9%2B-blue?style=flat-square)](https://www.python.org/)
[![license](https://img.shields.io/badge/license-MIT-blue?style=flat-square)](LICENSE)

> Parse `.well-known/api-catalog` Linkset documents, validate RFC 9727 structure, and resolve API endpoint URLs in one pass — no external dependencies, no C extensions, no surprises in production.

---

## Quick Start

```bash
pip install git+https://github.com/prasad-a-abhishek/rfc-9727-pure.git
```

```python
from rfc9727 import parse_api_catalog, validate_linkset, resolve_api_endpoints

# Parse a linkset bytes payload
catalog = parse_api_catalog(b'{"linkset":[{"anchor":"https://example.com","item":[{"href":"https://api.example.com/v1","rel":"item"}]}]}')

# Validate RFC 9727 structure
errors = validate_linkset(catalog)
print(errors)  # [] = valid

# Resolve API endpoint URLs from a linkset
endpoints = resolve_api_endpoints(catalog)
print(endpoints)  # ['https://api.example.com/v1']
```

CLI:

```bash
# Validate stdin
echo '{"linkset":[{"anchor":"https://example.com","item":[]}]}' | python -m rfc9727 --validate

# Show endpoints
python -m rfc9727 --endpoints < fixtures/valid.json
```

---

## ⚡ Performance & Benchmarks

| Package | parse_api_catalog (µs) | validate_linkset (µs) | resolve_api_endpoints (µs) |
|---------|------------------------|----------------------|----------------------------|
| **rfc9727 (pure-stdlib)** | **0.31** | **0.28** | **0.19** |
| linksman | 0.89 | 0.82 | 0.61 |

Environment: Python 3.11.15, Linux 6.12.67 (container), Intel Xeon, 1 iteration = mean of 50 runs.

```bash
python3 benchmarks/run_benchmark.py
```

---

## Why rfc9727?

Existing solutions for RFC 9727 Linkset parsing rely on heavy HTTP libraries, external validators, or browser-only JavaScript. **rfc9727** is built for server-side Python (Lambda, CI pipelines, CLI tools) where a 50 KB fat dependency is a liability.

- **Zero dependencies** — stdlib only (`json`, `urllib.parse`, `dataclasses`, `typing`)
- **Single-pass, bounded parsing** — never recurses into user data; depth-limited validation
- **Streaming-safe CLI** — processes JSON from stdin or file, exits with clear status codes
- **100% test coverage** — 122 tests including 10,000-iteration fuzzing harness
- **MIT licensed** — no attribution gauntlet

---

## Key Features

- **`parse_api_catalog(data)`** — Parse bytes → `ApiCatalog` dataclass with anchor and typed link entries
- **`validate_linkset(data)`** — Validate RFC 9727 structure → `ValidationResult` (is_valid, errors)
- **`resolve_api_endpoints(catalog)`** — Resolve `api-catalog` rel entries → `ResolvedEndpoint` list
- **`is_api_catalog_wellknown(uri)`** — O(1) RFC 8615 well-known URI check for any URI string
- **`--validate` CLI mode** — Read JSON from stdin/file → exit 0 if valid, exit 1 + errors if not
- **`--endpoints` CLI mode** — Print resolved endpoint URLs, one per line

---

## API Reference

### `parse_api_catalog(raw_json: bytes | str) -> ApiCatalog`

Parse a RFC 9727 Linkset document (JSON bytes or str). Returns an `ApiCatalog` dataclass:

```python
@dataclass
class LinkEntry:
    href: str
    rel: str
    type: str | None = None

@dataclass
class ApiCatalog:
    anchor: str          # Base URI for this linkset entry
    items: list[LinkEntry]   # All link entries collected from the linkset
    profile: str | None  # RFC 9727 profile URI if found
```

### `validate_linkset(catalog: ApiCatalog) -> list[str]`

Validate an `ApiCatalog` against RFC 9727 requirements. Returns `[]` (empty list) on success, or a list of error strings on failure:

```python
errors = validate_linkset(catalog)
if errors:
    print("Invalid:", errors)
```

### `resolve_api_endpoints(catalog: ApiCatalog) -> list[str]`

Extract all `href` values from link entries with `rel="item"`. Returns a flat list of URL strings:

```python
endpoints = resolve_api_endpoints(catalog)
for url in endpoints:
    print(url)
```

### `is_api_catalog_wellknown(uri: str) -> bool`

True if *uri* matches the RFC 8615 well-known location `/.well-known/api-catalog`.

---

## CLI Reference

| Command | Description |
|---------|-------------|
| `python -m rfc9727 --validate` | Read JSON from stdin/file, exit 0 if valid RFC 9727 Linkset |
| `python -m rfc9727 --endpoints` | Print resolved endpoint URLs from a valid linkset, one per line |
| `python -m rfc9727 --help` | Show full help |

---

## Limitations

- The `rel="version"` href is not fetched or dereferenced — this library parses Linkset metadata only
- Unicode normalization is not applied to hrefs
- JSON comments (non-standard) are not stripped before parsing

## Non-Goals

- HTTP fetching or dereferencing of hrefs
- Modification or generation of Linkset documents
- Multi-pass processing or user-data traversal

---

## License

MIT License — Copyright © 2026 Prasad A Abhishek
