Skip to content

3.0.0

Choose a tag to compare

@ekscrypto ekscrypto released this 23 Apr 05:34
· 41 commits to main since this release

Breaking: ACE-only hostnames and trie labels

Hostnames passed to PublicSuffixList.isUnrestricted(_:) and match(_:) must now be in ACE (Punycode) form. Raw Unicode hostnames like example.香港 are rejected by syntax validation — encode IDN labels as xn--… before calling:

// ❌ rejected since v3.0
list.isUnrestricted("example.香港")

// ✅ correct
list.isUnrestricted("example.xn--j6w193g")

This aligns with DNS wire format and RFC 5321 hostname syntax, and eliminates the silent UTF-8-vs-ACE mismatch that was possible in v2.

Changed

  • Breaking: isValidHost now rejects any byte ≥ 0x80.
  • Breaking: The embedded trie stores labels in ACE form only. Caches built from v2.0.x rules loaded via .filePath(_:) remain structurally valid but IDN labels won't match — rebuild caches on upgrade.
  • TrieBuilder.buildAndSerialize(rules:) auto-converts each label via Punycode before insertion, so callers can continue to pass the PSL .dat's native UTF-8 form in [[String]] rules.
  • Per-label cap tightened from 255 to 63 bytes (DNS RFC 1035).

Added

  • Punycode (internal) — self-contained RFC 3492 encoder. No external IDNA dependency.
  • walkForValidation enforces ASCII LDH on every stored label byte. New TrieValidationError.nonLDHLabelByte(at:byte:) case.
  • Structural check: label length ≤ 63 bytes. New TrieValidationError.labelTooLong(at:length:) case.

Trie format version unchanged at 2 — only the byte content of stored labels changed, not the binary layout.