/**
 * IP address canonicalization — collapse equivalent string forms into a
 * single key per RFC 5952 (IPv6) plus the dotted form for IPv4-mapped
 * IPv6 addresses.
 *
 * **Why this exists.** Without canonicalization, the four representations
 * `::1`, `::01`, `::0001`, and `0:0:0:0:0:0:0:1` are the same IPv6 address
 * but produce four distinct strings — so an attacker rotating
 * equivalent forms behind a trusted-passthrough proxy could defeat
 * per-IP rate limiting (each form gets a fresh bucket) and pollute
 * `audit_log.ip` forensics. The collision can extend to IPv4-mapped
 * IPv6 forms (`::ffff:127.0.0.1` vs `0:0:0:0:0:ffff:7f00:1` vs the
 * bare `127.0.0.1`) — three keys for one address.
 *
 * Canonicalization runs through {@link canonicalize_ip} which:
 *
 * 1. Lowercases and char-set filters (`IP_LITERAL_CHARS`) — non-IP
 *    strings (`'unknown'`, `'attacker:controlled'`, `'::1\n'`) pass
 *    through unchanged so downstream strict validators can still
 *    reject them.
 * 2. Parses via Hono's `convertIPv*ToBinary` family.
 * 3. Re-emits the canonical RFC 5952 string (lowercase hex,
 *    longest-zero-run compressed, IPv4-mapped emitted in the dotted
 *    form mandated by RFC 5952 §5).
 * 4. Strips the `::ffff:` prefix from dotted IPv4-mapped forms so the
 *    bucket collapses to plain IPv4 — the strip moves AFTER
 *    canonicalization because the dotted form is the only form the
 *    strip can recognize symmetrically.
 *
 * Mirrors `zzz_server::proxy::normalize_ip` (landed 2026-05-16) which
 * uses the same parse-then-canonicalize-then-strip ordering for the
 * same rate-limit-key-poisoning surface.
 *
 * @module
 */
/**
 * Allowed character set for a bare IP literal.
 *
 * Covers the union of IPv4 (digits + `.`), IPv6 (hex digits + `:`), and
 * IPv4-mapped IPv6 forms (`::ffff:127.0.0.1`). Anything outside this
 * set — brackets, whitespace, control bytes, letters g–z — disqualifies
 * the input from parsing.
 *
 * Same regex `proxy.ts`'s `validate_ip_strict` uses; exported here so
 * both modules can share one source of truth.
 */
export declare const IP_LITERAL_CHARS: RegExp;
/**
 * Canonicalize an IP address string.
 *
 * Returns the RFC 5952 canonical form for parseable IPv4 or IPv6
 * input. Returns the input unchanged (only lowercased) when the input
 * is non-IP (`'unknown'`), malformed (`'attacker:controlled'`,
 * `'::1\n'`), or any string the strict char-set filter rejects.
 *
 * **Idempotent.** `canonicalize_ip(canonicalize_ip(x)) === canonicalize_ip(x)`
 * for every input.
 *
 * **Order-safe for IPv4-mapped IPv6.** The `::ffff:` prefix strip
 * runs AFTER the canonical emit because the canonical form of an
 * IPv4-mapped IPv6 address is the dotted form (`::ffff:127.0.0.1`,
 * not `::ffff:7f00:1`). Stripping before canonicalize would miss the
 * full-hex form. Closes the
 * `normalize_ipv4_mapped_collapse_is_order_safe` test from the Rust
 * port.
 *
 * @example
 * canonicalize_ip('::0001')                    // → '::1'
 * canonicalize_ip('0:0:0:0:0:0:0:1')           // → '::1'
 * canonicalize_ip('2001:0DB8::0001')           // → '2001:db8::1'
 * canonicalize_ip('::ffff:127.0.0.1')          // → '127.0.0.1'
 * canonicalize_ip('0:0:0:0:0:ffff:7f00:1')     // → '127.0.0.1'
 * canonicalize_ip('::ffff:1')                  // → '::ffff:1' (NOT IPv4-mapped — group[5] is 0, not ffff)
 * canonicalize_ip('127.0.0.1')                 // → '127.0.0.1'
 * canonicalize_ip('not-an-ip')                 // → 'not-an-ip' (passes through)
 * canonicalize_ip('::1\n')                     // → '::1\n' (fails char-set; passes through)
 * canonicalize_ip('203.0.113.1:8080')          // → '203.0.113.1:8080' (passes through; validate_ip_strict rejects)
 */
export declare const canonicalize_ip: (ip: string) => string;
/**
 * Convert a 128-bit IPv6 binary value into its RFC 5952 canonical string form.
 *
 * - IPv4-mapped (groups[0..5] = 0, groups[5] = 0xffff) emits the
 *   `::ffff:a.b.c.d` dotted form per RFC 5952 §5.
 * - Otherwise: lowercase hex with no leading zeros per group (§4.1),
 *   the longest run of consecutive zero groups (≥ 2 groups) is
 *   replaced with `::` (§4.2.1, §4.2.3), and on equal-length runs the
 *   first one wins (§4.2.3). Single-zero groups stay as `0` (§4.2.2).
 *
 * Pure helper exported for the test suite to exercise the
 * canonicalization invariants directly without a full
 * `convertIPv6ToBinary` round-trip.
 *
 * @param bits - the 128-bit IPv6 value as `bigint`. Must satisfy `0n <= bits < 2n ** 128n`;
 *   throws `RangeError` otherwise. Silent truncation would mask caller bugs since the
 *   bit-extraction loop only consumes the low 128 bits.
 * @throws {RangeError} when `bits` is negative or exceeds 128 bits
 */
export declare const ipv6_bigint_to_canonical: (bits: bigint) => string;
//# sourceMappingURL=ip_canonical.d.ts.map