@astral/validations 4.7.0 → 4.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1364,6 +1364,26 @@ validate({
1364
1364
  });
1365
1365
  ```
1366
1366
 
1367
+ Позволяет делать optional вложенные правила:
1368
+
1369
+ ```ts
1370
+ type Values = { name: string | number; isAgree: boolean };
1371
+
1372
+ const validate = object<Values>({
1373
+ name: optional(
1374
+ when({
1375
+ is: (_, ctx) => Boolean(ctx.values?.isAgree),
1376
+ then: string(),
1377
+ otherwise: number(),
1378
+ })
1379
+ ),
1380
+ isAgree: optional(boolean()),
1381
+ });
1382
+
1383
+ // undefined
1384
+ validate({ isAgree: false, name: undefined });
1385
+ ```
1386
+
1367
1387
  ---
1368
1388
 
1369
1389
  ## when. Условная валидация
@@ -1471,6 +1491,7 @@ validate(new Date())
1471
1491
  Guard, поддерживающие асинхронную валидацию имеют постфиксы ```async```:
1472
1492
  - ```objectAsync```
1473
1493
  - ```stringAsync```
1494
+ - ```optionalAsync```
1474
1495
 
1475
1496
  Пример:
1476
1497
 
@@ -1494,9 +1515,21 @@ const validate = objectAsync<Values>({
1494
1515
  message: 'Nickname занят',
1495
1516
  });
1496
1517
  }),
1518
+ fullName: optionalAsync(stringAsync(async (value, ctx) => {
1519
+ const nicknameIsAvailable = await checkNickname(value);
1520
+
1521
+ if (nicknameIsAvailable) {
1522
+ return undefined;
1523
+ }
1524
+
1525
+ return ctx.createError({
1526
+ code: 'nickname-available',
1527
+ message: 'Nickname занят',
1528
+ });
1529
+ })),
1497
1530
  });
1498
1531
 
1499
- const result = await validate({ phone: '79308999999', nickname: 'Vasya' });
1532
+ const result = await validate({ phone: '79308999999', nickname: 'Vasya', fullName: '' });
1500
1533
 
1501
1534
  // { nickname: 'Nickname занят' }
1502
1535
  toPrettyError(result);
@@ -1,8 +1,19 @@
1
+ import { DeepPartial } from 'utility-types';
1
2
  import { ValidationContext } from '../types';
2
3
  import { ValidationTypes } from '../../types';
4
+ type Params<TLastSchemaValues extends Record<string, unknown>> = {
5
+ /**
6
+ * Value последнего валидируемого объекта
7
+ */
8
+ lastSchemaValue?: DeepPartial<TLastSchemaValues>;
9
+ /**
10
+ * Позволяет создать ctx, в котором будет соответсвующий isOptional
11
+ */
12
+ isOptional?: boolean;
13
+ };
3
14
  /**
4
15
  * @description Создает context валидации. Используется внутри фабрик guard и rule
5
16
  * @default по-дефолту сбрасывает все флаги в false
6
17
  */
7
- export declare function createContext<TValue extends ValidationTypes>(prevCtx: ValidationContext<{}, TValue> | undefined, value: TValue): ValidationContext<{}, TValue>;
8
- export declare function createContext<TValue extends ValidationTypes, TLastSchemaValues extends Record<string, unknown>>(prevCtx: ValidationContext<{}> | undefined, value: TValue, lastSchemaValue: TLastSchemaValues): ValidationContext<TLastSchemaValues, TValue>;
18
+ export declare function createContext<TValue extends ValidationTypes, TLastSchemaValues extends Record<string, unknown> = {}>(prevCtx: ValidationContext<Record<string, unknown>> | undefined, value: TValue, { lastSchemaValue, isOptional }?: Params<TLastSchemaValues>): ValidationContext<TLastSchemaValues>;
19
+ export {};
@@ -1,20 +1,21 @@
1
1
  import { createSimpleError } from '../../errors';
2
- export function createContext(prevCtx, value, lastSchemaValue) {
3
- if (prevCtx && !lastSchemaValue) {
4
- return prevCtx;
5
- }
6
- const currentLastSchemaValue = lastSchemaValue ? lastSchemaValue : undefined;
2
+ /**
3
+ * @description Создает context валидации. Используется внутри фабрик guard и rule
4
+ * @default по-дефолту сбрасывает все флаги в false
5
+ */
6
+ export function createContext(prevCtx, value, { lastSchemaValue, isOptional } = {}) {
7
7
  if (prevCtx) {
8
- return Object.assign(Object.assign({}, prevCtx), { values: currentLastSchemaValue });
8
+ return Object.assign(Object.assign({}, prevCtx), { isOptional: isOptional !== null && isOptional !== void 0 ? isOptional : prevCtx.isOptional, values: lastSchemaValue || prevCtx.values });
9
9
  }
10
10
  return {
11
- values: currentLastSchemaValue,
11
+ values: lastSchemaValue,
12
12
  global: {
13
13
  values: value,
14
14
  overrides: {
15
15
  objectIsPartial: false,
16
16
  },
17
17
  },
18
+ isOptional: isOptional !== null && isOptional !== void 0 ? isOptional : false,
18
19
  createError: createSimpleError,
19
20
  };
20
21
  }
@@ -11,7 +11,7 @@ export type ValidationContext<TLastSchemaValues extends Record<string, unknown>
11
11
  /**
12
12
  * @description Глобальные значения, идущие от самого верхнего правила к самому нижнему
13
13
  */
14
- global: {
14
+ global: DeepReadonly<{
15
15
  /**
16
16
  * @description Значения, которые валидируется guard самого высоко порядка
17
17
  */
@@ -25,9 +25,13 @@ export type ValidationContext<TLastSchemaValues extends Record<string, unknown>
25
25
  */
26
26
  objectIsPartial: boolean;
27
27
  };
28
- };
28
+ }>;
29
29
  /**
30
30
  * @description Фабрика ошибок. Возвращает новую ошибку валидации
31
31
  */
32
32
  createError: typeof createSimpleError;
33
+ /**
34
+ * @description Флаг, позволяющий отключать в guard'ах required правило. Первый guard, который примет isOptional===true сбросит его
35
+ */
36
+ isOptional: boolean;
33
37
  }>;
@@ -1,6 +1,23 @@
1
1
  import { ValidationResult } from '../../types';
2
2
  import { ValidationContext } from '../../context';
3
- import { GuardDefOptions, GuardValue } from '../types';
3
+ export type GuardDefOptions<AddDefOptions extends Record<string, unknown>> = Partial<AddDefOptions> & {
4
+ /**
5
+ * @description Переопределяет дефолтное сообщения ошибки для required
6
+ * @example string.define({ requiredMessage: 'ИНН не может быть пустым' })(inn())
7
+ */
8
+ requiredErrorMessage?: string;
9
+ /**
10
+ * @description Переопределяет сообщение об ошибке типа
11
+ * @example string.define({ typeErrorMessage: 'ИНН не может быть числом' })(inn())
12
+ */
13
+ typeErrorMessage?: string;
14
+ /**
15
+ * @description Позволяет выключать проверку на required
16
+ * @default false
17
+ */
18
+ isOptional?: boolean;
19
+ };
20
+ export type GuardValue = unknown;
4
21
  /**
5
22
  * @description Интерфейс функции guard, которая в прототипе содержит метод define
6
23
  */
@@ -28,11 +45,11 @@ export interface AsyncGuard<TLastSchemaValues extends Record<string, unknown> =
28
45
  /**
29
46
  * @description Функция, которая позволяет определять частную логику для guard
30
47
  */
31
- type GuardExecutor<AddDefOptions extends Record<string, unknown>> = (value: unknown, ctx: ValidationContext<Record<string, unknown>>, defOptions: GuardDefOptions<AddDefOptions>) => ValidationResult;
48
+ type GuardExecutor<TLastSchemaValues extends Record<string, unknown>, AddDefOptions extends Record<string, unknown>> = (value: unknown, ctx: ValidationContext<TLastSchemaValues>, defOptions: GuardDefOptions<AddDefOptions>) => ValidationResult;
32
49
  /**
33
50
  * @description Функция, которая позволяет определять частную логику для guard
34
51
  */
35
- type AsyncGuardExecutor<AddDefOptions extends Record<string, unknown>> = (value: unknown, ctx: ValidationContext<Record<string, unknown>>, defOptions: GuardDefOptions<AddDefOptions>) => Promise<ValidationResult>;
52
+ type AsyncGuardExecutor<TLastSchemaValues extends Record<string, unknown>, AddDefOptions extends Record<string, unknown>> = (value: unknown, ctx: ValidationContext<TLastSchemaValues>, defOptions: GuardDefOptions<AddDefOptions>) => Promise<ValidationResult>;
36
53
  /**
37
54
  * @description Создает guard. Guard - функция, проверяющая тип значения
38
55
  * По-дефолту проверяет value на required. Для выключения required необходимо использовать optional().
@@ -49,6 +66,6 @@ type AsyncGuardExecutor<AddDefOptions extends Record<string, unknown>> = (value:
49
66
  * });
50
67
  * ```
51
68
  */
52
- export declare function createGuard<TLastSchemaValues extends Record<string, unknown>, AddDefOptions extends Record<string, unknown> = {}>(executor: GuardExecutor<AddDefOptions>): Guard<TLastSchemaValues, AddDefOptions>;
53
- export declare function createGuard<TLastSchemaValues extends Record<string, unknown>, AddDefOptions extends Record<string, unknown> = {}>(executor: AsyncGuardExecutor<AddDefOptions>): AsyncGuard<TLastSchemaValues, AddDefOptions>;
69
+ export declare function createGuard<TLastSchemaValues extends Record<string, unknown>, AddDefOptions extends Record<string, unknown> = {}>(executor: GuardExecutor<TLastSchemaValues, AddDefOptions>): Guard<TLastSchemaValues, AddDefOptions>;
70
+ export declare function createGuard<TLastSchemaValues extends Record<string, unknown>, AddDefOptions extends Record<string, unknown> = {}>(executor: AsyncGuardExecutor<TLastSchemaValues, AddDefOptions>): AsyncGuard<TLastSchemaValues, AddDefOptions>;
54
71
  export {};
@@ -4,16 +4,20 @@ export function createGuard(executor) {
4
4
  // выделено в отдельную именованную функцию для того, чтобы ее можно было рекурсивно вызывать в define
5
5
  const createInnerGuard = (defOptions = {}) => {
6
6
  const guard = (value, prevCtx) => {
7
+ const actualDefOptions = Object.assign(Object.assign({}, defOptions), { isOptional: (prevCtx === null || prevCtx === void 0 ? void 0 : prevCtx.isOptional) || defOptions.isOptional });
7
8
  const ctx = createContext(prevCtx,
8
9
  // при создании контекста сейчас не имеет значение какого типа будет ctx.values
9
- value);
10
+ value, {
11
+ lastSchemaValue: value,
12
+ isOptional: false,
13
+ });
10
14
  const requiredResult = required({
11
- message: defOptions === null || defOptions === void 0 ? void 0 : defOptions.requiredErrorMessage,
15
+ message: actualDefOptions === null || actualDefOptions === void 0 ? void 0 : actualDefOptions.requiredErrorMessage,
12
16
  })(value, ctx);
13
- if ((defOptions === null || defOptions === void 0 ? void 0 : defOptions.isOptional) && requiredResult) {
17
+ if ((actualDefOptions === null || actualDefOptions === void 0 ? void 0 : actualDefOptions.isOptional) && requiredResult) {
14
18
  return undefined;
15
19
  }
16
- return requiredResult || executor(value, ctx, defOptions);
20
+ return requiredResult || executor(value, ctx, actualDefOptions);
17
21
  };
18
22
  guard.define = (overridesDefOptions) => createInnerGuard(overridesDefOptions);
19
23
  return guard;
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { object, objectAsync, OBJECT_TYPE_ERROR_INFO, type Schema, type SchemaValue, type ObjectGuard, type ObjectAsyncGuard, } from './object';
2
- export { optional } from './optional';
2
+ export { optional, optionalAsync } from './optional';
3
3
  export { string, STRING_TYPE_ERROR_INFO, stringAsync } from './string';
4
4
  export { date, INVALID_DATE_ERROR_INFO, DATE_TYPE_ERROR_INFO } from './date';
5
5
  export { number, NAN_NUMBER_ERROR_INFO, NUMBER_TYPE_ERROR_INFO, INFINITY_NUMBER_ERROR_INFO, } from './number';
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { object, objectAsync, OBJECT_TYPE_ERROR_INFO, } from './object';
2
- export { optional } from './optional';
2
+ export { optional, optionalAsync } from './optional';
3
3
  export { string, STRING_TYPE_ERROR_INFO, stringAsync } from './string';
4
4
  export { date, INVALID_DATE_ERROR_INFO, DATE_TYPE_ERROR_INFO } from './date';
5
5
  export { number, NAN_NUMBER_ERROR_INFO, NUMBER_TYPE_ERROR_INFO, INFINITY_NUMBER_ERROR_INFO, } from './number';
package/object/object.js CHANGED
@@ -27,7 +27,9 @@ import { OBJECT_TYPE_ERROR_INFO } from './constants';
27
27
  * ```
28
28
  */
29
29
  export const object = (schema) => createGuard((value, ctx, { typeErrorMessage, isPartial }) => {
30
- const context = createContext(ctx, value, value);
30
+ const context = createContext(ctx, value, {
31
+ lastSchemaValue: value,
32
+ });
31
33
  if (!isPlainObject(value)) {
32
34
  return context.createError(Object.assign(Object.assign({}, OBJECT_TYPE_ERROR_INFO), { message: typeErrorMessage || OBJECT_TYPE_ERROR_INFO.message }));
33
35
  }
@@ -45,7 +45,9 @@ import { OBJECT_TYPE_ERROR_INFO } from '../constants';
45
45
  * ```
46
46
  */
47
47
  export const objectAsync = (schema) => createGuard((value, ctx, { typeErrorMessage, isPartial }) => __awaiter(void 0, void 0, void 0, function* () {
48
- const context = createContext(ctx, value, value);
48
+ const context = createContext(ctx, value, {
49
+ lastSchemaValue: value,
50
+ });
49
51
  if (!isPlainObject(value)) {
50
52
  return context.createError(Object.assign(Object.assign({}, OBJECT_TYPE_ERROR_INFO), { message: typeErrorMessage || OBJECT_TYPE_ERROR_INFO.message }));
51
53
  }
@@ -1 +1,2 @@
1
1
  export * from './optional';
2
+ export * from './optionalAsync';
package/optional/index.js CHANGED
@@ -1 +1,2 @@
1
1
  export * from './optional';
2
+ export * from './optionalAsync';
@@ -1,7 +1,6 @@
1
- import { Guard } from '../core';
1
+ import { IndependentValidationRule, ValidationRule } from '../core';
2
2
  /**
3
3
  * @description Выключает проверку на required в guard
4
- * @param guard - правило, проверяющее тип значения
5
4
  * @example object({ name: optional(string(min(22))) })
6
5
  */
7
- export declare const optional: <TLastSchemaValues extends Record<string, unknown>>(guard: Guard<TLastSchemaValues, {}>) => Guard<TLastSchemaValues, {}>;
6
+ export declare const optional: <TLastSchemaValues extends Record<string, unknown>>(rule: ValidationRule<unknown, TLastSchemaValues>) => IndependentValidationRule<unknown, TLastSchemaValues>;
@@ -1,6 +1,6 @@
1
+ import { callRule, createContext, } from '../core';
1
2
  /**
2
3
  * @description Выключает проверку на required в guard
3
- * @param guard - правило, проверяющее тип значения
4
4
  * @example object({ name: optional(string(min(22))) })
5
5
  */
6
- export const optional = (guard) => guard.define({ isOptional: true });
6
+ export const optional = (rule) => (value, ctx) => callRule(rule, value, createContext(ctx, value, { isOptional: true }));
@@ -0,0 +1 @@
1
+ export * from './optionalAsync';
@@ -0,0 +1 @@
1
+ export * from './optionalAsync';
@@ -0,0 +1,6 @@
1
+ import { AsyncIndependentValidationRule, AsyncValidationRule } from '../../core';
2
+ /**
3
+ * @description Выключает проверку на required в guard. Предназначен для асинхронных правил.
4
+ * @example object({ name: optionalAsync(stringAsync(min(22))) })
5
+ */
6
+ export declare const optionalAsync: <TLastSchemaValues extends Record<string, unknown>>(rule: AsyncValidationRule<unknown, TLastSchemaValues>) => AsyncIndependentValidationRule<unknown, TLastSchemaValues>;
@@ -0,0 +1,18 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import { callAsyncRule, createContext, } from '../../core';
11
+ // TODO: необходимо добавить возможность использовать вложенные guards
12
+ /**
13
+ * @description Выключает проверку на required в guard. Предназначен для асинхронных правил.
14
+ * @example object({ name: optionalAsync(stringAsync(min(22))) })
15
+ */
16
+ export const optionalAsync = (rule) => (value, ctx) => __awaiter(void 0, void 0, void 0, function* () {
17
+ return callAsyncRule(rule, value, createContext(ctx, value, { isOptional: true }));
18
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@astral/validations",
3
- "version": "4.7.0",
3
+ "version": "4.8.1",
4
4
  "browser": "./index.js",
5
5
  "main": "./index.js",
6
6
  "dependencies": {
@@ -1,18 +0,0 @@
1
- export type GuardDefOptions<AddDefOptions extends Record<string, unknown>> = Partial<AddDefOptions> & {
2
- /**
3
- * @description Переопределяет дефолтное сообщения ошибки для required
4
- * @example string.define({ requiredMessage: 'ИНН не может быть пустым' })(inn())
5
- */
6
- requiredErrorMessage?: string;
7
- /**
8
- * @description Переопределяет сообщение об ошибке типа
9
- * @example string.define({ typeErrorMessage: 'ИНН не может быть числом' })(inn())
10
- */
11
- typeErrorMessage?: string;
12
- /**
13
- * @description Позволяет выключать проверку на required
14
- * @default false
15
- */
16
- isOptional?: boolean;
17
- };
18
- export type GuardValue = unknown;
@@ -1 +0,0 @@
1
- export {};