accept-ch-parse
v0.1.2
Published
Zero-dependency parser for the HTTP Accept-CH Client Hints header (RFC 8942) for Node.js (>=18, ESM+CJS) and Python (>=3.11)
Maintainers
Readme
accept-ch-parse
Zero-dependency parser for the HTTP Accept-CH Client Hints header (RFC 8942) — for Node.js and Python.
- Parses
Accept-CHinto a list of tokens with quality (q) values - Sorts output by descending
q(stable: ties keep input order) - Token names are case-sensitive per RFC 8942
- Forward-compatible: unregistered tokens are still parsed
- Empty / whitespace-only headers yield
[](not an error) - Zero runtime dependencies on both platforms
Why
HTTP Client Hints (RFC 8942) let servers request device / connection information via the Accept-CH response header. Example:
Accept-CH: Width, Viewport-Width, Content-DPR;q=0.8When this library was first published (2026-08-08), no zero-dependency
parser for Accept-CH existed on either registry — the names were free:
- npm
accept-ch-parse— confirmed 404 at build time (2026-08-08) - PyPI
accept-ch-parse— confirmed 404 at build time (2026-08-08)
This library fills that gap. It is small, dual-language, has zero runtime
deps, and gets out of your way. If you are reading this README after
2026-08-08 and the names are no longer 404, someone has published a
typosquat or fork — please verify the package you install matches the
GitHub repo at prasad-a-abhishek/accept-ch-parse before depending on it.
Install
Node.js (≥18)
npm install accept-ch-parseESM:
import { parseAcceptCH, serializeAcceptCH, getAcceptCHTokens } from 'accept-ch-parse';CJS:
const { parseAcceptCH, serializeAcceptCH, getAcceptCHTokens } = require('accept-ch-parse');Python (≥3.11)
pip install accept-ch-parsefrom accept_ch_parse import parse_accept_ch, serialize_accept_ch, get_accept_ch_tokensUsage
Basic parse
import { parseAcceptCH } from 'accept-ch-parse';
parseAcceptCH('Viewport-Width, Width, Content-DPR');
// → [
// { token: 'Viewport-Width', q: 1.0, raw: 'Viewport-Width' },
// { token: 'Width', q: 1.0, raw: 'Width' },
// { token: 'Content-DPR', q: 1.0, raw: 'Content-DPR' }
// ]from accept_ch_parse import parse_accept_ch
parse_accept_ch('Viewport-Width, Width, Content-DPR')
# → [AcceptCHToken(token='Viewport-Width', q=1.0, raw='Viewport-Width'),
# AcceptCHToken(token='Width', q=1.0, raw='Width'),
# AcceptCHToken(token='Content-DPR', q=1.0, raw='Content-DPR')]Quality weights — sorted by descending q
parseAcceptCH('Width;q=0.5, Viewport-Width, Content-DPR;q=0.8');
// → [
// { token: 'Viewport-Width', q: 1.0, raw: 'Viewport-Width' },
// { token: 'Content-DPR', q: 0.8, raw: 'Content-DPR;q=0.8' },
// { token: 'Width', q: 0.5, raw: 'Width;q=0.5' }
// ]Serialize
import { serializeAcceptCH } from 'accept-ch-parse';
serializeAcceptCH([
{ token: 'Viewport-Width', q: 1.0, raw: 'Viewport-Width' },
{ token: 'Width', q: 0.5, raw: 'Width' }
]);
// → 'Viewport-Width, Width;q=0.5'Just the token names
import { getAcceptCHTokens } from 'accept-ch-parse';
getAcceptCHTokens('Width;q=0.5, Viewport-Width, Content-DPR;q=0.8');
// → ['Viewport-Width', 'Content-DPR', 'Width'] (already q-sorted)Recognized Client Hints
The parser is forward-compatible — any syntactically valid token is accepted. The eight registered per RFC 8942 are:
| Token | Meaning |
|---|---|
| Viewport-Width | Layout viewport width in CSS pixels |
| Width | Resource width in CSS pixels |
| Content-DPR | Resource's intrinsic DPR (paired with Content-Length) |
| DPR | Current device pixel ratio |
| Device-Memory | Approximate device RAM in GiB |
| ECT | Effective connection type (slow-2g, 2g, 3g, 4g) |
| RTT | Round-trip time in ms |
| Downlink | Downlink speed in Mbps |
API
Node.js
parseAcceptCH(header: string): AcceptCHToken[]
serializeAcceptCH(tokens: AcceptCHToken[]): string
getAcceptCHTokens(header: string): string[]
interface AcceptCHToken {
token: string; // case-sensitive
q: number; // 0..1, default 1.0
raw: string; // exact substring parsed (trimmed)
}Throws TypeError if header is not a string (including null / undefined).
Python
parse_accept_ch(header: str) -> list[AcceptCHToken]
serialize_accept_ch(tokens: list[AcceptCHToken]) -> str
get_accept_ch_tokens(header: str) -> list[str]
class AcceptCHToken(NamedTuple):
token: str
q: float
raw: strRaises TypeError if header is not a string (including None).
Errors
| Input | Behaviour |
|---|---|
| '' / ' ' | Returns [] |
| null / undefined / non-string (Node) | TypeError |
| None / non-str (Python) | TypeError |
| Width;q=0.5 | Parsed; q = 0.5 |
| Width;Q=0.5 | Parsed; param names are case-insensitive (RFC 7230) |
| Width;q=0, X | Width sorts last; q clamped to [0, 1] |
| Width;q=2.5 | Clamped to 1.0 |
| Width;q=-0.5 | Clamped to 0.0 |
| Width;q= (malformed) | Treated as q = 1.0 (forgiving) |
| Width,,X | Empty entry dropped; Width and X parsed |
| viewport-width | Parsed as-is (case-sensitive, NOT folded to Viewport-Width) |
Run the tests
# Node
npm install
npm test
# Python
pip install -e .[test]
python -m pytestThe full suite is 60 Node.js tests + 61 Python tests, all green.
Limitations / Non-goals
Out of scope for this library (intentionally, per spec):
- Sending
Accept-CHor any other HTTP header — this library parses only. Save-Dataheader parsing — different header, different spec.Varyheader parsing — different header, different semantics.- A token registry — unregistered tokens are still accepted (forward compatibility). If you need to reject unknown tokens, do that at a higher layer.
- Quality value arithmetic (sum, threshold checks, etc.) — just parse and sort.
- Browser or HTTP client implementation.
References
- RFC 8942 — HTTP Client Hints
- MDN — Accept-CH
- MDN — Client Hints infrastructure
- Chrome Platform Status — Client Hints
- Web.dev — Responsive images with Client Hints
License
MIT — see LICENSE.
