npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@kanun-hq/plugin-phone

v0.1.2

Published

Phone number validation plugin for Kanun.

Readme

@kanun-hq/plugin-phone

Phone number validation plugin for Kanun.

@kanun-hq/plugin-phone adds a phone rule, a fluent Phone rule builder, a PhoneNumber utility wrapper, helper formatting functions, and Arkormˣ-style model cast helpers for phone-number-heavy validation flows.

The plugin uses libphonenumber-js/max for parsing, validation, country detection, formatting, and number type metadata.

Features

  • Adds the phone validation rule
  • Supports explicit countries, country fields, international parsing, phone number types, negated types, and lenient validation
  • Provides a fluent Phone rule builder for TypeScript-friendly rule definitions
  • Returns parsed PhoneNumber instances from await validator.validate()
  • Provides a phone helper for parsing and common formatting methods
  • Ships RawPhoneNumberCast and E164PhoneNumberCast helpers for Arkormˣ-style model casts

Installation

pnpm add kanun @kanun-hq/plugin-phone
npm install kanun @kanun-hq/plugin-phone
yarn add kanun @kanun-hq/plugin-phone

Registering The Plugin

Register the plugin once during application startup.

import { Validator } from 'kanun';
import { phoneValidatorPlugin } from '@kanun-hq/plugin-phone';

Validator.use(phoneValidatorPlugin);

You can also create a plugin instance explicitly.

import { Validator } from 'kanun';
import { createPhoneValidatorPlugin } from '@kanun-hq/plugin-phone';

Validator.use(createPhoneValidatorPlugin());

Basic Validation

Use the phone rule without parameters when the input includes enough country information, such as an international number.

const validator = Validator.make(
  { my_input: '+32 12 34 56 78' },
  { my_input: 'phone' },
);

const passes = await validator.passes();

When the input is national, pass one or more country codes.

const validator = Validator.make(
  { my_input: '012 34 56 78' },
  { my_input: 'phone:US,BE' },
);

await validator.validate();

The same rule can be written with the fluent builder:

import { Phone } from '@kanun-hq/plugin-phone';

const validator = Validator.make(
  { my_input: '012 34 56 78' },
  { my_input: Phone.country(['US', 'BE']) },
);

Validated Output

When the phone rule passes, validate() returns a parsed PhoneNumber instance for that field.

const validated = await Validator.make(
  { my_input: '012 34 56 78' },
  { my_input: 'phone:BE' },
).validate();

validated.my_input.formatE164(); // '+3212345678'
validated.my_input.getCountry(); // 'BE'
validated.my_input.getType(); // 'fixed_line'

This also works with builder rules.

const validated = await Validator.make(
  { my_input: '012 34 56 78' },
  { my_input: Phone.country(['US', 'BE']) },
).validate();

validated.my_input.formatInternational(); // '+32 12 34 56 78'

Country Field Validation

Use phone:field_name when another input field stores the country code.

const validated = await Validator.make(
  {
    my_input: '012 34 56 78',
    custom_country_field: 'BE',
  },
  {
    my_input: 'phone:custom_country_field',
    custom_country_field: 'required_with:my_input',
  },
).validate();

validated.my_input.formatE164(); // '+3212345678'

Builder form:

const validator = Validator.make(
  {
    my_input: '012 34 56 78',
    custom_country_field: 'BE',
  },
  {
    my_input: Phone.countryField('custom_country_field'),
    custom_country_field: 'required_with:my_input',
  },
);

International Numbers

Use INTERNATIONAL to allow country detection from the phone number itself while still applying any country constraints that follow.

const validator = Validator.make(
  { my_input: '+32 12 34 56 78' },
  { my_input: 'phone:INTERNATIONAL,BE' },
);

Builder form:

const validator = Validator.make(
  { my_input: '+32 12 34 56 78' },
  { my_input: Phone.international().country('BE') },
);

Phone Number Types

Use type parameters to require a specific number type.

const validator = Validator.make(
  { my_input: '+32 470 12 34 56' },
  { my_input: 'phone:mobile' },
);

Builder form:

const validator = Validator.make(
  { my_input: '+32 470 12 34 56' },
  { my_input: Phone.type('mobile') },
);

Use ! to reject a type.

const validator = Validator.make(
  { my_input: '+32 2 555 12 12' },
  { my_input: 'phone:!mobile' },
);

Builder form:

const validator = Validator.make(
  { my_input: '+32 2 555 12 12' },
  { my_input: Phone.notType('mobile') },
);

Supported type names:

  • mobile
  • fixed_line
  • fixed_line_or_mobile
  • toll_free
  • voip
  • premium_rate
  • shared_cost
  • personal_number
  • pager
  • uan
  • unknown
  • emergency
  • voicemail
  • short_code
  • standard_rate

Some type names depend on what libphonenumber-js metadata can identify. Unsupported metadata-specific types simply do not match PhoneNumber.isOfType().

Lenient Validation

Use LENIENT when you want possible phone numbers to pass even if they are not strictly valid according to numbering-plan metadata.

const validator = Validator.make(
  { my_input: '+1 200 123 0101' },
  { my_input: 'phone:LENIENT' },
);

Builder form:

const validator = Validator.make(
  { my_input: '+1 200 123 0101' },
  { my_input: Phone.lenient() },
);

PhoneNumber Utility

Use PhoneNumber directly when you want parsing, normalization, formatting, and comparisons outside a validator.

import { PhoneNumber } from '@kanun-hq/plugin-phone';

const phoneNumber = new PhoneNumber('+3212/34.56.78');

phoneNumber.format(); // '+3212345678'
phoneNumber.formatE164(); // '+3212345678'
phoneNumber.formatInternational(); // '+32 12 34 56 78'
phoneNumber.formatRFC3966(); // 'tel:+3212345678'
phoneNumber.formatNational(); // '012 34 56 78'
phoneNumber.formatNationalSignificant(); // '12 34 56 78'
phoneNumber.formatNationalSignificant(true); // '12345678'
phoneNumber.formatForCountry('BE'); // '012 34 56 78'
phoneNumber.formatForMobileDialingInCountry('US'); // '011 32 12 34 56 78'
phoneNumber.getType(); // 'fixed_line'
phoneNumber.isOfType('fixed_line'); // true
phoneNumber.getCountry(); // 'BE'
phoneNumber.isOfCountry('BE'); // true
phoneNumber.equals(new PhoneNumber('012 34 56 78', 'BE')); // true
phoneNumber.notEquals('+32 470 12 34 56'); // true

formatNationalSignificant() is useful when you want to display the subscriber-facing national number without the country calling code or leading national prefix:

new PhoneNumber('+234 903 123 4567').formatNationalSignificant(); // '903 123 4567'
new PhoneNumber('+234 903 123 4567').formatNationalSignificant(true); // '9031234567'
new PhoneNumber('+32 12 34 56 78').formatNationalSignificant(); // '12 34 56 78'

National numbers can be parsed with a country:

const phoneNumber = new PhoneNumber('012 34 56 78', 'BE');

phoneNumber.formatE164(); // '+3212345678'

Use the nullable parser when invalid input should not throw:

PhoneNumber.parse('not a phone number'); // null

Phone Helper

The phone helper is a callable shortcut around PhoneNumber.parse() with common formatting helpers attached.

import { phone } from '@kanun-hq/plugin-phone';

const parsed = phone('012 34 56 78', 'BE');

parsed?.formatE164(); // '+3212345678'

phone.format('+3212/34.56.78'); // '+3212345678'
phone.formatE164('012 34 56 78', 'BE'); // '+3212345678'
phone.formatInternational('012 34 56 78', 'BE'); // '+32 12 34 56 78'
phone.formatNational('+3212345678'); // '012 34 56 78'
phone.formatNationalSignificant('+234 903 123 4567'); // '903 123 4567'
phone.formatNationalSignificant('+234 903 123 4567', undefined, true); // '9031234567'

Arkormx Casts

Use RawPhoneNumberCast or E164PhoneNumberCast for model cast definitions.

import { Model } from 'arkormx';
import { RawPhoneNumberCast } from '@kanun-hq/plugin-phone';

export class User extends Model {
  protected override casts = {
    phone: new RawPhoneNumberCast('country_field'),
  } as const;
}

You can pass a fixed country instead of a field name.

export class User extends Model {
  protected override casts = {
    phone: new RawPhoneNumberCast('BE'),
  } as const;
}

E164PhoneNumberCast always stores E.164 strings.

import { E164PhoneNumberCast } from '@kanun-hq/plugin-phone';

export class User extends Model {
  protected override casts = {
    phone: new E164PhoneNumberCast('BE'),
  } as const;
}