@anjianshi/utils 2.1.7 → 2.1.9

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.
@@ -30,7 +30,7 @@ export declare class ConsoleHandler extends LogHandler {
30
30
  };
31
31
  private static readonly loggerColors;
32
32
  private static readonly loggerColorMap;
33
- static getLoggerColor(logger: string): "green" | "yellow" | "blue" | "cyan" | "magenta" | "greenBright" | "yellowBright" | "blueBright" | "cyanBright" | "magentaBright";
33
+ static getLoggerColor(logger: string): "blue" | "blueBright" | "cyan" | "cyanBright" | "green" | "greenBright" | "magenta" | "magentaBright" | "yellow" | "yellowBright";
34
34
  }
35
35
  /**
36
36
  * 写入文件日志
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anjianshi/utils",
3
- "version": "2.1.7",
3
+ "version": "2.1.9",
4
4
  "description": "Common JavaScript Utils",
5
5
  "homepage": "https://github.com/anjianshi/js-packages/utils",
6
6
  "bugs": {
@@ -26,10 +26,10 @@
26
26
  "typeorm": "^0.3.20",
27
27
  "vconsole": "^3.15.1",
28
28
  "typescript": "^5.4.4",
29
- "@anjianshi/presets-eslint-typescript": "4.0.4",
30
- "@anjianshi/presets-prettier": "3.0.0",
31
- "@anjianshi/presets-eslint-node": "4.0.4",
32
- "@anjianshi/presets-typescript": "3.1.4"
29
+ "@anjianshi/presets-eslint-typescript": "5.0.2",
30
+ "@anjianshi/presets-prettier": "3.0.1",
31
+ "@anjianshi/presets-eslint-node": "4.0.5",
32
+ "@anjianshi/presets-typescript": "3.1.5"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "chalk": "^5.3.0",
@@ -1,20 +1,28 @@
1
- import { Validator } from './base.js';
1
+ import { type CommonOptions, type Validator, type Validated } from './base.js';
2
2
  /** 验证元素数量任意、元素类型相同的数组 */
3
- export type ArrayOptions = {
3
+ export interface ArrayOptions extends CommonOptions {
4
4
  /** 验证数组各元素 */
5
- item: Validator;
5
+ item: Validator<unknown, CommonOptions>;
6
6
  /** 数组最小长度 */
7
7
  min?: number;
8
8
  /** 数组最大长度 */
9
9
  max?: number;
10
10
  /** 是否对数组元素进行去重 @defaults false */
11
11
  unique?: boolean;
12
- };
12
+ }
13
+ type ArrayValues<Options extends ArrayOptions> = Validated<Options extends {
14
+ item: Validator<infer T, CommonOptions>;
15
+ } ? T : never, Options extends {
16
+ item: Validator<unknown, infer T>;
17
+ } ? T : never>[];
18
+ export declare function getArrayValidator<Options extends ArrayOptions>(options: Options): Validator<ArrayValues<Options>, Options>;
13
19
  /** 验证元素数量固定、类型可以不同的数组 */
14
- export type TupleOptions = {
20
+ export interface TupleOptions extends CommonOptions {
15
21
  /** 验证数组各元素(validator 与元素一一对应) */
16
- tuple: Validator[];
17
- };
18
- export declare class ArrayValidator extends Validator<ArrayOptions | TupleOptions> {
19
- validate(field: string, value: unknown): import("../lang/may-success.js").Failed<void> | import("../lang/may-success.js").Success<unknown>;
22
+ tuple: Validator<unknown, CommonOptions>[];
20
23
  }
24
+ type TupleValues<Options extends TupleOptions> = {
25
+ [Key in keyof Options['tuple']]: Options['tuple'][Key] extends Validator<infer Value, infer Options> ? Validated<Value, Options> : never;
26
+ };
27
+ export declare function getTupleValidator<const Options extends TupleOptions>(options: Options): Validator<TupleValues<Options>, Options>;
28
+ export {};
@@ -1,44 +1,43 @@
1
1
  import { success, failed } from '../lang/index.js';
2
- import { Validator } from './base.js';
3
- export class ArrayValidator extends Validator {
4
- validate(field, value) {
5
- const superResult = super.validate(field, value);
6
- if (!superResult.success)
7
- return superResult;
8
- value = superResult.data;
9
- if (value === null || value === undefined)
10
- return superResult;
11
- const opt = this.options;
2
+ import { getValidatorGenerator, } from './base.js';
3
+ export function getArrayValidator(options) {
4
+ return getValidatorGenerator(function validate(field, value, options) {
12
5
  if (!Array.isArray(value))
13
6
  return failed(`${field} should be an array`);
14
7
  let formatted = [];
15
- if ('item' in opt) {
16
- if (typeof opt.min === 'number' && value.length < opt.min)
17
- return failed(`array ${field}'s length should >= ${opt.min}`);
18
- if (typeof opt.max === 'number' && value.length > opt.max)
19
- return failed(`array ${field}'s length should <= ${opt.max}`);
20
- for (let i = 0; i < value.length; i++) {
21
- const itemResult = opt.item.validate(`${field}[${i}]`, value[i]);
22
- if (itemResult.success)
23
- formatted.push(itemResult.data);
24
- else
25
- return itemResult;
26
- }
27
- if (opt.unique === true)
28
- formatted = [...new Set(formatted)];
8
+ if (typeof options.min === 'number' && value.length < options.min)
9
+ return failed(`array ${field}'s length should >= ${options.min}`);
10
+ if (typeof options.max === 'number' && value.length > options.max)
11
+ return failed(`array ${field}'s length should <= ${options.max}`);
12
+ const itemValidator = options.item;
13
+ for (let i = 0; i < value.length; i++) {
14
+ const itemResult = itemValidator(`${field}[${i}]`, value[i]);
15
+ if (itemResult.success)
16
+ formatted.push(itemResult.data);
17
+ else
18
+ return itemResult;
29
19
  }
30
- else {
31
- if (value.length > opt.tuple.length)
32
- return failed(`${field} should be a tuple with ${opt.tuple.length} items`);
33
- // 这种情况不能遍历 value,因为它的长度可能小于 opt.tuple
34
- for (let i = 0; i < opt.tuple.length; i++) {
35
- const itemResult = opt.tuple[i].validate(`${field}[${i}]`, value[i]);
36
- if (itemResult.success)
37
- formatted.push(itemResult.data);
38
- else
39
- return itemResult;
40
- }
20
+ if (options.unique === true)
21
+ formatted = [...new Set(formatted)];
22
+ return success(formatted);
23
+ })(options);
24
+ }
25
+ export function getTupleValidator(options) {
26
+ return getValidatorGenerator(function validate(field, value, options) {
27
+ if (!Array.isArray(value))
28
+ return failed(`${field} should be an array`);
29
+ if (value.length > options.tuple.length)
30
+ return failed(`${field} should be a tuple with ${options.tuple.length} items`);
31
+ const formatted = [];
32
+ // 这种情况不能遍历 value,因为它的长度可能小于 opt.tuple
33
+ for (let i = 0; i < options.tuple.length; i++) {
34
+ const itemValidator = options.tuple[i];
35
+ const itemResult = itemValidator(`${field}[${i}]`, value[i]);
36
+ if (itemResult.success)
37
+ formatted.push(itemResult.data);
38
+ else
39
+ return itemResult;
41
40
  }
42
41
  return success(formatted);
43
- }
42
+ })(options);
44
43
  }
@@ -1,26 +1,77 @@
1
1
  import { type MaySuccess } from '../lang/index.js';
2
- export interface BaseOptions {
3
- /**
4
- * 是否允许 null 值
5
- * @default false
6
- */
7
- null: boolean;
8
- /**
9
- * 字段是否必须有值(不能是 undefined)
10
- * @default true
11
- */
12
- required: boolean;
2
+ /**
3
+ * 支持传入进行验证的值类型
4
+ */
5
+ export type AllowedInputValue = PrimitiveType | null | undefined;
6
+ /**
7
+ * JavaScript 基础值类型
8
+ */
9
+ export type PrimitiveType = string | boolean | number | PrimitiveType[] | [...PrimitiveType[]] | {
10
+ [key: string]: PrimitiveType;
11
+ };
12
+ /**
13
+ * validator 通用参数
14
+ */
15
+ export interface CommonOptions<Defaults = unknown> {
16
+ /** 是否允许 null 值 @default false */
17
+ null?: boolean;
18
+ /** 字段是否必须有值(不能是 undefined) @default true */
19
+ required?: boolean;
13
20
  /**
14
21
  * 默认值,字段无值(或值为 undefined)时生效,值为 null 不会生效。
15
22
  * 指定后 required 选项将失去作用。
16
23
  */
17
- defaults: unknown;
24
+ defaults?: Defaults;
18
25
  }
19
- export declare class Validator<ExtraOptions = unknown> {
20
- readonly options: BaseOptions & ExtraOptions;
21
- constructor(options: Partial<BaseOptions> & ExtraOptions);
22
- /**
23
- * 各子类继承此方法补充验证逻辑
24
- */
25
- validate(field: string, value: unknown): MaySuccess<unknown>;
26
+ /**
27
+ * 补全了的 Options
28
+ */
29
+ type FullfiledOptions<Options extends Partial<CommonOptions>> = Omit<Options, keyof CommonOptions> & {
30
+ null: Options['null'] extends true ? true : false;
31
+ required: Options['required'] extends false ? false : true;
32
+ defaults: Options extends {
33
+ defaults: infer T;
34
+ } ? T : undefined;
35
+ };
36
+ /**
37
+ * 验证完成后能得到的值类型
38
+ */
39
+ export type Validated<Value, InputOptions extends CommonOptions> = FullfiledOptions<InputOptions> extends {
40
+ defaults: undefined;
41
+ } ? FullfiledOptions<InputOptions> extends {
42
+ required: false;
43
+ null: false;
44
+ } ? Value | undefined : FullfiledOptions<InputOptions> extends {
45
+ required: false;
46
+ null: true;
47
+ } ? Value | undefined | null : FullfiledOptions<InputOptions> extends {
48
+ required: true;
49
+ null: false;
50
+ } ? Value : FullfiledOptions<InputOptions> extends {
51
+ required: true;
52
+ null: true;
53
+ } ? Value | null : Value : FullfiledOptions<InputOptions> extends {
54
+ defaults: infer T;
55
+ null: false;
56
+ } ? Value | T : FullfiledOptions<InputOptions> extends {
57
+ defaults: infer T;
58
+ null: true;
59
+ } ? Value | T | null : never;
60
+ /**
61
+ * 最终生成的 validator 函数类型
62
+ */
63
+ export interface Validator<Value, InputOptions extends CommonOptions> {
64
+ (input: AllowedInputValue): MaySuccess<Validated<Value, InputOptions>>;
65
+ (field: string, input: AllowedInputValue): MaySuccess<Validated<Value, InputOptions>>;
26
66
  }
67
+ /**
68
+ * 返回支持指定格式的 options、并按照传入的逻辑进行验证的 validator 的生成器。
69
+ * 对 CommonOptions 相关内容的验证以自动包含在里面,只需要传入额外的验证逻辑即可。
70
+ */
71
+ export declare function getValidatorGenerator<Value, Options extends CommonOptions>(validate: (field: string, input: PrimitiveType | Validated<Value, Options>, options: Options) => MaySuccess<Value>): <const InputOptions extends Options>(inputOptions: InputOptions) => Validator<Value, InputOptions>;
72
+ /**
73
+ * 返回只进行基本检查,不带定制的验证逻辑的 validator。
74
+ * 同时也是定制 validator 最小化实现的例子。
75
+ */
76
+ export declare const getAnyValidator: <const InputOptions extends CommonOptions<unknown>>(inputOptions: InputOptions) => Validator<unknown, InputOptions>;
77
+ export {};
@@ -1,28 +1,40 @@
1
1
  import { success, failed } from '../lang/index.js';
2
- export class Validator {
3
- options;
4
- constructor(options) {
5
- this.options = {
6
- null: false,
7
- required: true,
8
- defaults: undefined,
9
- ...options,
10
- };
11
- }
12
- /**
13
- * 各子类继承此方法补充验证逻辑
14
- */
15
- validate(field, value) {
16
- if (typeof value === 'undefined') {
17
- if (typeof this.options.defaults !== 'undefined') {
18
- value = this.options.defaults;
2
+ // -----------------------------------
3
+ /**
4
+ * 返回支持指定格式的 options、并按照传入的逻辑进行验证的 validator 的生成器。
5
+ * 对 CommonOptions 相关内容的验证以自动包含在里面,只需要传入额外的验证逻辑即可。
6
+ */
7
+ export function getValidatorGenerator(validate) {
8
+ return function validatorGenerator(inputOptions) {
9
+ function validator(field, input) {
10
+ const { null: allowNull = false, required = true, defaults } = inputOptions;
11
+ if (typeof field !== 'string') {
12
+ input = field;
13
+ field = 'value';
19
14
  }
20
- else if (this.options.required) {
21
- return failed(`${field} is required`);
15
+ let value = input;
16
+ if (typeof value === 'undefined') {
17
+ if (typeof defaults !== 'undefined') {
18
+ value = defaults;
19
+ }
20
+ else if (required) {
21
+ return failed(`${field} is required`);
22
+ }
22
23
  }
24
+ if (value === null && !allowNull)
25
+ return failed(`${field} cannot be null`);
26
+ if (value === null || value === undefined)
27
+ return success(value);
28
+ return validate(field, value, inputOptions);
23
29
  }
24
- if (value === null && !this.options.null)
25
- return failed(`${field} cannot be null`);
26
- return success(value);
27
- }
30
+ return validator;
31
+ };
28
32
  }
33
+ // -----------------------------------
34
+ /**
35
+ * 返回只进行基本检查,不带定制的验证逻辑的 validator。
36
+ * 同时也是定制 validator 最小化实现的例子。
37
+ */
38
+ export const getAnyValidator = getValidatorGenerator(function validate(field, input) {
39
+ return success(input);
40
+ });
@@ -1,4 +1,3 @@
1
- import { Validator } from './base.js';
2
- export declare class BooleanValidator extends Validator {
3
- validate(field: string, value: unknown): import("../lang/may-success.js").Failed<void> | import("../lang/may-success.js").Success<unknown>;
4
- }
1
+ import { type CommonOptions } from './base.js';
2
+ export type BooleanOptions = CommonOptions<boolean>;
3
+ export declare const getBooleanValidator: <const InputOptions extends BooleanOptions>(inputOptions: InputOptions) => import("./base.js").Validator<boolean, InputOptions>;
@@ -1,28 +1,22 @@
1
1
  import { success, failed } from '../lang/index.js';
2
- import { Validator } from './base.js';
3
- export class BooleanValidator extends Validator {
4
- validate(field, value) {
5
- const superResult = super.validate(field, value);
6
- if (!superResult.success)
7
- return superResult;
8
- value = superResult.data;
9
- if (value === null || value === undefined)
10
- return superResult;
11
- if (typeof value === 'string') {
12
- const str = value.trim().toLowerCase();
13
- if (['1', 'true', 'on', 'yes'].includes(str))
14
- value = true;
15
- else if (['0', 'false', 'off', 'no'].includes(str))
16
- value = false;
17
- }
18
- else if (typeof value === 'number') {
19
- if (value === 1)
20
- value = true;
21
- else if (value === 0)
22
- value = false;
23
- }
24
- if (typeof value !== 'boolean')
25
- return failed(`${field} must be true or false`);
26
- return success(value);
2
+ import { getValidatorGenerator } from './base.js';
3
+ export const getBooleanValidator = getValidatorGenerator(function validate(field, input) {
4
+ let value = null;
5
+ if (typeof input === 'boolean') {
6
+ value = input;
27
7
  }
28
- }
8
+ else if (typeof input === 'string') {
9
+ const str = input.trim().toLowerCase();
10
+ if (['1', 'true', 'on', 'yes'].includes(str))
11
+ value = true;
12
+ else if (['0', 'false', 'off', 'no'].includes(str))
13
+ value = false;
14
+ }
15
+ else if (typeof input === 'number') {
16
+ if (input === 1)
17
+ value = true;
18
+ else if (input === 0)
19
+ value = false;
20
+ }
21
+ return value === null ? failed(`${field} must be true or false`) : success(value);
22
+ });
@@ -0,0 +1,59 @@
1
+ /**
2
+ * 实现创建 validator 的快捷方式
3
+ */
4
+ import { type ArrayOptions, type TupleOptions } from './array.js';
5
+ import { type Validator, type CommonOptions, type Validated } from './base.js';
6
+ import { type BooleanOptions } from './boolean.js';
7
+ import { type NumberOptions, type NumberValueWithChoices } from './number.js';
8
+ import { type RecordOptions, type StructOptions } from './object.js';
9
+ import { type StringOptions, type StringValueWithChoices } from './string.js';
10
+ export interface AnyDefinition extends CommonOptions {
11
+ type: 'any';
12
+ }
13
+ export interface BooleanDefinition extends BooleanOptions {
14
+ type: 'boolean';
15
+ }
16
+ export interface NumberDefinition extends NumberOptions {
17
+ type: 'number';
18
+ }
19
+ export interface StringDefinition extends StringOptions {
20
+ type: 'string';
21
+ }
22
+ export interface ArrayDefinition extends Omit<ArrayOptions, 'item'> {
23
+ type: 'array';
24
+ item: Definition;
25
+ }
26
+ export interface TupleDefinition extends Omit<TupleOptions, 'tuple'> {
27
+ type: 'tuple';
28
+ tuple: Definition[];
29
+ }
30
+ export interface StructDefinition extends Omit<StructOptions, 'struct'> {
31
+ type: 'struct';
32
+ struct: Record<string, Definition>;
33
+ }
34
+ export interface RecordDefinition extends Omit<RecordOptions, 'record'> {
35
+ type: 'record';
36
+ record: Definition;
37
+ }
38
+ export type Definition = AnyDefinition | BooleanDefinition | NumberDefinition | StringDefinition | ArrayDefinition | TupleDefinition | StructDefinition | RecordDefinition;
39
+ type ValueOfDefinition<Def extends Definition> = Def extends AnyDefinition ? unknown : Def extends BooleanDefinition ? boolean : Def extends NumberDefinition ? NumberValueWithChoices<Def> : Def extends StringDefinition ? StringValueWithChoices<Def> : Def extends ArrayDefinition ? Validated<ValueOfDefinition<Def['item']>, Def['item']>[] : Def extends TupleDefinition ? {
40
+ [Key in keyof Def['tuple']]: Def['tuple'][Key] extends Definition ? Validated<ValueOfDefinition<Def['tuple'][Key]>, Def['tuple'][Key]> : Def['tuple'][Key];
41
+ } : Def extends StructDefinition ? {
42
+ [Key in keyof Def['struct']]: Validated<ValueOfDefinition<Def['struct'][Key]>, Def['struct'][Key]>;
43
+ } : Def extends RecordDefinition ? Record<string, Validated<ValueOfDefinition<Def['record']>, Def['record']>> : never;
44
+ type OptionsFromDefinition<Def extends Definition> = Def extends ArrayDefinition ? Omit<Def, 'item'> & {
45
+ item: ValidatorForDefinition<Def['item']>;
46
+ } : Def extends TupleDefinition ? Omit<Def, 'tuple'> & {
47
+ tuple: {
48
+ [Key in keyof Def['tuple']]: Def['tuple'][Key] extends Definition ? ValidatorForDefinition<Def['tuple'][Key]> : never;
49
+ };
50
+ } : Def extends StructDefinition ? Omit<Def, 'struct'> & {
51
+ struct: {
52
+ [Key in keyof Def['struct']]: ValidatorForDefinition<Def['struct'][Key]>;
53
+ };
54
+ } : Def extends RecordDefinition ? Omit<Def, 'record'> & {
55
+ record: ValidatorForDefinition<Def['record']>;
56
+ } : Def;
57
+ type ValidatorForDefinition<Def extends Definition> = Validator<ValueOfDefinition<Def>, OptionsFromDefinition<Def>>;
58
+ export declare function getValidator<const InputDefinition extends Definition>(definition: InputDefinition): ValidatorForDefinition<InputDefinition>;
59
+ export {};
@@ -0,0 +1,88 @@
1
+ /**
2
+ * 实现创建 validator 的快捷方式
3
+ */
4
+ import { getArrayValidator, getTupleValidator, } from './array.js';
5
+ import { getAnyValidator } from './base.js';
6
+ import { getBooleanValidator } from './boolean.js';
7
+ import { getNumberValidator } from './number.js';
8
+ import { getRecordValidator, getStructValidator, } from './object.js';
9
+ import { getStringValidator } from './string.js';
10
+ export function getValidator(definition) {
11
+ switch (definition.type) {
12
+ case 'any':
13
+ return getAnyValidator(definition);
14
+ case 'boolean':
15
+ return getBooleanValidator(definition);
16
+ case 'number':
17
+ return getNumberValidator(definition);
18
+ case 'string':
19
+ return getStringValidator(definition);
20
+ case 'array':
21
+ // @ts-ignore 允许递归类型推断
22
+ return getArrayValidator({
23
+ // @ts-ignore 允许递归类型推断
24
+ ...definition,
25
+ item: getValidator(definition['item']),
26
+ });
27
+ case 'tuple':
28
+ return getTupleValidator({
29
+ ...definition,
30
+ tuple: definition['tuple'].map(def => getValidator(def)),
31
+ });
32
+ case 'struct': {
33
+ const struct = {};
34
+ for (const [key, def] of Object.entries(definition['struct']))
35
+ struct[key] = getValidator(def);
36
+ return getStructValidator({ ...definition, struct });
37
+ }
38
+ case 'record':
39
+ return getRecordValidator({
40
+ ...definition,
41
+ record: getValidator(definition['record']),
42
+ });
43
+ }
44
+ }
45
+ // ---------------- 测试用例 -----------------
46
+ // const v1 = getValidator({ type: 'string' })(1)
47
+ // const v2 = getValidator({ type: 'string', null: true })(1)
48
+ // const v3 = getValidator({ type: 'string', null: true, required: false })(1)
49
+ // const v4 = getValidator({ type: 'string', null: true, required: false, choices: ['a', 'b', 'c'] })(
50
+ // 1,
51
+ // )
52
+ // const v5 = getValidator({
53
+ // type: 'string',
54
+ // null: true,
55
+ // required: false,
56
+ // choices: ['a', 'b', 'c'],
57
+ // defaults: 'd',
58
+ // })(1)
59
+ // const v6 = getValidator({
60
+ // type: 'array',
61
+ // null: true,
62
+ // required: false,
63
+ // item: { type: 'string', null: true, choices: ['a', 'b', 'c'] },
64
+ // })(1)
65
+ // const v7 = getValidator({
66
+ // type: 'tuple',
67
+ // tuple: [
68
+ // { type: 'string', choices: ['a', 'b', 'c'] },
69
+ // { type: 'string', null: true },
70
+ // ],
71
+ // })(1)
72
+ // if (v7.success) {
73
+ // const [a, b] = v7.data
74
+ // }
75
+ // const v8 = getValidator({
76
+ // type: 'struct',
77
+ // struct: {
78
+ // x: { type: 'string', choices: ['a', 'b', 'c'] },
79
+ // y: { type: 'string', null: true },
80
+ // },
81
+ // })(1)
82
+ // if (v8.success) {
83
+ // const { x, y } = v8.data
84
+ // }
85
+ // const v9 = getValidator({
86
+ // type: 'record',
87
+ // record: { type: 'string', null: true, choices: ['a', 'b', 'c'] },
88
+ // })(1)
@@ -4,4 +4,4 @@ export * from './boolean.js';
4
4
  export * from './number.js';
5
5
  export * from './object.js';
6
6
  export * from './string.js';
7
- export * as validators from './factories.js';
7
+ export * from './factory.js';
@@ -4,4 +4,4 @@ export * from './boolean.js';
4
4
  export * from './number.js';
5
5
  export * from './object.js';
6
6
  export * from './string.js';
7
- export * as validators from './factories.js';
7
+ export * from './factory.js';
@@ -1,15 +1,17 @@
1
- import { type BaseOptions, Validator } from './base.js';
2
- export interface NumberOptions {
1
+ import { type CommonOptions } from './base.js';
2
+ export interface NumberOptions extends CommonOptions<number> {
3
3
  /** 数值最小值 */
4
4
  min?: number;
5
5
  /** 数值最大值 */
6
6
  max?: number;
7
7
  /** 是否允许小数 @default false */
8
- float: boolean;
9
- /** 指定可选值 */
10
- enum?: number[];
11
- }
12
- export declare class NumberValidator extends Validator<NumberOptions> {
13
- constructor(options?: Partial<BaseOptions & NumberOptions>);
14
- validate(field: string, value: unknown): import("../lang/may-success.js").Failed<void> | import("../lang/may-success.js").Success<unknown>;
8
+ float?: boolean;
9
+ /**
10
+ * 指定可选值
11
+ * 若指定,前几个选项将不再生效 */
12
+ choices?: number[];
15
13
  }
14
+ export type NumberValueWithChoices<Options extends NumberOptions> = Options extends {
15
+ choices: (infer T)[];
16
+ } ? T : number;
17
+ export declare function getNumberValidator<const Options extends NumberOptions>(options?: Options): import("./base.js").Validator<NumberValueWithChoices<Options>, Options>;
@@ -1,32 +1,23 @@
1
- import { success, failed } from '../lang/index.js';
2
- import { Validator } from './base.js';
3
- export class NumberValidator extends Validator {
4
- constructor(options = {}) {
5
- super({
6
- float: false,
7
- ...options,
8
- });
9
- }
10
- validate(field, value) {
11
- const superResult = super.validate(field, value);
12
- if (!superResult.success)
13
- return superResult;
14
- value = superResult.data;
15
- if (value === null || value === undefined)
16
- return superResult;
17
- const opt = this.options;
1
+ import { truthy, success, failed } from '../lang/index.js';
2
+ import { getValidatorGenerator } from './base.js';
3
+ export function getNumberValidator(options = {}) {
4
+ return getValidatorGenerator(function validate(field, value) {
18
5
  if (typeof value === 'string')
19
6
  value = parseFloat(value);
20
7
  if (typeof value !== 'number' || !isFinite(value))
21
8
  return failed(`${field} must be a valid number`);
22
- if (opt.enum !== undefined && !opt.enum.includes(value))
23
- return failed(`${field} can only be one of ${opt.enum.join(', ')}.`);
24
- if (!opt.float && value % 1 !== 0)
25
- return failed(`${field} must be a integer`);
26
- if (typeof opt.min === 'number' && value < opt.min)
27
- return failed(`${field} must >= ${opt.min}`);
28
- if (typeof opt.max === 'number' && value > opt.max)
29
- return failed(`${field} must <= ${opt.max}`);
9
+ if ('choices' in options && options.choices) {
10
+ if (!options.choices.includes(value))
11
+ return failed(`${field} can only be one of ${options.choices.join(', ')}.`);
12
+ }
13
+ else {
14
+ if (!truthy(options.float) && value % 1 !== 0)
15
+ return failed(`${field} must be a integer`);
16
+ if (typeof options.min === 'number' && value < options.min)
17
+ return failed(`${field} must >= ${options.min}`);
18
+ if (typeof options.max === 'number' && value > options.max)
19
+ return failed(`${field} must <= ${options.max}`);
20
+ }
30
21
  return success(value);
31
- }
22
+ })(options);
32
23
  }
@@ -1,20 +1,28 @@
1
- import { Validator } from './base.js';
1
+ import { type CommonOptions, type Validator, type Validated } from './base.js';
2
2
  /** 验证有明确键值对结构的对象 */
3
- export type StructOptions = {
3
+ export interface StructOptions extends CommonOptions {
4
4
  /** 定义对象结构,及各个值的验证规则 */
5
- struct: Record<string, Validator>;
5
+ struct: Record<string, Validator<unknown, CommonOptions>>;
6
+ }
7
+ type StructValues<Options extends StructOptions> = {
8
+ [Key in keyof Options['struct']]: Options['struct'][Key] extends Validator<infer Value, infer Options> ? Validated<Value, Options> : never;
6
9
  };
10
+ export declare function getStructValidator<const Options extends StructOptions>(options: Options): Validator<StructValues<Options>, Options>;
7
11
  /**
8
12
  * 验证有任意多个 key,但值的类型固定的对象
9
13
  */
10
- export type RecordOptions = {
14
+ export interface RecordOptions extends CommonOptions {
11
15
  /** 验证单个值 */
12
- record: Validator;
16
+ record: Validator<unknown, CommonOptions>;
13
17
  /** 对象至少要有几项 */
14
18
  min?: number;
15
19
  /** 对象最多有几项 */
16
20
  max?: number;
17
- };
18
- export declare class ObjectValidator extends Validator<StructOptions | RecordOptions> {
19
- validate(field: string, value: unknown): import("../lang/may-success.js").Failed<void> | import("../lang/may-success.js").Success<unknown>;
20
21
  }
22
+ type RecordValues<Options extends RecordOptions> = Record<string, Validated<Options extends {
23
+ record: Validator<infer T, CommonOptions>;
24
+ } ? T : never, Options extends {
25
+ record: Validator<unknown, infer T>;
26
+ } ? T : never>>;
27
+ export declare function getRecordValidator<Options extends RecordOptions>(options: Options): Validator<RecordValues<Options>, Options>;
28
+ export {};
@@ -1,52 +1,49 @@
1
1
  import isPlainObject from 'lodash/isPlainObject.js';
2
2
  import { success, failed } from '../lang/index.js';
3
- import { Validator } from './base.js';
4
- export class ObjectValidator extends Validator {
5
- validate(field, value) {
6
- const superResult = super.validate(field, value);
7
- if (!superResult.success)
8
- return superResult;
9
- value = superResult.data;
10
- if (value === null || value === undefined)
11
- return superResult;
12
- const opt = this.options;
3
+ import { getValidatorGenerator, } from './base.js';
4
+ export function getStructValidator(options) {
5
+ return getValidatorGenerator(function validate(field, value, options) {
13
6
  if (!isPlainObject(value))
14
7
  return failed(`${field} should be a plain object`);
15
8
  const formatted = {};
16
- if ('struct' in opt) {
17
- for (const [key, itemValidator] of Object.entries(opt.struct)) {
18
- const itemResult = itemValidator.validate(`${field}["${key}"]`, value[key]);
19
- if (itemResult.success) {
20
- if (itemResult.data !== undefined)
21
- formatted[key] = itemResult.data;
22
- }
23
- else {
24
- return itemResult;
25
- }
9
+ for (const [key, itemValidator] of Object.entries(options.struct)) {
10
+ const itemResult = itemValidator(`${field}["${key}"]`, value[key]);
11
+ if (itemResult.success) {
12
+ if (itemResult.data !== undefined)
13
+ formatted[key] = itemResult.data;
14
+ }
15
+ else {
16
+ return itemResult;
26
17
  }
27
18
  }
28
- else {
29
- for (const [key, itemValue] of Object.entries(value)) {
30
- // record 场景下,值为 undefined 的项目视为不存在,不保留在验证结果里,
31
- // 不然一些因为不想赋值而填充了 undefined 值的项目可能意外触发验证失败,或意外得到了默认值。
32
- // (因此 validator required 选项和 defaults 选项也没有意义了)
33
- if (itemValue === undefined)
34
- continue;
35
- const itemResult = opt.record.validate(`${field}["${key}"]`, itemValue);
36
- if (itemResult.success) {
37
- if (itemResult.data !== undefined)
38
- formatted[key] = itemResult.data;
39
- }
40
- else {
41
- return itemResult;
42
- }
19
+ return success(formatted);
20
+ })(options);
21
+ }
22
+ export function getRecordValidator(options) {
23
+ return getValidatorGenerator(function validate(field, value, options) {
24
+ if (!isPlainObject(value))
25
+ return failed(`${field} should be a plain object`);
26
+ const formatted = {};
27
+ for (const [key, itemValue] of Object.entries(value)) {
28
+ // record 场景下,值为 undefined 的项目视为不存在,不保留在验证结果里,
29
+ // 不然一些因为不想赋值而填充了 undefined 值的项目可能意外触发验证失败,或意外得到了默认值。
30
+ // (因此 validator 的 required 选项和 defaults 选项也没有意义了)
31
+ if (itemValue === undefined)
32
+ continue;
33
+ const itemResult = options.record(`${field}["${key}"]`, itemValue);
34
+ if (itemResult.success) {
35
+ if (itemResult.data !== undefined)
36
+ formatted[key] = itemResult.data;
37
+ }
38
+ else {
39
+ return itemResult;
43
40
  }
44
- const length = Object.keys(formatted).length;
45
- if (typeof opt.min === 'number' && length < opt.min)
46
- return failed(`size of ${field} should >= ${opt.min}`);
47
- if (typeof opt.max === 'number' && length > opt.max)
48
- return failed(`size of ${field} should <= ${opt.max}`);
49
41
  }
42
+ const length = Object.keys(formatted).length;
43
+ if (typeof options.min === 'number' && length < options.min)
44
+ return failed(`size of ${field} should >= ${options.min}`);
45
+ if (typeof options.max === 'number' && length > options.max)
46
+ return failed(`size of ${field} should <= ${options.max}`);
50
47
  return success(formatted);
51
- }
48
+ })(options);
52
49
  }
@@ -1,17 +1,22 @@
1
- import { type BaseOptions, Validator } from './base.js';
2
- export interface StringOptions {
1
+ import { type CommonOptions } from './base.js';
2
+ export interface StringOptions extends CommonOptions<string> {
3
3
  /** 字符串最小长度。defaults='' 时默认为 0,否则默认为 1 */
4
- min: number;
4
+ min?: number;
5
5
  /** 字符串最大长度。 */
6
6
  max?: number;
7
- /** 字符串需匹配此正则 */
8
- pattern?: RegExp;
9
- /** 指定一个数组或 TypeScript enum,字段值必须在此 enum 之中 */
10
- enum?: string[] | Record<string, string>;
11
- /** 验证之前,是否先清除两侧空白字符(默认开启) */
12
- trim: boolean;
13
- }
14
- export declare class StringValidator extends Validator<StringOptions> {
15
- constructor(options?: Partial<BaseOptions & StringOptions>);
16
- validate(field: string, value: unknown): import("../lang/may-success.js").Failed<void> | import("../lang/may-success.js").Success<unknown>;
7
+ /** 字符串需匹配此正则,也可传入关键词使用预置的正则。正则通常应以 ^ 和 $ 开头结尾。 */
8
+ pattern?: RegExp | 'uuid' | 'mobile';
9
+ /**
10
+ * 指定一个数组或 TypeScript enum,字段值必须在此 enum 之中。
11
+ * 若指定,前几个选项将不再生效。
12
+ */
13
+ choices?: string[] | Record<string, string>;
14
+ /** 验证之前,是否先清除两侧空白字符 @default true */
15
+ trim?: boolean;
17
16
  }
17
+ export type StringValueWithChoices<Options extends StringOptions> = Options extends {
18
+ choices: (infer T)[];
19
+ } ? T : Options extends {
20
+ choices: Record<string, infer T>;
21
+ } ? T : string;
22
+ export declare function getStringValidator<const Options extends StringOptions>(options?: Options): import("./base.js").Validator<StringValueWithChoices<Options>, Options>;
@@ -1,35 +1,35 @@
1
1
  import { success, failed } from '../lang/index.js';
2
- import { Validator } from './base.js';
3
- export class StringValidator extends Validator {
4
- constructor(options = {}) {
5
- super({
6
- min: options.defaults === '' ? 0 : 1,
7
- trim: true,
8
- ...options,
9
- });
10
- }
11
- validate(field, value) {
12
- const opt = this.options;
13
- const superResult = super.validate(field, value);
14
- if (!superResult.success)
15
- return superResult;
16
- value = superResult.data;
17
- if (value === null || value === undefined)
18
- return superResult;
2
+ import { getValidatorGenerator } from './base.js';
3
+ export function getStringValidator(options = {}) {
4
+ return getValidatorGenerator(function validate(field, value) {
19
5
  if (typeof value !== 'string')
20
6
  return failed(`${field} must be a string`);
21
- const formatted = opt.trim ? value.trim() : value;
22
- if (typeof opt.min === 'number' && formatted.length < opt.min)
23
- return failed(`${field}'s length must >= ${opt.min}`);
24
- if (typeof opt.max === 'number' && formatted.length > opt.max)
25
- return failed(`${field}'s length must <= ${opt.max}`);
26
- if (opt.pattern && !opt.pattern.exec(formatted))
27
- return failed(`${field} does not match the pattern.`);
28
- if (opt.enum !== undefined) {
29
- const validValues = Array.isArray(opt.enum) ? opt.enum : Object.values(opt.enum);
7
+ const trim = options.trim ?? true;
8
+ const formatted = trim ? value.trim() : value;
9
+ if ('choices' in options && options.choices) {
10
+ const validValues = Array.isArray(options.choices)
11
+ ? options.choices
12
+ : Object.values(options.choices);
30
13
  if (!validValues.includes(formatted))
31
14
  return failed(`${field} can only be one of ${validValues.join(', ')}.`);
32
15
  }
16
+ else {
17
+ const { min = options.defaults === '' ? 0 : 1, max, pattern } = options;
18
+ if (typeof min === 'number' && formatted.length < min)
19
+ return failed(`${field}'s length must >= ${min}`);
20
+ if (typeof max === 'number' && formatted.length > max)
21
+ return failed(`${field}'s length must <= ${max}`);
22
+ if (pattern !== undefined) {
23
+ if (pattern instanceof RegExp && !pattern.exec(formatted))
24
+ return failed(`${field} does not match the pattern`);
25
+ if (pattern === 'mobile' && !/^1\d{10}$/.test(formatted))
26
+ return failed(`${field} is not a valid mobile number`);
27
+ if (pattern === 'uuid') {
28
+ if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(formatted))
29
+ return failed(`${field} is not a valid uuid`);
30
+ }
31
+ }
32
+ }
33
33
  return success(formatted);
34
- }
34
+ })(options);
35
35
  }
@@ -1,18 +0,0 @@
1
- /**
2
- * 创建 validator 的快捷方式
3
- */
4
- import { ArrayValidator, type ArrayOptions } from './array.js';
5
- import { Validator, type BaseOptions } from './base.js';
6
- import { BooleanValidator } from './boolean.js';
7
- import { NumberValidator } from './number.js';
8
- import { ObjectValidator, type RecordOptions } from './object.js';
9
- import { StringValidator } from './string.js';
10
- /** 仅进行基本检查(如检查空值),不检查具体格式 */
11
- export declare function any(options?: Partial<BaseOptions>): Validator<unknown>;
12
- export declare function string(...args: ConstructorParameters<typeof StringValidator>): StringValidator;
13
- export declare function number(...args: ConstructorParameters<typeof NumberValidator>): NumberValidator;
14
- export declare function boolean(options?: Partial<BaseOptions>): BooleanValidator;
15
- export declare function array(options: Validator | (ArrayOptions & Partial<BaseOptions>)): ArrayValidator;
16
- export declare function tuple(validators: Validator[], baseOptions?: Partial<BaseOptions>): ArrayValidator;
17
- export declare function struct(validators: Record<string, Validator>, options?: Partial<BaseOptions>): ObjectValidator;
18
- export declare function record(options: Validator | (RecordOptions & Partial<BaseOptions>)): ObjectValidator;
@@ -1,41 +0,0 @@
1
- /**
2
- * 创建 validator 的快捷方式
3
- */
4
- import { ArrayValidator } from './array.js';
5
- import { Validator } from './base.js';
6
- import { BooleanValidator } from './boolean.js';
7
- import { NumberValidator } from './number.js';
8
- import { ObjectValidator } from './object.js';
9
- import { StringValidator } from './string.js';
10
- /** 仅进行基本检查(如检查空值),不检查具体格式 */
11
- export function any(options) {
12
- return new Validator(options ?? {});
13
- }
14
- export function string(...args) {
15
- return new StringValidator(...args);
16
- }
17
- export function number(...args) {
18
- return new NumberValidator(...args);
19
- }
20
- export function boolean(options = {}) {
21
- return new BooleanValidator(options);
22
- }
23
- export function array(options) {
24
- if (options instanceof Validator)
25
- options = { item: options };
26
- return new ArrayValidator(options);
27
- }
28
- export function tuple(validators, baseOptions) {
29
- return new ArrayValidator({
30
- ...(baseOptions ?? {}),
31
- tuple: validators,
32
- });
33
- }
34
- export function struct(validators, options = {}) {
35
- return new ObjectValidator({ ...options, struct: validators });
36
- }
37
- export function record(options) {
38
- if (options instanceof Validator)
39
- options = { record: options };
40
- return new ObjectValidator(options);
41
- }