@astral/validations 3.0.0-beta.3 → 3.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 CHANGED
@@ -20,6 +20,7 @@
20
20
  - [min](#min-number)
21
21
  - [max](#max-number)
22
22
  - [integer](#integer)
23
+ - [positiveNumber](#positivenumber)
23
24
  - [string](#string)
24
25
  - [min](#min-string)
25
26
  - [max](#max-string)
@@ -57,8 +58,11 @@
57
58
  - [or](#or)
58
59
  - [Integrations](#integrations)
59
60
  - [react-hook-form](#react-hook-form)
60
- - [Error message customization](#error-message-customization)
61
- - [Exclusion managing](#exclusion-managing)
61
+ - [Guides](#guides)
62
+ - [Переиспользование объектов схемы](#переиспользование-объектов-схемы)
63
+ - [Переиспользование объектов схемы, с условной валидацией и зависимыми полями](#переиспользование-объектов-схемы-с-условной-валидацией-и-зависимыми-полями)
64
+ - [Error message customization](#error-message-customization)
65
+ - [Exclusion managing](#exclusion-managing)
62
66
 
63
67
  ---
64
68
 
@@ -76,7 +80,9 @@ yarn add @astral/validations
76
80
 
77
81
  # Basic usage
78
82
 
79
- Валидация объекта с вложенным массивом
83
+ Валидация объекта с вложенным массивом.
84
+
85
+ ### [Codesandbox](https://codesandbox.io/s/astral-validations-basic-usage-jkpjr5?file=/main.ts)
80
86
 
81
87
  ```ts
82
88
  import {
@@ -86,7 +92,8 @@ import {
86
92
  string,
87
93
  optional,
88
94
  min,
89
- number
95
+ number,
96
+ toPrettyError,
90
97
  } from '@astral/validations';
91
98
 
92
99
  type Permission = {
@@ -125,13 +132,15 @@ validate({
125
132
  },
126
133
  });
127
134
 
128
- // Error in info.permissions.0.description: Обязательно
129
- validate({
130
- name: 'Vasya',
131
- info: {
132
- permissions: [{ id: 1 }],
133
- },
134
- });
135
+ // { info: { permissions: [{ description: 'Обязательно' }] } }
136
+ toPrettyError(
137
+ validate({
138
+ name: 'Vasya',
139
+ info: {
140
+ permissions: [{ id: 1 }],
141
+ },
142
+ })
143
+ );
135
144
  ```
136
145
 
137
146
  Валидация отдельных value
@@ -255,6 +264,27 @@ validate(3.14)
255
264
 
256
265
  ---
257
266
 
267
+ ### positiveNumber
268
+
269
+ Проверяет является ли значение положительным числом.
270
+
271
+ ```ts
272
+ import { number, positiveNumber } from '@astral/validations';
273
+
274
+ const validate = number(positiveNumber(3));
275
+
276
+ // undefined
277
+ validate(3)
278
+
279
+ // { message: 'Только положительное числа' }
280
+ validate(0)
281
+
282
+ // { message: 'Только положительное числа' }
283
+ validate(-1)
284
+ ```
285
+
286
+ ---
287
+
258
288
  ## string
259
289
 
260
290
  - Возвращает ошибку если:
@@ -658,7 +688,8 @@ import {
658
688
  string,
659
689
  optional,
660
690
  min,
661
- number
691
+ number,
692
+ toPrettyError
662
693
  } from '@astral/validations';
663
694
 
664
695
  type User = {
@@ -692,13 +723,15 @@ validate({
692
723
  },
693
724
  });
694
725
 
695
- // Error in info.permissions.0.description: Обязательно
696
- validate({
697
- name: 'Vasya',
698
- info: {
699
- permissions: [{ id: 1 }],
700
- },
701
- });
726
+ // { info: { permissions: [{ description: 'Обязательно' }] } }
727
+ toPrettyError(
728
+ validate({
729
+ name: 'Vasya',
730
+ info: {
731
+ permissions: [{ id: 1 }],
732
+ },
733
+ })
734
+ );
702
735
  ```
703
736
 
704
737
  ### partial
@@ -706,7 +739,7 @@ validate({
706
739
  Позволяет сделать все поля объекта optional.
707
740
 
708
741
  ```ts
709
- import { partial, object, string } from '@astral/validations';
742
+ import { partial, object, string, toPrettyError } from '@astral/validations';
710
743
 
711
744
  type Values = {
712
745
  name: string;
@@ -718,8 +751,10 @@ const validateRequired = object<Values>({
718
751
  surname: string()
719
752
  })
720
753
 
721
- // { message: 'Ошибка в свойстве name: Обязательно' }
722
- validateRequired({});
754
+ // { name: 'Обязательно' }
755
+ toPrettyError(
756
+ validateRequired({})
757
+ );
723
758
 
724
759
  const validatePartial = partial(
725
760
  object<Values>({
@@ -800,7 +835,8 @@ validate({
800
835
  import {
801
836
  array,
802
837
  arrayItem,
803
- min
838
+ min,
839
+ toPrettyError
804
840
  } from '@astral/validations';
805
841
 
806
842
  type User = {
@@ -824,8 +860,10 @@ validate([{ name: 'Vasya' }]);
824
860
  // { message: 'Не меньше: 1' }
825
861
  validate([]);
826
862
 
827
- // { cause: { errorArray: [{ name: { message: 'Не является строкой' } }] } }
828
- validate([{ name: 22 }]);
863
+ // [{ name: 'Не является строкой' }]
864
+ toPrettyError(
865
+ validate([{ name: 22 }])
866
+ );
829
867
  ```
830
868
 
831
869
  ### arrayItem
@@ -833,7 +871,7 @@ validate([{ name: 22 }]);
833
871
  Применяет переданные правила валидации к каждому элементу массива.
834
872
 
835
873
  ```ts
836
- import { array, arrayItem, object, string, optional } from '@astral/validations';
874
+ import { array, arrayItem, object, string, optional, toPrettyError } from '@astral/validations';
837
875
 
838
876
  type User = {
839
877
  name: string;
@@ -853,16 +891,20 @@ const validate = array(
853
891
  validate([{ name: 'Vasya' }]);
854
892
 
855
893
  // { cause: { errorArray: [{ name: { message: 'Не является строкой' } }] } }
856
- validate([{ name: 22 }]);
894
+ toPrettyError(
895
+ validate([{ name: 22 }])
896
+ );
857
897
  ```
858
898
 
859
899
  ```ts
860
- import { array, arrayItem, string, min } from '@astral/validations';
900
+ import { array, arrayItem, string, min, toPrettyError } from '@astral/validations';
861
901
 
862
902
  const validate = array(arrayItem(string(min(3))));
863
903
 
864
- // { cause: { arrayError: [undefined, { message: 'Мин. символов: 3' }] } }
865
- validate(['vasya', 'ma']);
904
+ // [undefined, 'Мин. символов: 3']
905
+ toPrettyError(
906
+ validate(['vasya', 'ma'])
907
+ );
866
908
  ```
867
909
 
868
910
  ---
@@ -920,10 +962,12 @@ const validate = object<Values, Values>({
920
962
  });
921
963
 
922
964
  // undefined
923
- const result1 = validate({ isAgree: false, name: '' });
965
+ validate({ isAgree: false, name: '' });
924
966
 
925
- // Required error для name
926
- const result2 = validate({ isAgree: true, name: '' });
967
+ // { name: 'Обязательно' }
968
+ toPrettyError(
969
+ validate({ isAgree: true, name: '' })
970
+ );
927
971
  ```
928
972
 
929
973
  ---
@@ -959,7 +1003,7 @@ validateCustomString(20);
959
1003
  ## Базовый пример
960
1004
 
961
1005
  ```ts
962
- import { string, object } from '@astral/validations';
1006
+ import { string, object, toPrettyError } from '@astral/validations';
963
1007
 
964
1008
  type Values = {
965
1009
  name: string;
@@ -980,8 +1024,10 @@ const validate = object<Values, Values>({
980
1024
  }),
981
1025
  });
982
1026
 
983
- // { cause: { errorMap: { nickname: { message: 'Символ "_" запрещен', code: 'nickname-symbol' } } } }
984
- validate({ name: 'Vasya', nickname: 'va_sya' });
1027
+ // { nickname: 'Символ "_" запрещен' }
1028
+ toPrettyError(
1029
+ validate({ name: 'Vasya', nickname: 'va_sya' })
1030
+ );
985
1031
  ```
986
1032
 
987
1033
  ## Связанные поля
@@ -989,7 +1035,7 @@ validate({ name: 'Vasya', nickname: 'va_sya' });
989
1035
  В ```ctx.global.values``` находится value, принятое самым верхнеуровневым guard'ом.
990
1036
 
991
1037
  ```ts
992
- import { object, string } from '@astral/validations';
1038
+ import { object, string, toPrettyError } from '@astral/validations';
993
1039
 
994
1040
  type Values = {
995
1041
  password: string;
@@ -1010,8 +1056,10 @@ const validate = object<Values, Values>({
1010
1056
  }),
1011
1057
  });
1012
1058
 
1013
- // Error.message "Пароли не совпадают" для repeatPassword
1014
- validate({ password: 'qywerty123', repeatPassword: 'qywerty1234' });
1059
+ // { repeatPassword: 'Пароли не совпадают' }
1060
+ toPrettyError(
1061
+ validate({ password: 'qywerty123', repeatPassword: 'qywerty1234' })
1062
+ );
1015
1063
  ```
1016
1064
 
1017
1065
  ## Переиспользуемое правило
@@ -1099,10 +1147,12 @@ const validate = object<Values, Values>({
1099
1147
  });
1100
1148
 
1101
1149
  // undefined
1102
- const result1 = validate({ isAgree: false, name: '' });
1150
+ validate({ isAgree: false, name: '' });
1103
1151
 
1104
- // Required error для name
1105
- const result2 = validate({ isAgree: true, name: '' });
1152
+ // { name: 'Обязательно' }
1153
+ toPrettyError(
1154
+ validate({ isAgree: true, name: '' })
1155
+ );
1106
1156
  ```
1107
1157
 
1108
1158
  When для ветки объекта:
@@ -1123,12 +1173,13 @@ const validate = object<Values, Values>({
1123
1173
  }),
1124
1174
  });
1125
1175
 
1126
- // Error.message "Обязательно" для info
1127
- validate({ name: 'Vasya' });
1176
+ // { info: 'Обязательно' }
1177
+ toPrettyError(
1178
+ validate({ name: 'Vasya' })
1179
+ );
1128
1180
 
1129
1181
  // undefined
1130
1182
  validate({ name: 'Kolya' });
1131
-
1132
1183
  ```
1133
1184
 
1134
1185
  ---
@@ -1184,6 +1235,8 @@ validate(new Date())
1184
1235
 
1185
1236
  Для интеграции с react-hook-form необходимо использовать пакет ```@astral/validations-react-hook-form-resolver```.
1186
1237
 
1238
+ ### [Codesandbox](https://codesandbox.io/s/astral-validations-react-hook-form-tnq4of?file=/src/Form.tsx)
1239
+
1187
1240
  ```tsx
1188
1241
  import { object, string, optional } from '@astral/validations';
1189
1242
  import { resolver } from '@astral/validations-react-hook-form-resolver';
@@ -1222,7 +1275,85 @@ const Form = () => {
1222
1275
  };
1223
1276
  ```
1224
1277
 
1225
- # Error message customization
1278
+ # Guides
1279
+
1280
+
1281
+ ## Переиспользование объектов схемы
1282
+
1283
+ ```ts
1284
+ type Address = {
1285
+ street: string;
1286
+ };
1287
+
1288
+ const address = object<Address>({ street: string() });
1289
+
1290
+ type Organization = {
1291
+ address: Address;
1292
+ };
1293
+
1294
+ const organization = object<Organization>({ address });
1295
+
1296
+ type Values = {
1297
+ name: string;
1298
+ org: Organization;
1299
+ };
1300
+
1301
+ const validateValues = object<Values>({
1302
+ name: string(),
1303
+ org: organization,
1304
+ });
1305
+ ```
1306
+
1307
+ ---
1308
+
1309
+ ## Переиспользование объектов схемы, с условной валидацией и зависимыми полями
1310
+
1311
+ ```ts
1312
+ type RusOrganization = {
1313
+ inn: string;
1314
+ isIP: boolean;
1315
+ };
1316
+
1317
+ type EngOrganization = {
1318
+ name: string;
1319
+ };
1320
+
1321
+ type Values = {
1322
+ isRus: boolean;
1323
+ org: { data: RusOrganization | EngOrganization };
1324
+ };
1325
+
1326
+ // второй параметр generic - это глобально валидируемое значение. Для формы это весь values
1327
+ const rusOrganization = object<RusOrganization, Values>({
1328
+ inn: string(
1329
+ // автоматический вывод типа для ctx.global.values
1330
+ when({
1331
+ is: (_, ctx) => Boolean(ctx.global.values.isRus),
1332
+ then: rusOrganization,
1333
+ otherwise: engOrganization,
1334
+ }),
1335
+ ),
1336
+ isIP: optional(boolean()),
1337
+ });
1338
+
1339
+ const engOrganization = object<EngOrganization, Values>({ name: string() });
1340
+
1341
+ // необходимо явно указать Values для типизации ctx.global.values
1342
+ const organization = when<Values>({
1343
+ is: (_, ctx) => Boolean(ctx.global.values.isRus),
1344
+ then: rusOrganization,
1345
+ otherwise: engOrganization,
1346
+ });
1347
+
1348
+ const validate = object<Values, Values>({
1349
+ isRus: optional(boolean()),
1350
+ org: organization,
1351
+ });
1352
+ ```
1353
+
1354
+ ---
1355
+
1356
+ ## Error message customization
1226
1357
 
1227
1358
  Сообщения об ошибках по умолчанию могут быть заменены на пользовательские.
1228
1359
  Для этого необходимо использовать параметры `message` или `getMessage` у валидационных методов:
@@ -1245,7 +1376,7 @@ validateKPP('123123');
1245
1376
 
1246
1377
  ---
1247
1378
 
1248
- # Exclusion managing
1379
+ ## Exclusion managing
1249
1380
 
1250
1381
  Метод `exclude` предоставляет возможность обхода валидации для конкретного значения.
1251
1382
  Если функция вернет `true`,
package/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { deepPartial } from './deepPartial';
10
10
  export { min, STRING_MIN_ERROR_CODE, ARRAY_MIN_ERROR_CODE, DATE_MIN_ERROR_CODE, NUMBER_MIN_ERROR_CODE, } from './min';
11
11
  export { max, STRING_MAX_ERROR_CODE, ARRAY_MAX_ERROR_CODE, DATE_MAX_ERROR_CODE, NUMBER_MAX_ERROR_CODE, } from './max';
12
12
  export { integer, INTEGER_ERROR_INFO } from './integer';
13
+ export { positiveNumber, POSITIVE_NUMBER_ERROR_INFO } from './positiveNumber';
13
14
  export { or } from './or';
14
15
  export { pattern, PATTERN_ERROR_CODE } from './pattern';
15
16
  export { onlyNumber, ONLY_NUMBER_ERROR_CODE } from './onlyNumber';
@@ -25,3 +26,4 @@ export { ogrnUL, OGRN_UL_ERROR_INFO } from './ogrnUL';
25
26
  export { ogrnIP, OGRN_IP_ERROR_INFO } from './ogrnIP';
26
27
  export { any } from './any';
27
28
  export { when } from './when';
29
+ export { toPrettyError } from './toPrettyError';
package/index.js CHANGED
@@ -10,6 +10,7 @@ export { deepPartial } from './deepPartial';
10
10
  export { min, STRING_MIN_ERROR_CODE, ARRAY_MIN_ERROR_CODE, DATE_MIN_ERROR_CODE, NUMBER_MIN_ERROR_CODE, } from './min';
11
11
  export { max, STRING_MAX_ERROR_CODE, ARRAY_MAX_ERROR_CODE, DATE_MAX_ERROR_CODE, NUMBER_MAX_ERROR_CODE, } from './max';
12
12
  export { integer, INTEGER_ERROR_INFO } from './integer';
13
+ export { positiveNumber, POSITIVE_NUMBER_ERROR_INFO } from './positiveNumber';
13
14
  export { or } from './or';
14
15
  export { pattern, PATTERN_ERROR_CODE } from './pattern';
15
16
  export { onlyNumber, ONLY_NUMBER_ERROR_CODE } from './onlyNumber';
@@ -25,3 +26,4 @@ export { ogrnUL, OGRN_UL_ERROR_INFO } from './ogrnUL';
25
26
  export { ogrnIP, OGRN_IP_ERROR_INFO } from './ogrnIP';
26
27
  export { any } from './any';
27
28
  export { when } from './when';
29
+ export { toPrettyError } from './toPrettyError';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astral/validations",
3
- "version": "3.0.0-beta.3",
3
+ "version": "3.1.0",
4
4
  "browser": "./index.js",
5
5
  "main": "./index.js",
6
6
  "dependencies": {
@@ -0,0 +1,2 @@
1
+ import { ErrorInfo } from '../core';
2
+ export declare const POSITIVE_NUMBER_ERROR_INFO: ErrorInfo;
@@ -0,0 +1,5 @@
1
+ import { createErrorCode } from '../core';
2
+ export const POSITIVE_NUMBER_ERROR_INFO = {
3
+ code: createErrorCode('positive-number'),
4
+ message: 'Только положительные числа',
5
+ };
@@ -0,0 +1,2 @@
1
+ export * from './constants';
2
+ export * from './positiveNumber';
@@ -0,0 +1,2 @@
1
+ export * from './constants';
2
+ export * from './positiveNumber';
@@ -0,0 +1,25 @@
1
+ type PositiveNumberParams = {
2
+ /**
3
+ * @description Замена стандартного сообщения ошибки.
4
+ */
5
+ message?: string;
6
+ };
7
+ /**
8
+ * @description
9
+ * Проверяет является ли значение положительным числом.
10
+ * @example
11
+ * ```ts
12
+ * const validate = number(positiveNumber(3));
13
+ *
14
+ * // undefined
15
+ * validate(3)
16
+ *
17
+ * // { message: 'Только положительное числа' }
18
+ * validate(0)
19
+ *
20
+ * // { message: 'Только положительное числа' }
21
+ * validate(-1)
22
+ * ```
23
+ */
24
+ export declare const positiveNumber: <TValues>(params?: PositiveNumberParams) => (value: number, prevCtx?: import("../core").ValidationContext<TValues> | undefined) => import("../core").ValidationResult;
25
+ export {};
@@ -0,0 +1,31 @@
1
+ import { createRule } from '../core';
2
+ import { POSITIVE_NUMBER_ERROR_INFO } from './constants';
3
+ const isPositiveNumber = (number) => {
4
+ return number > 0;
5
+ };
6
+ /**
7
+ * @description
8
+ * Проверяет является ли значение положительным числом.
9
+ * @example
10
+ * ```ts
11
+ * const validate = number(positiveNumber(3));
12
+ *
13
+ * // undefined
14
+ * validate(3)
15
+ *
16
+ * // { message: 'Только положительное числа' }
17
+ * validate(0)
18
+ *
19
+ * // { message: 'Только положительное числа' }
20
+ * validate(-1)
21
+ * ```
22
+ */
23
+ export const positiveNumber = (params) => createRule((value, ctx) => {
24
+ if (!isPositiveNumber(value)) {
25
+ return ctx.createError({
26
+ message: (params === null || params === void 0 ? void 0 : params.message) || POSITIVE_NUMBER_ERROR_INFO.message,
27
+ code: POSITIVE_NUMBER_ERROR_INFO.code,
28
+ });
29
+ }
30
+ return undefined;
31
+ });
@@ -0,0 +1 @@
1
+ export * from './toPrettyError';
@@ -0,0 +1 @@
1
+ export * from './toPrettyError';
@@ -0,0 +1,39 @@
1
+ import { ValidationResult } from '../core';
2
+ import { PlainValidationResult } from '../toPlainError';
3
+ /**
4
+ * @description Преобразует ошибку валидации в формат для вывода (например, в консоль)
5
+ * @param validationResult
6
+ * @example
7
+ * ```ts
8
+ *
9
+ * type ListItem = { description: string };
10
+ *
11
+ * type Values = {
12
+ * info: { name: string };
13
+ * list: ListItem[];
14
+ * };
15
+ *
16
+ * const validate = object<Values>({
17
+ * info: object<Values['info']>({ name: string() }),
18
+ * list: array(
19
+ * arrayItem(
20
+ * object<ListItem>({
21
+ * description: string(),
22
+ * }),
23
+ * ),
24
+ * ),
25
+ * });
26
+ *
27
+ * const error = validate({
28
+ * info: { name: 22 },
29
+ * list: [{}],
30
+ * });
31
+ *
32
+ * // {
33
+ * // info: { name: 'Не является строкой' },
34
+ * // list: [{ description: 'Обязательно' }],
35
+ * // }
36
+ * toPrettyError(error);
37
+ * ```
38
+ */
39
+ export declare const toPrettyError: (validationResult: ValidationResult) => PlainValidationResult<string>;
@@ -0,0 +1,38 @@
1
+ import { toPlainError } from '../toPlainError';
2
+ /**
3
+ * @description Преобразует ошибку валидации в формат для вывода (например, в консоль)
4
+ * @param validationResult
5
+ * @example
6
+ * ```ts
7
+ *
8
+ * type ListItem = { description: string };
9
+ *
10
+ * type Values = {
11
+ * info: { name: string };
12
+ * list: ListItem[];
13
+ * };
14
+ *
15
+ * const validate = object<Values>({
16
+ * info: object<Values['info']>({ name: string() }),
17
+ * list: array(
18
+ * arrayItem(
19
+ * object<ListItem>({
20
+ * description: string(),
21
+ * }),
22
+ * ),
23
+ * ),
24
+ * });
25
+ *
26
+ * const error = validate({
27
+ * info: { name: 22 },
28
+ * list: [{}],
29
+ * });
30
+ *
31
+ * // {
32
+ * // info: { name: 'Не является строкой' },
33
+ * // list: [{ description: 'Обязательно' }],
34
+ * // }
35
+ * toPrettyError(error);
36
+ * ```
37
+ */
38
+ export const toPrettyError = (validationResult) => toPlainError(validationResult, (err) => err.message);