@naturalcycles/nodejs-lib 15.125.3 → 15.125.4
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/validation/ajv/getAjv.js +40 -21
- package/package.json +2 -2
- package/src/validation/ajv/getAjv.ts +40 -23
|
@@ -633,8 +633,10 @@ export function createAjv(opt) {
|
|
|
633
633
|
const monthLengths = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
|
|
634
634
|
const DASH_CODE = '-'.charCodeAt(0);
|
|
635
635
|
const ZERO_CODE = '0'.charCodeAt(0);
|
|
636
|
+
const NINE_CODE = '9'.charCodeAt(0);
|
|
636
637
|
const PLUS_CODE = '+'.charCodeAt(0);
|
|
637
638
|
const COLON_CODE = ':'.charCodeAt(0);
|
|
639
|
+
const DOT_CODE = '.'.charCodeAt(0);
|
|
638
640
|
/**
|
|
639
641
|
* This is a performance optimized correct validation
|
|
640
642
|
* for ISO dates formatted as YYYY-MM-DD.
|
|
@@ -650,13 +652,10 @@ function isIsoDateValid(s) {
|
|
|
650
652
|
if (s.charCodeAt(4) !== DASH_CODE || s.charCodeAt(7) !== DASH_CODE)
|
|
651
653
|
return false;
|
|
652
654
|
// fast parse numbers without substrings/Number()
|
|
653
|
-
const year = (s
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
const month = (s.charCodeAt(5) - ZERO_CODE) * 10 + (s.charCodeAt(6) - ZERO_CODE);
|
|
658
|
-
const day = (s.charCodeAt(8) - ZERO_CODE) * 10 + (s.charCodeAt(9) - ZERO_CODE);
|
|
659
|
-
if (month < 1 || month > 12 || day < 1)
|
|
655
|
+
const year = parseDigits(s, 0, 4);
|
|
656
|
+
const month = parseDigits(s, 5, 2);
|
|
657
|
+
const day = parseDigits(s, 8, 2);
|
|
658
|
+
if (year < 0 || month < 1 || month > 12 || day < 1)
|
|
660
659
|
return false;
|
|
661
660
|
if (month !== 2) {
|
|
662
661
|
return day <= monthLengths[month];
|
|
@@ -666,15 +665,16 @@ function isIsoDateValid(s) {
|
|
|
666
665
|
}
|
|
667
666
|
/**
|
|
668
667
|
* This is a performance optimized correct validation
|
|
669
|
-
* for ISO datetimes formatted as "YYYY-MM-DDTHH:MM:SS"
|
|
670
|
-
*
|
|
668
|
+
* for ISO datetimes formatted as "YYYY-MM-DDTHH:MM:SS",
|
|
669
|
+
* optionally followed by fractional seconds of 1 to 9 digits (e.g. ".231" or ".231263"),
|
|
670
|
+
* followed by nothing, "Z" or "+hh:mm" or "-hh:mm".
|
|
671
671
|
*
|
|
672
672
|
* - Slightly more performant than using `localTime`.
|
|
673
673
|
* - More performant than string splitting and `Number()` conversions
|
|
674
674
|
* - Less performant than regex, but it does not allow invalid dates.
|
|
675
675
|
*/
|
|
676
676
|
function isIsoDateTimeValid(s) {
|
|
677
|
-
if (s.length < 19 || s.length >
|
|
677
|
+
if (s.length < 19 || s.length > 35)
|
|
678
678
|
return false;
|
|
679
679
|
if (s.charCodeAt(10) !== 84)
|
|
680
680
|
return false; // 'T'
|
|
@@ -684,7 +684,16 @@ function isIsoDateTimeValid(s) {
|
|
|
684
684
|
const timePart = s.slice(11, 19); // HH:MM:SS
|
|
685
685
|
if (!isIsoTimeValid(timePart))
|
|
686
686
|
return false;
|
|
687
|
-
|
|
687
|
+
let zoneStart = 19;
|
|
688
|
+
if (s.charCodeAt(19) === DOT_CODE) {
|
|
689
|
+
zoneStart = 20;
|
|
690
|
+
while (isDigitCode(s.charCodeAt(zoneStart)))
|
|
691
|
+
zoneStart++;
|
|
692
|
+
const fractionDigits = zoneStart - 20;
|
|
693
|
+
if (fractionDigits < 1 || fractionDigits > 9)
|
|
694
|
+
return false;
|
|
695
|
+
}
|
|
696
|
+
const zonePart = s.slice(zoneStart); // nothing or Z or +/-hh:mm
|
|
688
697
|
if (!isIsoTimezoneValid(zonePart))
|
|
689
698
|
return false;
|
|
690
699
|
return true;
|
|
@@ -702,13 +711,13 @@ function isIsoTimeValid(s) {
|
|
|
702
711
|
return false;
|
|
703
712
|
if (s.charCodeAt(2) !== COLON_CODE || s.charCodeAt(5) !== COLON_CODE)
|
|
704
713
|
return false;
|
|
705
|
-
const hour = (s
|
|
714
|
+
const hour = parseDigits(s, 0, 2);
|
|
706
715
|
if (hour < 0 || hour > 23)
|
|
707
716
|
return false;
|
|
708
|
-
const minute = (s
|
|
717
|
+
const minute = parseDigits(s, 3, 2);
|
|
709
718
|
if (minute < 0 || minute > 59)
|
|
710
719
|
return false;
|
|
711
|
-
const second = (s
|
|
720
|
+
const second = parseDigits(s, 6, 2);
|
|
712
721
|
if (second < 0 || second > 59)
|
|
713
722
|
return false;
|
|
714
723
|
return true;
|
|
@@ -733,14 +742,14 @@ function isIsoTimezoneValid(s) {
|
|
|
733
742
|
return false;
|
|
734
743
|
const isWestern = s[0] === '-';
|
|
735
744
|
const isEastern = s[0] === '+';
|
|
736
|
-
const hour = (s
|
|
745
|
+
const hour = parseDigits(s, 1, 2);
|
|
737
746
|
if (hour < 0)
|
|
738
747
|
return false;
|
|
739
748
|
if (isWestern && hour > 12)
|
|
740
749
|
return false;
|
|
741
750
|
if (isEastern && hour > 14)
|
|
742
751
|
return false;
|
|
743
|
-
const minute = (s
|
|
752
|
+
const minute = parseDigits(s, 4, 2);
|
|
744
753
|
if (minute < 0 || minute > 59)
|
|
745
754
|
return false;
|
|
746
755
|
if (isEastern && hour === 14 && minute > 0)
|
|
@@ -760,13 +769,23 @@ function isIsoMonthValid(s) {
|
|
|
760
769
|
if (s.charCodeAt(4) !== DASH_CODE)
|
|
761
770
|
return false;
|
|
762
771
|
// fast parse numbers without substrings/Number()
|
|
763
|
-
const year = (s
|
|
764
|
-
|
|
765
|
-
(s.charCodeAt(2) - ZERO_CODE) * 10 +
|
|
766
|
-
(s.charCodeAt(3) - ZERO_CODE);
|
|
767
|
-
const month = (s.charCodeAt(5) - ZERO_CODE) * 10 + (s.charCodeAt(6) - ZERO_CODE);
|
|
772
|
+
const year = parseDigits(s, 0, 4);
|
|
773
|
+
const month = parseDigits(s, 5, 2);
|
|
768
774
|
return _isBetween(year, 1900, 2500, '[]') && _isBetween(month, 1, 12, '[]');
|
|
769
775
|
}
|
|
776
|
+
function parseDigits(s, start, count) {
|
|
777
|
+
let value = 0;
|
|
778
|
+
for (let i = start; i < start + count; i++) {
|
|
779
|
+
const code = s.charCodeAt(i);
|
|
780
|
+
if (!isDigitCode(code))
|
|
781
|
+
return -1;
|
|
782
|
+
value = value * 10 + code - ZERO_CODE;
|
|
783
|
+
}
|
|
784
|
+
return value;
|
|
785
|
+
}
|
|
786
|
+
function isDigitCode(code) {
|
|
787
|
+
return code >= ZERO_CODE && code <= NINE_CODE;
|
|
788
|
+
}
|
|
770
789
|
function isPrimitive(data) {
|
|
771
790
|
return data !== null && typeof data === 'object';
|
|
772
791
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/nodejs-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "15.125.
|
|
4
|
+
"version": "15.125.4",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@naturalcycles/js-lib": "^15",
|
|
7
7
|
"@standard-schema/spec": "^1",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"yaml": "^2"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
|
-
"@naturalcycles/dev-lib": "
|
|
17
|
+
"@naturalcycles/dev-lib": "0.0.0",
|
|
18
18
|
"typescript": "^7"
|
|
19
19
|
},
|
|
20
20
|
"exports": {
|
|
@@ -738,8 +738,10 @@ const monthLengths = [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
|
|
738
738
|
|
|
739
739
|
const DASH_CODE = '-'.charCodeAt(0)
|
|
740
740
|
const ZERO_CODE = '0'.charCodeAt(0)
|
|
741
|
+
const NINE_CODE = '9'.charCodeAt(0)
|
|
741
742
|
const PLUS_CODE = '+'.charCodeAt(0)
|
|
742
743
|
const COLON_CODE = ':'.charCodeAt(0)
|
|
744
|
+
const DOT_CODE = '.'.charCodeAt(0)
|
|
743
745
|
|
|
744
746
|
/**
|
|
745
747
|
* This is a performance optimized correct validation
|
|
@@ -755,16 +757,12 @@ function isIsoDateValid(s: string): boolean {
|
|
|
755
757
|
if (s.charCodeAt(4) !== DASH_CODE || s.charCodeAt(7) !== DASH_CODE) return false
|
|
756
758
|
|
|
757
759
|
// fast parse numbers without substrings/Number()
|
|
758
|
-
const year =
|
|
759
|
-
(s.charCodeAt(0) - ZERO_CODE) * 1000 +
|
|
760
|
-
(s.charCodeAt(1) - ZERO_CODE) * 100 +
|
|
761
|
-
(s.charCodeAt(2) - ZERO_CODE) * 10 +
|
|
762
|
-
(s.charCodeAt(3) - ZERO_CODE)
|
|
760
|
+
const year = parseDigits(s, 0, 4)
|
|
763
761
|
|
|
764
|
-
const month = (s
|
|
765
|
-
const day = (s
|
|
762
|
+
const month = parseDigits(s, 5, 2)
|
|
763
|
+
const day = parseDigits(s, 8, 2)
|
|
766
764
|
|
|
767
|
-
if (month < 1 || month > 12 || day < 1) return false
|
|
765
|
+
if (year < 0 || month < 1 || month > 12 || day < 1) return false
|
|
768
766
|
|
|
769
767
|
if (month !== 2) {
|
|
770
768
|
return day <= monthLengths[month]!
|
|
@@ -776,15 +774,16 @@ function isIsoDateValid(s: string): boolean {
|
|
|
776
774
|
|
|
777
775
|
/**
|
|
778
776
|
* This is a performance optimized correct validation
|
|
779
|
-
* for ISO datetimes formatted as "YYYY-MM-DDTHH:MM:SS"
|
|
780
|
-
*
|
|
777
|
+
* for ISO datetimes formatted as "YYYY-MM-DDTHH:MM:SS",
|
|
778
|
+
* optionally followed by fractional seconds of 1 to 9 digits (e.g. ".231" or ".231263"),
|
|
779
|
+
* followed by nothing, "Z" or "+hh:mm" or "-hh:mm".
|
|
781
780
|
*
|
|
782
781
|
* - Slightly more performant than using `localTime`.
|
|
783
782
|
* - More performant than string splitting and `Number()` conversions
|
|
784
783
|
* - Less performant than regex, but it does not allow invalid dates.
|
|
785
784
|
*/
|
|
786
785
|
function isIsoDateTimeValid(s: string): boolean {
|
|
787
|
-
if (s.length < 19 || s.length >
|
|
786
|
+
if (s.length < 19 || s.length > 35) return false
|
|
788
787
|
if (s.charCodeAt(10) !== 84) return false // 'T'
|
|
789
788
|
|
|
790
789
|
const datePart = s.slice(0, 10) // YYYY-MM-DD
|
|
@@ -793,7 +792,15 @@ function isIsoDateTimeValid(s: string): boolean {
|
|
|
793
792
|
const timePart = s.slice(11, 19) // HH:MM:SS
|
|
794
793
|
if (!isIsoTimeValid(timePart)) return false
|
|
795
794
|
|
|
796
|
-
|
|
795
|
+
let zoneStart = 19
|
|
796
|
+
if (s.charCodeAt(19) === DOT_CODE) {
|
|
797
|
+
zoneStart = 20
|
|
798
|
+
while (isDigitCode(s.charCodeAt(zoneStart))) zoneStart++
|
|
799
|
+
const fractionDigits = zoneStart - 20
|
|
800
|
+
if (fractionDigits < 1 || fractionDigits > 9) return false
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const zonePart = s.slice(zoneStart) // nothing or Z or +/-hh:mm
|
|
797
804
|
if (!isIsoTimezoneValid(zonePart)) return false
|
|
798
805
|
|
|
799
806
|
return true
|
|
@@ -811,13 +818,13 @@ function isIsoTimeValid(s: string): boolean {
|
|
|
811
818
|
if (s.length !== 8) return false
|
|
812
819
|
if (s.charCodeAt(2) !== COLON_CODE || s.charCodeAt(5) !== COLON_CODE) return false
|
|
813
820
|
|
|
814
|
-
const hour = (s
|
|
821
|
+
const hour = parseDigits(s, 0, 2)
|
|
815
822
|
if (hour < 0 || hour > 23) return false
|
|
816
823
|
|
|
817
|
-
const minute = (s
|
|
824
|
+
const minute = parseDigits(s, 3, 2)
|
|
818
825
|
if (minute < 0 || minute > 59) return false
|
|
819
826
|
|
|
820
|
-
const second = (s
|
|
827
|
+
const second = parseDigits(s, 6, 2)
|
|
821
828
|
if (second < 0 || second > 59) return false
|
|
822
829
|
|
|
823
830
|
return true
|
|
@@ -840,12 +847,12 @@ function isIsoTimezoneValid(s: string): boolean {
|
|
|
840
847
|
const isWestern = s[0] === '-'
|
|
841
848
|
const isEastern = s[0] === '+'
|
|
842
849
|
|
|
843
|
-
const hour = (s
|
|
850
|
+
const hour = parseDigits(s, 1, 2)
|
|
844
851
|
if (hour < 0) return false
|
|
845
852
|
if (isWestern && hour > 12) return false
|
|
846
853
|
if (isEastern && hour > 14) return false
|
|
847
854
|
|
|
848
|
-
const minute = (s
|
|
855
|
+
const minute = parseDigits(s, 4, 2)
|
|
849
856
|
if (minute < 0 || minute > 59) return false
|
|
850
857
|
|
|
851
858
|
if (isEastern && hour === 14 && minute > 0) return false // max is +14:00
|
|
@@ -864,17 +871,27 @@ function isIsoMonthValid(s: string): boolean {
|
|
|
864
871
|
if (s.charCodeAt(4) !== DASH_CODE) return false
|
|
865
872
|
|
|
866
873
|
// fast parse numbers without substrings/Number()
|
|
867
|
-
const year =
|
|
868
|
-
(s.charCodeAt(0) - ZERO_CODE) * 1000 +
|
|
869
|
-
(s.charCodeAt(1) - ZERO_CODE) * 100 +
|
|
870
|
-
(s.charCodeAt(2) - ZERO_CODE) * 10 +
|
|
871
|
-
(s.charCodeAt(3) - ZERO_CODE)
|
|
874
|
+
const year = parseDigits(s, 0, 4)
|
|
872
875
|
|
|
873
|
-
const month = (s
|
|
876
|
+
const month = parseDigits(s, 5, 2)
|
|
874
877
|
|
|
875
878
|
return _isBetween(year, 1900, 2500, '[]') && _isBetween(month, 1, 12, '[]')
|
|
876
879
|
}
|
|
877
880
|
|
|
881
|
+
function parseDigits(s: string, start: number, count: number): number {
|
|
882
|
+
let value = 0
|
|
883
|
+
for (let i = start; i < start + count; i++) {
|
|
884
|
+
const code = s.charCodeAt(i)
|
|
885
|
+
if (!isDigitCode(code)) return -1
|
|
886
|
+
value = value * 10 + code - ZERO_CODE
|
|
887
|
+
}
|
|
888
|
+
return value
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function isDigitCode(code: number): boolean {
|
|
892
|
+
return code >= ZERO_CODE && code <= NINE_CODE
|
|
893
|
+
}
|
|
894
|
+
|
|
878
895
|
function isPrimitive(data: any): boolean {
|
|
879
896
|
return data !== null && typeof data === 'object'
|
|
880
897
|
}
|