Skip to content

Verified against Google libphonenumber

The engine is compiled from Google libphonenumber metadata (Engine), and the claim that Telixon returns Google’s answers is enforced by a conformance suite rather than assumed from the compilation. The suite runs Google’s own implementation next to Telixon on the same inputs and fails CI on a single disagreement outside an allowlist that is currently empty.

Google publishes no npm package for libphonenumber; its JavaScript port lives as source inside the google/libphonenumber repository. The conformance suite fetches that source at the exact commit the engine was compiled from, recorded in PROVENANCE.json, and executes it with the Closure dependencies it expects. Fetched sources are cached per commit, so only the first run needs network access.

Because the engine and the oracle share one commit, there is no metadata version drift. A mismatch is a real behavioral difference, never an artifact of comparing against a newer or older numbering plan.

Twelve query methods are compared:

  • Per number: isValid, isPossible, isPossibleWithReason, getNumberType, getNationalNumber, getCallingCode, getRegion, formatE164, formatNational, formatInternational, formatRfc3966.
  • As its own axis, because it takes a region argument: isValidForRegion, compared against Google’s isValidNumberForRegion.

getCallingCodeForRegion is additionally compared with Google’s getCountryCodeForRegion for every region. A compile-time completeness check fails the build if a method is dropped from the comparison list.

Telixon never throws on input, while Google rejects some inputs at parse. For those inputs the contract is agreement on rejection: Telixon must judge the same input not possible, and valid for no candidate region on the isValidForRegion axis. An input Telixon accepts while Google rejects fails the gate.

Every case derives deterministically from Google’s example numbers, one for each region and number type Google publishes an example for, so there is no snapshot to drift and no randomness to replay. One case is one exact input string parsed under identical conditions on both sides. The gate asserts that all 245 supported regions are covered and that zero corpus inputs were skipped.

Family Cases Contract
Examples The canonical E.164 of every example Both sides parse; every method compared
Display variants International display, national display parsed with its region, RFC3966 URI, whitespace padding Both sides parse; every method compared
Mutations 1 to 3 trailing digits truncated, 1 to 2 digits appended, first national digit shifted, unassigned calling codes Every method compared; when Google rejects at parse, the rejection contract applies

The comparison is differential: a mutation that happens to stay valid is still a case, because both sides must agree on whatever the verdict is.

Two sweeps extend the corpus. The prefix sweep parses every digit prefix of every example on both sides and requires isPossibleWithReason to match verbatim at every prefix; this is the verdict surface the input controllers expose per keystroke. The isValidForRegion sweep compares, for every corpus case, the exact set of candidate regions each side judges the number valid for; candidates are every region sharing the input’s calling code plus two fixed foreign probes confirming the cross-calling-code guard returns false on both sides.

For a given calling code, the engine’s answer depends only on a bounded prefix of deciding digits and the total length; past the deciding digits, further input cannot change the outcome. Numbers that reach the same automaton position at the same length therefore form one case, and the whole input space collapses into a finite case set.

The case-coverage gate builds one number for every reachable case at every national length from 1 to 15 and requires zero divergences from Google. It runs in CI on every pull request and every push to main. The same collapse drives a second isValidForRegion sweep, one number per case per calling code, which covers that method’s entire reachable domain.

The gate asserts an exact match minus an explicit allowlist in which every accepted mismatch must carry a reason:

packages/core/conformance/known-divergences.ts
// Mismatches we explicitly accept. Every entry needs a reason; anything not listed fails the gate.
export const KNOWN_DIVERGENCES: readonly KnownDivergence[] = [];

The list is empty: every compared behavior matches Google exactly. The audit is two-sided so the list cannot rot. A mismatch absent from the list fails the gate as a regression, and a listed entry that no longer occurs fails the gate as stale.

The input controllers must resolve exactly like the parser. A gate types 100,000 enumerated inputs character by character, international inputs into the international controller and national inputs into the national controller with their region, then requires the controller’s getPhoneNumber() to equal parsePhoneNumber of the displayed value across all eleven per-number methods. parsePhoneNumber is the Google-gated path, so the controllers are transitively held to the same parity.

Because outcomes collapse into cases, enumerating every deciding-digit combination at every national length from 1 to 15, for every calling code, covers every distinct outcome the engine can produce for international input. That space is 1,838,775,900 inputs. A weekly workflow checks every one of them against Google in ten disjoint shards that together cover the space; shards run to completion independently, so a divergence in one cannot cancel the evidence of the others.

On every pull request and push to main, two lighter differential runs stand in for the weekly proof: the case-coverage gate above and a 1,000,000-input sample drawn from a distinct-number enumeration spanning international and national inputs.

The parity claim is geographic. Non-geographic calling codes (world numbering plan region 001, for example +800) are out of scope; the sweeps count them as skipped rather than silently dropping them.

The suite lives in the repository and runs from its root. It is excluded from pnpm test, which stays offline.

Terminal window
pnpm conformance

This runs the corpus gate, the prefix and isValidForRegion sweeps, case coverage, and the controller consistency gate, and prints the per-method report with the shared commit in its header. The enumerations run separately:

Terminal window
FUZZ_N=1000000 pnpm fuzz
FUZZ_AIRTIGHT=1 FUZZ_MINLEN=1 FUZZ_MAXLEN=15 pnpm fuzz

The first command is the per-push sample; the second covers the full 1,838,775,900-input space and shards with FUZZ_SHARD_COUNT and FUZZ_SHARD_INDEX, exactly as the weekly workflow runs it. Every push to main regenerates the live report at telixon.dev/parity.html.

  • Provenance documents which Google libphonenumber revision the engine is compiled from and how the pin is enforced.
  • The model explains why a finite machine can be enumerated exhaustively in the first place.
  • Migrate from Google libphonenumber maps every Google API to its Telixon equivalent.