@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 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/financial/currency) | Format Rupiah, terbilang, split amounts, percentages |
72
- | [Text](https://toolkit.adamm.cloud/docs/text-utils/text) | Title case, slugs, abbreviations, case conversion, masking |
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
- let normalized;
741
- if (cleaned.startsWith("+62")) {
742
- normalized = "0" + cleaned.substring(3);
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 = normalizeToNational(cleaned);
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 = normalizeToNational(cleaned);
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 = normalizeToNational(cleaned);
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 = normalizeToNational(cleaned);
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 = normalizeToNational2(cleaned);
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 = getRegion(normalized);
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 = normalizeToNational2(cleaned);
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) {