@indodev/toolkit 0.0.1

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.
@@ -0,0 +1,730 @@
1
+ 'use strict';
2
+
3
+ // src/phone/constants.ts
4
+ var OPERATOR_PREFIXES = {
5
+ // Telkomsel (Halo, Simpati, by.U)
6
+ "0811": "Telkomsel",
7
+ "0812": "Telkomsel",
8
+ "0813": "Telkomsel",
9
+ "0821": "Telkomsel",
10
+ "0822": "Telkomsel",
11
+ "0823": "Telkomsel",
12
+ "0851": "Telkomsel",
13
+ "0852": "Telkomsel",
14
+ "0853": "Telkomsel",
15
+ // XL Axiata (XL Prepaid, XL Prioritas, LIVE.ON)
16
+ "0817": "XL",
17
+ "0818": "XL",
18
+ "0819": "XL",
19
+ "0859": "XL",
20
+ "0877": "XL",
21
+ "0878": "XL",
22
+ "0879": "XL",
23
+ // Indosat Ooredoo (IM3, Mentari)
24
+ // Note: Tri (3 Indonesia) merged with Indosat
25
+ "0814": "Indosat",
26
+ "0815": "Indosat",
27
+ "0816": "Indosat",
28
+ "0855": "Indosat",
29
+ "0856": "Indosat",
30
+ "0857": "Indosat",
31
+ "0858": "Indosat",
32
+ "0895": "Indosat",
33
+ "0896": "Indosat",
34
+ "0897": "Indosat",
35
+ "0898": "Indosat",
36
+ "0899": "Indosat",
37
+ // Smartfren (Smartfren Power Up)
38
+ "0881": "Smartfren",
39
+ "0882": "Smartfren",
40
+ "0883": "Smartfren",
41
+ "0884": "Smartfren",
42
+ "0885": "Smartfren",
43
+ "0886": "Smartfren",
44
+ "0887": "Smartfren",
45
+ "0888": "Smartfren",
46
+ "0889": "Smartfren",
47
+ // Axis (Acquired by XL but maintains separate branding)
48
+ "0831": "Axis",
49
+ "0832": "Axis",
50
+ "0833": "Axis",
51
+ "0838": "Axis"
52
+ };
53
+ var AREA_CODES = {
54
+ // ========================================
55
+ // JAKARTA, BANTEN & WEST JAVA
56
+ // ========================================
57
+ // Jakarta & Greater Jakarta
58
+ "021": "Jakarta",
59
+ // Banten
60
+ "0252": "Lebak",
61
+ "0253": "Pandeglang",
62
+ "0254": "Cilegon & Serang",
63
+ // West Java
64
+ "022": "Bandung",
65
+ "0231": "Cirebon",
66
+ "0232": "Kuningan",
67
+ "0233": "Majalengka",
68
+ "0234": "Indramayu",
69
+ "0251": "Bogor",
70
+ "0260": "Subang",
71
+ "0261": "Sumedang",
72
+ "0262": "Garut",
73
+ "0263": "Cianjur",
74
+ "0264": "Purwakarta",
75
+ "0265": "Tasikmalaya",
76
+ "0266": "Sukabumi",
77
+ "0267": "Karawang",
78
+ // ========================================
79
+ // CENTRAL JAVA & YOGYAKARTA
80
+ // ========================================
81
+ // Central Java
82
+ "024": "Semarang",
83
+ "0271": "Solo",
84
+ "0272": "Klaten",
85
+ "0273": "Wonogiri",
86
+ "0275": "Purworejo",
87
+ "0276": "Boyolali",
88
+ "0280": "Cilacap",
89
+ "0281": "Banyumas & Purbalingga",
90
+ "0282": "Cilacap",
91
+ "0283": "Tegal & Brebes",
92
+ "0284": "Pemalang",
93
+ "0285": "Pekalongan",
94
+ "0286": "Banjarnegara & Wonosobo",
95
+ "0287": "Kebumen",
96
+ "0289": "Bumiayu",
97
+ "0291": "Kudus & Jepara",
98
+ "0292": "Grobogan",
99
+ "0293": "Magelang",
100
+ "0294": "Kendal",
101
+ "0295": "Pati & Rembang",
102
+ "0296": "Blora",
103
+ "0297": "Karimun Jawa",
104
+ "0298": "Salatiga",
105
+ // Yogyakarta
106
+ "0274": "Yogyakarta",
107
+ // ========================================
108
+ // EAST JAVA, BALI & NUSA TENGGARA
109
+ // ========================================
110
+ // East Java
111
+ "031": "Surabaya",
112
+ "0321": "Mojokerto & Jombang",
113
+ "0322": "Lamongan",
114
+ "0323": "Sampang",
115
+ "0324": "Pamekasan",
116
+ "0325": "Bawean",
117
+ "0326": "Masalembu",
118
+ "0327": "Kangean",
119
+ "0328": "Sumenep",
120
+ "0331": "Jember",
121
+ "0332": "Bondowoso",
122
+ "0333": "Banyuwangi",
123
+ "0334": "Lumajang",
124
+ "0335": "Probolinggo",
125
+ "0336": "Jember",
126
+ "0338": "Situbondo",
127
+ "0341": "Malang",
128
+ "0342": "Blitar",
129
+ "0343": "Pasuruan",
130
+ "0351": "Madiun",
131
+ "0352": "Ponorogo",
132
+ "0353": "Bojonegoro",
133
+ "0354": "Kediri",
134
+ "0355": "Tulungagung",
135
+ "0356": "Tuban",
136
+ "0357": "Pacitan",
137
+ "0358": "Nganjuk",
138
+ // Bali
139
+ "0361": "Denpasar",
140
+ "0362": "Singaraja",
141
+ "0363": "Amlapura",
142
+ "0365": "Negara",
143
+ "0366": "Tabanan",
144
+ "0368": "Gianyar",
145
+ // Nusa Tenggara Barat (NTB)
146
+ "0370": "Mataram",
147
+ "0371": "Sumbawa",
148
+ "0372": "West Sumbawa",
149
+ "0373": "Dompu",
150
+ "0374": "Bima",
151
+ "0376": "East Lombok",
152
+ // Nusa Tenggara Timur (NTT)
153
+ "0379": "Alor",
154
+ "0380": "Kupang",
155
+ "0381": "Ende",
156
+ "0382": "Sikka",
157
+ "0383": "East Flores",
158
+ "0384": "Ngada",
159
+ "0385": "Manggarai",
160
+ "0386": "West Manggarai",
161
+ "0387": "Sumba",
162
+ "0388": "North & South Central Timor",
163
+ "0389": "Belu",
164
+ // ========================================
165
+ // SULAWESI
166
+ // ========================================
167
+ // South Sulawesi
168
+ "0410": "Pangkajene",
169
+ "0411": "Makassar",
170
+ "0413": "Bantaeng & Bulukumba",
171
+ "0414": "Selayar",
172
+ "0417": "Malino",
173
+ "0418": "Takalar",
174
+ "0419": "Jeneponto",
175
+ "0420": "Enrekang",
176
+ "0421": "Pare Pare",
177
+ "0423": "Tana Toraja",
178
+ "0427": "Barru",
179
+ "0471": "Luwu",
180
+ "0472": "Wajo (Pitumpanua)",
181
+ "0473": "North Luwu",
182
+ "0474": "East Luwu",
183
+ "0475": "Sorowako",
184
+ "0481": "Bone",
185
+ "0482": "Sinjai",
186
+ "0484": "Soppeng",
187
+ "0485": "Wajo",
188
+ // West Sulawesi
189
+ "0422": "Majene",
190
+ "0426": "Mamuju",
191
+ "0428": "Polewali",
192
+ "0429": "Central Mamuju",
193
+ // Central Sulawesi
194
+ "0409": "Morowali",
195
+ "0445": "Buol",
196
+ "0450": "Parigi Moutong",
197
+ "0451": "Palu",
198
+ "0452": "Poso",
199
+ "0453": "Toli-Toli",
200
+ "0454": "Tinombo",
201
+ "0455": "Moutong",
202
+ "0457": "Donggala",
203
+ "0458": "Tentena",
204
+ "0461": "Banggai",
205
+ "0462": "Banggai Island",
206
+ "0463": "Bunta",
207
+ "0464": "Tojo Una-Una",
208
+ "0465": "North Morowali",
209
+ // Southeast Sulawesi
210
+ "0401": "Kendari",
211
+ "0402": "Buton",
212
+ "0403": "Muna",
213
+ "0404": "Wakatobi",
214
+ "0405": "Kolaka",
215
+ "0408": "Konawe",
216
+ // North Sulawesi
217
+ "0430": "South Minahasa",
218
+ "0431": "Manado",
219
+ "0432": "Sangihe",
220
+ "0433": "Talaud",
221
+ "0434": "Bolaang Mongondow",
222
+ "0438": "Bitung",
223
+ // Gorontalo
224
+ "0435": "Gorontalo",
225
+ "0442": "North Gorontalo",
226
+ "0443": "Pohuwato",
227
+ // ========================================
228
+ // KALIMANTAN
229
+ // ========================================
230
+ // West Kalimantan
231
+ "0534": "Ketapang",
232
+ "0535": "Kayong Utara",
233
+ "0561": "Pontianak",
234
+ "0562": "Sambas & Singkawang",
235
+ "0563": "Landak",
236
+ "0564": "Sanggau",
237
+ "0565": "Sintang",
238
+ "0567": "Kapuas Hulu",
239
+ "0568": "Melawi",
240
+ // Central Kalimantan
241
+ "0513": "Kapuas",
242
+ "0519": "North Barito",
243
+ "0526": "South & East Barito",
244
+ "0528": "Murung Raya",
245
+ "0531": "East Kotawaringin",
246
+ "0532": "West Kotawaringin",
247
+ "0536": "Palangka Raya",
248
+ "0537": "Gunung Mas",
249
+ "0538": "Seruyan",
250
+ "0539": "Seruyan & East Kotawaringin",
251
+ // South Kalimantan
252
+ "0511": "Banjarmasin",
253
+ "0512": "Tanah Laut",
254
+ "0517": "Hulu Sungai Selatan",
255
+ "0518": "Tanah Bumbu",
256
+ "0527": "Hulu Sungai Utara",
257
+ // East Kalimantan
258
+ "0541": "Samarinda",
259
+ "0542": "Balikpapan",
260
+ "0543": "Paser",
261
+ "0545": "West Kutai",
262
+ "0548": "Bontang",
263
+ "0549": "East Kutai",
264
+ "0554": "Berau",
265
+ // North Kalimantan
266
+ "0551": "Tarakan",
267
+ "0552": "Bulungan",
268
+ "0553": "Malinau",
269
+ "0556": "Nunukan",
270
+ // ========================================
271
+ // SUMATRA
272
+ // ========================================
273
+ // Aceh
274
+ "0627": "Subulussalam & Dairi (North Sumatra)",
275
+ "0629": "Southeast Aceh",
276
+ "0641": "Langsa",
277
+ "0642": "Gayo Lues",
278
+ "0643": "Central Aceh",
279
+ "0644": "Bireuen",
280
+ "0645": "Lhokseumawe",
281
+ "0646": "East Aceh",
282
+ "0650": "Simeulue",
283
+ "0651": "Banda Aceh",
284
+ "0652": "Sabang",
285
+ "0653": "Pidie",
286
+ "0654": "Aceh Jaya",
287
+ "0655": "West Aceh",
288
+ "0656": "South Aceh",
289
+ "0657": "South Aceh",
290
+ "0658": "Singkil",
291
+ "0659": "Southwest Aceh",
292
+ // North Sumatra
293
+ "061": "Medan",
294
+ "0620": "Pangkalan Brandan",
295
+ "0621": "Tebing Tinggi",
296
+ "0622": "Pematang Siantar",
297
+ "0623": "Asahan",
298
+ "0624": "Labuhan Batu",
299
+ "0625": "Parapat",
300
+ "0626": "Samosir",
301
+ // '0627': 'Dairi', // for this prefix, it same with Subulussalam (Aceh)
302
+ "0628": "Karo",
303
+ "0630": "South Nias",
304
+ "0631": "Sibolga",
305
+ "0632": "Toba Samosir",
306
+ "0633": "North Tapanuli",
307
+ "0634": "Padang Sidempuan",
308
+ "0635": "South Tapanuli",
309
+ "0636": "Mandailing Natal",
310
+ "0638": "Barus",
311
+ "0639": "Nias",
312
+ // West Sumatra
313
+ "0751": "Padang",
314
+ "0752": "Bukittinggi",
315
+ "0753": "Pasaman",
316
+ "0754": "Sawahlunto",
317
+ "0755": "Solok",
318
+ "0756": "South Pesisir",
319
+ "0757": "South Pesisir",
320
+ "0759": "Mentawai",
321
+ // Riau
322
+ "0760": "Kuantan Singingi",
323
+ "0761": "Pekanbaru",
324
+ "0762": "Kampar",
325
+ "0763": "Bengkalis",
326
+ "0764": "Siak",
327
+ "0765": "Dumai",
328
+ "0766": "Bengkalis",
329
+ "0767": "Rokan Hulu",
330
+ "0768": "Indragiri Hilir",
331
+ "0769": "Indragiri Hulu",
332
+ // Riau Islands
333
+ "0770": "Muka Kuning Batamindo",
334
+ "0771": "Tanjungpinang",
335
+ "0772": "Anambas",
336
+ "0773": "Natuna",
337
+ "0776": "Lingga",
338
+ "0777": "Great Karimun",
339
+ "0778": "Batam",
340
+ "0779": "Kundur",
341
+ // Jambi
342
+ "0741": "Jambi",
343
+ "0742": "West Tanjung Jabung",
344
+ "0743": "Batanghari",
345
+ "0744": "Tebo",
346
+ "0745": "Sarolangun",
347
+ "0746": "Merangin",
348
+ "0747": "Bungo",
349
+ "0748": "Kerinci",
350
+ // South Sumatra
351
+ "0702": "Empat Lawang",
352
+ "0711": "Palembang",
353
+ "0712": "Ogan Komering Ilir",
354
+ "0713": "Prabumulih",
355
+ "0714": "Musi Banyuasin",
356
+ "0730": "Pagar Alam",
357
+ "0731": "Lahat",
358
+ "0733": "Lubuklinggau",
359
+ "0734": "Muara Enim",
360
+ "0735": "Ogan Komering Ulu",
361
+ // Bangka Belitung
362
+ "0715": "Belinyu",
363
+ "0716": "West Bangka",
364
+ "0717": "Pangkal Pinang",
365
+ "0718": "Central & South Bangka",
366
+ "0719": "Belitung",
367
+ // Bengkulu
368
+ "0732": "Rejang Lebong",
369
+ "0736": "Bengkulu",
370
+ "0737": "North Bengkulu",
371
+ "0739": "South Bengkulu",
372
+ // Lampung
373
+ "0721": "Bandar Lampung",
374
+ "0722": "Tanggamus",
375
+ "0723": "Way Kanan",
376
+ "0724": "North Lampung",
377
+ "0725": "Metro",
378
+ "0726": "Tulang Bawang",
379
+ "0727": "South Lampung",
380
+ "0728": "West Lampung",
381
+ "0729": "Pringsewu",
382
+ // ========================================
383
+ // MALUKU & PAPUA
384
+ // ========================================
385
+ // Maluku
386
+ "0910": "Ambon",
387
+ "0911": "Southeast Maluku",
388
+ "0913": "Tual",
389
+ "0914": "Saumlaki",
390
+ "0916": "Namlea",
391
+ "0918": "Ternate",
392
+ "0921": "Sanana",
393
+ "0924": "Tobelo",
394
+ // Papua & West Papua
395
+ "0901": "Timika",
396
+ "0902": "Agats",
397
+ "0951": "Sorong",
398
+ "0952": "South Sorong",
399
+ "0967": "Manokwari",
400
+ "0969": "Sorong",
401
+ "0971": "Merauke",
402
+ "0975": "Boven Digoel",
403
+ "0979": "Tembagapura",
404
+ "0981": "Jayapura",
405
+ "0986": "Wamena"
406
+ };
407
+
408
+ // src/phone/validate.ts
409
+ function validatePhoneNumber(phone) {
410
+ if (!phone || typeof phone !== "string") {
411
+ return false;
412
+ }
413
+ if (!/^[\d\s\-+().]+$/.test(phone)) {
414
+ return false;
415
+ }
416
+ const cleaned = phone.replace(/[\s\-().]/g, "");
417
+ let normalized;
418
+ if (cleaned.startsWith("+62")) {
419
+ normalized = "0" + cleaned.substring(3);
420
+ } else if (cleaned.startsWith("62") && !cleaned.startsWith("620")) {
421
+ normalized = "0" + cleaned.substring(2);
422
+ } else if (cleaned.startsWith("0")) {
423
+ normalized = cleaned;
424
+ } else {
425
+ return false;
426
+ }
427
+ if (normalized.startsWith("08")) {
428
+ return validateMobileNumber(normalized);
429
+ }
430
+ if (normalized.startsWith("0")) {
431
+ return validateLandlineNumber(normalized);
432
+ }
433
+ return false;
434
+ }
435
+ function validateMobileNumber(phone) {
436
+ if (phone.length < 10 || phone.length > 13) {
437
+ return false;
438
+ }
439
+ const prefix = phone.substring(0, 4);
440
+ if (!OPERATOR_PREFIXES[prefix]) {
441
+ return false;
442
+ }
443
+ if (!/^\d+$/.test(phone)) {
444
+ return false;
445
+ }
446
+ return true;
447
+ }
448
+ function validateLandlineNumber(phone) {
449
+ if (phone.length < 9 || phone.length > 11) {
450
+ return false;
451
+ }
452
+ const areaCode3 = phone.substring(0, 3);
453
+ const areaCode4 = phone.substring(0, 4);
454
+ if (AREA_CODES[areaCode3] || AREA_CODES[areaCode4]) {
455
+ return true;
456
+ }
457
+ if (/^0[2-9]\d{7,9}$/.test(phone)) {
458
+ return true;
459
+ }
460
+ return false;
461
+ }
462
+ function isMobileNumber(phone) {
463
+ if (!validatePhoneNumber(phone)) {
464
+ return false;
465
+ }
466
+ const cleaned = phone.replace(/[^\d+]/g, "");
467
+ let normalized;
468
+ if (cleaned.startsWith("+62")) {
469
+ normalized = "0" + cleaned.substring(3);
470
+ } else if (cleaned.startsWith("62")) {
471
+ normalized = "0" + cleaned.substring(2);
472
+ } else {
473
+ normalized = cleaned;
474
+ }
475
+ return normalized.startsWith("08");
476
+ }
477
+ function isLandlineNumber(phone) {
478
+ if (!validatePhoneNumber(phone)) {
479
+ return false;
480
+ }
481
+ return !isMobileNumber(phone);
482
+ }
483
+
484
+ // src/phone/format.ts
485
+ function formatPhoneNumber(phone, format = "national") {
486
+ if (!validatePhoneNumber(phone)) {
487
+ return phone;
488
+ }
489
+ const cleaned = cleanPhoneNumber(phone);
490
+ let normalized;
491
+ if (cleaned.startsWith("+62")) {
492
+ normalized = "0" + cleaned.substring(3);
493
+ } else if (cleaned.startsWith("62") && !cleaned.startsWith("620")) {
494
+ normalized = "0" + cleaned.substring(2);
495
+ } else {
496
+ normalized = cleaned;
497
+ }
498
+ switch (format) {
499
+ case "international":
500
+ return toInternational(normalized);
501
+ case "national":
502
+ case "display":
503
+ return toNational(normalized);
504
+ case "e164":
505
+ return toE164(normalized);
506
+ default:
507
+ return phone;
508
+ }
509
+ }
510
+ function toInternational(phone) {
511
+ const cleaned = cleanPhoneNumber(phone);
512
+ if (!cleaned) {
513
+ return phone;
514
+ }
515
+ const normalized = normalizeToNational(cleaned);
516
+ if (!normalized) {
517
+ return phone;
518
+ }
519
+ const withoutZero = normalized.substring(1);
520
+ if (normalized.startsWith("08")) {
521
+ if (withoutZero.length === 11) {
522
+ return `+62 ${withoutZero.substring(0, 3)}-${withoutZero.substring(3, 7)}-${withoutZero.substring(7)}`;
523
+ } else if (withoutZero.length === 10) {
524
+ return `+62 ${withoutZero.substring(0, 3)}-${withoutZero.substring(3, 6)}-${withoutZero.substring(6)}`;
525
+ } else if (withoutZero.length === 9) {
526
+ return `+62 ${withoutZero.substring(0, 3)}-${withoutZero.substring(3)}`;
527
+ } else if (withoutZero.length === 12) {
528
+ return `+62 ${withoutZero.substring(0, 3)}-${withoutZero.substring(3, 7)}-${withoutZero.substring(7)}`;
529
+ }
530
+ }
531
+ const areaCodeLength = getAreaCodeLength(normalized);
532
+ const areaCode = normalized.substring(1, areaCodeLength + 1);
533
+ const localNumber = normalized.substring(areaCodeLength + 1);
534
+ return `+62 ${areaCode}-${localNumber}`;
535
+ }
536
+ function toNational(phone) {
537
+ const cleaned = cleanPhoneNumber(phone);
538
+ if (!cleaned) {
539
+ return phone;
540
+ }
541
+ const normalized = normalizeToNational(cleaned);
542
+ if (!normalized) {
543
+ return phone;
544
+ }
545
+ if (normalized.startsWith("08")) {
546
+ if (normalized.length === 12) {
547
+ return `${normalized.substring(0, 4)}-${normalized.substring(4, 8)}-${normalized.substring(8)}`;
548
+ } else if (normalized.length === 11) {
549
+ return `${normalized.substring(0, 4)}-${normalized.substring(4, 7)}-${normalized.substring(7)}`;
550
+ } else if (normalized.length === 10) {
551
+ return `${normalized.substring(0, 4)}-${normalized.substring(4)}`;
552
+ } else if (normalized.length === 13) {
553
+ return `${normalized.substring(0, 4)}-${normalized.substring(4, 8)}-${normalized.substring(8)}`;
554
+ }
555
+ }
556
+ const areaCodeLength = getAreaCodeLength(normalized);
557
+ const areaCodeWithZero = normalized.substring(0, areaCodeLength + 1);
558
+ const localNumber = normalized.substring(areaCodeLength + 1);
559
+ return `${areaCodeWithZero}-${localNumber}`;
560
+ }
561
+ function toE164(phone) {
562
+ const cleaned = cleanPhoneNumber(phone);
563
+ if (!cleaned) {
564
+ return phone;
565
+ }
566
+ const normalized = normalizeToNational(cleaned);
567
+ if (!normalized) {
568
+ return phone;
569
+ }
570
+ return "62" + normalized.substring(1);
571
+ }
572
+ function cleanPhoneNumber(phone) {
573
+ if (!phone || typeof phone !== "string") {
574
+ return "";
575
+ }
576
+ return phone.replace(/[^\d+]/g, "");
577
+ }
578
+ function normalizeToNational(phone) {
579
+ if (phone.startsWith("+62")) {
580
+ return "0" + phone.substring(3);
581
+ } else if (phone.startsWith("62") && !phone.startsWith("620")) {
582
+ return "0" + phone.substring(2);
583
+ } else if (phone.startsWith("0")) {
584
+ return phone;
585
+ }
586
+ return "";
587
+ }
588
+ function getAreaCodeLength(normalized) {
589
+ const fourDigitCode = normalized.substring(0, 5);
590
+ if (AREA_CODES[fourDigitCode]) {
591
+ return 4;
592
+ }
593
+ const threeDigitCode = normalized.substring(0, 4);
594
+ if (AREA_CODES[threeDigitCode]) {
595
+ return 3;
596
+ }
597
+ const twoDigitCode = normalized.substring(0, 3);
598
+ if (AREA_CODES[twoDigitCode]) {
599
+ return 2;
600
+ }
601
+ return 2;
602
+ }
603
+ function maskPhoneNumber(phone, options = {}) {
604
+ const cleaned = cleanPhoneNumber(phone);
605
+ if (!cleaned) {
606
+ return phone;
607
+ }
608
+ const isInternational = cleaned.startsWith("+");
609
+ let toMask;
610
+ if (isInternational) {
611
+ toMask = cleaned;
612
+ } else {
613
+ const normalized = normalizeToNational(cleaned);
614
+ toMask = normalized || cleaned;
615
+ }
616
+ if (toMask.length < 4) {
617
+ return phone;
618
+ }
619
+ const { char = "*", separator } = options;
620
+ let { start = 4, end = 4 } = options;
621
+ if (start + end >= toMask.length) {
622
+ if (toMask.length < 10) {
623
+ const minMaskLength = 1;
624
+ const availableForVisible = toMask.length - minMaskLength;
625
+ if (availableForVisible >= 2) {
626
+ start = Math.floor(availableForVisible / 2);
627
+ end = availableForVisible - start;
628
+ } else {
629
+ return toMask;
630
+ }
631
+ } else {
632
+ return toMask;
633
+ }
634
+ }
635
+ const startPart = toMask.substring(0, start);
636
+ const endPart = toMask.substring(toMask.length - end);
637
+ const maskLength = toMask.length - start - end;
638
+ const masked = startPart + char.repeat(maskLength) + endPart;
639
+ if (separator) {
640
+ return `${masked.substring(0, start)}${separator}${masked.substring(start, masked.length - end)}${separator}${masked.substring(masked.length - end)}`;
641
+ }
642
+ return masked;
643
+ }
644
+
645
+ // src/phone/parse.ts
646
+ function parsePhoneNumber(phone) {
647
+ if (!validatePhoneNumber(phone)) {
648
+ return null;
649
+ }
650
+ const cleaned = cleanPhoneNumber(phone);
651
+ const normalized = normalizeToNational2(cleaned);
652
+ if (!normalized) {
653
+ return null;
654
+ }
655
+ const countryCode = "62";
656
+ const number = normalized.substring(1);
657
+ const isMobile = normalized.startsWith("08");
658
+ const isLandline = !isMobile;
659
+ let operator = null;
660
+ let region = null;
661
+ if (isMobile) {
662
+ operator = getOperator(normalized);
663
+ } else {
664
+ region = getRegion(normalized);
665
+ }
666
+ return {
667
+ countryCode,
668
+ operator,
669
+ number,
670
+ formatted: {
671
+ international: toInternational(normalized),
672
+ national: toNational(normalized),
673
+ e164: toE164(normalized)
674
+ },
675
+ isValid: true,
676
+ isMobile,
677
+ isLandline,
678
+ region
679
+ };
680
+ }
681
+ function getOperator(phone) {
682
+ if (!isMobileNumber(phone)) {
683
+ return null;
684
+ }
685
+ const cleaned = cleanPhoneNumber(phone);
686
+ const normalized = normalizeToNational2(cleaned);
687
+ if (!normalized || normalized.length < 4) {
688
+ return null;
689
+ }
690
+ const prefix = normalized.substring(0, 4);
691
+ return OPERATOR_PREFIXES[prefix] || null;
692
+ }
693
+ function getRegion(phone) {
694
+ if (!phone.startsWith("0")) {
695
+ return null;
696
+ }
697
+ const areaCode4 = phone.substring(0, 4);
698
+ if (AREA_CODES[areaCode4]) {
699
+ return AREA_CODES[areaCode4];
700
+ }
701
+ const areaCode3 = phone.substring(0, 3);
702
+ if (AREA_CODES[areaCode3]) {
703
+ return AREA_CODES[areaCode3];
704
+ }
705
+ return null;
706
+ }
707
+ function normalizeToNational2(phone) {
708
+ if (phone.startsWith("+62")) {
709
+ return "0" + phone.substring(3);
710
+ } else if (phone.startsWith("62")) {
711
+ return "0" + phone.substring(2);
712
+ } else if (phone.startsWith("0")) {
713
+ return phone;
714
+ }
715
+ return "";
716
+ }
717
+
718
+ exports.cleanPhoneNumber = cleanPhoneNumber;
719
+ exports.formatPhoneNumber = formatPhoneNumber;
720
+ exports.getOperator = getOperator;
721
+ exports.isLandlineNumber = isLandlineNumber;
722
+ exports.isMobileNumber = isMobileNumber;
723
+ exports.maskPhoneNumber = maskPhoneNumber;
724
+ exports.parsePhoneNumber = parsePhoneNumber;
725
+ exports.toE164 = toE164;
726
+ exports.toInternational = toInternational;
727
+ exports.toNational = toNational;
728
+ exports.validatePhoneNumber = validatePhoneNumber;
729
+ //# sourceMappingURL=index.cjs.map
730
+ //# sourceMappingURL=index.cjs.map