Fix phone validation performance by using Set/Map instead of Array lookups (#17843)

## Summary
- **`isValidCountryCode`** was using `Array.includes()` on ~250 country
codes (O(n) per call). Replaced with a `Set.has()` lookup (O(1)).
- **`getCountryCodesForCallingCode`** was iterating all ~250 countries
and calling `getCountryCallingCode()` on each one **every invocation**.
Replaced with a precomputed `Map<callingCode, CountryCode[]>` built once
at module load (O(1) per call).

Both functions are called from `transformPhonesValue` on every phone
field mutation, causing cumulative overhead visible in profiling (p95
self-time ~20-35ms).

## Test plan
- [ ] Verify phone field creation/update still works correctly (country
code validation, calling code resolution)
- [ ] Verify spreadsheet import with phone fields still validates
properly
- [ ] Confirm no regression in `isValidCountryCode` or
`getCountryCodesForCallingCode` behavior


Made with [Cursor](https://cursor.com)

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Félix Malfait
2026-02-10 19:56:43 +00:00
committed by GitHub
co-authored by Cursor
parent 05a6a96b13
commit 9c984b137f
2 changed files with 25 additions and 10 deletions
@@ -1,15 +1,30 @@
import { getCountries, getCountryCallingCode } from 'libphonenumber-js';
import {
type CountryCode,
getCountries,
getCountryCallingCode,
} from 'libphonenumber-js';
const ALL_COUNTRIES_CODE = getCountries();
// Precompute a map from calling code to country codes for O(1) lookups
const CALLING_CODE_TO_COUNTRIES = new Map<string, CountryCode[]>();
export const getCountryCodesForCallingCode = (callingCode: string) => {
for (const country of getCountries()) {
const callingCode = getCountryCallingCode(country);
const existing = CALLING_CODE_TO_COUNTRIES.get(callingCode);
if (existing) {
existing.push(country);
} else {
CALLING_CODE_TO_COUNTRIES.set(callingCode, [country]);
}
}
export const getCountryCodesForCallingCode = (
callingCode: string,
): CountryCode[] => {
const cleanCallingCode = callingCode.startsWith('+')
? callingCode.slice(1)
: callingCode;
return ALL_COUNTRIES_CODE.filter((country) => {
const countryCallingCode = getCountryCallingCode(country);
return countryCallingCode === cleanCallingCode;
});
return CALLING_CODE_TO_COUNTRIES.get(cleanCallingCode) ?? [];
};
@@ -1,7 +1,7 @@
import { type CountryCode, getCountries } from 'libphonenumber-js';
const ALL_COUNTRIES_CODE = getCountries();
const ALL_COUNTRIES_CODE_SET = new Set<string>(getCountries());
export const isValidCountryCode = (input: string): input is CountryCode => {
return ALL_COUNTRIES_CODE.includes(input as unknown as CountryCode);
return ALL_COUNTRIES_CODE_SET.has(input);
};