Bad input never throws
parsePhoneNumber always returns. Invalid input answers with null and a typed reason, not an exception.
Parsing, validation, formatting, and a headless input controller. Zero dependencies.
100% geographic parity, verified against Google libphonenumberAbout 4x faster parsing, measured in CI
npm install @telixon/coreOne await at startup loads the engine into memory. The rest of the API is synchronous.
import { ensureEngineReady, parsePhoneNumber } from '@telixon/core';
await ensureEngineReady();
const number = parsePhoneNumber('+1 (415) 555-0132');
number.isValid(); // truenumber.getRegion(); // 'US'number.getNumberType(); // 'FIXED_LINE_OR_MOBILE'number.formatE164(); // '+14155550132'The conformance suite loads Google libphonenumber's own source at the exact commit the engine was compiled from, so there is no version drift between the library and its reference.
On every push to main and every pull request, CI replays the whole conformance corpus against Google's source, across every supported region, and fails the build on any divergence. Once a week, an exhaustive enumeration re-proves the result across every input the engine can distinguish, at every length. The input controllers go through the same gate: every input is typed character by character, and the controller's resolved number must equal the parser's.
Explore the results on the live dashboard.
Parity is measured over geographic regions. Non-geographic ranges (Google's region 001, for example +800) are out of scope.
Telixon compiles Google libphonenumber's metadata and rules into a deterministic finite automaton (DFA), stored as compact binary tables. Most libraries interpret those rules at runtime, on every parse. Telixon does that work once, ahead of time.
Parsing a number is a walk over the DFA, one digit per step. The state the walk ends on already holds the region, the number type, whether the number is valid, and how to format it.
The engine is lazy by design. Its tables stay out of your initial bundle until you call ensureEngineReady(), which loads and decodes them off the main thread. You choose the moment; nothing loads at import time.
One digit, one step
Measured by the benchmark suite in the repository. Full, dated runs on the live benchmarks.
A complete API for a phone-number field. It handles every action a real input has: insert, delete forward or backward, replace a selection, undo, redo. The caret and the format stay correct on every keystroke.
Hard case, Argentina mobile
In Argentina, a mobile number carries two dialing prefixes that aren't part of the number itself: a leading 0 and a mobile 15. So Telixon runs a full round-trip on every keystroke:
0 and the 15, adds the mobile 9, and gets the canonical national number.9, restores the 0 and the 15, and applies the resolved format.The caret survives the whole strip-and-restore round-trip.
+5491123456789Each editing method takes the current value and selection, and returns the next value and caret.
import { createNationalInputController,} from '@telixon/core';
const controller = createNationalInputController({ defaultRegion: 'AR',});
controller.insert('011 15-2345-678', '9', 15, 15);// '011 15-2345-6789', caret 16
controller.deleteBackward('011 15-2345-6789', 16, 16);// '011 15-2345-678', caret 15
controller.undo(); // '011 15-2345-6789'controller.redo(); // '011 15-2345-678'createPhoneInput binds the controller to a real <input> and handles beforeinput, paste, drop, word delete, and IME.
import { createPhoneInput,} from '@telixon/web-sdk';
const phoneInput = createPhoneInput({ mode: 'national', input: document.querySelector('input')!, defaultRegion: 'AR',});
phoneInput.subscribe(({ validationError }) => { hint.textContent = validationError?.kind ?? '';});
phoneInput.getPhoneNumber().formatE164();// '+5491123456789' for the value aboveThe full controller API lives in the docs.
What it is like to have Telixon in a codebase.
Validation failures return a typed reason with the data needed to act. Eight variants, one discriminated union, exhaustive in a switch.
partial.getValidationError()// { kind: 'TOO_SHORT', minLength: 10 }Region codes are a literal union generated from the metadata; number types mirror libphonenumber's. A typo is a compile error.
controller.setRegion('USA')// compile error: not assignable to RegionCodeparsePhoneNumber always returns. Invalid input answers with null and a typed reason, not an exception.
Importing has no side effects: no fetches, no globals. The engine loads only when you call for it.
Validity, possibility with reason, type, region, and four formats: E.164, international, national, RFC 3966.
ESM, named exports only, sideEffects false. Bundlers drop what you don't use.
A separate sync-init entry initializes the engine where async won't do.
A searchable, locale-aware region list with a stable order, as pure state.
Twenty beforeinput types plus IME composition: typing, autocorrect, paste, drop, cut, word and line deletion. Anything unrecognized is cancelled; nothing reaches the field the controller did not produce.
Bounded history on every field: the undo and redo shortcuts drive it, and the historyUndo and historyRedo input intents are honored.
Flags derived arithmetically from Unicode letters, one pure function, no assets.
Each region's example number, run through the same format masks that format the input.
Telixon is an ecosystem built around one core, with thin layers you add only where you need them.
Every layer is optional. Take the raw API and build exactly the behavior you need, or take a ready-made layer.