@wazo/sdk 0.41.2 → 0.42.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/wazo-sdk.js +12 -12
  2. package/esm/api/confd.js +2 -0
  3. package/esm/domain/CallerID.js +24 -0
  4. package/esm/domain/Features.js +7 -1
  5. package/esm/index.js +1 -0
  6. package/esm/types/api/confd.d.ts +2 -0
  7. package/esm/types/api/confd.d.ts.map +1 -1
  8. package/esm/types/domain/CallerID.d.ts +22 -0
  9. package/esm/types/domain/CallerID.d.ts.map +1 -0
  10. package/esm/types/domain/Features.d.ts +2 -0
  11. package/esm/types/domain/Features.d.ts.map +1 -1
  12. package/esm/types/index.d.ts +1 -0
  13. package/esm/types/index.d.ts.map +1 -1
  14. package/esm/types/simple/index.d.ts +2 -0
  15. package/esm/types/simple/index.d.ts.map +1 -1
  16. package/esm/types/utils/PhoneNumberUtil.d.ts +5 -6
  17. package/esm/types/utils/PhoneNumberUtil.d.ts.map +1 -1
  18. package/esm/utils/PhoneNumberUtil.js +27 -19
  19. package/esm/utils/__tests__/PhoneNumberUtil.test.js +23 -6
  20. package/lib/api/confd.js +2 -0
  21. package/lib/domain/CallerID.js +27 -0
  22. package/lib/domain/Features.js +6 -1
  23. package/lib/index.js +4 -2
  24. package/lib/types/api/confd.d.ts +2 -0
  25. package/lib/types/api/confd.d.ts.map +1 -1
  26. package/lib/types/domain/CallerID.d.ts +22 -0
  27. package/lib/types/domain/CallerID.d.ts.map +1 -0
  28. package/lib/types/domain/Features.d.ts +2 -0
  29. package/lib/types/domain/Features.d.ts.map +1 -1
  30. package/lib/types/index.d.ts +1 -0
  31. package/lib/types/index.d.ts.map +1 -1
  32. package/lib/types/simple/index.d.ts +2 -0
  33. package/lib/types/simple/index.d.ts.map +1 -1
  34. package/lib/types/utils/PhoneNumberUtil.d.ts +5 -6
  35. package/lib/types/utils/PhoneNumberUtil.d.ts.map +1 -1
  36. package/lib/utils/PhoneNumberUtil.js +28 -25
  37. package/lib/utils/__tests__/PhoneNumberUtil.test.js +22 -5
  38. package/package.json +2 -3
  39. package/src/api/confd.ts +3 -0
  40. package/src/domain/CallerID.ts +49 -0
  41. package/src/domain/Features.ts +9 -1
  42. package/src/index.ts +1 -0
  43. package/src/utils/PhoneNumberUtil.ts +31 -24
  44. package/src/utils/__tests__/PhoneNumberUtil.test.ts +31 -6
@@ -1,8 +1,7 @@
1
- import LibPhoneNumber from 'google-libphonenumber';
2
- declare const PhoneNumberUtil: LibPhoneNumber.PhoneNumberUtil;
3
- declare const PhoneNumberFormat: typeof LibPhoneNumber.PhoneNumberFormat, AsYouTypeFormatter: typeof LibPhoneNumber.AsYouTypeFormatter;
4
- declare const getDisplayableNumber: (rawNumber: string, country: string, asYouType?: boolean) => string;
1
+ import { CountryCode } from 'libphonenumber-js';
2
+ declare const guessParsePhoneNumber: (number: string, defaultCountry?: CountryCode, guessingCountries?: CountryCode[]) => import("libphonenumber-js").PhoneNumber | undefined;
3
+ declare const getDisplayableNumber: (rawNumber: string, country: CountryCode, asYouType?: boolean, guessingCountries?: CountryCode[] | undefined) => string;
5
4
  declare const parsePhoneNumber: (phoneNumber: string) => string;
6
- declare const getCallableNumber: (number: string, country?: string | null) => string | null | undefined;
7
- export { PhoneNumberUtil, PhoneNumberFormat, parsePhoneNumber, AsYouTypeFormatter, getDisplayableNumber, getCallableNumber };
5
+ declare const getCallableNumber: (number: string, country?: CountryCode) => string;
6
+ export { parsePhoneNumber, getDisplayableNumber, guessParsePhoneNumber, getCallableNumber, type CountryCode };
8
7
  //# sourceMappingURL=PhoneNumberUtil.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PhoneNumberUtil.d.ts","sourceRoot":"","sources":["../../../src/utils/PhoneNumberUtil.ts"],"names":[],"mappings":"AAAA,OAAO,cAAc,MAAM,uBAAuB,CAAC;AAEnD,QAAA,MAAM,eAAe,gCAA+C,CAAC;AACrE,QAAA,MACE,iBAAiB,2CACjB,kBAAkB,0CACF,CAAC;AAoBnB,QAAA,MAAM,oBAAoB,cAAe,MAAM,WAAW,MAAM,0BAAsB,MA+BrF,CAAC;AAEF,QAAA,MAAM,gBAAgB,gBAAiB,MAAM,KAAG,MAAoD,CAAC;AAErG,QAAA,MAAM,iBAAiB,WAAY,MAAM,YAAY,MAAM,GAAG,IAAI,KAAG,MAAM,GAAG,IAAI,GAAG,SAUpF,CAAC;AAEF,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,CAAC"}
1
+ {"version":3,"file":"PhoneNumberUtil.d.ts","sourceRoot":"","sources":["../../../src/utils/PhoneNumberUtil.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsD,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAsBpG,QAAA,MAAM,qBAAqB,WAAY,MAAM,mBAAmB,WAAW,sBAAqB,WAAW,EAAE,wDAc5G,CAAC;AAEF,QAAA,MAAM,oBAAoB,cAAe,MAAM,WAAW,WAAW,2CAAwC,WAAW,EAAE,GAAG,SAAS,KAAgC,MA6BrK,CAAC;AAEF,QAAA,MAAM,gBAAgB,gBAAiB,MAAM,KAAG,MAAoD,CAAC;AAErG,QAAA,MAAM,iBAAiB,WAAY,MAAM,YAAY,WAAW,KAAG,MAOlE,CAAC;AAEF,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,iBAAiB,EAAE,KAAK,WAAW,EAAE,CAAC"}
@@ -1,17 +1,10 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.getCallableNumber = exports.getDisplayableNumber = exports.AsYouTypeFormatter = exports.parsePhoneNumber = exports.PhoneNumberFormat = exports.PhoneNumberUtil = void 0;
7
- const google_libphonenumber_1 = __importDefault(require("google-libphonenumber"));
8
- const PhoneNumberUtil = google_libphonenumber_1.default.PhoneNumberUtil.getInstance();
9
- exports.PhoneNumberUtil = PhoneNumberUtil;
10
- const { PhoneNumberFormat, AsYouTypeFormatter, } = google_libphonenumber_1.default;
11
- exports.PhoneNumberFormat = PhoneNumberFormat;
12
- exports.AsYouTypeFormatter = AsYouTypeFormatter;
3
+ exports.getCallableNumber = exports.guessParsePhoneNumber = exports.getDisplayableNumber = exports.parsePhoneNumber = void 0;
4
+ const libphonenumber_js_1 = require("libphonenumber-js");
13
5
  // eslint-disable-next-line
14
6
  const EXTRA_CHAR_REGEXP = /[^+*\d]/g;
7
+ const DEFAULT_GUESSING_COUNTRIES = ['US', 'FR', 'GB', 'AU'];
15
8
  const shouldBeFormatted = (number) => {
16
9
  if (!number || number.length <= 5) {
17
10
  return false;
@@ -24,7 +17,20 @@ const isSameCountry = (country1, country2) => {
24
17
  }
25
18
  return country1 === country2;
26
19
  };
27
- const getDisplayableNumber = (rawNumber, country, asYouType = false) => {
20
+ const guessParsePhoneNumber = (number, defaultCountry, guessingCountries = DEFAULT_GUESSING_COUNTRIES) => {
21
+ const mergedCountries = [...new Set([defaultCountry, ...guessingCountries])].filter(Boolean);
22
+ let parsedNumber;
23
+ for (let i = 0; i < mergedCountries.length; i++) {
24
+ const country = mergedCountries[i];
25
+ parsedNumber = (0, libphonenumber_js_1.parsePhoneNumber)(number, country);
26
+ if (parsedNumber.isValid()) {
27
+ break;
28
+ }
29
+ }
30
+ return parsedNumber;
31
+ };
32
+ exports.guessParsePhoneNumber = guessParsePhoneNumber;
33
+ const getDisplayableNumber = (rawNumber, country, asYouType = false, guessingCountries = DEFAULT_GUESSING_COUNTRIES) => {
28
34
  if (!rawNumber) {
29
35
  return rawNumber;
30
36
  }
@@ -34,20 +40,19 @@ const getDisplayableNumber = (rawNumber, country, asYouType = false) => {
34
40
  }
35
41
  let displayValue = '';
36
42
  if (asYouType) {
37
- const formatter = new AsYouTypeFormatter(country);
38
- number.split('').forEach(char => {
39
- displayValue = formatter.inputDigit(char);
40
- });
43
+ displayValue = new libphonenumber_js_1.AsYouType(country).input(number);
41
44
  }
42
45
  else {
43
46
  try {
44
- const parsedNumber = PhoneNumberUtil.parseAndKeepRawInput(number, country);
45
- const numberCountry = PhoneNumberUtil.getRegionCodeForNumber(parsedNumber);
46
- const format = isSameCountry(String(numberCountry), country) ? PhoneNumberFormat.NATIONAL : PhoneNumberFormat.INTERNATIONAL;
47
- displayValue = PhoneNumberUtil.format(parsedNumber, format);
47
+ const parsedNumber = guessParsePhoneNumber(number, country, guessingCountries);
48
+ if (parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.isValid()) {
49
+ displayValue = isSameCountry(String(parsedNumber.country), country) ? parsedNumber.formatNational() : parsedNumber.formatInternational();
50
+ }
51
+ else {
52
+ displayValue = rawNumber;
53
+ }
48
54
  }
49
- catch (_) {
50
- // Avoid to crash when phone number like `0080510` can't be parsed
55
+ catch (error) {
51
56
  displayValue = rawNumber;
52
57
  }
53
58
  }
@@ -58,10 +63,8 @@ const parsePhoneNumber = (phoneNumber) => phoneNumber.replace(EXTRA_CHAR_REGEXP,
58
63
  exports.parsePhoneNumber = parsePhoneNumber;
59
64
  const getCallableNumber = (number, country) => {
60
65
  try {
61
- if (country) {
62
- return getDisplayableNumber(number, country).replace(EXTRA_CHAR_REGEXP, '');
63
- }
64
- return parsePhoneNumber(number);
66
+ const callableNumber = country ? getDisplayableNumber(number, country) : number;
67
+ return parsePhoneNumber(callableNumber);
65
68
  }
66
69
  catch (_) {
67
70
  return number;
@@ -13,7 +13,7 @@ describe('Formatting phone numbers', () => {
13
13
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+33675456545', 'FR')).toBe('06 75 45 65 45');
14
14
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('0675456545', 'FR')).toBe('06 75 45 65 45');
15
15
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+33675456545', 'US')).toBe('+33 6 75 45 65 45');
16
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('0675456545', 'US')).toBe('+1 0675456545');
16
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('0675456545', 'US')).toBe('+33 6 75 45 65 45');
17
17
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('4188884356', 'US')).toBe('(418) 888-4356');
18
18
  });
19
19
  it('should format real phone numbers when typing', () => {
@@ -21,12 +21,12 @@ describe('Formatting phone numbers', () => {
21
21
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('067545', 'FR', true)).toBe('06 75 45');
22
22
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+3367545', 'US', true)).toBe('+33 6 75 45');
23
23
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('067545', 'US', true)).toBe('067545');
24
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('418808', 'US', true)).toBe('418-808');
24
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('418808', 'US', true)).toBe('(418) 808');
25
25
  });
26
26
  it('should use the international number if the country number is different from the given country', () => {
27
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('00201005803648', 'FR')).toBe('+20 100 580 3648');
28
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+14188004554', 'FR')).toBe('+1 418-800-4554');
29
- expect((0, PhoneNumberUtil_1.getDisplayableNumber)('00201005803648', 'EG')).toBe('0100 580 3648');
27
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('00201005803648', 'FR')).toBe('+20 10 05803648');
28
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('+14188004554', 'FR')).toBe('+1 418 800 4554');
29
+ expect((0, PhoneNumberUtil_1.getDisplayableNumber)('00201005803648', 'EG')).toBe('010 05803648');
30
30
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('0634543450', 'FR')).toBe('06 34 54 34 50');
31
31
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('4188034302', 'US')).toBe('(418) 803-4302');
32
32
  expect((0, PhoneNumberUtil_1.getDisplayableNumber)('8775863230', 'CA')).toBe('(877) 586-3230');
@@ -53,3 +53,20 @@ describe('getCallableNumber', () => {
53
53
  expect((0, PhoneNumberUtil_1.getCallableNumber)('9000')).toBe('9000');
54
54
  });
55
55
  });
56
+ describe('guessParsePhoneNumber', () => {
57
+ it('should guess with default countries', () => {
58
+ const parsedNumber = (0, PhoneNumberUtil_1.guessParsePhoneNumber)('0675456545');
59
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.country).toBe('FR');
60
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.isValid()).toBe(true);
61
+ });
62
+ it('should guess with specified countries', () => {
63
+ const parsedNumber = (0, PhoneNumberUtil_1.guessParsePhoneNumber)('752097514', undefined, ['JP']);
64
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.country).toBe('JP');
65
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.isValid()).toBe(true);
66
+ });
67
+ it('should used use defaultCountry has guesser', () => {
68
+ const parsedNumber = (0, PhoneNumberUtil_1.guessParsePhoneNumber)('752097514', 'JP', []);
69
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.country).toBe('JP');
70
+ expect(parsedNumber === null || parsedNumber === void 0 ? void 0 : parsedNumber.isValid()).toBe(true);
71
+ });
72
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wazo/sdk",
3
- "version": "0.41.2",
3
+ "version": "0.42.0-rc.0",
4
4
  "description": "Wazo's JavaScript Software Development Kit.",
5
5
  "main": "index.js",
6
6
  "types": "lib/types/index.d.ts",
@@ -50,10 +50,10 @@
50
50
  "events": "^3.3.0",
51
51
  "fstream": "^1.0.12",
52
52
  "getstats": "wazo-platform/getStats#fix_stats_mobile",
53
- "google-libphonenumber": "^3.2.32",
54
53
  "js-base64": "^3.7.7",
55
54
  "json-to-graphql-query": "^2.2.5",
56
55
  "jsrsasign": "^11.1.0",
56
+ "libphonenumber-js": "^1.10.60",
57
57
  "moment": "^2.30.1",
58
58
  "reconnecting-websocket": "^4.4.0",
59
59
  "sdp-transform": "^2.14.2",
@@ -70,7 +70,6 @@
70
70
  "@rollup/plugin-terser": "^0.4.4",
71
71
  "@rollup/plugin-typescript": "^11.1.6",
72
72
  "@types/events": "^3.0.3",
73
- "@types/google-libphonenumber": "^7.4.30",
74
73
  "@types/jest": "^29.5.2",
75
74
  "@types/jsrsasign": "^10.5.8",
76
75
  "@types/node": "^20.4.0",
package/src/api/confd.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import ApiRequester from '../utils/api-requester';
2
2
  import type { UUID, ListConfdUsersResponse, ListApplicationsResponse } from '../domain/types';
3
3
  import type { MeetingCreateArguments, MeetingUpdateArguments } from '../domain/Meeting';
4
+ import CallerID from '../domain/CallerID';
4
5
  import Profile from '../domain/Profile';
5
6
  import SipLine from '../domain/SipLine';
6
7
  import ExternalApp from '../domain/ExternalApp';
@@ -35,6 +36,7 @@ export interface ConfD {
35
36
  guestGetMeeting: (meetingUuid: string) => Promise<Meeting>;
36
37
  guestAuthorizationRequest: (userUuid: string, meetingUuid: string, username: string) => Promise<any>;
37
38
  guestAuthorizationCheck: (userUuid: string, meetingUuid: string, authorizationUuid: string) => Promise<any>;
39
+ getOutgoingCallerIDs: (userUuid: string) => Promise<CallerID[]>;
38
40
  }
39
41
 
40
42
  export default ((client: ApiRequester, baseUrl: string): ConfD => ({
@@ -107,4 +109,5 @@ export default ((client: ApiRequester, baseUrl: string): ConfD => ({
107
109
  guest_name: username,
108
110
  }).then(MeetingAuthorization.parse),
109
111
  guestAuthorizationCheck: (userUuid: string, meetingUuid: string, authorizationUuid: string): Promise<any> => client.get(`${baseUrl}/guests/${userUuid}/meetings/${meetingUuid}/authorizations/${authorizationUuid}`, null),
112
+ getOutgoingCallerIDs: (userUuid: string): Promise<CallerID[]> => client.get(`${baseUrl}/users/${userUuid}/callerids/outgoing`, null).then(CallerID.parseMany),
110
113
  }));
@@ -0,0 +1,49 @@
1
+ import newFrom from '../utils/new-from';
2
+
3
+ type Response = {
4
+ number?: string;
5
+ type: 'main' | 'associated' | 'anonymous';
6
+ };
7
+
8
+ type ListResponse = {
9
+ items: Response[];
10
+ };
11
+
12
+ type Arguments = {
13
+ number?: string;
14
+ type: 'main' | 'associated' | 'anonymous';
15
+ };
16
+
17
+ export default class CallerID {
18
+ number?: string;
19
+
20
+ idType: 'main' | 'associated' | 'anonymous';
21
+
22
+ type: string;
23
+
24
+ static parse(plain: Response): CallerID {
25
+ return new CallerID({
26
+ number: plain.number,
27
+ type: plain.type,
28
+ });
29
+ }
30
+
31
+ static parseMany(plain: ListResponse): CallerID[] {
32
+ return plain.items.map(item => CallerID.parse(item));
33
+ }
34
+
35
+ static newFrom(profile: CallerID) {
36
+ return newFrom(profile, CallerID);
37
+ }
38
+
39
+ constructor({
40
+ type,
41
+ number,
42
+ }: Arguments) {
43
+ this.idType = type;
44
+ this.number = number;
45
+ // Useful to compare instead of instanceof with minified code
46
+ this.type = 'CallerID';
47
+ }
48
+
49
+ }
@@ -1,6 +1,6 @@
1
1
  import getApiClient from '../service/getApiClient';
2
2
 
3
- export const FEATURES = ['chat', 'video', 'call_recording', 'fax', 'mobile_double_call', 'mobile_gsm', 'meeting'];
3
+ export const FEATURES = ['chat', 'video', 'call_recording', 'fax', 'mobile_double_call', 'mobile_gsm', 'meeting', 'caller_id'];
4
4
  export const getScopeName = (featureName: string) => `enterprise.app.${featureName}`;
5
5
  const scopesToCheck = FEATURES.map(getScopeName); // Handle available features on the engine.
6
6
 
@@ -19,6 +19,8 @@ class Features {
19
19
 
20
20
  _hasMeeting: boolean;
21
21
 
22
+ _hasCallerID: boolean;
23
+
22
24
  constructor() {
23
25
  this._hasChat = true;
24
26
  this._hasVideo = true;
@@ -27,6 +29,7 @@ class Features {
27
29
  this._hasMobileDoubleCall = true;
28
30
  this._hasMobileGsm = true;
29
31
  this._hasMeeting = true;
32
+ this._hasCallerID = false;
30
33
  }
31
34
 
32
35
  async fetchAccess() {
@@ -51,6 +54,7 @@ class Features {
51
54
  this._hasMobileDoubleCall = this._hasFeatures(scopes, 'mobile_double_call');
52
55
  this._hasMobileGsm = this._hasFeatures(scopes, 'mobile_gsm');
53
56
  this._hasMeeting = this._hasFeatures(scopes, 'meeting');
57
+ this._hasCallerID = this._hasFeatures(scopes, 'caller_id');
54
58
  }
55
59
 
56
60
  hasChat() {
@@ -81,6 +85,10 @@ class Features {
81
85
  return this._hasMeeting;
82
86
  }
83
87
 
88
+ hasCallerID() {
89
+ return this._hasCallerID;
90
+ }
91
+
84
92
  _hasFeatures(scopes: Record<string, any>, featureName: string) {
85
93
  const scopeName = getScopeName(featureName);
86
94
 
package/src/index.ts CHANGED
@@ -24,6 +24,7 @@ export { default as Room } from './domain/Room';
24
24
  export { default as CallSession } from './domain/CallSession';
25
25
  export { default as IndirectTransfer } from './domain/IndirectTransfer';
26
26
  export { default as SwitchboardCall } from './domain/SwitchboardCall';
27
+ export { default as CallerID } from './domain/CallerID';
27
28
  export { default as WebRTCPhone } from './domain/Phone/WebRTCPhone';
28
29
  export { default as CTIPhone } from './domain/Phone/CTIPhone';
29
30
  export { default as Meeting, MeetingCreateArguments, MeetingUpdateArguments } from './domain/Meeting';
@@ -1,12 +1,8 @@
1
- import LibPhoneNumber from 'google-libphonenumber';
1
+ import { AsYouType, parsePhoneNumber as libParsePhoneNumber, CountryCode } from 'libphonenumber-js';
2
2
 
3
- const PhoneNumberUtil = LibPhoneNumber.PhoneNumberUtil.getInstance();
4
- const {
5
- PhoneNumberFormat,
6
- AsYouTypeFormatter,
7
- } = LibPhoneNumber;
8
3
  // eslint-disable-next-line
9
4
  const EXTRA_CHAR_REGEXP = /[^+*\d]/g;
5
+ const DEFAULT_GUESSING_COUNTRIES: CountryCode[] = ['US', 'FR', 'GB', 'AU'];
10
6
 
11
7
  const shouldBeFormatted = (number: string | null | undefined) => {
12
8
  if (!number || number.length <= 5) {
@@ -24,7 +20,23 @@ const isSameCountry = (country1: string, country2: string) => {
24
20
  return country1 === country2;
25
21
  };
26
22
 
27
- const getDisplayableNumber = (rawNumber: string, country: string, asYouType = false): string => {
23
+ const guessParsePhoneNumber = (number: string, defaultCountry?: CountryCode, guessingCountries: CountryCode[] = DEFAULT_GUESSING_COUNTRIES) => {
24
+ const mergedCountries = [...new Set([defaultCountry, ...guessingCountries])].filter(Boolean) as CountryCode[];
25
+
26
+ let parsedNumber;
27
+ for (let i = 0; i < mergedCountries.length; i++) {
28
+ const country = mergedCountries[i];
29
+
30
+ parsedNumber = libParsePhoneNumber(number, country);
31
+ if (parsedNumber.isValid()) {
32
+ break;
33
+ }
34
+ }
35
+
36
+ return parsedNumber;
37
+ };
38
+
39
+ const getDisplayableNumber = (rawNumber: string, country: CountryCode, asYouType = false, guessingCountries: CountryCode[] | undefined = DEFAULT_GUESSING_COUNTRIES): string => {
28
40
  if (!rawNumber) {
29
41
  return rawNumber;
30
42
  }
@@ -38,18 +50,16 @@ const getDisplayableNumber = (rawNumber: string, country: string, asYouType = fa
38
50
  let displayValue = '';
39
51
 
40
52
  if (asYouType) {
41
- const formatter = new AsYouTypeFormatter(country);
42
- number.split('').forEach(char => {
43
- displayValue = formatter.inputDigit(char);
44
- });
53
+ displayValue = new AsYouType(country).input(number);
45
54
  } else {
46
55
  try {
47
- const parsedNumber = PhoneNumberUtil.parseAndKeepRawInput(number, country);
48
- const numberCountry = PhoneNumberUtil.getRegionCodeForNumber(parsedNumber);
49
- const format = isSameCountry(String(numberCountry), country) ? PhoneNumberFormat.NATIONAL : PhoneNumberFormat.INTERNATIONAL;
50
- displayValue = PhoneNumberUtil.format(parsedNumber, format);
51
- } catch (_) {
52
- // Avoid to crash when phone number like `0080510` can't be parsed
56
+ const parsedNumber = guessParsePhoneNumber(number, country, guessingCountries);
57
+ if (parsedNumber?.isValid()) {
58
+ displayValue = isSameCountry(String(parsedNumber.country), country) ? parsedNumber.formatNational() : parsedNumber.formatInternational();
59
+ } else {
60
+ displayValue = rawNumber;
61
+ }
62
+ } catch (error) {
53
63
  displayValue = rawNumber;
54
64
  }
55
65
  }
@@ -59,16 +69,13 @@ const getDisplayableNumber = (rawNumber: string, country: string, asYouType = fa
59
69
 
60
70
  const parsePhoneNumber = (phoneNumber: string): string => phoneNumber.replace(EXTRA_CHAR_REGEXP, '');
61
71
 
62
- const getCallableNumber = (number: string, country?: string | null): string | null | undefined => {
72
+ const getCallableNumber = (number: string, country?: CountryCode): string => {
63
73
  try {
64
- if (country) {
65
- return getDisplayableNumber(number, country).replace(EXTRA_CHAR_REGEXP, '');
66
- }
67
-
68
- return parsePhoneNumber(number);
74
+ const callableNumber = country ? getDisplayableNumber(number, country) : number;
75
+ return parsePhoneNumber(callableNumber);
69
76
  } catch (_) {
70
77
  return number;
71
78
  }
72
79
  };
73
80
 
74
- export { PhoneNumberUtil, PhoneNumberFormat, parsePhoneNumber, AsYouTypeFormatter, getDisplayableNumber, getCallableNumber };
81
+ export { parsePhoneNumber, getDisplayableNumber, guessParsePhoneNumber, getCallableNumber, type CountryCode };
@@ -1,4 +1,4 @@
1
- import { getDisplayableNumber, getCallableNumber } from '../PhoneNumberUtil';
1
+ import { getDisplayableNumber, getCallableNumber, guessParsePhoneNumber } from '../PhoneNumberUtil';
2
2
 
3
3
  describe('Formatting phone numbers', () => {
4
4
  it('should not format all phone numbers', () => {
@@ -8,30 +8,34 @@ describe('Formatting phone numbers', () => {
8
8
  expect(getDisplayableNumber('#23445433', 'DE')).toBe('#23445433');
9
9
  expect(getDisplayableNumber('0080510', 'FR')).toBe('0080510');
10
10
  });
11
+
11
12
  it('should format real phone numbers', () => {
12
13
  expect(getDisplayableNumber('+33675456545', 'FR')).toBe('06 75 45 65 45');
13
14
  expect(getDisplayableNumber('0675456545', 'FR')).toBe('06 75 45 65 45');
14
15
  expect(getDisplayableNumber('+33675456545', 'US')).toBe('+33 6 75 45 65 45');
15
- expect(getDisplayableNumber('0675456545', 'US')).toBe('+1 0675456545');
16
+ expect(getDisplayableNumber('0675456545', 'US')).toBe('+33 6 75 45 65 45');
16
17
  expect(getDisplayableNumber('4188884356', 'US')).toBe('(418) 888-4356');
17
18
  });
19
+
18
20
  it('should format real phone numbers when typing', () => {
19
21
  expect(getDisplayableNumber('+3367545', 'FR', true)).toBe('+33 6 75 45');
20
22
  expect(getDisplayableNumber('067545', 'FR', true)).toBe('06 75 45');
21
23
  expect(getDisplayableNumber('+3367545', 'US', true)).toBe('+33 6 75 45');
22
24
  expect(getDisplayableNumber('067545', 'US', true)).toBe('067545');
23
- expect(getDisplayableNumber('418808', 'US', true)).toBe('418-808');
25
+ expect(getDisplayableNumber('418808', 'US', true)).toBe('(418) 808');
24
26
  });
27
+
25
28
  it('should use the international number if the country number is different from the given country', () => {
26
- expect(getDisplayableNumber('00201005803648', 'FR')).toBe('+20 100 580 3648');
27
- expect(getDisplayableNumber('+14188004554', 'FR')).toBe('+1 418-800-4554');
28
- expect(getDisplayableNumber('00201005803648', 'EG')).toBe('0100 580 3648');
29
+ expect(getDisplayableNumber('00201005803648', 'FR')).toBe('+20 10 05803648');
30
+ expect(getDisplayableNumber('+14188004554', 'FR')).toBe('+1 418 800 4554');
31
+ expect(getDisplayableNumber('00201005803648', 'EG')).toBe('010 05803648');
29
32
  expect(getDisplayableNumber('0634543450', 'FR')).toBe('06 34 54 34 50');
30
33
  expect(getDisplayableNumber('4188034302', 'US')).toBe('(418) 803-4302');
31
34
  expect(getDisplayableNumber('8775863230', 'CA')).toBe('(877) 586-3230');
32
35
  expect(getDisplayableNumber('8775863230', 'US')).toBe('(877) 586-3230');
33
36
  });
34
37
  });
38
+
35
39
  describe('getCallableNumber', () => {
36
40
  it('works with a country', () => {
37
41
  expect(getCallableNumber('+33 6 75 45 12 34', 'FR')).toBe('0675451234');
@@ -43,6 +47,7 @@ describe('getCallableNumber', () => {
43
47
  expect(getCallableNumber('*10', 'US')).toBe('*10');
44
48
  expect(getCallableNumber('9000', 'US')).toBe('9000');
45
49
  });
50
+
46
51
  it('works without a country', () => {
47
52
  expect(getCallableNumber('06 75 45')).toBe('067545');
48
53
  expect(getCallableNumber('067-545')).toBe('067545');
@@ -52,3 +57,23 @@ describe('getCallableNumber', () => {
52
57
  expect(getCallableNumber('9000')).toBe('9000');
53
58
  });
54
59
  });
60
+
61
+ describe('guessParsePhoneNumber', () => {
62
+ it('should guess with default countries', () => {
63
+ const parsedNumber = guessParsePhoneNumber('0675456545');
64
+ expect(parsedNumber?.country).toBe('FR');
65
+ expect(parsedNumber?.isValid()).toBe(true);
66
+ });
67
+
68
+ it('should guess with specified countries', () => {
69
+ const parsedNumber = guessParsePhoneNumber('752097514', undefined, ['JP']);
70
+ expect(parsedNumber?.country).toBe('JP');
71
+ expect(parsedNumber?.isValid()).toBe(true);
72
+ });
73
+
74
+ it('should used use defaultCountry has guesser', () => {
75
+ const parsedNumber = guessParsePhoneNumber('752097514', 'JP', []);
76
+ expect(parsedNumber?.country).toBe('JP');
77
+ expect(parsedNumber?.isValid()).toBe(true);
78
+ });
79
+ });