@whiteslove/parsing-lexicon 0.2.0 → 0.2.2

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 (94) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +137 -137
  3. package/index.d.ts +374 -374
  4. package/package.json +77 -75
  5. package/src/central-asia-locations.js +211 -211
  6. package/src/central-asia.js +233 -233
  7. package/src/contact.d.ts +44 -44
  8. package/src/contact.js +159 -159
  9. package/src/countries.js +175 -175
  10. package/src/country-context.d.ts +10 -10
  11. package/src/country-context.js +22 -22
  12. package/src/currency.d.ts +14 -14
  13. package/src/currency.js +66 -66
  14. package/src/geo.js +169 -169
  15. package/src/geography-central-asia.js +42 -42
  16. package/src/geography-detection.d.ts +14 -14
  17. package/src/geography-detection.js +64 -64
  18. package/src/geography-display.d.ts +13 -13
  19. package/src/geography-display.js +113 -113
  20. package/src/geography.js +210 -210
  21. package/src/hiring-advanced.js +147 -147
  22. package/src/hiring-candidate-fields.d.ts +9 -9
  23. package/src/hiring-candidate-fields.js +92 -92
  24. package/src/hiring-context.d.ts +64 -64
  25. package/src/hiring-context.js +412 -412
  26. package/src/hiring-language-extensions.d.ts +19 -19
  27. package/src/hiring-language-extensions.js +139 -139
  28. package/src/hiring-languages.js +135 -135
  29. package/src/hiring-location-fields.d.ts +3 -3
  30. package/src/hiring-location-fields.js +53 -53
  31. package/src/hiring-professions-ro.js +191 -191
  32. package/src/hiring-professions.js +437 -437
  33. package/src/hiring-requirements.d.ts +16 -16
  34. package/src/hiring-requirements.js +170 -170
  35. package/src/hiring-salary-context.d.ts +27 -27
  36. package/src/hiring-salary-context.js +76 -76
  37. package/src/hiring-semantics.d.ts +29 -29
  38. package/src/hiring-semantics.js +218 -218
  39. package/src/hiring-skills.d.ts +21 -21
  40. package/src/hiring-skills.js +315 -315
  41. package/src/hiring-source-aliases.d.ts +32 -32
  42. package/src/hiring-source-aliases.js +185 -185
  43. package/src/hiring-source-semantics.d.ts +31 -31
  44. package/src/hiring-source-semantics.js +293 -293
  45. package/src/hiring-temporal.d.ts +15 -15
  46. package/src/hiring-temporal.js +67 -67
  47. package/src/hiring-vacancy-fields.d.ts +5 -5
  48. package/src/hiring-vacancy-fields.js +23 -23
  49. package/src/hiring-work-semantics.d.ts +34 -34
  50. package/src/hiring-work-semantics.js +72 -72
  51. package/src/hiring.js +180 -180
  52. package/src/housing-address.d.ts +18 -18
  53. package/src/housing-address.js +217 -210
  54. package/src/housing-commercial.d.ts +1 -0
  55. package/src/housing-commercial.js +9 -0
  56. package/src/housing-context.d.ts +36 -36
  57. package/src/housing-context.js +183 -183
  58. package/src/housing-features.js +30 -30
  59. package/src/housing-intent.d.ts +9 -9
  60. package/src/housing-intent.js +132 -132
  61. package/src/housing-listing-fields.js +131 -131
  62. package/src/housing-money.d.ts +7 -7
  63. package/src/housing-money.js +102 -102
  64. package/src/housing-source-aliases.d.ts +8 -8
  65. package/src/housing-source-aliases.js +51 -51
  66. package/src/housing-structured.d.ts +55 -55
  67. package/src/housing-structured.js +219 -219
  68. package/src/housing.js +200 -200
  69. package/src/index.js +45 -44
  70. package/src/kz-location-extensions.js +307 -307
  71. package/src/landmarks.js +21 -21
  72. package/src/lexicon-core.js +58 -58
  73. package/src/location-merge.js +106 -106
  74. package/src/locations.js +456 -456
  75. package/src/money-core.d.ts +10 -10
  76. package/src/money-core.js +81 -81
  77. package/src/money-lexicon.d.ts +5 -5
  78. package/src/money-lexicon.js +79 -79
  79. package/src/money.js +154 -154
  80. package/src/normalization.js +384 -384
  81. package/src/odesa-metropolitan.js +144 -144
  82. package/src/romania-geography.js +47 -47
  83. package/src/tashkent-colloquial.js +80 -72
  84. package/src/tashkent-housing-geography.d.ts +38 -38
  85. package/src/tashkent-housing-geography.js +211 -211
  86. package/src/tashkent-pois.js +215 -215
  87. package/src/tashkent-residential-complexes.js +272 -272
  88. package/src/ua-location-extensions-major.js +118 -118
  89. package/src/ua-location-extensions-metro.js +9 -9
  90. package/src/ua-location-extensions-regional.js +246 -246
  91. package/src/ua-secondary-cities.d.ts +2 -2
  92. package/src/ua-secondary-cities.js +57 -57
  93. package/src/ukraine.js +75 -75
  94. package/src/uz-location-extensions.js +259 -259
package/src/contact.d.ts CHANGED
@@ -1,44 +1,44 @@
1
- export type PhoneLikeSpan = Readonly<{
2
- start: number;
3
- end: number;
4
- raw: string;
5
- digits: string;
6
- }>;
7
-
8
- export type ParsedPhoneNumber = Readonly<{
9
- start: number;
10
- end: number;
11
- raw: string;
12
- digits: string;
13
- number: string;
14
- nationalNumber: string;
15
- national: string;
16
- international: string;
17
- country: string | null;
18
- countryCallingCode: string;
19
- extension: string | null;
20
- valid: boolean;
21
- possible: boolean;
22
- }>;
23
-
24
- export type ParsePhoneOptions = Readonly<{
25
- countryHint?: string | null;
26
- includePossible?: boolean;
27
- }>;
28
-
29
- export type TelegramContact = Readonly<{
30
- start: number;
31
- end: number;
32
- raw: string;
33
- username: string;
34
- handle: string;
35
- url: string;
36
- source: 'mention' | 'url' | 'tg';
37
- }>;
38
-
39
- export function findPhoneLikeSpans(value: unknown): readonly PhoneLikeSpan[];
40
- export function maskPhoneLikeSpans(value: unknown, replacement?: string): string;
41
- export function parsePhoneNumbers(value: unknown, options?: ParsePhoneOptions): readonly ParsedPhoneNumber[];
42
- export function normalizePhone(value: unknown, options?: ParsePhoneOptions): ParsedPhoneNumber | null;
43
- export function findTelegramContacts(value: unknown): readonly TelegramContact[];
44
- export function normalizeTelegramContact(value: unknown): TelegramContact | null;
1
+ export type PhoneLikeSpan = Readonly<{
2
+ start: number;
3
+ end: number;
4
+ raw: string;
5
+ digits: string;
6
+ }>;
7
+
8
+ export type ParsedPhoneNumber = Readonly<{
9
+ start: number;
10
+ end: number;
11
+ raw: string;
12
+ digits: string;
13
+ number: string;
14
+ nationalNumber: string;
15
+ national: string;
16
+ international: string;
17
+ country: string | null;
18
+ countryCallingCode: string;
19
+ extension: string | null;
20
+ valid: boolean;
21
+ possible: boolean;
22
+ }>;
23
+
24
+ export type ParsePhoneOptions = Readonly<{
25
+ countryHint?: string | null;
26
+ includePossible?: boolean;
27
+ }>;
28
+
29
+ export type TelegramContact = Readonly<{
30
+ start: number;
31
+ end: number;
32
+ raw: string;
33
+ username: string;
34
+ handle: string;
35
+ url: string;
36
+ source: 'mention' | 'url' | 'tg';
37
+ }>;
38
+
39
+ export function findPhoneLikeSpans(value: unknown): readonly PhoneLikeSpan[];
40
+ export function maskPhoneLikeSpans(value: unknown, replacement?: string): string;
41
+ export function parsePhoneNumbers(value: unknown, options?: ParsePhoneOptions): readonly ParsedPhoneNumber[];
42
+ export function normalizePhone(value: unknown, options?: ParsePhoneOptions): ParsedPhoneNumber | null;
43
+ export function findTelegramContacts(value: unknown): readonly TelegramContact[];
44
+ export function normalizeTelegramContact(value: unknown): TelegramContact | null;
package/src/contact.js CHANGED
@@ -1,159 +1,159 @@
1
- import { parsePhoneNumberFromString } from 'libphonenumber-js/min';
2
-
3
- // Broad phone-like detection used by other parsers for exclusion/classification.
4
- // It deliberately stays tolerant and does not validate against a country plan.
5
- const PHONE_LIKE_RE = /\+?\d(?:[\t \u00a0().-]*\d){9,}/g;
6
-
7
- // Contact extraction may start from shorter national formats, but candidates are
8
- // only returned after libphonenumber validation.
9
- const PHONE_CANDIDATE_RE = /\+?\d(?:[\t \u00a0().-]*\d){6,}(?:[\t \u00a0]*(?:ext\.?|extension|x|доб\.?|дод\.?)\s*\d{1,6})?/giu;
10
- const PHONE_EXTENSION_RE = /[\t \u00a0]*(?:ext\.?|extension|x|доб\.?|дод\.?)\s*(\d{1,6})$/iu;
11
-
12
- const TELEGRAM_USERNAME_RE = /^[A-Za-z0-9_]{5,32}$/;
13
- const TELEGRAM_LINK_RE = /(?:https?:\/\/)?(?:t\.me|telegram\.me|telegram\.dog)\/([A-Za-z0-9_]{5,32})(?:\/[0-9]+)?(?:[/?#][^\s]*)?/giu;
14
- const TELEGRAM_TG_RE = /tg:\/\/resolve\?[^\s]*?\bdomain=([A-Za-z0-9_]{5,32})\b[^\s]*/giu;
15
- const TELEGRAM_MENTION_RE = /(^|[^\p{L}\p{N}_@])@([A-Za-z0-9_]{5,32})\b/gu;
16
-
17
- function normalizedCountryHint(value) {
18
- const country = String(value || '').trim().toUpperCase();
19
- return /^[A-Z]{2}$/.test(country) ? country : undefined;
20
- }
21
-
22
- function splitPhoneExtension(raw) {
23
- const match = String(raw || '').match(PHONE_EXTENSION_RE);
24
- if (!match) return { base: String(raw || '').trim(), extension: null };
25
- return {
26
- base: String(raw || '').slice(0, match.index).trim(),
27
- extension: match[1] || null,
28
- };
29
- }
30
-
31
- export function findPhoneLikeSpans(value) {
32
- const text = String(value || '');
33
- const spans = [];
34
- for (const match of text.matchAll(PHONE_LIKE_RE)) {
35
- const raw = match[0];
36
- const digits = raw.replace(/\D/g, '');
37
- if (digits.length < 10) continue;
38
- const start = match.index ?? 0;
39
- spans.push(Object.freeze({
40
- start,
41
- end: start + raw.length,
42
- raw,
43
- digits,
44
- }));
45
- }
46
- return Object.freeze(spans);
47
- }
48
-
49
- export function maskPhoneLikeSpans(value, replacement = ' ') {
50
- const text = String(value || '');
51
- const spans = findPhoneLikeSpans(text);
52
- if (!spans.length) return text;
53
-
54
- let out = '';
55
- let cursor = 0;
56
- for (const span of spans) {
57
- out += text.slice(cursor, span.start);
58
- out += replacement;
59
- cursor = span.end;
60
- }
61
- return out + text.slice(cursor);
62
- }
63
-
64
- export function parsePhoneNumbers(value, options = {}) {
65
- const text = String(value || '');
66
- const countryHint = normalizedCountryHint(options.countryHint);
67
- const includePossible = options.includePossible === true;
68
- const out = [];
69
- const seen = new Set();
70
-
71
- for (const match of text.matchAll(PHONE_CANDIDATE_RE)) {
72
- const raw = match[0].trim();
73
- const { base, extension } = splitPhoneExtension(raw);
74
- const parsed = parsePhoneNumberFromString(base, countryHint);
75
- if (!parsed) continue;
76
- const valid = parsed.isValid();
77
- const possible = parsed.isPossible();
78
- if (!valid && !(includePossible && possible)) continue;
79
-
80
- const key = `${parsed.number}:${extension || parsed.ext || ''}`;
81
- if (seen.has(key)) continue;
82
- seen.add(key);
83
-
84
- const start = match.index ?? 0;
85
- out.push(Object.freeze({
86
- start,
87
- end: start + match[0].length,
88
- raw,
89
- digits: base.replace(/\D/g, ''),
90
- number: parsed.number,
91
- nationalNumber: parsed.nationalNumber,
92
- national: parsed.formatNational(),
93
- international: parsed.formatInternational(),
94
- country: parsed.country || null,
95
- countryCallingCode: parsed.countryCallingCode,
96
- extension: extension || parsed.ext || null,
97
- valid,
98
- possible,
99
- }));
100
- }
101
-
102
- return Object.freeze(out);
103
- }
104
-
105
- export function normalizePhone(value, options = {}) {
106
- return parsePhoneNumbers(value, options)[0] || null;
107
- }
108
-
109
- function telegramContact(username, raw, start, source) {
110
- const normalized = String(username || '').replace(/^@/, '');
111
- if (!TELEGRAM_USERNAME_RE.test(normalized)) return null;
112
- return Object.freeze({
113
- start,
114
- end: start + raw.length,
115
- raw,
116
- username: normalized,
117
- handle: `@${normalized}`,
118
- url: `https://t.me/${normalized}`,
119
- source,
120
- });
121
- }
122
-
123
- /**
124
- * Extract public Telegram username contacts from mentions and links.
125
- * Message/channel transport parsing stays in consumers; this helper owns only
126
- * reusable contact normalization.
127
- */
128
- export function findTelegramContacts(value) {
129
- const text = String(value || '');
130
- const found = [];
131
- const seen = new Set();
132
-
133
- const push = (username, raw, start, source) => {
134
- const contact = telegramContact(username, raw, start, source);
135
- if (!contact) return;
136
- const key = contact.username.toLowerCase();
137
- if (seen.has(key)) return;
138
- seen.add(key);
139
- found.push(contact);
140
- };
141
-
142
- for (const match of text.matchAll(TELEGRAM_LINK_RE)) {
143
- push(match[1], match[0], match.index ?? 0, 'url');
144
- }
145
- for (const match of text.matchAll(TELEGRAM_TG_RE)) {
146
- push(match[1], match[0], match.index ?? 0, 'tg');
147
- }
148
- for (const match of text.matchAll(TELEGRAM_MENTION_RE)) {
149
- const prefix = match[1] || '';
150
- const raw = `@${match[2]}`;
151
- push(match[2], raw, (match.index ?? 0) + prefix.length, 'mention');
152
- }
153
-
154
- return Object.freeze(found.sort((a, b) => a.start - b.start));
155
- }
156
-
157
- export function normalizeTelegramContact(value) {
158
- return findTelegramContacts(value)[0] || null;
159
- }
1
+ import { parsePhoneNumberFromString } from 'libphonenumber-js/min';
2
+
3
+ // Broad phone-like detection used by other parsers for exclusion/classification.
4
+ // It deliberately stays tolerant and does not validate against a country plan.
5
+ const PHONE_LIKE_RE = /\+?\d(?:[\t \u00a0().-]*\d){9,}/g;
6
+
7
+ // Contact extraction may start from shorter national formats, but candidates are
8
+ // only returned after libphonenumber validation.
9
+ const PHONE_CANDIDATE_RE = /\+?\d(?:[\t \u00a0().-]*\d){6,}(?:[\t \u00a0]*(?:ext\.?|extension|x|доб\.?|дод\.?)\s*\d{1,6})?/giu;
10
+ const PHONE_EXTENSION_RE = /[\t \u00a0]*(?:ext\.?|extension|x|доб\.?|дод\.?)\s*(\d{1,6})$/iu;
11
+
12
+ const TELEGRAM_USERNAME_RE = /^[A-Za-z0-9_]{5,32}$/;
13
+ const TELEGRAM_LINK_RE = /(?:https?:\/\/)?(?:t\.me|telegram\.me|telegram\.dog)\/([A-Za-z0-9_]{5,32})(?:\/[0-9]+)?(?:[/?#][^\s]*)?/giu;
14
+ const TELEGRAM_TG_RE = /tg:\/\/resolve\?[^\s]*?\bdomain=([A-Za-z0-9_]{5,32})\b[^\s]*/giu;
15
+ const TELEGRAM_MENTION_RE = /(^|[^\p{L}\p{N}_@])@([A-Za-z0-9_]{5,32})\b/gu;
16
+
17
+ function normalizedCountryHint(value) {
18
+ const country = String(value || '').trim().toUpperCase();
19
+ return /^[A-Z]{2}$/.test(country) ? country : undefined;
20
+ }
21
+
22
+ function splitPhoneExtension(raw) {
23
+ const match = String(raw || '').match(PHONE_EXTENSION_RE);
24
+ if (!match) return { base: String(raw || '').trim(), extension: null };
25
+ return {
26
+ base: String(raw || '').slice(0, match.index).trim(),
27
+ extension: match[1] || null,
28
+ };
29
+ }
30
+
31
+ export function findPhoneLikeSpans(value) {
32
+ const text = String(value || '');
33
+ const spans = [];
34
+ for (const match of text.matchAll(PHONE_LIKE_RE)) {
35
+ const raw = match[0];
36
+ const digits = raw.replace(/\D/g, '');
37
+ if (digits.length < 10) continue;
38
+ const start = match.index ?? 0;
39
+ spans.push(Object.freeze({
40
+ start,
41
+ end: start + raw.length,
42
+ raw,
43
+ digits,
44
+ }));
45
+ }
46
+ return Object.freeze(spans);
47
+ }
48
+
49
+ export function maskPhoneLikeSpans(value, replacement = ' ') {
50
+ const text = String(value || '');
51
+ const spans = findPhoneLikeSpans(text);
52
+ if (!spans.length) return text;
53
+
54
+ let out = '';
55
+ let cursor = 0;
56
+ for (const span of spans) {
57
+ out += text.slice(cursor, span.start);
58
+ out += replacement;
59
+ cursor = span.end;
60
+ }
61
+ return out + text.slice(cursor);
62
+ }
63
+
64
+ export function parsePhoneNumbers(value, options = {}) {
65
+ const text = String(value || '');
66
+ const countryHint = normalizedCountryHint(options.countryHint);
67
+ const includePossible = options.includePossible === true;
68
+ const out = [];
69
+ const seen = new Set();
70
+
71
+ for (const match of text.matchAll(PHONE_CANDIDATE_RE)) {
72
+ const raw = match[0].trim();
73
+ const { base, extension } = splitPhoneExtension(raw);
74
+ const parsed = parsePhoneNumberFromString(base, countryHint);
75
+ if (!parsed) continue;
76
+ const valid = parsed.isValid();
77
+ const possible = parsed.isPossible();
78
+ if (!valid && !(includePossible && possible)) continue;
79
+
80
+ const key = `${parsed.number}:${extension || parsed.ext || ''}`;
81
+ if (seen.has(key)) continue;
82
+ seen.add(key);
83
+
84
+ const start = match.index ?? 0;
85
+ out.push(Object.freeze({
86
+ start,
87
+ end: start + match[0].length,
88
+ raw,
89
+ digits: base.replace(/\D/g, ''),
90
+ number: parsed.number,
91
+ nationalNumber: parsed.nationalNumber,
92
+ national: parsed.formatNational(),
93
+ international: parsed.formatInternational(),
94
+ country: parsed.country || null,
95
+ countryCallingCode: parsed.countryCallingCode,
96
+ extension: extension || parsed.ext || null,
97
+ valid,
98
+ possible,
99
+ }));
100
+ }
101
+
102
+ return Object.freeze(out);
103
+ }
104
+
105
+ export function normalizePhone(value, options = {}) {
106
+ return parsePhoneNumbers(value, options)[0] || null;
107
+ }
108
+
109
+ function telegramContact(username, raw, start, source) {
110
+ const normalized = String(username || '').replace(/^@/, '');
111
+ if (!TELEGRAM_USERNAME_RE.test(normalized)) return null;
112
+ return Object.freeze({
113
+ start,
114
+ end: start + raw.length,
115
+ raw,
116
+ username: normalized,
117
+ handle: `@${normalized}`,
118
+ url: `https://t.me/${normalized}`,
119
+ source,
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Extract public Telegram username contacts from mentions and links.
125
+ * Message/channel transport parsing stays in consumers; this helper owns only
126
+ * reusable contact normalization.
127
+ */
128
+ export function findTelegramContacts(value) {
129
+ const text = String(value || '');
130
+ const found = [];
131
+ const seen = new Set();
132
+
133
+ const push = (username, raw, start, source) => {
134
+ const contact = telegramContact(username, raw, start, source);
135
+ if (!contact) return;
136
+ const key = contact.username.toLowerCase();
137
+ if (seen.has(key)) return;
138
+ seen.add(key);
139
+ found.push(contact);
140
+ };
141
+
142
+ for (const match of text.matchAll(TELEGRAM_LINK_RE)) {
143
+ push(match[1], match[0], match.index ?? 0, 'url');
144
+ }
145
+ for (const match of text.matchAll(TELEGRAM_TG_RE)) {
146
+ push(match[1], match[0], match.index ?? 0, 'tg');
147
+ }
148
+ for (const match of text.matchAll(TELEGRAM_MENTION_RE)) {
149
+ const prefix = match[1] || '';
150
+ const raw = `@${match[2]}`;
151
+ push(match[2], raw, (match.index ?? 0) + prefix.length, 'mention');
152
+ }
153
+
154
+ return Object.freeze(found.sort((a, b) => a.start - b.start));
155
+ }
156
+
157
+ export function normalizeTelegramContact(value) {
158
+ return findTelegramContacts(value)[0] || null;
159
+ }