@indodev/toolkit 0.3.4 → 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/dist/index.d.cts CHANGED
@@ -1,8 +1,9 @@
1
1
  export { NIKInfo, MaskOptions as NIKMaskOptions, formatBirthDate, formatNIK, getAge, isValidForBirthDate, isValidForGender, maskNIK, parseNIK, validateNIK } from './nik/index.cjs';
2
- export { PhoneFormat, PhoneInfo, MaskOptions as PhoneMaskOptions, cleanPhoneNumber, formatPhoneNumber, generateSmsLink, generateTelLink, generateWALink, getOperator, isLandlineNumber, isMobileNumber, isProvider, maskPhoneNumber, parsePhoneNumber, toE164, toInternational, toNational, validatePhoneNumber } from './phone/index.cjs';
2
+ export { P as PhoneFormat, l as PhoneInfo, M as PhoneMaskOptions, d as cleanPhoneNumber, f as formatPhoneNumber, h as generateSmsLink, j as generateTelLink, e as generateWALink, g as getOperator, a as isLandlineNumber, i as isMobileNumber, k as isProvider, m as maskPhoneNumber, p as parsePhoneNumber, c as toE164, t as toInternational, b as toNational, v as validatePhoneNumber } from './parse-BDfy3aQw.cjs';
3
3
  export { NPWPInfo, MaskOptions as NPWPMaskOptions, formatNPWP, maskNPWP, parseNPWP, validateNPWP } from './npwp/index.cjs';
4
4
  export { f as formatPlate, g as getRegionFromPlate, v as validatePlate } from './utils-DDVlOusI.cjs';
5
5
  export { V as VINOptions, a as VINValidationResult, v as validateVIN } from './types-i5e6R0AS.cjs';
6
6
  export { c as EmailInfo, b as EmailMaskOptions, E as EmailValidationOptions, a as EmailValidationResult, g as getEmailInfo, m as maskEmail, n as normalizeEmail, v as validateEmail } from './email-validator-R9L5unIw.cjs';
7
7
  export { R as RupiahOptions, W as WordOptions, d as addRupiahSymbol, c as calculateTax, b as formatAccounting, a as formatCompact, f as formatRupiah, p as parseRupiah, r as roundToClean, t as toWords } from './utils-OG1yMaAa.cjs';
8
8
  export { C as CompareOptions, E as ExtractOptions, m as SanitizeOptions, S as SlugifyOptions, T as TitleCaseOptions, o as TruncateOptions, c as capitalize, k as compareStrings, d as contractAbbreviation, e as expandAbbreviation, g as extractWords, j as isAlay, n as normalizeWhitespace, p as profanityFilter, r as removeAccents, h as removeStopwords, b as sanitize, l as similarity, s as slugify, i as toFormal, a as toSentenceCase, t as toTitleCase, f as truncate } from './compare-Ku_8OhuU.cjs';
9
+ export { AgeOptions, AgeResult, DAY_NAMES, DAY_NAMES_SHORT, DateStyle, InvalidDateError, InvalidDateRangeError, MONTH_NAMES, MONTH_NAMES_SHORT, TIMEZONE_MAP, VALID_UTC_OFFSETS, daysInMonth, formatDate, formatDateRange, getAge as getAgeFromDate, getIndonesianTimezone, isLeapYear, isValidDate, isWeekend, isWorkingDay, parseDate, toRelativeTime } from './datetime/index.cjs';
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  export { NIKInfo, MaskOptions as NIKMaskOptions, formatBirthDate, formatNIK, getAge, isValidForBirthDate, isValidForGender, maskNIK, parseNIK, validateNIK } from './nik/index.js';
2
- export { PhoneFormat, PhoneInfo, MaskOptions as PhoneMaskOptions, cleanPhoneNumber, formatPhoneNumber, generateSmsLink, generateTelLink, generateWALink, getOperator, isLandlineNumber, isMobileNumber, isProvider, maskPhoneNumber, parsePhoneNumber, toE164, toInternational, toNational, validatePhoneNumber } from './phone/index.js';
2
+ export { P as PhoneFormat, l as PhoneInfo, M as PhoneMaskOptions, d as cleanPhoneNumber, f as formatPhoneNumber, h as generateSmsLink, j as generateTelLink, e as generateWALink, g as getOperator, a as isLandlineNumber, i as isMobileNumber, k as isProvider, m as maskPhoneNumber, p as parsePhoneNumber, c as toE164, t as toInternational, b as toNational, v as validatePhoneNumber } from './parse-BDfy3aQw.js';
3
3
  export { NPWPInfo, MaskOptions as NPWPMaskOptions, formatNPWP, maskNPWP, parseNPWP, validateNPWP } from './npwp/index.js';
4
4
  export { f as formatPlate, g as getRegionFromPlate, v as validatePlate } from './utils-DDVlOusI.js';
5
5
  export { V as VINOptions, a as VINValidationResult, v as validateVIN } from './types-i5e6R0AS.js';
6
6
  export { c as EmailInfo, b as EmailMaskOptions, E as EmailValidationOptions, a as EmailValidationResult, g as getEmailInfo, m as maskEmail, n as normalizeEmail, v as validateEmail } from './email-validator-R9L5unIw.js';
7
7
  export { R as RupiahOptions, W as WordOptions, d as addRupiahSymbol, c as calculateTax, b as formatAccounting, a as formatCompact, f as formatRupiah, p as parseRupiah, r as roundToClean, t as toWords } from './utils-OG1yMaAa.js';
8
8
  export { C as CompareOptions, E as ExtractOptions, m as SanitizeOptions, S as SlugifyOptions, T as TitleCaseOptions, o as TruncateOptions, c as capitalize, k as compareStrings, d as contractAbbreviation, e as expandAbbreviation, g as extractWords, j as isAlay, n as normalizeWhitespace, p as profanityFilter, r as removeAccents, h as removeStopwords, b as sanitize, l as similarity, s as slugify, i as toFormal, a as toSentenceCase, t as toTitleCase, f as truncate } from './compare-Ku_8OhuU.js';
9
+ export { AgeOptions, AgeResult, DAY_NAMES, DAY_NAMES_SHORT, DateStyle, InvalidDateError, InvalidDateRangeError, MONTH_NAMES, MONTH_NAMES_SHORT, TIMEZONE_MAP, VALID_UTC_OFFSETS, daysInMonth, formatDate, formatDateRange, getAge as getAgeFromDate, getIndonesianTimezone, isLeapYear, isValidDate, isWeekend, isWorkingDay, parseDate, toRelativeTime } from './datetime/index.js';
package/dist/index.js CHANGED
@@ -715,7 +715,7 @@ function isMobileNumber(phone) {
715
715
  let normalized;
716
716
  if (cleaned.startsWith("+62")) {
717
717
  normalized = "0" + cleaned.substring(3);
718
- } else if (cleaned.startsWith("62")) {
718
+ } else if (cleaned.startsWith("62") && !cleaned.startsWith("620")) {
719
719
  normalized = "0" + cleaned.substring(2);
720
720
  } else {
721
721
  normalized = cleaned;
@@ -729,19 +729,70 @@ function isLandlineNumber(phone) {
729
729
  return !isMobileNumber(phone);
730
730
  }
731
731
 
732
+ // src/phone/utils.ts
733
+ function normalizePhoneNumber(phone) {
734
+ if (!phone || typeof phone !== "string") {
735
+ return "";
736
+ }
737
+ if (phone.startsWith("+62")) {
738
+ return "0" + phone.substring(3);
739
+ }
740
+ if (phone.startsWith("62") && !phone.startsWith("620")) {
741
+ return "0" + phone.substring(2);
742
+ }
743
+ if (phone.startsWith("0")) {
744
+ return phone;
745
+ }
746
+ return "";
747
+ }
748
+ function normalizeToNational(phone) {
749
+ if (phone.startsWith("+62")) {
750
+ return "0" + phone.substring(3);
751
+ }
752
+ if (phone.startsWith("62") && !phone.startsWith("620")) {
753
+ return "0" + phone.substring(2);
754
+ }
755
+ if (phone.startsWith("0")) {
756
+ return phone;
757
+ }
758
+ return "";
759
+ }
760
+ function getLandlineRegion(phone) {
761
+ if (!phone || typeof phone !== "string") {
762
+ return null;
763
+ }
764
+ const cleaned = phone.replace(/[^\d+]/g, "");
765
+ const normalized = normalizeToNational(cleaned);
766
+ if (!normalized || !normalized.startsWith("0")) {
767
+ return null;
768
+ }
769
+ if (normalized.startsWith("08")) {
770
+ return null;
771
+ }
772
+ const areaCode4 = normalized.substring(0, 4);
773
+ if (AREA_CODES[areaCode4]) {
774
+ return AREA_CODES[areaCode4];
775
+ }
776
+ const areaCode3 = normalized.substring(0, 3);
777
+ if (AREA_CODES[areaCode3]) {
778
+ return AREA_CODES[areaCode3];
779
+ }
780
+ const areaCode2 = normalized.substring(0, 2);
781
+ if (AREA_CODES[areaCode2]) {
782
+ return AREA_CODES[areaCode2];
783
+ }
784
+ return null;
785
+ }
786
+
732
787
  // src/phone/format.ts
733
788
  function formatPhoneNumber(phone, format = "national") {
734
789
  if (!validatePhoneNumber(phone)) {
735
790
  return phone;
736
791
  }
737
792
  const cleaned = cleanPhoneNumber(phone);
738
- let normalized;
739
- if (cleaned.startsWith("+62")) {
740
- normalized = "0" + cleaned.substring(3);
741
- } else if (cleaned.startsWith("62") && !cleaned.startsWith("620")) {
742
- normalized = "0" + cleaned.substring(2);
743
- } else {
744
- normalized = cleaned;
793
+ const normalized = normalizePhoneNumber(cleaned);
794
+ if (!normalized) {
795
+ return phone;
745
796
  }
746
797
  switch (format) {
747
798
  case "international":
@@ -760,7 +811,7 @@ function toInternational(phone) {
760
811
  if (!cleaned) {
761
812
  return phone;
762
813
  }
763
- const normalized = normalizeToNational(cleaned);
814
+ const normalized = normalizePhoneNumber(cleaned);
764
815
  if (!normalized) {
765
816
  return phone;
766
817
  }
@@ -786,7 +837,7 @@ function toNational(phone) {
786
837
  if (!cleaned) {
787
838
  return phone;
788
839
  }
789
- const normalized = normalizeToNational(cleaned);
840
+ const normalized = normalizePhoneNumber(cleaned);
790
841
  if (!normalized) {
791
842
  return phone;
792
843
  }
@@ -811,7 +862,7 @@ function toE164(phone) {
811
862
  if (!cleaned) {
812
863
  return phone;
813
864
  }
814
- const normalized = normalizeToNational(cleaned);
865
+ const normalized = normalizePhoneNumber(cleaned);
815
866
  if (!normalized) {
816
867
  return phone;
817
868
  }
@@ -823,16 +874,6 @@ function cleanPhoneNumber(phone) {
823
874
  }
824
875
  return phone.replace(/[^\d+]/g, "");
825
876
  }
826
- function normalizeToNational(phone) {
827
- if (phone.startsWith("+62")) {
828
- return "0" + phone.substring(3);
829
- } else if (phone.startsWith("62") && !phone.startsWith("620")) {
830
- return "0" + phone.substring(2);
831
- } else if (phone.startsWith("0")) {
832
- return phone;
833
- }
834
- return "";
835
- }
836
877
  function getAreaCodeLength(normalized) {
837
878
  const fourDigitCode = normalized.substring(0, 5);
838
879
  if (AREA_CODES[fourDigitCode]) {
@@ -858,7 +899,7 @@ function maskPhoneNumber(phone, options = {}) {
858
899
  if (isInternational) {
859
900
  toMask = cleaned;
860
901
  } else {
861
- const normalized = normalizeToNational(cleaned);
902
+ const normalized = normalizePhoneNumber(cleaned);
862
903
  toMask = normalized || cleaned;
863
904
  }
864
905
  if (toMask.length < 4) {
@@ -927,7 +968,7 @@ function parsePhoneNumber(phone) {
927
968
  return null;
928
969
  }
929
970
  const cleaned = cleanPhoneNumber(phone);
930
- const normalized = normalizeToNational2(cleaned);
971
+ const normalized = normalizePhoneNumber(cleaned);
931
972
  if (!normalized) {
932
973
  return null;
933
974
  }
@@ -940,7 +981,7 @@ function parsePhoneNumber(phone) {
940
981
  if (isMobile) {
941
982
  operator = getOperator(normalized);
942
983
  } else {
943
- region = getRegion(normalized);
984
+ region = getLandlineRegion(normalized);
944
985
  }
945
986
  return {
946
987
  countryCode,
@@ -962,7 +1003,7 @@ function getOperator(phone) {
962
1003
  return null;
963
1004
  }
964
1005
  const cleaned = cleanPhoneNumber(phone);
965
- const normalized = normalizeToNational2(cleaned);
1006
+ const normalized = normalizePhoneNumber(cleaned);
966
1007
  if (!normalized || normalized.length < 4) {
967
1008
  return null;
968
1009
  }
@@ -976,30 +1017,6 @@ function isProvider(phone, providerName) {
976
1017
  }
977
1018
  return operator.toLowerCase() === providerName.toLowerCase();
978
1019
  }
979
- function getRegion(phone) {
980
- if (!phone.startsWith("0")) {
981
- return null;
982
- }
983
- const areaCode4 = phone.substring(0, 4);
984
- if (AREA_CODES[areaCode4]) {
985
- return AREA_CODES[areaCode4];
986
- }
987
- const areaCode3 = phone.substring(0, 3);
988
- if (AREA_CODES[areaCode3]) {
989
- return AREA_CODES[areaCode3];
990
- }
991
- return null;
992
- }
993
- function normalizeToNational2(phone) {
994
- if (phone.startsWith("+62")) {
995
- return "0" + phone.substring(3);
996
- } else if (phone.startsWith("62")) {
997
- return "0" + phone.substring(2);
998
- } else if (phone.startsWith("0")) {
999
- return phone;
1000
- }
1001
- return "";
1002
- }
1003
1020
 
1004
1021
  // src/npwp/validate.ts
1005
1022
  function validateNPWP(npwp) {
@@ -3320,6 +3337,425 @@ function similarity(str1, str2) {
3320
3337
  return 1 - distance / maxLength;
3321
3338
  }
3322
3339
 
3323
- export { addRupiahSymbol, calculateTax, capitalize2 as capitalize, cleanPhoneNumber, compareStrings, contractAbbreviation, expandAbbreviation, extractWords, formatAccounting, formatBirthDate, formatCompact, formatNIK, formatNPWP, formatPhoneNumber, formatPlate, formatRupiah, generateSmsLink, generateTelLink, generateWALink, getAge, getEmailInfo, getOperator, getRegionFromPlate, isAlay, isLandlineNumber, isMobileNumber, isProvider, isValidForBirthDate, isValidForGender, maskEmail, maskNIK, maskNPWP, maskPhoneNumber, normalizeEmail, normalizeWhitespace, parseNIK, parseNPWP, parsePhoneNumber, parseRupiah, profanityFilter, removeAccents, removeStopwords, roundToClean, sanitize, similarity, slugify, toE164, toFormal, toInternational, toNational, toSentenceCase, toTitleCase, toWords, truncate, validateEmail, validateNIK, validateNPWP, validatePhoneNumber, validatePlate, validateVIN };
3340
+ // src/datetime/types.ts
3341
+ var InvalidDateError = class extends Error {
3342
+ constructor(message = "Invalid date provided") {
3343
+ super(message);
3344
+ /** Error code for programmatic identification */
3345
+ this.code = "INVALID_DATE";
3346
+ this.name = "InvalidDateError";
3347
+ }
3348
+ };
3349
+ var InvalidDateRangeError = class extends Error {
3350
+ constructor(message = "End date must be after start date") {
3351
+ super(message);
3352
+ /** Error code for programmatic identification */
3353
+ this.code = "INVALID_DATE_RANGE";
3354
+ this.name = "InvalidDateRangeError";
3355
+ }
3356
+ };
3357
+
3358
+ // src/datetime/constants.ts
3359
+ var MONTH_NAMES = [
3360
+ "",
3361
+ // Placeholder for 0-index
3362
+ "Januari",
3363
+ "Februari",
3364
+ "Maret",
3365
+ "April",
3366
+ "Mei",
3367
+ "Juni",
3368
+ "Juli",
3369
+ "Agustus",
3370
+ "September",
3371
+ "Oktober",
3372
+ "November",
3373
+ "Desember"
3374
+ ];
3375
+ var MONTH_NAMES_SHORT = [
3376
+ "",
3377
+ // Placeholder for 0-index
3378
+ "Jan",
3379
+ "Feb",
3380
+ "Mar",
3381
+ "Apr",
3382
+ "Mei",
3383
+ "Jun",
3384
+ "Jul",
3385
+ "Agu",
3386
+ "Sep",
3387
+ "Okt",
3388
+ "Nov",
3389
+ "Des"
3390
+ ];
3391
+ var DAY_NAMES = [
3392
+ "Minggu",
3393
+ "Senin",
3394
+ "Selasa",
3395
+ "Rabu",
3396
+ "Kamis",
3397
+ "Jumat",
3398
+ "Sabtu"
3399
+ ];
3400
+ var DAY_NAMES_SHORT = [
3401
+ "Min",
3402
+ "Sen",
3403
+ "Sel",
3404
+ "Rab",
3405
+ "Kam",
3406
+ "Jum",
3407
+ "Sab"
3408
+ ];
3409
+ var TIMEZONE_MAP = {
3410
+ // UTC+7 - WIB
3411
+ "Asia/Jakarta": "WIB",
3412
+ "Asia/Pontianak": "WIB",
3413
+ // UTC+8 - WITA
3414
+ "Asia/Makassar": "WITA",
3415
+ "Asia/Denpasar": "WITA",
3416
+ "Asia/Manado": "WITA",
3417
+ "Asia/Palu": "WITA",
3418
+ // UTC+9 - WIT
3419
+ "Asia/Jayapura": "WIT"
3420
+ };
3421
+ var VALID_UTC_OFFSETS = [7, 8, 9];
3422
+
3423
+ // src/datetime/calc.ts
3424
+ function isLeapYear(year) {
3425
+ if (!Number.isFinite(year) || !Number.isInteger(year)) {
3426
+ return false;
3427
+ }
3428
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
3429
+ }
3430
+ function daysInMonth(month, year) {
3431
+ if (!Number.isFinite(month) || !Number.isInteger(month) || !Number.isFinite(year) || !Number.isInteger(year) || month < 1 || month > 12) {
3432
+ return 0;
3433
+ }
3434
+ if ([1, 3, 5, 7, 8, 10, 12].includes(month)) {
3435
+ return 31;
3436
+ }
3437
+ if ([4, 6, 9, 11].includes(month)) {
3438
+ return 30;
3439
+ }
3440
+ return isLeapYear(year) ? 29 : 28;
3441
+ }
3442
+ function isValidDate(date) {
3443
+ return date instanceof Date && !Number.isNaN(date.getTime());
3444
+ }
3445
+ function isWeekend(date) {
3446
+ const day = date.getDay();
3447
+ return day === 0 || day === 6;
3448
+ }
3449
+ function isWorkingDay(date) {
3450
+ const day = date.getDay();
3451
+ return day >= 1 && day <= 5;
3452
+ }
3453
+ function normalizeDate(date) {
3454
+ let result;
3455
+ if (date instanceof Date) {
3456
+ result = date;
3457
+ } else if (typeof date === "number") {
3458
+ result = new Date(date);
3459
+ } else if (typeof date === "string") {
3460
+ result = new Date(date);
3461
+ } else {
3462
+ throw new InvalidDateError("Date must be a Date, string, or number");
3463
+ }
3464
+ if (Number.isNaN(result.getTime())) {
3465
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3466
+ }
3467
+ return result;
3468
+ }
3469
+ function getAge2(birthDate, options = {}) {
3470
+ const birth = normalizeDate(birthDate);
3471
+ const from = options.fromDate ? normalizeDate(options.fromDate) : /* @__PURE__ */ new Date();
3472
+ if (birth.getTime() > from.getTime()) {
3473
+ throw new InvalidDateError(
3474
+ "Birth date cannot be in the future relative to fromDate"
3475
+ );
3476
+ }
3477
+ let years = from.getFullYear() - birth.getFullYear();
3478
+ let months = from.getMonth() - birth.getMonth();
3479
+ let days = from.getDate() - birth.getDate();
3480
+ if (days < 0) {
3481
+ months--;
3482
+ const prevMonth = from.getMonth() === 0 ? 11 : from.getMonth() - 1;
3483
+ const prevMonthYear = from.getMonth() === 0 ? from.getFullYear() - 1 : from.getFullYear();
3484
+ days += daysInMonth(prevMonth + 1, prevMonthYear);
3485
+ }
3486
+ if (months < 0) {
3487
+ years--;
3488
+ months += 12;
3489
+ }
3490
+ const result = { years, months, days };
3491
+ if (options.asString) {
3492
+ return formatAgeString(result);
3493
+ }
3494
+ return result;
3495
+ }
3496
+ function formatAgeString(age) {
3497
+ const parts = [];
3498
+ if (age.years > 0) {
3499
+ parts.push(`${age.years} Tahun`);
3500
+ }
3501
+ if (age.months > 0) {
3502
+ parts.push(`${age.months} Bulan`);
3503
+ }
3504
+ if (age.days > 0) {
3505
+ parts.push(`${age.days} Hari`);
3506
+ }
3507
+ if (parts.length === 0) {
3508
+ return "0 Hari";
3509
+ }
3510
+ return parts.join(" ");
3511
+ }
3512
+
3513
+ // src/datetime/parse.ts
3514
+ function parseDate(dateStr) {
3515
+ const trimmed = dateStr.trim();
3516
+ if (!trimmed) {
3517
+ return null;
3518
+ }
3519
+ if (trimmed.includes(" ") || trimmed.includes(":")) {
3520
+ return null;
3521
+ }
3522
+ const parts = trimmed.split(/[-/.]/);
3523
+ if (parts.length !== 3) {
3524
+ return null;
3525
+ }
3526
+ const nums = parts.map((p) => parseInt(p, 10));
3527
+ if (nums.some((n) => Number.isNaN(n) || n < 0)) {
3528
+ return null;
3529
+ }
3530
+ const [first, second, third] = nums;
3531
+ let day;
3532
+ let month;
3533
+ let year;
3534
+ if (first > 999 && first > 31) {
3535
+ year = first;
3536
+ month = second;
3537
+ day = third;
3538
+ } else {
3539
+ if (third < 1e3) {
3540
+ return null;
3541
+ }
3542
+ day = first;
3543
+ month = second;
3544
+ year = third;
3545
+ }
3546
+ if (month < 1 || month > 12) {
3547
+ return null;
3548
+ }
3549
+ if (year < 1e3 || year > 9999) {
3550
+ return null;
3551
+ }
3552
+ const maxDays = daysInMonth(month, year);
3553
+ if (maxDays === 0 || day < 1 || day > maxDays) {
3554
+ return null;
3555
+ }
3556
+ const date = new Date(year, month - 1, day);
3557
+ if (date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day) {
3558
+ return null;
3559
+ }
3560
+ return date;
3561
+ }
3562
+
3563
+ // src/datetime/format.ts
3564
+ function normalizeDate2(date) {
3565
+ let result;
3566
+ if (date instanceof Date) {
3567
+ result = date;
3568
+ } else if (typeof date === "number") {
3569
+ result = new Date(date);
3570
+ } else if (typeof date === "string") {
3571
+ result = new Date(date);
3572
+ } else {
3573
+ throw new InvalidDateError("Date must be a Date, string, or number");
3574
+ }
3575
+ if (Number.isNaN(result.getTime())) {
3576
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3577
+ }
3578
+ return result;
3579
+ }
3580
+ function formatDate(date, style = "long") {
3581
+ const d = normalizeDate2(date);
3582
+ const day = d.getDate();
3583
+ const month = d.getMonth() + 1;
3584
+ const year = d.getFullYear();
3585
+ const dayOfWeek = d.getDay();
3586
+ switch (style) {
3587
+ case "full":
3588
+ return `${DAY_NAMES[dayOfWeek]}, ${day} ${MONTH_NAMES[month]} ${year}`;
3589
+ case "long":
3590
+ return `${day} ${MONTH_NAMES[month]} ${year}`;
3591
+ case "medium":
3592
+ return `${day} ${MONTH_NAMES_SHORT[month]} ${year}`;
3593
+ case "short": {
3594
+ const dd = String(day).padStart(2, "0");
3595
+ const mm = String(month).padStart(2, "0");
3596
+ return `${dd}/${mm}/${year}`;
3597
+ }
3598
+ case "weekday":
3599
+ return DAY_NAMES[dayOfWeek];
3600
+ case "month":
3601
+ return MONTH_NAMES[month];
3602
+ default:
3603
+ throw new InvalidDateError(`Unknown format style: ${style}`);
3604
+ }
3605
+ }
3606
+ function formatDateRange(start, end, style = "long") {
3607
+ const s = normalizeDate2(start);
3608
+ const e = normalizeDate2(end);
3609
+ if (e.getTime() < s.getTime()) {
3610
+ throw new InvalidDateRangeError();
3611
+ }
3612
+ if (style === "short") {
3613
+ return `${formatDate(s, "short")} - ${formatDate(e, "short")}`;
3614
+ }
3615
+ if (style === "full") {
3616
+ return `${formatDate(s, "full")} - ${formatDate(e, "full")}`;
3617
+ }
3618
+ const sDay = s.getDate();
3619
+ const eDay = e.getDate();
3620
+ const sMonth = s.getMonth() + 1;
3621
+ const eMonth = e.getMonth() + 1;
3622
+ const sYear = s.getFullYear();
3623
+ const eYear = e.getFullYear();
3624
+ if (sDay === eDay && sMonth === eMonth && sYear === eYear) {
3625
+ return formatDate(s, style);
3626
+ }
3627
+ if (sYear !== eYear) {
3628
+ return `${formatDate(s, style)} - ${formatDate(e, style)}`;
3629
+ }
3630
+ if (sMonth !== eMonth) {
3631
+ if (style === "long") {
3632
+ return `${sDay} ${MONTH_NAMES[sMonth]} - ${eDay} ${MONTH_NAMES[eMonth]} ${eYear}`;
3633
+ }
3634
+ return `${sDay} ${MONTH_NAMES_SHORT[sMonth]} - ${eDay} ${MONTH_NAMES_SHORT[eMonth]} ${eYear}`;
3635
+ }
3636
+ if (style === "long") {
3637
+ return `${sDay} - ${eDay} ${MONTH_NAMES[eMonth]} ${eYear}`;
3638
+ }
3639
+ return `${sDay} - ${eDay} ${MONTH_NAMES_SHORT[eMonth]} ${eYear}`;
3640
+ }
3641
+
3642
+ // src/datetime/relative.ts
3643
+ function normalizeDate3(date) {
3644
+ let result;
3645
+ if (date instanceof Date) {
3646
+ result = date;
3647
+ } else if (typeof date === "number") {
3648
+ result = new Date(date);
3649
+ } else if (typeof date === "string") {
3650
+ result = new Date(date);
3651
+ } else {
3652
+ throw new InvalidDateError("Date must be a Date, string, or number");
3653
+ }
3654
+ if (Number.isNaN(result.getTime())) {
3655
+ throw new InvalidDateError(`Unable to parse date: ${String(date)}`);
3656
+ }
3657
+ return result;
3658
+ }
3659
+ function toRelativeTime(date, baseDate = /* @__PURE__ */ new Date()) {
3660
+ const d = normalizeDate3(date);
3661
+ const base = normalizeDate3(baseDate);
3662
+ const diffMs = d.getTime() - base.getTime();
3663
+ const diffSec = Math.floor(diffMs / 1e3);
3664
+ const diffMin = Math.floor(diffMs / (1e3 * 60));
3665
+ const diffHour = Math.floor(diffMs / (1e3 * 60 * 60));
3666
+ const diffDay = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
3667
+ if (diffMs === 0) {
3668
+ return "Sekarang";
3669
+ }
3670
+ if (diffMs > 0) {
3671
+ if (diffSec < 60) {
3672
+ return "Baru saja";
3673
+ }
3674
+ if (diffMin < 60) {
3675
+ return `${diffMin} menit lagi`;
3676
+ }
3677
+ if (diffHour < 24) {
3678
+ return `${diffHour} jam lagi`;
3679
+ }
3680
+ if (diffHour < 48) {
3681
+ return "Besok";
3682
+ }
3683
+ if (diffDay <= 30) {
3684
+ return `${diffDay} hari lagi`;
3685
+ }
3686
+ return formatDate(d, "long");
3687
+ }
3688
+ const absDiffSec = Math.abs(diffSec);
3689
+ const absDiffMin = Math.abs(diffMin);
3690
+ const absDiffHour = Math.abs(diffHour);
3691
+ const absDiffDay = Math.abs(diffDay);
3692
+ if (absDiffSec < 60) {
3693
+ return "Baru saja";
3694
+ }
3695
+ if (absDiffMin < 60) {
3696
+ return `${absDiffMin} menit yang lalu`;
3697
+ }
3698
+ if (absDiffHour < 24) {
3699
+ return `${absDiffHour} jam yang lalu`;
3700
+ }
3701
+ if (absDiffHour < 48) {
3702
+ return "Kemarin";
3703
+ }
3704
+ if (absDiffDay <= 30) {
3705
+ return `${absDiffDay} hari yang lalu`;
3706
+ }
3707
+ return formatDate(d, "long");
3708
+ }
3709
+
3710
+ // src/datetime/timezone.ts
3711
+ function getIndonesianTimezone(input) {
3712
+ if (typeof input === "number") {
3713
+ if (!Number.isFinite(input) || !Number.isInteger(input)) {
3714
+ return null;
3715
+ }
3716
+ switch (input) {
3717
+ case 7:
3718
+ return "WIB";
3719
+ case 8:
3720
+ return "WITA";
3721
+ case 9:
3722
+ return "WIT";
3723
+ default:
3724
+ return null;
3725
+ }
3726
+ }
3727
+ if (typeof input !== "string") {
3728
+ return null;
3729
+ }
3730
+ const trimmed = input.trim();
3731
+ const offsetMatch = trimmed.match(/^([+-])(\d{2}):?(\d{2})$/);
3732
+ if (offsetMatch) {
3733
+ const sign = offsetMatch[1];
3734
+ const hours = parseInt(offsetMatch[2], 10);
3735
+ const minutes = parseInt(offsetMatch[3], 10);
3736
+ if (sign === "-") {
3737
+ return null;
3738
+ }
3739
+ if (minutes !== 0) {
3740
+ return null;
3741
+ }
3742
+ switch (hours) {
3743
+ case 7:
3744
+ return "WIB";
3745
+ case 8:
3746
+ return "WITA";
3747
+ case 9:
3748
+ return "WIT";
3749
+ default:
3750
+ return null;
3751
+ }
3752
+ }
3753
+ if (TIMEZONE_MAP[trimmed]) {
3754
+ return TIMEZONE_MAP[trimmed];
3755
+ }
3756
+ return null;
3757
+ }
3758
+
3759
+ export { DAY_NAMES, DAY_NAMES_SHORT, InvalidDateError, InvalidDateRangeError, MONTH_NAMES, MONTH_NAMES_SHORT, TIMEZONE_MAP, VALID_UTC_OFFSETS, addRupiahSymbol, calculateTax, capitalize2 as capitalize, cleanPhoneNumber, compareStrings, contractAbbreviation, daysInMonth, expandAbbreviation, extractWords, formatAccounting, formatBirthDate, formatCompact, formatDate, formatDateRange, formatNIK, formatNPWP, formatPhoneNumber, formatPlate, formatRupiah, generateSmsLink, generateTelLink, generateWALink, getAge, getAge2 as getAgeFromDate, getEmailInfo, getIndonesianTimezone, getOperator, getRegionFromPlate, isAlay, isLandlineNumber, isLeapYear, isMobileNumber, isProvider, isValidDate, isValidForBirthDate, isValidForGender, isWeekend, isWorkingDay, maskEmail, maskNIK, maskNPWP, maskPhoneNumber, normalizeEmail, normalizeWhitespace, parseDate, parseNIK, parseNPWP, parsePhoneNumber, parseRupiah, profanityFilter, removeAccents, removeStopwords, roundToClean, sanitize, similarity, slugify, toE164, toFormal, toInternational, toNational, toRelativeTime, toSentenceCase, toTitleCase, toWords, truncate, validateEmail, validateNIK, validateNPWP, validatePhoneNumber, validatePlate, validateVIN };
3324
3760
  //# sourceMappingURL=index.js.map
3325
3761
  //# sourceMappingURL=index.js.map