@indodev/toolkit 0.4.0 → 0.4.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.
- package/README.md +19 -3
- package/dist/index.cjs +66 -49
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +66 -49
- package/dist/index.js.map +1 -1
- package/dist/parse-BDfy3aQw.d.cts +542 -0
- package/dist/parse-BDfy3aQw.d.ts +542 -0
- package/dist/phone/index.cjs +82 -49
- package/dist/phone/index.cjs.map +1 -1
- package/dist/phone/index.d.cts +38 -485
- package/dist/phone/index.d.ts +38 -485
- package/dist/phone/index.js +80 -50
- package/dist/phone/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
</div>
|
|
11
11
|
|
|
12
|
-
TypeScript utilities for Indonesian data. Handles Rupiah formatting, terbilang, NIK validation, phone normalization, and text rules that generic libraries don't cover.
|
|
12
|
+
TypeScript utilities for Indonesian data. Handles Rupiah formatting, terbilang, NIK validation, phone normalization, date formatting, and text rules that generic libraries don't cover.
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -64,12 +64,27 @@ toTitleCase('pt bank central asia tbk'); // 'PT Bank Central Asia Tbk'
|
|
|
64
64
|
slugify('Pria & Wanita'); // 'pria-dan-wanita'
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
Format dates with Indonesian locale:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import {
|
|
71
|
+
formatDate,
|
|
72
|
+
parseDate,
|
|
73
|
+
toRelativeTime,
|
|
74
|
+
} from '@indodev/toolkit/datetime';
|
|
75
|
+
|
|
76
|
+
formatDate(new Date('2026-01-02'), 'long'); // '2 Januari 2026'
|
|
77
|
+
parseDate('02-01-2026'); // Date(2026, 0, 2)
|
|
78
|
+
toRelativeTime(new Date(Date.now() - 3600000)); // '1 jam yang lalu'
|
|
79
|
+
```
|
|
80
|
+
|
|
67
81
|
## Modules
|
|
68
82
|
|
|
69
83
|
| Module | Description |
|
|
70
84
|
| --------------------------------------------------------------- | -------------------------------------------------------------- |
|
|
71
|
-
| [Currency](https://toolkit.adamm.cloud/docs/
|
|
72
|
-
| [Text](https://toolkit.adamm.cloud/docs/
|
|
85
|
+
| [Currency](https://toolkit.adamm.cloud/docs/utilities/currency) | Format Rupiah, terbilang, split amounts, percentages |
|
|
86
|
+
| [Text](https://toolkit.adamm.cloud/docs/utilities/text) | Title case, slugs, abbreviations, case conversion, masking |
|
|
87
|
+
| [DateTime](https://toolkit.adamm.cloud/docs/utilities/datetime) | Indonesian date formatting, relative time, age calculation |
|
|
73
88
|
| [NIK](https://toolkit.adamm.cloud/docs/identity/nik) | Validate, parse, and mask Indonesian National Identity Numbers |
|
|
74
89
|
| [NPWP](https://toolkit.adamm.cloud/docs/identity/npwp) | Validate and format Tax Identification Numbers |
|
|
75
90
|
| [Phone](https://toolkit.adamm.cloud/docs/contact/phone) | Format, validate, and detect mobile operators |
|
|
@@ -77,6 +92,7 @@ slugify('Pria & Wanita'); // 'pria-dan-wanita'
|
|
|
77
92
|
| [Plate](https://toolkit.adamm.cloud/docs/vehicles/plate) | Validate license plates with region detection |
|
|
78
93
|
| [VIN](https://toolkit.adamm.cloud/docs/vehicles/vin) | Validate Vehicle Identification Numbers (ISO 3779) |
|
|
79
94
|
|
|
95
|
+
|
|
80
96
|
Full docs, examples, and API reference at [toolkit.adamm.cloud](https://toolkit.adamm.cloud/docs)
|
|
81
97
|
|
|
82
98
|
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -717,7 +717,7 @@ function isMobileNumber(phone) {
|
|
|
717
717
|
let normalized;
|
|
718
718
|
if (cleaned.startsWith("+62")) {
|
|
719
719
|
normalized = "0" + cleaned.substring(3);
|
|
720
|
-
} else if (cleaned.startsWith("62")) {
|
|
720
|
+
} else if (cleaned.startsWith("62") && !cleaned.startsWith("620")) {
|
|
721
721
|
normalized = "0" + cleaned.substring(2);
|
|
722
722
|
} else {
|
|
723
723
|
normalized = cleaned;
|
|
@@ -731,19 +731,70 @@ function isLandlineNumber(phone) {
|
|
|
731
731
|
return !isMobileNumber(phone);
|
|
732
732
|
}
|
|
733
733
|
|
|
734
|
+
// src/phone/utils.ts
|
|
735
|
+
function normalizePhoneNumber(phone) {
|
|
736
|
+
if (!phone || typeof phone !== "string") {
|
|
737
|
+
return "";
|
|
738
|
+
}
|
|
739
|
+
if (phone.startsWith("+62")) {
|
|
740
|
+
return "0" + phone.substring(3);
|
|
741
|
+
}
|
|
742
|
+
if (phone.startsWith("62") && !phone.startsWith("620")) {
|
|
743
|
+
return "0" + phone.substring(2);
|
|
744
|
+
}
|
|
745
|
+
if (phone.startsWith("0")) {
|
|
746
|
+
return phone;
|
|
747
|
+
}
|
|
748
|
+
return "";
|
|
749
|
+
}
|
|
750
|
+
function normalizeToNational(phone) {
|
|
751
|
+
if (phone.startsWith("+62")) {
|
|
752
|
+
return "0" + phone.substring(3);
|
|
753
|
+
}
|
|
754
|
+
if (phone.startsWith("62") && !phone.startsWith("620")) {
|
|
755
|
+
return "0" + phone.substring(2);
|
|
756
|
+
}
|
|
757
|
+
if (phone.startsWith("0")) {
|
|
758
|
+
return phone;
|
|
759
|
+
}
|
|
760
|
+
return "";
|
|
761
|
+
}
|
|
762
|
+
function getLandlineRegion(phone) {
|
|
763
|
+
if (!phone || typeof phone !== "string") {
|
|
764
|
+
return null;
|
|
765
|
+
}
|
|
766
|
+
const cleaned = phone.replace(/[^\d+]/g, "");
|
|
767
|
+
const normalized = normalizeToNational(cleaned);
|
|
768
|
+
if (!normalized || !normalized.startsWith("0")) {
|
|
769
|
+
return null;
|
|
770
|
+
}
|
|
771
|
+
if (normalized.startsWith("08")) {
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
774
|
+
const areaCode4 = normalized.substring(0, 4);
|
|
775
|
+
if (AREA_CODES[areaCode4]) {
|
|
776
|
+
return AREA_CODES[areaCode4];
|
|
777
|
+
}
|
|
778
|
+
const areaCode3 = normalized.substring(0, 3);
|
|
779
|
+
if (AREA_CODES[areaCode3]) {
|
|
780
|
+
return AREA_CODES[areaCode3];
|
|
781
|
+
}
|
|
782
|
+
const areaCode2 = normalized.substring(0, 2);
|
|
783
|
+
if (AREA_CODES[areaCode2]) {
|
|
784
|
+
return AREA_CODES[areaCode2];
|
|
785
|
+
}
|
|
786
|
+
return null;
|
|
787
|
+
}
|
|
788
|
+
|
|
734
789
|
// src/phone/format.ts
|
|
735
790
|
function formatPhoneNumber(phone, format = "national") {
|
|
736
791
|
if (!validatePhoneNumber(phone)) {
|
|
737
792
|
return phone;
|
|
738
793
|
}
|
|
739
794
|
const cleaned = cleanPhoneNumber(phone);
|
|
740
|
-
|
|
741
|
-
if (
|
|
742
|
-
|
|
743
|
-
} else if (cleaned.startsWith("62") && !cleaned.startsWith("620")) {
|
|
744
|
-
normalized = "0" + cleaned.substring(2);
|
|
745
|
-
} else {
|
|
746
|
-
normalized = cleaned;
|
|
795
|
+
const normalized = normalizePhoneNumber(cleaned);
|
|
796
|
+
if (!normalized) {
|
|
797
|
+
return phone;
|
|
747
798
|
}
|
|
748
799
|
switch (format) {
|
|
749
800
|
case "international":
|
|
@@ -762,7 +813,7 @@ function toInternational(phone) {
|
|
|
762
813
|
if (!cleaned) {
|
|
763
814
|
return phone;
|
|
764
815
|
}
|
|
765
|
-
const normalized =
|
|
816
|
+
const normalized = normalizePhoneNumber(cleaned);
|
|
766
817
|
if (!normalized) {
|
|
767
818
|
return phone;
|
|
768
819
|
}
|
|
@@ -788,7 +839,7 @@ function toNational(phone) {
|
|
|
788
839
|
if (!cleaned) {
|
|
789
840
|
return phone;
|
|
790
841
|
}
|
|
791
|
-
const normalized =
|
|
842
|
+
const normalized = normalizePhoneNumber(cleaned);
|
|
792
843
|
if (!normalized) {
|
|
793
844
|
return phone;
|
|
794
845
|
}
|
|
@@ -813,7 +864,7 @@ function toE164(phone) {
|
|
|
813
864
|
if (!cleaned) {
|
|
814
865
|
return phone;
|
|
815
866
|
}
|
|
816
|
-
const normalized =
|
|
867
|
+
const normalized = normalizePhoneNumber(cleaned);
|
|
817
868
|
if (!normalized) {
|
|
818
869
|
return phone;
|
|
819
870
|
}
|
|
@@ -825,16 +876,6 @@ function cleanPhoneNumber(phone) {
|
|
|
825
876
|
}
|
|
826
877
|
return phone.replace(/[^\d+]/g, "");
|
|
827
878
|
}
|
|
828
|
-
function normalizeToNational(phone) {
|
|
829
|
-
if (phone.startsWith("+62")) {
|
|
830
|
-
return "0" + phone.substring(3);
|
|
831
|
-
} else if (phone.startsWith("62") && !phone.startsWith("620")) {
|
|
832
|
-
return "0" + phone.substring(2);
|
|
833
|
-
} else if (phone.startsWith("0")) {
|
|
834
|
-
return phone;
|
|
835
|
-
}
|
|
836
|
-
return "";
|
|
837
|
-
}
|
|
838
879
|
function getAreaCodeLength(normalized) {
|
|
839
880
|
const fourDigitCode = normalized.substring(0, 5);
|
|
840
881
|
if (AREA_CODES[fourDigitCode]) {
|
|
@@ -860,7 +901,7 @@ function maskPhoneNumber(phone, options = {}) {
|
|
|
860
901
|
if (isInternational) {
|
|
861
902
|
toMask = cleaned;
|
|
862
903
|
} else {
|
|
863
|
-
const normalized =
|
|
904
|
+
const normalized = normalizePhoneNumber(cleaned);
|
|
864
905
|
toMask = normalized || cleaned;
|
|
865
906
|
}
|
|
866
907
|
if (toMask.length < 4) {
|
|
@@ -929,7 +970,7 @@ function parsePhoneNumber(phone) {
|
|
|
929
970
|
return null;
|
|
930
971
|
}
|
|
931
972
|
const cleaned = cleanPhoneNumber(phone);
|
|
932
|
-
const normalized =
|
|
973
|
+
const normalized = normalizePhoneNumber(cleaned);
|
|
933
974
|
if (!normalized) {
|
|
934
975
|
return null;
|
|
935
976
|
}
|
|
@@ -942,7 +983,7 @@ function parsePhoneNumber(phone) {
|
|
|
942
983
|
if (isMobile) {
|
|
943
984
|
operator = getOperator(normalized);
|
|
944
985
|
} else {
|
|
945
|
-
region =
|
|
986
|
+
region = getLandlineRegion(normalized);
|
|
946
987
|
}
|
|
947
988
|
return {
|
|
948
989
|
countryCode,
|
|
@@ -964,7 +1005,7 @@ function getOperator(phone) {
|
|
|
964
1005
|
return null;
|
|
965
1006
|
}
|
|
966
1007
|
const cleaned = cleanPhoneNumber(phone);
|
|
967
|
-
const normalized =
|
|
1008
|
+
const normalized = normalizePhoneNumber(cleaned);
|
|
968
1009
|
if (!normalized || normalized.length < 4) {
|
|
969
1010
|
return null;
|
|
970
1011
|
}
|
|
@@ -978,30 +1019,6 @@ function isProvider(phone, providerName) {
|
|
|
978
1019
|
}
|
|
979
1020
|
return operator.toLowerCase() === providerName.toLowerCase();
|
|
980
1021
|
}
|
|
981
|
-
function getRegion(phone) {
|
|
982
|
-
if (!phone.startsWith("0")) {
|
|
983
|
-
return null;
|
|
984
|
-
}
|
|
985
|
-
const areaCode4 = phone.substring(0, 4);
|
|
986
|
-
if (AREA_CODES[areaCode4]) {
|
|
987
|
-
return AREA_CODES[areaCode4];
|
|
988
|
-
}
|
|
989
|
-
const areaCode3 = phone.substring(0, 3);
|
|
990
|
-
if (AREA_CODES[areaCode3]) {
|
|
991
|
-
return AREA_CODES[areaCode3];
|
|
992
|
-
}
|
|
993
|
-
return null;
|
|
994
|
-
}
|
|
995
|
-
function normalizeToNational2(phone) {
|
|
996
|
-
if (phone.startsWith("+62")) {
|
|
997
|
-
return "0" + phone.substring(3);
|
|
998
|
-
} else if (phone.startsWith("62")) {
|
|
999
|
-
return "0" + phone.substring(2);
|
|
1000
|
-
} else if (phone.startsWith("0")) {
|
|
1001
|
-
return phone;
|
|
1002
|
-
}
|
|
1003
|
-
return "";
|
|
1004
|
-
}
|
|
1005
1022
|
|
|
1006
1023
|
// src/npwp/validate.ts
|
|
1007
1024
|
function validateNPWP(npwp) {
|