minted_network 1.0.1 copy "minted_network: ^1.0.1" to clipboard
minted_network: ^1.0.1 copied to clipboard

Well-modelled Dart value types for networking: IP addresses, CIDR blocks, hostnames, DNS names, MAC addresses and ports. Part of the minted family.

Pub Version Pub Points Package checks License: BSD-3-Clause

minted_network #

Network addresses and names as well-modelled value types.

Part of the minted family: pure-Dart value types built on parse, don't validate, so the parser is the only door in and anything that came through it is well-formed by construction. Once you hold an IpAddress, it is an address, in its RFC 5952 canonical spelling.

Install #

dart pub add minted_network

minted comes with it, holding the shared vocabulary (ParseOutcome, MintedFailure, Digit, Digits, the Uint tower). Nothing here drags in another domain's engine, and it's pure Dart, so unlike InternetAddress it works on the web too.

What's in the box #

Type What it guarantees Standard
Hostname the RFC 1123 grammar and both length limits; ASCII only, never an address RFC 1123
DnsName the permissive counterpart: underscores and the rest of RFC 2181, so DKIM and SRV fit RFC 2181
IpAddress v4 or v6, canonicalised per RFC 5952; a leading zero refused, not read as octal RFC 4291
Cidr a network block: host bits must be clear, and contains masks rather than matching text RFC 4632
MacAddress 48 or 64 bits, four notations folded to one; the I/G and U/L bits read back IEEE Std 802
Port the 0-65535 bound; the RFC 6335 band read back rather than gated on RFC 6335

Hostname enforces what Uri waves through: -bad.com, a..b.com and a 64-character label all pass Uri without complaint. DnsName is the permissive counterpart rather than a relaxed Hostname, so widening (fromHostname) always works and narrowing (tryToHostname) is a parse.

A quick taste #

// IpAddress: four spellings of one v6 address are four different map keys as Strings.
final address = IpAddress.tryParse('2001:0DB8:0:0:0:0:0:1')!;
address.value;    // '2001:db8::1'   (leading zeros gone, longest zero run compressed)
address.version;  // IpVersion.v6
IpAddress.tryParse('10.0.0.1')!.isPrivate;   // true   (RFC 1918; fc00::/7 for v6)

// a leading zero is refused rather than read, because inet_aton calls 010 octal and most
// parsers call it ten: accept it and one component can filter what another connects to.
IpAddress.parse('192.168.010.1').reasonOrNull?.message;
// '"010" has a leading zero, which is ambiguous between decimal and octal'

// Cidr holds an IpAddress and a prefix length rather than text, so contains() masks bits:
final block = Cidr.tryParse('10.0.0.0/8')!;
block.lastAddress.value;  // '10.255.255.255'
block.contains(IpAddress.tryParse('10.1.2.3')!);    // true
block.contains(IpAddress.tryParse('100.0.0.1')!);   // false, where a text prefix match says true

// host bits set is refused rather than silently masked, and the failure offers what you meant:
Cidr.parse('192.168.1.5/24').reasonOrNull?.message;
// 'has host bits set below the prefix; the network is "192.168.1.0/24"'

// Hostname: case and a trailing root dot normalise away, so one name has exactly one value:
final host = Hostname.tryParse('WWW.Example.COM.')!;
host.value;   // 'www.example.com'
host.fqdn;    // 'www.example.com.'   (rebuilds the trailing dot, which names the root)
Hostname.tryParse('192.168.1.1');    // null: that's an address, not a hostname
Hostname.tryParse('_sip.example.com');   // null: an underscore makes it a DNS name

// DnsName: the names DKIM, DMARC, ACME and SRV actually use:
final dmarc = DnsName.tryParse('_DMARC.Example.COM.')!;
dmarc.isUnderscored;         // true: an RFC 8552 attribute leaf, reported rather than gated on
DnsName.fromHostname(host);  // total, where dmarc.tryToHostname() is null

// MacAddress: four notations spell one address, and both widths keep theirs:
final mac = MacAddress.tryParse('00-00-5E-00-53-00')!;
mac.value;                    // '00:00:5e:00:53:00'   (canonical)
mac.isLocallyAdministered;    // false   (the U/L bit)
MacAddress.tryParse('0000.5e00.5300') == mac;   // true: Cisco's dot-quad is the same address

// Port: exactly a Uint16's range, so that type owns the bound. The RFC 6335 band reads back:
final port = Port.tryFrom(8080)!;
port.range;                   // PortRange.user
Port.tryFrom(0)!.isWildcard;  // true: bind(0) asks the OS for a free port
Port.tryFrom(65536);          // null, one past the 16-bit ceiling

The runnable version is the example.

One shape, every type #

  • Type.tryParse(input) hands back the value, or null when the input isn't valid
  • Type.parse(input) hands back a ParseOutcome: the value, or a typed failure (IpAddressFailure, CidrFailure, HostnameFailure, DnsNameFailure, MacAddressFailure) you can switch on, or read as a form-field message via .reasonOrNull. No door throws
  • value equality, a canonical form to read back (.value, .asString on a Cidr), and an assembly factory (from, fromOctets, fromLabels) for parts you already hold
  • Port is a constraint on a number rather than a parsed text form, so it takes tryFrom(int) and carries no failure vocabulary: with one invariant, null says everything a failure could

The minted README is the family guide: the whole catalogue, handling failures, and the one caveat (never cast into a minted type).

0
likes
90
points
102
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Well-modelled Dart value types for networking: IP addresses, CIDR blocks, hostnames, DNS names, MAC addresses and ports. Part of the minted family.

Homepage
Repository (GitHub)
View/report issues

Topics

#validation #type-safety #networking #domain-driven-design #standards

License

BSD-3-Clause (license)

Dependencies

collection, ipaddr, meta, minted

More

Packages that depend on minted_network