@verifyhash/ip-cidr
v0.1.2
Published
Zero-dependency IPv4 (and basic IPv6) address/CIDR toolkit for Node.js: validate/parse, CIDR math (network, broadcast, first/last host, host count), ip<->integer, and containment.
Maintainers
Readme
ip-cidr
A tiny, zero-dependency IPv4 (and basic IPv6) address/CIDR toolkit for Node.js. It does the boring arithmetic you always end up rewriting: validate an address, parse a CIDR block, ask whether a block contains an IP, and compute the network/broadcast/first/last host and host count of a subnet — plus lossless ip↔integer conversion.
No network access, no runtime dependencies, no servers. Just math on the bits.
Install
npm install @verifyhash/ip-cidrPublished as @verifyhash/ip-cidr;
source lives in the verifyhash/libs monorepo.
Zero runtime dependencies — you can also vendor the folder directly.
Who it's for
- Anyone writing allow/deny lists, firewall-ish rules, or geo/ASN buckets who
needs a correct
contains(cidr, ip)without pulling a dependency tree. - Tools and CLIs that display subnet facts (network, broadcast, usable range, host count) for an operator.
- Code that stores IPs as integers (databases, bitmaps) and needs a reliable round-trip to/from dotted-quad.
Install & usage
Zero runtime dependencies (tests use only built-in assert) — you can also
vendor the folder directly. In your project:
const ip = require('@verifyhash/ip-cidr'); // during development: require('./index.js')
// Containment
ip.contains('10.0.0.0/8', '10.1.2.3'); // => true
ip.contains('192.168.1.0/24', '192.168.2.9'); // => false
// Subnet facts
const net = ip.parseCidr('192.168.1.10/24');
net.network; // '192.168.1.0'
net.broadcast; // '192.168.1.255'
net.firstHost; // '192.168.1.1'
net.lastHost; // '192.168.1.254'
net.hostCount; // 254 (usable hosts)
net.size; // 256 (total addresses)
// Address <-> integer (lossless, unsigned 32-bit)
ip.ipv4ToInt('192.168.1.1'); // 3232235777
ip.intToIpv4(3232235777); // '192.168.1.1'
// Validation / dispatch
ip.isValidIPv4('1.2.3.256'); // false
ip.ipVersion('::1'); // 6
ip.parse('8.8.8.8'); // { version: 4, address: '8.8.8.8', int: 134744072 }API
IPv4 core
isValidIPv4(str) → boolean— strict dotted-quad. Rejects leading zeros (01.2.3.4), out-of-range octets (1.2.3.256), and wrong segment counts.ipv4ToInt(str) → number— unsigned 32-bit integer (throws on invalid).intToIpv4(int) → string— inverse (throws if outside0 … 4294967295).
IPv6 core
isValidIPv6(str) → boolean— supports::compression and an embedded IPv4 tail (::ffff:192.168.0.1). Zone ids (%eth0) are rejected.ipv6ToBigInt(str) → bigint/bigIntToIpv6(bigint) → string— lossless round-trip viaBigInt.bigIntToIpv6returns the fully expanded (uncompressed) form, e.g.0:0:0:0:0:0:0:1, not::1.
Unified
ipVersion(str) → 4 | 6 | nullparse(str) → { version, address, int }for v4 or{ version, address, big }for v6 (throws on invalid).parseCidr(str) → object— see fields below (throws on invalid).contains(cidr, ip) → boolean— throws only ifcidr/ipcan't be parsed; returnsfalse(never throws) when the two are different address families.
parseCidr result
IPv4 blocks return numeric and string forms:
| field | example (192.168.1.10/24) | notes |
|-------|------------------------------|-------|
| network / networkInt | 192.168.1.0 / 3232235776 | masked base address |
| broadcast / broadcastInt | 192.168.1.255 / … | all-ones host |
| firstHost / lastHost | 192.168.1.1 / 192.168.1.254 | usable range |
| size | 256 | total addresses in the block |
| hostCount | 254 | usable hosts (size − 2, except /31,/32) |
| prefix | 24 | |
IPv6 blocks return network, firstHost, lastHost (strings) plus
networkBig, firstHostBig, lastHostBig, size, and hostCount as
BigInt. There is no broadcast for IPv6 — broadcast is not an IPv6
concept.
/31 and /32 edge cases
/32 is treated as a single usable host (hostCount: 1). /31 follows
RFC 3021: both addresses are usable point-to-point endpoints, so
hostCount: 2 with no network/broadcast reservation. Wider IPv4 blocks use the
conventional size − 2 usable-host count.
IPv6 support scope (honest limits)
IPv6 is partial by design. Fully supported:
- Validation & parsing —
isValidIPv6,ipv6ToBigInt, including::compression and embedded-IPv4 tails. - Containment —
contains(cidr, ip)andparseCidrwork for v6 usingBigInt, so there is no precision loss across the full 128-bit space.
Intentionally not provided for IPv6:
- No
broadcast/first-usable/last-usable reservation semantics (v6 has no broadcast).firstHost/lastHostare just the block's low/high addresses. bigIntToIpv6emits the expanded form only — it does not produce the RFC 5952 shortest/canonical::form.- No zone identifiers (
fe80::1%eth0), no scoped/link-local special handling, no CIDR aggregation/summarization, and no IPv4-mapped normalization beyond parsing the embedded-IPv4 syntax.
If you need full IPv6 canonicalization or aggregation, use a dedicated library. IPv4 is the first-class, fully-featured path here.
Running the tests
One command, no framework (Node's built-in assert):
node test/index.test.js
# or
npm testThe suite covers: containment true/false cases, /31 and /32 edges,
network/broadcast math for known blocks, ip↔int round-trips, invalid-input
rejection (bad octets, out-of-range masks, malformed strings), and the IPv6
validate/parse/contain paths.
License
MIT — see LICENSE.
