first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,77 @@
# `range/range.ts`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`RangeClient` is the heart of the package — the k-anonymity Pwned
Passwords client. `checkPassword(password, opts?)` hashes the input,
slices the 5-hex prefix, dispatches `GET https://api.pwnedpasswords.com/range/<prefix>`
with the mandatory `Add-Padding: true` header, scans the streamed
response for a matching 35-char suffix, and returns
`{ pwned, count }`. It owns the strict / off framing, the NTLM mode
opt-in, the differential-privacy decoy fan-out, the AbortSignal
threading, and the OTel emission.
## Features
- Mandatory `Add-Padding: true` on every fetch — including every
differential-privacy decoy.
- SHA-1 (default) and NTLM (caller-injected adapter) hash modes.
- Constant-time suffix compare on every parsed line.
- Streaming line parser (no `Response.text()` call ever).
- Differential-privacy decoy fan-out when `extraPrefixes > 0` — real
prefix's response is the only one consulted.
- AbortSignal threaded into fetch and the streaming parser.
- OTel histograms for duration; counters for outcome, errors,
throttling.
- Optional `assertOutgoingRequestSafe: true` runtime privacy gate.
## Usage: Basic Example
```ts
import { RangeClient } from "@suaveplan/hibp";
const client = new RangeClient();
const { pwned, count } = await client.checkPassword("hunter2");
```
## Usage: Advanced Example
```ts
import { RangeClient } from "@suaveplan/hibp";
const client = new RangeClient({
extraPrefixes: 3,
rng: myRng,
assertOutgoingRequestSafe: true,
strict: true,
});
const ac = new AbortController();
setTimeout(() => ac.abort(), 5_000);
const result = await client.checkPassword("hunter2", { signal: ac.signal });
```
## API Reference
- `CheckPasswordOptions` — `mode?`, `signal?`, `extraPrefixes?`.
- `RangeClientOptions` — `fetchFn`, `clock`, `rng`, `strict`,
`ntlmHash`, `extraPrefixes`, `assertOutgoingRequestSafe`.
- `new RangeClient(options?)`.
- `client.checkPassword(password, opts?): Promise<PwnedResult>`.
## Implementation Notes
The class is stateless beyond its constructor options — every public
method takes the password and a per-call options bag, so a single
`RangeClient` instance is safe to share across a process. Error paths
funnel through the metrics emission so dashboards see counters even
for failures the caller catches and retries. The `assertOutgoingRequestSafe`
gate constructs a `Request` to feed into `assertNoLeak`, then re-issues
the actual fetch with the same `RequestInit` so the privacy check
exercises the real outgoing bytes. The NTLM adapter is required at
runtime if `mode: "ntlm"` is ever passed; a missing adapter surfaces as
`HIBP.MALFORMED_RESPONSE` because the caller has misconfigured the
client rather than the server having misbehaved.