@salespark/toolkit 2.0.0 → 2.1.0
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 +783 -556
- package/dist/index.cjs +1130 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +167 -1
- package/dist/index.d.ts +167 -1
- package/dist/index.js +1118 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -440,12 +440,1129 @@ var addThousandsSpace = (value) => {
|
|
|
440
440
|
return value;
|
|
441
441
|
}
|
|
442
442
|
};
|
|
443
|
+
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
444
|
+
var isNilTextOrEmpty = (value) => {
|
|
445
|
+
try {
|
|
446
|
+
if (value === null || value === void 0 || value === "") return true;
|
|
447
|
+
if (typeof value === "string") {
|
|
448
|
+
const v = value.trim().toLowerCase();
|
|
449
|
+
return v === "null" || v === "undefined";
|
|
450
|
+
}
|
|
451
|
+
return false;
|
|
452
|
+
} catch {
|
|
453
|
+
return true;
|
|
454
|
+
}
|
|
455
|
+
};
|
|
456
|
+
var formatCurrency = (value, withoutCurrencySymbol = false, currency = "EUR", locale = "pt-PT") => {
|
|
457
|
+
try {
|
|
458
|
+
const numValue = value === void 0 || value === null || value === "" ? 0 : Number(value);
|
|
459
|
+
if (isNaN(numValue) || !isFinite(numValue)) {
|
|
460
|
+
return withoutCurrencySymbol ? "0,00" : "0,00 \u20AC";
|
|
461
|
+
}
|
|
462
|
+
const intlOptions = {
|
|
463
|
+
style: withoutCurrencySymbol ? "decimal" : "currency",
|
|
464
|
+
currency,
|
|
465
|
+
minimumFractionDigits: 2,
|
|
466
|
+
maximumFractionDigits: 2
|
|
467
|
+
};
|
|
468
|
+
return new Intl.NumberFormat(locale, intlOptions).format(numValue);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
const numValue = Number(value) || 0;
|
|
471
|
+
const formatted = numValue.toFixed(2).replace(".", ",");
|
|
472
|
+
return withoutCurrencySymbol ? formatted : `${formatted} \u20AC`;
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
var parseName = (name) => {
|
|
476
|
+
try {
|
|
477
|
+
if (name === void 0 || name === null || name === "") {
|
|
478
|
+
return { firstName: "", lastName: "" };
|
|
479
|
+
}
|
|
480
|
+
const cleanName = name.toString().trim().replace(/\s+/g, " ");
|
|
481
|
+
if (cleanName === "") {
|
|
482
|
+
return { firstName: "", lastName: "" };
|
|
483
|
+
}
|
|
484
|
+
if (!cleanName.includes(" ")) {
|
|
485
|
+
return { firstName: cleanName, lastName: "" };
|
|
486
|
+
}
|
|
487
|
+
const nameParts = cleanName.split(" ");
|
|
488
|
+
const firstName = nameParts[0];
|
|
489
|
+
const lastName = nameParts[nameParts.length - 1];
|
|
490
|
+
return { firstName, lastName };
|
|
491
|
+
} catch (error) {
|
|
492
|
+
const fallbackName = name ? String(name).trim() : "";
|
|
493
|
+
return { firstName: fallbackName, lastName: "" };
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
var symbolToCurrency = (symbol) => {
|
|
497
|
+
try {
|
|
498
|
+
if (!symbol || typeof symbol !== "string") {
|
|
499
|
+
return "EUR";
|
|
500
|
+
}
|
|
501
|
+
const normalizedSymbol = symbol.trim();
|
|
502
|
+
switch (normalizedSymbol) {
|
|
503
|
+
case "\u20AC":
|
|
504
|
+
return "EUR";
|
|
505
|
+
case "\xA3":
|
|
506
|
+
return "GBP";
|
|
507
|
+
case "$":
|
|
508
|
+
return "USD";
|
|
509
|
+
case "\xA5":
|
|
510
|
+
case "\uFFE5":
|
|
511
|
+
return "JPY";
|
|
512
|
+
case "\u20B9":
|
|
513
|
+
return "INR";
|
|
514
|
+
case "\u20BD":
|
|
515
|
+
return "RUB";
|
|
516
|
+
case "\xA2":
|
|
517
|
+
return "USD";
|
|
518
|
+
// US cents
|
|
519
|
+
case "\u20A9":
|
|
520
|
+
return "KRW";
|
|
521
|
+
// South Korean Won
|
|
522
|
+
case "\u20AA":
|
|
523
|
+
return "ILS";
|
|
524
|
+
// Israeli Shekel
|
|
525
|
+
case "\u20A6":
|
|
526
|
+
return "NGN";
|
|
527
|
+
// Nigerian Naira
|
|
528
|
+
case "\u20A8":
|
|
529
|
+
return "PKR";
|
|
530
|
+
// Pakistani Rupee
|
|
531
|
+
case "\u20B1":
|
|
532
|
+
return "PHP";
|
|
533
|
+
// Philippine Peso
|
|
534
|
+
case "\u20AB":
|
|
535
|
+
return "VND";
|
|
536
|
+
// Vietnamese Dong
|
|
537
|
+
case "\u20A1":
|
|
538
|
+
return "CRC";
|
|
539
|
+
// Costa Rican Colon
|
|
540
|
+
case "\u20B2":
|
|
541
|
+
return "PYG";
|
|
542
|
+
// Paraguayan Guarani
|
|
543
|
+
case "\u20B4":
|
|
544
|
+
return "UAH";
|
|
545
|
+
// Ukrainian Hryvnia
|
|
546
|
+
case "\u20B5":
|
|
547
|
+
return "GHS";
|
|
548
|
+
// Ghanaian Cedi
|
|
549
|
+
case "\u20B6":
|
|
550
|
+
return "EUR";
|
|
551
|
+
// Livre tournois (historical, fallback to EUR)
|
|
552
|
+
case "\u20B8":
|
|
553
|
+
return "KZT";
|
|
554
|
+
// Kazakhstani Tenge
|
|
555
|
+
case "\u20BA":
|
|
556
|
+
return "TRY";
|
|
557
|
+
// Turkish Lira
|
|
558
|
+
case "\u20BB":
|
|
559
|
+
return "EUR";
|
|
560
|
+
// Nordic mark (historical, fallback to EUR)
|
|
561
|
+
case "\u20BC":
|
|
562
|
+
return "AZN";
|
|
563
|
+
// Azerbaijani Manat
|
|
564
|
+
case "\u20BE":
|
|
565
|
+
return "GEL";
|
|
566
|
+
// Georgian Lari
|
|
567
|
+
case "\u20BF":
|
|
568
|
+
return "BTC";
|
|
569
|
+
// Bitcoin
|
|
570
|
+
case "\uFDFC":
|
|
571
|
+
return "SAR";
|
|
572
|
+
// Saudi Riyal
|
|
573
|
+
case "\uFF04":
|
|
574
|
+
return "USD";
|
|
575
|
+
// Full-width dollar sign
|
|
576
|
+
case "\uFFE0":
|
|
577
|
+
return "USD";
|
|
578
|
+
// Full-width cent sign
|
|
579
|
+
case "\uFFE1":
|
|
580
|
+
return "GBP";
|
|
581
|
+
// Full-width pound sign
|
|
582
|
+
case "\uFFE2":
|
|
583
|
+
return "GBP";
|
|
584
|
+
// Full-width not sign (sometimes used for pound)
|
|
585
|
+
case "\uFFE3":
|
|
586
|
+
return "JPY";
|
|
587
|
+
// Full-width macron (sometimes used for yen)
|
|
588
|
+
case "\uFFE4":
|
|
589
|
+
return "EUR";
|
|
590
|
+
// Full-width lira sign
|
|
591
|
+
case "\uFFE6":
|
|
592
|
+
return "KRW";
|
|
593
|
+
// Full-width won sign
|
|
594
|
+
// Additional common symbols
|
|
595
|
+
case "R":
|
|
596
|
+
return "ZAR";
|
|
597
|
+
// South African Rand (when used as symbol)
|
|
598
|
+
case "R$":
|
|
599
|
+
return "BRL";
|
|
600
|
+
// Brazilian Real
|
|
601
|
+
case "C$":
|
|
602
|
+
return "CAD";
|
|
603
|
+
// Canadian Dollar
|
|
604
|
+
case "A$":
|
|
605
|
+
return "AUD";
|
|
606
|
+
// Australian Dollar
|
|
607
|
+
case "S$":
|
|
608
|
+
return "SGD";
|
|
609
|
+
// Singapore Dollar
|
|
610
|
+
case "HK$":
|
|
611
|
+
return "HKD";
|
|
612
|
+
// Hong Kong Dollar
|
|
613
|
+
case "NZ$":
|
|
614
|
+
return "NZD";
|
|
615
|
+
// New Zealand Dollar
|
|
616
|
+
case "kr":
|
|
617
|
+
case "Kr":
|
|
618
|
+
return "SEK";
|
|
619
|
+
// Swedish Krona (fallback, could be NOK or DKK)
|
|
620
|
+
case "z\u0142":
|
|
621
|
+
return "PLN";
|
|
622
|
+
// Polish Zloty
|
|
623
|
+
case "K\u010D":
|
|
624
|
+
return "CZK";
|
|
625
|
+
// Czech Koruna
|
|
626
|
+
case "Ft":
|
|
627
|
+
return "HUF";
|
|
628
|
+
// Hungarian Forint
|
|
629
|
+
case "lei":
|
|
630
|
+
return "RON";
|
|
631
|
+
// Romanian Leu
|
|
632
|
+
case "\u043B\u0432":
|
|
633
|
+
return "BGN";
|
|
634
|
+
// Bulgarian Lev
|
|
635
|
+
case "kn":
|
|
636
|
+
return "HRK";
|
|
637
|
+
// Croatian Kuna
|
|
638
|
+
case "din":
|
|
639
|
+
return "RSD";
|
|
640
|
+
// Serbian Dinar
|
|
641
|
+
case "\u0434\u0435\u043D":
|
|
642
|
+
return "MKD";
|
|
643
|
+
// Macedonian Denar
|
|
644
|
+
default:
|
|
645
|
+
return "EUR";
|
|
646
|
+
}
|
|
647
|
+
} catch (error) {
|
|
648
|
+
return "EUR";
|
|
649
|
+
}
|
|
650
|
+
};
|
|
651
|
+
var currencyToSymbol = (currency) => {
|
|
652
|
+
try {
|
|
653
|
+
if (!currency || typeof currency !== "string") {
|
|
654
|
+
return "\u20AC";
|
|
655
|
+
}
|
|
656
|
+
const normalizedCurrency = currency.trim().toUpperCase();
|
|
657
|
+
switch (normalizedCurrency) {
|
|
658
|
+
case "EUR":
|
|
659
|
+
return "\u20AC";
|
|
660
|
+
case "GBP":
|
|
661
|
+
return "\xA3";
|
|
662
|
+
case "USD":
|
|
663
|
+
return "$";
|
|
664
|
+
case "JPY":
|
|
665
|
+
return "\xA5";
|
|
666
|
+
case "INR":
|
|
667
|
+
return "\u20B9";
|
|
668
|
+
case "RUB":
|
|
669
|
+
return "\u20BD";
|
|
670
|
+
case "CNY":
|
|
671
|
+
return "\xA5";
|
|
672
|
+
case "KRW":
|
|
673
|
+
return "\u20A9";
|
|
674
|
+
// South Korean Won
|
|
675
|
+
case "ILS":
|
|
676
|
+
return "\u20AA";
|
|
677
|
+
// Israeli Shekel
|
|
678
|
+
case "NGN":
|
|
679
|
+
return "\u20A6";
|
|
680
|
+
// Nigerian Naira
|
|
681
|
+
case "PKR":
|
|
682
|
+
return "\u20A8";
|
|
683
|
+
// Pakistani Rupee
|
|
684
|
+
case "PHP":
|
|
685
|
+
return "\u20B1";
|
|
686
|
+
// Philippine Peso
|
|
687
|
+
case "VND":
|
|
688
|
+
return "\u20AB";
|
|
689
|
+
// Vietnamese Dong
|
|
690
|
+
case "CRC":
|
|
691
|
+
return "\u20A1";
|
|
692
|
+
// Costa Rican Colon
|
|
693
|
+
case "PYG":
|
|
694
|
+
return "\u20B2";
|
|
695
|
+
// Paraguayan Guarani
|
|
696
|
+
case "UAH":
|
|
697
|
+
return "\u20B4";
|
|
698
|
+
// Ukrainian Hryvnia
|
|
699
|
+
case "GHS":
|
|
700
|
+
return "\u20B5";
|
|
701
|
+
// Ghanaian Cedi
|
|
702
|
+
case "KZT":
|
|
703
|
+
return "\u20B8";
|
|
704
|
+
// Kazakhstani Tenge
|
|
705
|
+
case "TRY":
|
|
706
|
+
return "\u20BA";
|
|
707
|
+
// Turkish Lira
|
|
708
|
+
case "AZN":
|
|
709
|
+
return "\u20BC";
|
|
710
|
+
// Azerbaijani Manat
|
|
711
|
+
case "GEL":
|
|
712
|
+
return "\u20BE";
|
|
713
|
+
// Georgian Lari
|
|
714
|
+
case "BTC":
|
|
715
|
+
return "\u20BF";
|
|
716
|
+
// Bitcoin
|
|
717
|
+
case "SAR":
|
|
718
|
+
return "\uFDFC";
|
|
719
|
+
// Saudi Riyal
|
|
720
|
+
case "ZAR":
|
|
721
|
+
return "R";
|
|
722
|
+
// South African Rand
|
|
723
|
+
case "BRL":
|
|
724
|
+
return "R$";
|
|
725
|
+
// Brazilian Real
|
|
726
|
+
case "CAD":
|
|
727
|
+
return "C$";
|
|
728
|
+
// Canadian Dollar
|
|
729
|
+
case "AUD":
|
|
730
|
+
return "A$";
|
|
731
|
+
// Australian Dollar
|
|
732
|
+
case "SGD":
|
|
733
|
+
return "S$";
|
|
734
|
+
// Singapore Dollar
|
|
735
|
+
case "HKD":
|
|
736
|
+
return "HK$";
|
|
737
|
+
// Hong Kong Dollar
|
|
738
|
+
case "NZD":
|
|
739
|
+
return "NZ$";
|
|
740
|
+
// New Zealand Dollar
|
|
741
|
+
case "SEK":
|
|
742
|
+
return "kr";
|
|
743
|
+
// Swedish Krona
|
|
744
|
+
case "NOK":
|
|
745
|
+
return "kr";
|
|
746
|
+
// Norwegian Krone
|
|
747
|
+
case "DKK":
|
|
748
|
+
return "kr";
|
|
749
|
+
// Danish Krone
|
|
750
|
+
case "PLN":
|
|
751
|
+
return "z\u0142";
|
|
752
|
+
// Polish Zloty
|
|
753
|
+
case "CZK":
|
|
754
|
+
return "K\u010D";
|
|
755
|
+
// Czech Koruna
|
|
756
|
+
case "HUF":
|
|
757
|
+
return "Ft";
|
|
758
|
+
// Hungarian Forint
|
|
759
|
+
case "RON":
|
|
760
|
+
return "lei";
|
|
761
|
+
// Romanian Leu
|
|
762
|
+
case "BGN":
|
|
763
|
+
return "\u043B\u0432";
|
|
764
|
+
// Bulgarian Lev
|
|
765
|
+
case "HRK":
|
|
766
|
+
return "kn";
|
|
767
|
+
// Croatian Kuna
|
|
768
|
+
case "RSD":
|
|
769
|
+
return "din";
|
|
770
|
+
// Serbian Dinar
|
|
771
|
+
case "MKD":
|
|
772
|
+
return "\u0434\u0435\u043D";
|
|
773
|
+
// Macedonian Denar
|
|
774
|
+
case "CHF":
|
|
775
|
+
return "CHF";
|
|
776
|
+
// Swiss Franc (commonly written as CHF)
|
|
777
|
+
case "THB":
|
|
778
|
+
return "\u0E3F";
|
|
779
|
+
// Thai Baht
|
|
780
|
+
case "MYR":
|
|
781
|
+
return "RM";
|
|
782
|
+
// Malaysian Ringgit
|
|
783
|
+
case "IDR":
|
|
784
|
+
return "Rp";
|
|
785
|
+
// Indonesian Rupiah
|
|
786
|
+
case "CLP":
|
|
787
|
+
return "$";
|
|
788
|
+
// Chilean Peso (uses $ symbol)
|
|
789
|
+
case "COP":
|
|
790
|
+
return "$";
|
|
791
|
+
// Colombian Peso (uses $ symbol)
|
|
792
|
+
case "MXN":
|
|
793
|
+
return "$";
|
|
794
|
+
// Mexican Peso (uses $ symbol)
|
|
795
|
+
case "ARS":
|
|
796
|
+
return "$";
|
|
797
|
+
// Argentine Peso (uses $ symbol)
|
|
798
|
+
case "UYU":
|
|
799
|
+
return "$";
|
|
800
|
+
// Uruguayan Peso (uses $ symbol)
|
|
801
|
+
case "PEN":
|
|
802
|
+
return "S/";
|
|
803
|
+
// Peruvian Sol
|
|
804
|
+
case "BOB":
|
|
805
|
+
return "Bs";
|
|
806
|
+
// Bolivian Boliviano
|
|
807
|
+
case "EGP":
|
|
808
|
+
return "\xA3";
|
|
809
|
+
// Egyptian Pound (uses £ symbol)
|
|
810
|
+
case "LBP":
|
|
811
|
+
return "\xA3";
|
|
812
|
+
// Lebanese Pound (uses £ symbol)
|
|
813
|
+
case "SYP":
|
|
814
|
+
return "\xA3";
|
|
815
|
+
// Syrian Pound (uses £ symbol)
|
|
816
|
+
default:
|
|
817
|
+
return "\u20AC";
|
|
818
|
+
}
|
|
819
|
+
} catch (error) {
|
|
820
|
+
return "\u20AC";
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
var isNullUndefinedOrEmptyEnforced = isNilTextOrEmpty;
|
|
443
824
|
var addSpaceBetweenNumbers = addThousandsSpace;
|
|
444
825
|
|
|
826
|
+
// src/utils/validations.ts
|
|
827
|
+
function isPTTaxId(value) {
|
|
828
|
+
try {
|
|
829
|
+
if (value === null || value === void 0) return false;
|
|
830
|
+
let nif = String(value).trim();
|
|
831
|
+
nif = nif.replace(/[^0-9]/g, "");
|
|
832
|
+
if (nif.length !== 9) return false;
|
|
833
|
+
if (!/^\d{9}$/.test(nif)) return false;
|
|
834
|
+
if (/^(\d)\1{8}$/.test(nif)) return false;
|
|
835
|
+
const first = nif[0];
|
|
836
|
+
const defaultAllowed = /* @__PURE__ */ new Set(["1", "2", "3", "5", "6", "8", "9"]);
|
|
837
|
+
if (!defaultAllowed.has(first)) return false;
|
|
838
|
+
let sum = 0;
|
|
839
|
+
for (let i = 0; i < 8; i++) {
|
|
840
|
+
const digit = parseInt(nif[i], 10);
|
|
841
|
+
const weight = 9 - i;
|
|
842
|
+
sum += digit * weight;
|
|
843
|
+
}
|
|
844
|
+
const mod11 = sum % 11;
|
|
845
|
+
const checkDigit = mod11 < 2 ? 0 : 11 - mod11;
|
|
846
|
+
return checkDigit === parseInt(nif[8], 10);
|
|
847
|
+
} catch {
|
|
848
|
+
return false;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
var isValidPTTaxId = isPTTaxId;
|
|
852
|
+
|
|
853
|
+
// src/utils/iban.ts
|
|
854
|
+
function isValidIBAN(value) {
|
|
855
|
+
try {
|
|
856
|
+
if (!value || typeof value !== "string") return false;
|
|
857
|
+
const iban = value.replace(/[\s-]/g, "").toUpperCase();
|
|
858
|
+
if (iban.length < 15 || iban.length > 34) return false;
|
|
859
|
+
if (!/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/.test(iban)) return false;
|
|
860
|
+
const countryCode = iban.slice(0, 2);
|
|
861
|
+
const spec = countrySpecs[countryCode];
|
|
862
|
+
if (!spec?.bban_regexp || !spec.chars) return false;
|
|
863
|
+
if (spec.chars !== iban.length) return false;
|
|
864
|
+
if (!/^[0-9]{2}$/.test(iban.slice(2, 4))) return false;
|
|
865
|
+
const bban = iban.slice(4);
|
|
866
|
+
if (!new RegExp(spec.bban_regexp).test(bban)) return false;
|
|
867
|
+
if (spec.bban_validation_func && !spec.bban_validation_func(bban))
|
|
868
|
+
return false;
|
|
869
|
+
return isValidIBANChecksum(iban);
|
|
870
|
+
} catch {
|
|
871
|
+
return false;
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
function isValidIBANChecksum(iban) {
|
|
875
|
+
const rearranged = iban.slice(4) + iban.slice(0, 4);
|
|
876
|
+
const numericString = rearranged.split("").map((char) => {
|
|
877
|
+
const code = char.charCodeAt(0);
|
|
878
|
+
return code >= 65 ? (code - 55).toString() : char;
|
|
879
|
+
}).join("");
|
|
880
|
+
return mod97(numericString) === 1;
|
|
881
|
+
}
|
|
882
|
+
function mod97(numStr) {
|
|
883
|
+
let remainder = numStr;
|
|
884
|
+
while (remainder.length > 2) {
|
|
885
|
+
const chunk2 = remainder.slice(0, 9);
|
|
886
|
+
const chunkNum = parseInt(chunk2, 10);
|
|
887
|
+
if (isNaN(chunkNum)) return NaN;
|
|
888
|
+
remainder = chunkNum % 97 + remainder.slice(chunk2.length);
|
|
889
|
+
}
|
|
890
|
+
return parseInt(remainder, 10) % 97;
|
|
891
|
+
}
|
|
892
|
+
var countrySpecs = {
|
|
893
|
+
AD: {
|
|
894
|
+
chars: 24,
|
|
895
|
+
bban_regexp: "^[0-9]{8}[A-Z0-9]{12}$",
|
|
896
|
+
IBANRegistry: true,
|
|
897
|
+
SEPA: true
|
|
898
|
+
},
|
|
899
|
+
AE: { chars: 23, bban_regexp: "^[0-9]{3}[0-9]{16}$", IBANRegistry: true },
|
|
900
|
+
AL: { chars: 28, bban_regexp: "^[0-9]{8}[A-Z0-9]{16}$", IBANRegistry: true },
|
|
901
|
+
AT: { chars: 20, bban_regexp: "^[0-9]{16}$", IBANRegistry: true, SEPA: true },
|
|
902
|
+
AZ: { chars: 28, bban_regexp: "^[A-Z]{4}[A-Z0-9]{20}$", IBANRegistry: true },
|
|
903
|
+
BA: {
|
|
904
|
+
chars: 20,
|
|
905
|
+
bban_regexp: "^[0-9]{16}$",
|
|
906
|
+
bban_validation_func: checkMod97BBAN,
|
|
907
|
+
IBANRegistry: true
|
|
908
|
+
},
|
|
909
|
+
BE: {
|
|
910
|
+
chars: 16,
|
|
911
|
+
bban_regexp: "^[0-9]{12}$",
|
|
912
|
+
bban_validation_func: checkBelgianBBAN,
|
|
913
|
+
IBANRegistry: true,
|
|
914
|
+
SEPA: true
|
|
915
|
+
},
|
|
916
|
+
BG: {
|
|
917
|
+
chars: 22,
|
|
918
|
+
bban_regexp: "^[A-Z]{4}[0-9]{6}[A-Z0-9]{8}$",
|
|
919
|
+
IBANRegistry: true,
|
|
920
|
+
SEPA: true
|
|
921
|
+
},
|
|
922
|
+
BH: { chars: 22, bban_regexp: "^[A-Z]{4}[A-Z0-9]{14}$", IBANRegistry: true },
|
|
923
|
+
BR: {
|
|
924
|
+
chars: 29,
|
|
925
|
+
bban_regexp: "^[0-9]{23}[A-Z]{1}[A-Z0-9]{1}$",
|
|
926
|
+
IBANRegistry: true
|
|
927
|
+
},
|
|
928
|
+
BY: {
|
|
929
|
+
chars: 28,
|
|
930
|
+
bban_regexp: "^[A-Z]{4}[0-9]{4}[A-Z0-9]{16}$",
|
|
931
|
+
IBANRegistry: true
|
|
932
|
+
},
|
|
933
|
+
CH: {
|
|
934
|
+
chars: 21,
|
|
935
|
+
bban_regexp: "^[0-9]{5}[A-Z0-9]{12}$",
|
|
936
|
+
IBANRegistry: true,
|
|
937
|
+
SEPA: true
|
|
938
|
+
},
|
|
939
|
+
CR: { chars: 22, bban_regexp: "^[0-9]{18}$", IBANRegistry: true },
|
|
940
|
+
CY: {
|
|
941
|
+
chars: 28,
|
|
942
|
+
bban_regexp: "^[0-9]{8}[A-Z0-9]{16}$",
|
|
943
|
+
IBANRegistry: true,
|
|
944
|
+
SEPA: true
|
|
945
|
+
},
|
|
946
|
+
CZ: {
|
|
947
|
+
chars: 24,
|
|
948
|
+
bban_regexp: "^[0-9]{20}$",
|
|
949
|
+
bban_validation_func: checkCzechSlovakBBAN,
|
|
950
|
+
IBANRegistry: true,
|
|
951
|
+
SEPA: true
|
|
952
|
+
},
|
|
953
|
+
DE: { chars: 22, bban_regexp: "^[0-9]{18}$", IBANRegistry: true, SEPA: true },
|
|
954
|
+
DK: { chars: 18, bban_regexp: "^[0-9]{14}$", IBANRegistry: true, SEPA: true },
|
|
955
|
+
DO: { chars: 28, bban_regexp: "^[A-Z]{4}[0-9]{20}$", IBANRegistry: true },
|
|
956
|
+
EE: {
|
|
957
|
+
chars: 20,
|
|
958
|
+
bban_regexp: "^[0-9]{16}$",
|
|
959
|
+
bban_validation_func: checkEstonianBBAN,
|
|
960
|
+
IBANRegistry: true,
|
|
961
|
+
SEPA: true
|
|
962
|
+
},
|
|
963
|
+
EG: { chars: 29, bban_regexp: "^[0-9]{25}$", IBANRegistry: true },
|
|
964
|
+
ES: {
|
|
965
|
+
chars: 24,
|
|
966
|
+
bban_regexp: "^[0-9]{20}$",
|
|
967
|
+
bban_validation_func: checkSpanishBBAN,
|
|
968
|
+
IBANRegistry: true,
|
|
969
|
+
SEPA: true
|
|
970
|
+
},
|
|
971
|
+
FI: { chars: 18, bban_regexp: "^[0-9]{14}$", IBANRegistry: true, SEPA: true },
|
|
972
|
+
FO: { chars: 18, bban_regexp: "^[0-9]{14}$", IBANRegistry: true },
|
|
973
|
+
FR: {
|
|
974
|
+
chars: 27,
|
|
975
|
+
bban_regexp: "^[0-9]{10}[A-Z0-9]{11}[0-9]{2}$",
|
|
976
|
+
bban_validation_func: checkFrenchBBAN,
|
|
977
|
+
IBANRegistry: true,
|
|
978
|
+
SEPA: true
|
|
979
|
+
},
|
|
980
|
+
GB: {
|
|
981
|
+
chars: 22,
|
|
982
|
+
bban_regexp: "^[A-Z]{4}[0-9]{14}$",
|
|
983
|
+
IBANRegistry: true,
|
|
984
|
+
SEPA: true
|
|
985
|
+
},
|
|
986
|
+
GE: { chars: 22, bban_regexp: "^[A-Z0-9]{2}[0-9]{16}$", IBANRegistry: true },
|
|
987
|
+
GI: {
|
|
988
|
+
chars: 23,
|
|
989
|
+
bban_regexp: "^[A-Z]{4}[A-Z0-9]{15}$",
|
|
990
|
+
IBANRegistry: true,
|
|
991
|
+
SEPA: true
|
|
992
|
+
},
|
|
993
|
+
GL: { chars: 18, bban_regexp: "^[0-9]{14}$", IBANRegistry: true },
|
|
994
|
+
GR: {
|
|
995
|
+
chars: 27,
|
|
996
|
+
bban_regexp: "^[0-9]{7}[A-Z0-9]{16}$",
|
|
997
|
+
IBANRegistry: true,
|
|
998
|
+
SEPA: true
|
|
999
|
+
},
|
|
1000
|
+
GT: { chars: 28, bban_regexp: "^[A-Z0-9]{24}$", IBANRegistry: true },
|
|
1001
|
+
HR: {
|
|
1002
|
+
chars: 21,
|
|
1003
|
+
bban_regexp: "^[0-9]{17}$",
|
|
1004
|
+
bban_validation_func: checkCroatianBBAN,
|
|
1005
|
+
IBANRegistry: true,
|
|
1006
|
+
SEPA: true
|
|
1007
|
+
},
|
|
1008
|
+
HU: {
|
|
1009
|
+
chars: 28,
|
|
1010
|
+
bban_regexp: "^[0-9]{24}$",
|
|
1011
|
+
bban_validation_func: checkHungarianBBAN,
|
|
1012
|
+
IBANRegistry: true,
|
|
1013
|
+
SEPA: true
|
|
1014
|
+
},
|
|
1015
|
+
IE: {
|
|
1016
|
+
chars: 22,
|
|
1017
|
+
bban_regexp: "^[A-Z0-9]{4}[0-9]{14}$",
|
|
1018
|
+
IBANRegistry: true,
|
|
1019
|
+
SEPA: true
|
|
1020
|
+
},
|
|
1021
|
+
IL: { chars: 23, bban_regexp: "^[0-9]{19}$", IBANRegistry: true },
|
|
1022
|
+
IS: { chars: 26, bban_regexp: "^[0-9]{22}$", IBANRegistry: true, SEPA: true },
|
|
1023
|
+
IT: {
|
|
1024
|
+
chars: 27,
|
|
1025
|
+
bban_regexp: "^[A-Z]{1}[0-9]{10}[A-Z0-9]{12}$",
|
|
1026
|
+
IBANRegistry: true,
|
|
1027
|
+
SEPA: true
|
|
1028
|
+
},
|
|
1029
|
+
JO: {
|
|
1030
|
+
chars: 30,
|
|
1031
|
+
bban_regexp: "^[A-Z]{4}[0-9]{4}[A-Z0-9]{18}$",
|
|
1032
|
+
IBANRegistry: true
|
|
1033
|
+
},
|
|
1034
|
+
KW: { chars: 30, bban_regexp: "^[A-Z]{4}[A-Z0-9]{22}$", IBANRegistry: true },
|
|
1035
|
+
KZ: { chars: 20, bban_regexp: "^[0-9]{3}[A-Z0-9]{13}$", IBANRegistry: true },
|
|
1036
|
+
LB: { chars: 28, bban_regexp: "^[0-9]{4}[A-Z0-9]{20}$", IBANRegistry: true },
|
|
1037
|
+
LC: { chars: 32, bban_regexp: "^[A-Z]{4}[A-Z0-9]{24}$", IBANRegistry: true },
|
|
1038
|
+
LI: {
|
|
1039
|
+
chars: 21,
|
|
1040
|
+
bban_regexp: "^[0-9]{5}[A-Z0-9]{12}$",
|
|
1041
|
+
IBANRegistry: true,
|
|
1042
|
+
SEPA: true
|
|
1043
|
+
},
|
|
1044
|
+
LT: { chars: 20, bban_regexp: "^[0-9]{16}$", IBANRegistry: true, SEPA: true },
|
|
1045
|
+
LU: {
|
|
1046
|
+
chars: 20,
|
|
1047
|
+
bban_regexp: "^[0-9]{3}[A-Z0-9]{13}$",
|
|
1048
|
+
IBANRegistry: true,
|
|
1049
|
+
SEPA: true
|
|
1050
|
+
},
|
|
1051
|
+
LV: {
|
|
1052
|
+
chars: 21,
|
|
1053
|
+
bban_regexp: "^[A-Z]{4}[A-Z0-9]{13}$",
|
|
1054
|
+
IBANRegistry: true,
|
|
1055
|
+
SEPA: true
|
|
1056
|
+
},
|
|
1057
|
+
MC: {
|
|
1058
|
+
chars: 27,
|
|
1059
|
+
bban_regexp: "^[0-9]{10}[A-Z0-9]{11}[0-9]{2}$",
|
|
1060
|
+
bban_validation_func: checkFrenchBBAN,
|
|
1061
|
+
IBANRegistry: true,
|
|
1062
|
+
SEPA: true
|
|
1063
|
+
},
|
|
1064
|
+
MD: {
|
|
1065
|
+
chars: 24,
|
|
1066
|
+
bban_regexp: "^[A-Z0-9]{2}[A-Z0-9]{18}$",
|
|
1067
|
+
IBANRegistry: true
|
|
1068
|
+
},
|
|
1069
|
+
ME: {
|
|
1070
|
+
chars: 22,
|
|
1071
|
+
bban_regexp: "^[0-9]{18}$",
|
|
1072
|
+
bban_validation_func: checkMod97BBAN,
|
|
1073
|
+
IBANRegistry: true
|
|
1074
|
+
},
|
|
1075
|
+
MK: {
|
|
1076
|
+
chars: 19,
|
|
1077
|
+
bban_regexp: "^[0-9]{3}[A-Z0-9]{10}[0-9]{2}$",
|
|
1078
|
+
bban_validation_func: checkMod97BBAN,
|
|
1079
|
+
IBANRegistry: true
|
|
1080
|
+
},
|
|
1081
|
+
MR: { chars: 27, bban_regexp: "^[0-9]{23}$", IBANRegistry: true },
|
|
1082
|
+
MT: {
|
|
1083
|
+
chars: 31,
|
|
1084
|
+
bban_regexp: "^[A-Z]{4}[0-9]{5}[A-Z0-9]{18}$",
|
|
1085
|
+
IBANRegistry: true,
|
|
1086
|
+
SEPA: true
|
|
1087
|
+
},
|
|
1088
|
+
MU: {
|
|
1089
|
+
chars: 30,
|
|
1090
|
+
bban_regexp: "^[A-Z]{4}[0-9]{19}[A-Z]{3}$",
|
|
1091
|
+
IBANRegistry: true
|
|
1092
|
+
},
|
|
1093
|
+
NL: {
|
|
1094
|
+
chars: 18,
|
|
1095
|
+
bban_regexp: "^[A-Z]{4}[0-9]{10}$",
|
|
1096
|
+
IBANRegistry: true,
|
|
1097
|
+
SEPA: true
|
|
1098
|
+
},
|
|
1099
|
+
NO: {
|
|
1100
|
+
chars: 15,
|
|
1101
|
+
bban_regexp: "^[0-9]{11}$",
|
|
1102
|
+
bban_validation_func: checkNorwegianBBAN,
|
|
1103
|
+
IBANRegistry: true,
|
|
1104
|
+
SEPA: true
|
|
1105
|
+
},
|
|
1106
|
+
PK: { chars: 24, bban_regexp: "^[A-Z0-9]{4}[0-9]{16}$", IBANRegistry: true },
|
|
1107
|
+
PL: {
|
|
1108
|
+
chars: 28,
|
|
1109
|
+
bban_regexp: "^[0-9]{24}$",
|
|
1110
|
+
bban_validation_func: checkPolishBBAN,
|
|
1111
|
+
IBANRegistry: true,
|
|
1112
|
+
SEPA: true
|
|
1113
|
+
},
|
|
1114
|
+
PS: { chars: 29, bban_regexp: "^[A-Z0-9]{4}[0-9]{21}$", IBANRegistry: true },
|
|
1115
|
+
PT: {
|
|
1116
|
+
chars: 25,
|
|
1117
|
+
bban_regexp: "^[0-9]{21}$",
|
|
1118
|
+
bban_validation_func: checkMod97BBAN,
|
|
1119
|
+
IBANRegistry: true,
|
|
1120
|
+
SEPA: true
|
|
1121
|
+
},
|
|
1122
|
+
QA: { chars: 29, bban_regexp: "^[A-Z]{4}[A-Z0-9]{21}$", IBANRegistry: true },
|
|
1123
|
+
RO: {
|
|
1124
|
+
chars: 24,
|
|
1125
|
+
bban_regexp: "^[A-Z]{4}[A-Z0-9]{16}$",
|
|
1126
|
+
IBANRegistry: true,
|
|
1127
|
+
SEPA: true
|
|
1128
|
+
},
|
|
1129
|
+
RS: {
|
|
1130
|
+
chars: 22,
|
|
1131
|
+
bban_regexp: "^[0-9]{18}$",
|
|
1132
|
+
bban_validation_func: checkMod97BBAN,
|
|
1133
|
+
IBANRegistry: true
|
|
1134
|
+
},
|
|
1135
|
+
SA: { chars: 24, bban_regexp: "^[0-9]{2}[A-Z0-9]{18}$", IBANRegistry: true },
|
|
1136
|
+
SE: { chars: 24, bban_regexp: "^[0-9]{20}$", IBANRegistry: true, SEPA: true },
|
|
1137
|
+
SI: {
|
|
1138
|
+
chars: 19,
|
|
1139
|
+
bban_regexp: "^[0-9]{15}$",
|
|
1140
|
+
bban_validation_func: checkMod97BBAN,
|
|
1141
|
+
IBANRegistry: true,
|
|
1142
|
+
SEPA: true
|
|
1143
|
+
},
|
|
1144
|
+
SK: {
|
|
1145
|
+
chars: 24,
|
|
1146
|
+
bban_regexp: "^[0-9]{20}$",
|
|
1147
|
+
bban_validation_func: checkCzechSlovakBBAN,
|
|
1148
|
+
IBANRegistry: true,
|
|
1149
|
+
SEPA: true
|
|
1150
|
+
},
|
|
1151
|
+
SM: {
|
|
1152
|
+
chars: 27,
|
|
1153
|
+
bban_regexp: "^[A-Z]{1}[0-9]{10}[A-Z0-9]{12}$",
|
|
1154
|
+
IBANRegistry: true,
|
|
1155
|
+
SEPA: true
|
|
1156
|
+
},
|
|
1157
|
+
TN: { chars: 24, bban_regexp: "^[0-9]{20}$", IBANRegistry: true },
|
|
1158
|
+
TR: { chars: 26, bban_regexp: "^[0-9]{5}[A-Z0-9]{17}$", IBANRegistry: true },
|
|
1159
|
+
UA: { chars: 29, bban_regexp: "^[0-9]{6}[A-Z0-9]{19}$", IBANRegistry: true },
|
|
1160
|
+
VG: { chars: 24, bban_regexp: "^[A-Z0-9]{4}[0-9]{16}$", IBANRegistry: true },
|
|
1161
|
+
XK: { chars: 20, bban_regexp: "^[0-9]{16}$", IBANRegistry: true }
|
|
1162
|
+
};
|
|
1163
|
+
function checkMod97BBAN(bban) {
|
|
1164
|
+
const stripped = bban.replace(/[\s.]+/g, "");
|
|
1165
|
+
return mod97(stripped) === 1;
|
|
1166
|
+
}
|
|
1167
|
+
function checkBelgianBBAN(bban) {
|
|
1168
|
+
const stripped = bban.replace(/[\s.]+/g, "");
|
|
1169
|
+
const checkingPart = parseInt(stripped.substring(0, stripped.length - 2), 10);
|
|
1170
|
+
const checksum = parseInt(stripped.substring(stripped.length - 2), 10);
|
|
1171
|
+
const remainder = checkingPart % 97 === 0 ? 97 : checkingPart % 97;
|
|
1172
|
+
return remainder === checksum;
|
|
1173
|
+
}
|
|
1174
|
+
function checkCzechSlovakBBAN(bban) {
|
|
1175
|
+
const weightsPrefix = [10, 5, 8, 4, 2, 1];
|
|
1176
|
+
const weightsSuffix = [6, 3, 7, 9, 10, 5, 8, 4, 2, 1];
|
|
1177
|
+
const controlPrefix = parseInt(bban.charAt(9), 10);
|
|
1178
|
+
const controlSuffix = parseInt(bban.charAt(19), 10);
|
|
1179
|
+
const prefix = bban.substring(4, 9);
|
|
1180
|
+
const suffix = bban.substring(10, 19);
|
|
1181
|
+
let sum = 0;
|
|
1182
|
+
for (let i = 0; i < prefix.length; i++) {
|
|
1183
|
+
sum += parseInt(prefix.charAt(i), 10) * weightsPrefix[i];
|
|
1184
|
+
}
|
|
1185
|
+
let remainder = sum % 11;
|
|
1186
|
+
if (controlPrefix !== (remainder === 0 ? 0 : remainder === 1 ? 1 : 11 - remainder)) {
|
|
1187
|
+
return false;
|
|
1188
|
+
}
|
|
1189
|
+
sum = 0;
|
|
1190
|
+
for (let i = 0; i < suffix.length; i++) {
|
|
1191
|
+
sum += parseInt(suffix.charAt(i), 10) * weightsSuffix[i];
|
|
1192
|
+
}
|
|
1193
|
+
remainder = sum % 11;
|
|
1194
|
+
return controlSuffix === (remainder === 0 ? 0 : remainder === 1 ? 1 : 11 - remainder);
|
|
1195
|
+
}
|
|
1196
|
+
function checkEstonianBBAN(bban) {
|
|
1197
|
+
const weights = [7, 1, 3, 7, 1, 3, 7, 1, 3, 7, 1, 3, 7];
|
|
1198
|
+
const controlDigit = parseInt(bban.charAt(15), 10);
|
|
1199
|
+
const toCheck = bban.substring(2, 15);
|
|
1200
|
+
let sum = 0;
|
|
1201
|
+
for (let i = 0; i < toCheck.length; i++) {
|
|
1202
|
+
sum += parseInt(toCheck.charAt(i), 10) * weights[i];
|
|
1203
|
+
}
|
|
1204
|
+
const remainder = sum % 10;
|
|
1205
|
+
return controlDigit === (remainder === 0 ? 0 : 10 - remainder);
|
|
1206
|
+
}
|
|
1207
|
+
function checkSpanishBBAN(bban) {
|
|
1208
|
+
const weightsBankBranch = [4, 8, 5, 10, 9, 7, 3, 6];
|
|
1209
|
+
const weightsAccount = [1, 2, 4, 8, 5, 10, 9, 7, 3, 6];
|
|
1210
|
+
const controlBankBranch = parseInt(bban.charAt(8), 10);
|
|
1211
|
+
const controlAccount = parseInt(bban.charAt(9), 10);
|
|
1212
|
+
const bankBranch = bban.substring(0, 8);
|
|
1213
|
+
const account = bban.substring(10, 20);
|
|
1214
|
+
let sum = 0;
|
|
1215
|
+
for (let i = 0; i < 8; i++) {
|
|
1216
|
+
sum += parseInt(bankBranch.charAt(i), 10) * weightsBankBranch[i];
|
|
1217
|
+
}
|
|
1218
|
+
let remainder = sum % 11;
|
|
1219
|
+
if (controlBankBranch !== (remainder === 0 ? 0 : remainder === 1 ? 1 : 11 - remainder)) {
|
|
1220
|
+
return false;
|
|
1221
|
+
}
|
|
1222
|
+
sum = 0;
|
|
1223
|
+
for (let i = 0; i < 10; i++) {
|
|
1224
|
+
sum += parseInt(account.charAt(i), 10) * weightsAccount[i];
|
|
1225
|
+
}
|
|
1226
|
+
remainder = sum % 11;
|
|
1227
|
+
return controlAccount === (remainder === 0 ? 0 : remainder === 1 ? 1 : 11 - remainder);
|
|
1228
|
+
}
|
|
1229
|
+
function checkFrenchBBAN(bban) {
|
|
1230
|
+
const stripped = bban.replace(/[\s.]+/g, "");
|
|
1231
|
+
const normalized = Array.from(stripped);
|
|
1232
|
+
for (let i = 0; i < stripped.length; i++) {
|
|
1233
|
+
const c = normalized[i].charCodeAt(0);
|
|
1234
|
+
if (c >= 65) {
|
|
1235
|
+
switch (c) {
|
|
1236
|
+
case 65:
|
|
1237
|
+
case 74:
|
|
1238
|
+
normalized[i] = "1";
|
|
1239
|
+
break;
|
|
1240
|
+
case 66:
|
|
1241
|
+
case 75:
|
|
1242
|
+
case 83:
|
|
1243
|
+
normalized[i] = "2";
|
|
1244
|
+
break;
|
|
1245
|
+
case 67:
|
|
1246
|
+
case 76:
|
|
1247
|
+
case 84:
|
|
1248
|
+
normalized[i] = "3";
|
|
1249
|
+
break;
|
|
1250
|
+
case 68:
|
|
1251
|
+
case 77:
|
|
1252
|
+
case 85:
|
|
1253
|
+
normalized[i] = "4";
|
|
1254
|
+
break;
|
|
1255
|
+
case 69:
|
|
1256
|
+
case 78:
|
|
1257
|
+
case 86:
|
|
1258
|
+
normalized[i] = "5";
|
|
1259
|
+
break;
|
|
1260
|
+
case 70:
|
|
1261
|
+
case 79:
|
|
1262
|
+
case 87:
|
|
1263
|
+
normalized[i] = "6";
|
|
1264
|
+
break;
|
|
1265
|
+
case 71:
|
|
1266
|
+
case 80:
|
|
1267
|
+
case 88:
|
|
1268
|
+
normalized[i] = "7";
|
|
1269
|
+
break;
|
|
1270
|
+
case 72:
|
|
1271
|
+
case 81:
|
|
1272
|
+
case 89:
|
|
1273
|
+
normalized[i] = "8";
|
|
1274
|
+
break;
|
|
1275
|
+
case 73:
|
|
1276
|
+
case 82:
|
|
1277
|
+
case 90:
|
|
1278
|
+
normalized[i] = "9";
|
|
1279
|
+
break;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
return mod97(normalized.join("")) === 0;
|
|
1284
|
+
}
|
|
1285
|
+
function checkCroatianBBAN(bban) {
|
|
1286
|
+
const controlBankBranch = parseInt(bban.charAt(6), 10);
|
|
1287
|
+
const controlAccount = parseInt(bban.charAt(16), 10);
|
|
1288
|
+
const bankBranch = bban.substring(0, 6);
|
|
1289
|
+
const account = bban.substring(7, 16);
|
|
1290
|
+
return checkMod11(bankBranch, controlBankBranch) && checkMod11(account, controlAccount);
|
|
1291
|
+
}
|
|
1292
|
+
function checkMod11(toCheck, control) {
|
|
1293
|
+
let nr = 10;
|
|
1294
|
+
for (let i = 0; i < toCheck.length; i++) {
|
|
1295
|
+
nr += parseInt(toCheck.charAt(i), 10);
|
|
1296
|
+
if (nr % 10 !== 0) {
|
|
1297
|
+
nr = nr % 10;
|
|
1298
|
+
}
|
|
1299
|
+
nr = nr * 2;
|
|
1300
|
+
nr = nr % 11;
|
|
1301
|
+
}
|
|
1302
|
+
return control === (11 - nr === 10 ? 0 : 11 - nr);
|
|
1303
|
+
}
|
|
1304
|
+
function checkHungarianBBAN(bban) {
|
|
1305
|
+
const weights = [9, 7, 3, 1, 9, 7, 3, 1, 9, 7, 3, 1, 9, 7, 3];
|
|
1306
|
+
const controlDigitBankBranch = parseInt(bban.charAt(7), 10);
|
|
1307
|
+
const toCheckBankBranch = bban.substring(0, 7);
|
|
1308
|
+
let sum = 0;
|
|
1309
|
+
for (let i = 0; i < toCheckBankBranch.length; i++) {
|
|
1310
|
+
sum += parseInt(toCheckBankBranch.charAt(i), 10) * weights[i];
|
|
1311
|
+
}
|
|
1312
|
+
const remainder = sum % 10;
|
|
1313
|
+
if (controlDigitBankBranch !== (remainder === 0 ? 0 : 10 - remainder)) {
|
|
1314
|
+
return false;
|
|
1315
|
+
}
|
|
1316
|
+
if (bban.endsWith("00000000")) {
|
|
1317
|
+
const toCheckAccount = bban.substring(8, 15);
|
|
1318
|
+
const controlDigitAccount = parseInt(bban.charAt(15), 10);
|
|
1319
|
+
sum = 0;
|
|
1320
|
+
for (let i = 0; i < toCheckAccount.length; i++) {
|
|
1321
|
+
sum += parseInt(toCheckAccount.charAt(i), 10) * weights[i];
|
|
1322
|
+
}
|
|
1323
|
+
const accountRemainder = sum % 10;
|
|
1324
|
+
return controlDigitAccount === (accountRemainder === 0 ? 0 : 10 - accountRemainder);
|
|
1325
|
+
} else {
|
|
1326
|
+
const toCheckAccount = bban.substring(8, 23);
|
|
1327
|
+
const controlDigitAccount = parseInt(bban.charAt(23), 10);
|
|
1328
|
+
sum = 0;
|
|
1329
|
+
for (let i = 0; i < toCheckAccount.length; i++) {
|
|
1330
|
+
sum += parseInt(toCheckAccount.charAt(i), 10) * weights[i];
|
|
1331
|
+
}
|
|
1332
|
+
const accountRemainder = sum % 10;
|
|
1333
|
+
return controlDigitAccount === (accountRemainder === 0 ? 0 : 10 - accountRemainder);
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
function checkNorwegianBBAN(bban) {
|
|
1337
|
+
const weights = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];
|
|
1338
|
+
const stripped = bban.replace(/[\s.]+/g, "");
|
|
1339
|
+
const controlDigit = parseInt(stripped.charAt(10), 10);
|
|
1340
|
+
const toCheck = stripped.substring(0, 10);
|
|
1341
|
+
let sum = 0;
|
|
1342
|
+
for (let i = 0; i < 10; i++) {
|
|
1343
|
+
sum += parseInt(toCheck.charAt(i), 10) * weights[i];
|
|
1344
|
+
}
|
|
1345
|
+
const remainder = sum % 11;
|
|
1346
|
+
return controlDigit === (remainder === 0 ? 0 : 11 - remainder);
|
|
1347
|
+
}
|
|
1348
|
+
function checkPolishBBAN(bban) {
|
|
1349
|
+
const weights = [3, 9, 7, 1, 3, 9, 7];
|
|
1350
|
+
const controlDigit = parseInt(bban.charAt(7), 10);
|
|
1351
|
+
const toCheck = bban.substring(0, 7);
|
|
1352
|
+
let sum = 0;
|
|
1353
|
+
for (let i = 0; i < 7; i++) {
|
|
1354
|
+
sum += parseInt(toCheck.charAt(i), 10) * weights[i];
|
|
1355
|
+
}
|
|
1356
|
+
const remainder = sum % 10;
|
|
1357
|
+
return controlDigit === (remainder === 0 ? 0 : 10 - remainder);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// src/utils/security.ts
|
|
1361
|
+
var checkMarkdownSecurity = (markdownText) => {
|
|
1362
|
+
if (!markdownText || typeof markdownText !== "string") {
|
|
1363
|
+
return {
|
|
1364
|
+
isValid: true,
|
|
1365
|
+
text: "",
|
|
1366
|
+
risks: [],
|
|
1367
|
+
sanitized: false
|
|
1368
|
+
};
|
|
1369
|
+
}
|
|
1370
|
+
const securityCheck = {
|
|
1371
|
+
isValid: true,
|
|
1372
|
+
text: markdownText,
|
|
1373
|
+
risks: [],
|
|
1374
|
+
sanitized: false
|
|
1375
|
+
};
|
|
1376
|
+
const securityPatterns = {
|
|
1377
|
+
// Critical risks
|
|
1378
|
+
scriptTags: {
|
|
1379
|
+
pattern: /<\s*script\b[^>]*>[\s\S]*?<\s*\/\s*script\s*>/gi,
|
|
1380
|
+
severity: "critical",
|
|
1381
|
+
description: "Script tags detected - high XSS risk"
|
|
1382
|
+
},
|
|
1383
|
+
eventHandlers: {
|
|
1384
|
+
pattern: /on\w+\s*=\s*["'][^"']*["']/gi,
|
|
1385
|
+
severity: "critical",
|
|
1386
|
+
description: "JavaScript event handlers detected"
|
|
1387
|
+
},
|
|
1388
|
+
javascriptUrls: {
|
|
1389
|
+
pattern: /javascript\s*:[^"'\s>]*/gi,
|
|
1390
|
+
severity: "critical",
|
|
1391
|
+
description: "JavaScript URLs detected"
|
|
1392
|
+
},
|
|
1393
|
+
// High risks
|
|
1394
|
+
iframes: {
|
|
1395
|
+
pattern: /<\s*iframe\b[^>]*>[\s\S]*?<\s*\/\s*iframe\s*>/gi,
|
|
1396
|
+
severity: "high",
|
|
1397
|
+
description: "Iframe elements detected - potential embedding risk"
|
|
1398
|
+
},
|
|
1399
|
+
objectTags: {
|
|
1400
|
+
pattern: /<\s*object\b[^>]*>[\s\S]*?<\s*\/\s*object\s*>/gi,
|
|
1401
|
+
severity: "high",
|
|
1402
|
+
description: "Object tags detected - potential code execution"
|
|
1403
|
+
},
|
|
1404
|
+
embedTags: {
|
|
1405
|
+
pattern: /<\s*embed\b[^>]*>[\s\S]*?<\s*\/\s*embed\s*>/gi,
|
|
1406
|
+
severity: "high",
|
|
1407
|
+
description: "Embed tags detected - potential code execution"
|
|
1408
|
+
},
|
|
1409
|
+
formTags: {
|
|
1410
|
+
pattern: /<\s*form\b[^>]*>[\s\S]*?<\s*\/\s*form\s*>/gi,
|
|
1411
|
+
severity: "high",
|
|
1412
|
+
description: "Form elements detected - potential data submission risk"
|
|
1413
|
+
},
|
|
1414
|
+
// Medium risks
|
|
1415
|
+
dataUrls: {
|
|
1416
|
+
pattern: /data:\s*[^,\s]+,[^"'\s)>]*/gi,
|
|
1417
|
+
severity: "medium",
|
|
1418
|
+
description: "Data URLs detected - potential data exfiltration"
|
|
1419
|
+
},
|
|
1420
|
+
baseTags: {
|
|
1421
|
+
pattern: /<\s*base\b[^>]*\/?>/gi,
|
|
1422
|
+
severity: "medium",
|
|
1423
|
+
description: "Base tags detected - potential URL hijacking"
|
|
1424
|
+
},
|
|
1425
|
+
styleTags: {
|
|
1426
|
+
pattern: /<\s*style\b[^>]*>[\s\S]*?<\s*\/\s*style\s*>/gi,
|
|
1427
|
+
severity: "medium",
|
|
1428
|
+
description: "Style tags detected - potential CSS injection"
|
|
1429
|
+
},
|
|
1430
|
+
linkTags: {
|
|
1431
|
+
pattern: /<\s*link\b[^>]*\/?>/gi,
|
|
1432
|
+
severity: "medium",
|
|
1433
|
+
description: "Link tags detected - potential resource hijacking"
|
|
1434
|
+
},
|
|
1435
|
+
// Low risks
|
|
1436
|
+
metaTags: {
|
|
1437
|
+
pattern: /<\s*meta\b[^>]*\/?>/gi,
|
|
1438
|
+
severity: "low",
|
|
1439
|
+
description: "Meta tags detected - potential information disclosure"
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
const additionalChecks = [
|
|
1443
|
+
{
|
|
1444
|
+
pattern: /(file|ftp|ws|wss|gopher|ldap|telnet):/gi,
|
|
1445
|
+
type: "suspiciousProtocol",
|
|
1446
|
+
description: "Suspicious URL protocol detected",
|
|
1447
|
+
severity: "high"
|
|
1448
|
+
},
|
|
1449
|
+
{
|
|
1450
|
+
pattern: /(contenteditable|autofocus|formaction|srcdoc|srclang)/gi,
|
|
1451
|
+
type: "dangerousAttributes",
|
|
1452
|
+
description: "Potentially dangerous HTML attributes detected",
|
|
1453
|
+
severity: "medium"
|
|
1454
|
+
},
|
|
1455
|
+
{
|
|
1456
|
+
pattern: /(expression|eval|setTimeout|setInterval|Function|constructor)/gi,
|
|
1457
|
+
type: "dangerousJavaScript",
|
|
1458
|
+
description: "Potentially dangerous JavaScript functions detected",
|
|
1459
|
+
severity: "high"
|
|
1460
|
+
},
|
|
1461
|
+
{
|
|
1462
|
+
pattern: /(vbscript|livescript|mocha):/gi,
|
|
1463
|
+
type: "dangerousScriptProtocol",
|
|
1464
|
+
description: "Dangerous scripting protocols detected",
|
|
1465
|
+
severity: "critical"
|
|
1466
|
+
},
|
|
1467
|
+
{
|
|
1468
|
+
pattern: /<!--[\s\S]*?-->/g,
|
|
1469
|
+
type: "htmlComments",
|
|
1470
|
+
description: "HTML comments detected - potential information leakage",
|
|
1471
|
+
severity: "low"
|
|
1472
|
+
}
|
|
1473
|
+
];
|
|
1474
|
+
const sanitizeContent = (text, pattern, replacement = "") => {
|
|
1475
|
+
return text.replace(pattern, replacement);
|
|
1476
|
+
};
|
|
1477
|
+
let contentWasSanitized = false;
|
|
1478
|
+
Object.entries(securityPatterns).forEach(([riskType, config]) => {
|
|
1479
|
+
if (config.pattern.test(markdownText)) {
|
|
1480
|
+
securityCheck.isValid = false;
|
|
1481
|
+
securityCheck.risks.push({
|
|
1482
|
+
type: riskType,
|
|
1483
|
+
description: config.description,
|
|
1484
|
+
severity: config.severity
|
|
1485
|
+
});
|
|
1486
|
+
securityCheck.text = sanitizeContent(securityCheck.text, config.pattern);
|
|
1487
|
+
contentWasSanitized = true;
|
|
1488
|
+
}
|
|
1489
|
+
});
|
|
1490
|
+
additionalChecks.forEach((check) => {
|
|
1491
|
+
if (check.pattern.test(markdownText)) {
|
|
1492
|
+
securityCheck.isValid = false;
|
|
1493
|
+
securityCheck.risks.push({
|
|
1494
|
+
type: check.type,
|
|
1495
|
+
description: check.description,
|
|
1496
|
+
severity: check.severity
|
|
1497
|
+
});
|
|
1498
|
+
securityCheck.text = sanitizeContent(securityCheck.text, check.pattern);
|
|
1499
|
+
contentWasSanitized = true;
|
|
1500
|
+
}
|
|
1501
|
+
});
|
|
1502
|
+
securityCheck.sanitized = contentWasSanitized;
|
|
1503
|
+
const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
1504
|
+
securityCheck.risks.sort((a, b) => {
|
|
1505
|
+
const aSeverity = severityOrder[a.severity || "low"];
|
|
1506
|
+
const bSeverity = severityOrder[b.severity || "low"];
|
|
1507
|
+
return aSeverity - bSeverity;
|
|
1508
|
+
});
|
|
1509
|
+
return securityCheck;
|
|
1510
|
+
};
|
|
1511
|
+
var sanitizeMarkdown = (text) => {
|
|
1512
|
+
if (!text || typeof text !== "string") {
|
|
1513
|
+
return "";
|
|
1514
|
+
}
|
|
1515
|
+
try {
|
|
1516
|
+
return text.replace(/<[^>]*>/g, "").replace(/javascript:/gi, "").replace(/data:[^,]*,[^"'\s>)]*/gi, "").replace(/(file|ftp|ws|wss|vbscript|livescript|mocha):/gi, "").replace(/&[#\w]+;/g, "").replace(/expression\s*\([^)]*\)/gi, "").replace(/\s+/g, " ").trim();
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
return "";
|
|
1519
|
+
}
|
|
1520
|
+
};
|
|
1521
|
+
var assessSecurityRisks = (risks) => {
|
|
1522
|
+
if (!risks || risks.length === 0) {
|
|
1523
|
+
return {
|
|
1524
|
+
score: 0,
|
|
1525
|
+
level: "safe",
|
|
1526
|
+
recommendations: ["Content appears safe to use"]
|
|
1527
|
+
};
|
|
1528
|
+
}
|
|
1529
|
+
const severityScores = { critical: 100, high: 50, medium: 20, low: 5 };
|
|
1530
|
+
const score = risks.reduce((total, risk) => {
|
|
1531
|
+
return total + (severityScores[risk.severity || "low"] || 5);
|
|
1532
|
+
}, 0);
|
|
1533
|
+
let level;
|
|
1534
|
+
if (score >= 100) level = "critical";
|
|
1535
|
+
else if (score >= 50) level = "high";
|
|
1536
|
+
else if (score >= 20) level = "medium";
|
|
1537
|
+
else if (score >= 5) level = "low";
|
|
1538
|
+
else level = "safe";
|
|
1539
|
+
const recommendations = [];
|
|
1540
|
+
const hasCritical = risks.some((r) => r.severity === "critical");
|
|
1541
|
+
const hasHigh = risks.some((r) => r.severity === "high");
|
|
1542
|
+
if (hasCritical) {
|
|
1543
|
+
recommendations.push("URGENT: Critical security risks detected - do not render this content");
|
|
1544
|
+
recommendations.push("Use aggressive sanitization before any processing");
|
|
1545
|
+
}
|
|
1546
|
+
if (hasHigh) {
|
|
1547
|
+
recommendations.push("High security risks detected - sanitization strongly recommended");
|
|
1548
|
+
recommendations.push("Consider rejecting this content or applying strict filtering");
|
|
1549
|
+
}
|
|
1550
|
+
if (level === "medium") {
|
|
1551
|
+
recommendations.push("Medium security risks detected - apply sanitization");
|
|
1552
|
+
recommendations.push("Review content carefully before use");
|
|
1553
|
+
}
|
|
1554
|
+
if (level === "low") {
|
|
1555
|
+
recommendations.push("Low security risks detected - basic sanitization recommended");
|
|
1556
|
+
}
|
|
1557
|
+
recommendations.push("Always validate content from untrusted sources");
|
|
1558
|
+
recommendations.push("Consider implementing Content Security Policy (CSP)");
|
|
1559
|
+
return { score, level, recommendations };
|
|
1560
|
+
};
|
|
1561
|
+
|
|
445
1562
|
// src/index.ts
|
|
446
1563
|
var isBrowser = typeof globalThis !== "undefined" && typeof globalThis.document !== "undefined";
|
|
447
1564
|
var isNode = typeof process !== "undefined" && !!process.versions?.node;
|
|
448
1565
|
|
|
449
|
-
export { addSpaceBetweenNumbers, addThousandsSpace, areArraysDeepEqualUnordered, areArraysEqual, basicSanitize, chunk, clamp, cleanObject, compact, debounce, deburr, difference, fill, flatten, flattenDepth, flattenDepthBase, flattenOnce, formatBytes, getStringSimilarity, groupBy, hasNilOrEmpty, humanFileSize, intersection, isBrowser, isFlattenable, isNil, isNilEmptyOrZeroLen, isNilOrEmpty, isNilOrNaN, isNilOrZeroLen, isNilText, isNode, isNullOrUndefined, isNullOrUndefinedEmptyOrZero, isNullOrUndefinedInArray, isNullOrUndefinedOrNaN, isNullOrUndefinedTextInc, isNullUndefinedOrEmpty, isNullUndefinedOrZero, objectToString, omit, otp, parseToNumber, pick, pluck, pushAll, randomDigits, removeDiacritics, round, safeParseInt, sanitize, shuffle, slugify, sortBy, stringSimilarity, throttle, toInteger, toNumber, uniq, uniqBy };
|
|
1566
|
+
export { addSpaceBetweenNumbers, addThousandsSpace, areArraysDeepEqualUnordered, areArraysEqual, assessSecurityRisks, basicSanitize, checkMarkdownSecurity, chunk, clamp, cleanObject, compact, currencyToSymbol, debounce, deburr, delay, difference, fill, flatten, flattenDepth, flattenDepthBase, flattenOnce, formatBytes, formatCurrency, getStringSimilarity, groupBy, hasNilOrEmpty, humanFileSize, intersection, isBrowser, isFlattenable, isNil, isNilEmptyOrZeroLen, isNilOrEmpty, isNilOrNaN, isNilOrZeroLen, isNilText, isNilTextOrEmpty, isNode, isNullOrUndefined, isNullOrUndefinedEmptyOrZero, isNullOrUndefinedInArray, isNullOrUndefinedOrNaN, isNullOrUndefinedTextInc, isNullUndefinedOrEmpty, isNullUndefinedOrEmptyEnforced, isNullUndefinedOrZero, isPTTaxId, isValidIBAN, isValidPTTaxId, objectToString, omit, otp, parseName, parseToNumber, pick, pluck, pushAll, randomDigits, removeDiacritics, round, safeParseInt, sanitize, sanitizeMarkdown, shuffle, slugify, sortBy, stringSimilarity, symbolToCurrency, throttle, toInteger, toNumber, uniq, uniqBy };
|
|
450
1567
|
//# sourceMappingURL=index.js.map
|
|
451
1568
|
//# sourceMappingURL=index.js.map
|