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,66 @@
# `uri/format-uri`
## Purpose
Build a canonical `otpauth://` URI from an `OtpUri` value object. Round-trips
with `parseUri` for every valid input. Percent-encodes the label so colons
and spaces survive transport; rejects literal `:` in the label (the colon is
reserved for the issuer-prefix convention and an unencoded `:` would produce
an ambiguous URI).
## Features
- TOTP and HOTP variants.
- Label and issuer percent-encoded.
- Query parameters emitted in a stable order (`secret, issuer, algorithm,
digits, period | counter`).
- Strict-mode Zod boundary at the entry point.
## Usage: Basic Example
```ts
import { formatUri } from "@suaveplan/otp/uri";
const url = formatUri({
type: "totp",
label: "alice@example.com",
secret: "JBSWY3DPEHPK3PXP",
issuer: "Example",
});
```
## Usage: Advanced Example
```ts
import { formatUri } from "@suaveplan/otp/uri";
const url = formatUri({
type: "hotp",
label: "alice",
secret: "JBSWY3DPEHPK3PXP",
counter: 17n,
algorithm: "SHA256",
digits: 8,
});
```
## API Reference
- `formatUri(opts: OtpUri): string` — returns the URI. Raises
`OtpError(OTP.URI_MALFORMED)` on Zod failure or on labels containing `:`.
## Implementation Notes
The output is a stable canonical form so two calls with identically-typed
input produce byte-identical URLs. This matters when the URI is hashed for
QR-code de-duplication or audited via diff tooling. Field order is preserved
via a `Map` rather than a plain object so insertion order is stable across
JS engines.
Optional fields are omitted when they match the per-spec default — e.g.
TOTP URIs without a `period` parameter elide it rather than emitting
`period=30`. This keeps the output minimal and matches the wire format
emitted by reference Google Authenticator implementations. Round-tripping
through `parseUri` recovers the canonical option bag because both ends
agree on the defaults; consumers that need an explicit `period=30` should
set it on the input.