@strictly/define 0.0.8 → 0.0.10
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/.out/index.d.ts +2 -0
- package/.out/index.js +2 -0
- package/.out/transformers/flatteners/flatten_type_to.d.ts +1 -1
- package/.out/transformers/flatteners/flatten_type_to.js +1 -1
- package/.out/tsconfig.tsbuildinfo +1 -1
- package/.out/types/builders.d.ts +1 -2
- package/.out/types/builders.js +3 -7
- package/.out/validation/validator.d.ts +9 -7
- package/.out/validation/validator.js +6 -0
- package/.out/validation/validators/minimum_string_length_validator.js +1 -1
- package/.out/validation/validators/optional_validator_proxy.d.ts +13 -0
- package/.out/validation/validators/optional_validator_proxy.js +26 -0
- package/.out/validation/validators/regexp_validator.d.ts +29 -0
- package/.out/validation/validators/regexp_validator.js +37 -0
- package/.out/validation/validators/specs/minimum_string_length_validator.tests.d.ts +1 -0
- package/.out/validation/validators/specs/minimum_string_length_validator.tests.js +69 -0
- package/.out/validation/validators/specs/regexp_validator.tests.d.ts +1 -0
- package/.out/validation/validators/specs/regexp_validator.tests.js +98 -0
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-check-types.log +1 -1
- package/dist/index.cjs +87 -8
- package/dist/index.d.cts +53 -10
- package/dist/index.d.ts +53 -10
- package/dist/index.js +83 -8
- package/index.ts +2 -0
- package/package.json +1 -1
- package/transformers/flatteners/flatten_type_to.ts +2 -2
- package/types/builders.ts +4 -13
- package/validation/validator.ts +13 -4
- package/validation/validators/minimum_string_length_validator.ts +1 -1
- package/validation/validators/optional_validator_proxy.ts +52 -0
- package/validation/validators/regexp_validator.ts +61 -0
- package/validation/validators/specs/minimum_string_length_validator.tests.ts +76 -0
- package/validation/validators/specs/regexp_validator.tests.ts +108 -0
package/.out/types/builders.d.ts
CHANGED
|
@@ -9,8 +9,7 @@ declare class TypeDefBuilder<T extends ValidatingTypeDef> implements ValidatingT
|
|
|
9
9
|
readonly definition: T;
|
|
10
10
|
constructor(definition: T);
|
|
11
11
|
enforce<E2>(rule: Rule<E2, ValueOfType<Type<T>>> | Validator<ValueOfType<Type<T>>, E2, never, never>): TypeDefBuilder<ValidatingTypeDefWithError<T, E2>>;
|
|
12
|
-
required(): TypeDefBuilder<
|
|
13
|
-
required<RequiredError>(rule: Rule<RequiredError, ValueOfType<typeof this.narrow>>): TypeDefBuilder<ValidatingTypeDefWithError<T, RequiredError>>;
|
|
12
|
+
required(): TypeDefBuilder<T>;
|
|
14
13
|
readonly(): TypeDefBuilder<T>;
|
|
15
14
|
get _type(): TypeOfType<Type<T>>;
|
|
16
15
|
get narrow(): ValidatingType<T>;
|
package/.out/types/builders.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isAnnotatedValidator, validate, } from 'validation/validator';
|
|
1
|
+
import { isAnnotatedValidator, mergeAnnotations, validate, } from 'validation/validator';
|
|
2
2
|
import { TypeDefType, } from './definitions';
|
|
3
3
|
import { typeOfType, } from './type_of_type';
|
|
4
4
|
function emptyRule() {
|
|
@@ -14,7 +14,7 @@ class TypeDefBuilder {
|
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
15
15
|
{
|
|
16
16
|
...this.definition,
|
|
17
|
-
...(isAnnotatedValidator(rule) ? rule.annotations(null, null) : {}),
|
|
17
|
+
...(isAnnotatedValidator(rule) ? mergeAnnotations(rule.annotations(null, null), this.definition) : {}),
|
|
18
18
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
19
|
rule: (value) => {
|
|
20
20
|
return this.definition.rule(value) ?? validate(rule, value, null, null);
|
|
@@ -22,15 +22,11 @@ class TypeDefBuilder {
|
|
|
22
22
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
|
-
required(
|
|
25
|
+
required() {
|
|
26
26
|
return new TypeDefBuilder(
|
|
27
27
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
28
28
|
{
|
|
29
29
|
...this.definition,
|
|
30
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
|
-
rule: (v) => {
|
|
32
|
-
return this.definition.rule(v) || rule?.(v);
|
|
33
|
-
},
|
|
34
30
|
required: true,
|
|
35
31
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
36
32
|
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { type Simplify } from 'type-fest';
|
|
2
|
+
export type Annotations = {
|
|
3
|
+
readonly required: boolean;
|
|
4
|
+
readonly readonly: boolean;
|
|
5
|
+
};
|
|
2
6
|
export type FunctionalValidator<V = any, E = any, ValuePath extends string = any, Context = any> = (v: V, valuePath: ValuePath, context: Context) => E | null;
|
|
3
7
|
export type AnnotatedValidator<V = any, E = any, ValuePath extends string = any, Context = any> = {
|
|
4
8
|
readonly validate: (v: V, valuePath: ValuePath, context: Context) => E | null;
|
|
5
|
-
readonly annotations: (valuePath: ValuePath, context: Context) =>
|
|
6
|
-
readonly required: boolean;
|
|
7
|
-
readonly readonly: boolean;
|
|
8
|
-
};
|
|
9
|
+
readonly annotations: (valuePath: ValuePath, context: Context) => Annotations;
|
|
9
10
|
};
|
|
10
11
|
export type Validator<V = any, E = any, ValuePath extends string = any, Context = any> = FunctionalValidator<V, E, ValuePath, Context> | AnnotatedValidator<V, E, ValuePath, Context>;
|
|
11
12
|
export type ErrorOfValidator<V extends Validator> = V extends Validator<infer _V, infer E> ? E : never;
|
|
@@ -15,7 +16,8 @@ export type ValidationError<Type extends string, Data = {}> = Simplify<{
|
|
|
15
16
|
export declare function isFunctionalValidator<V, E, ValuePath extends string, Context>(v: Validator<V, E, ValuePath, Context>): v is FunctionalValidator<V, E, ValuePath, Context>;
|
|
16
17
|
export declare function isAnnotatedValidator<V, E, ValuePath extends string, Context>(v: Validator<V, E, ValuePath, Context>): v is AnnotatedValidator<V, E, ValuePath, Context>;
|
|
17
18
|
export declare function validate<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, v: V, valuePath: ValuePath, context: Context): E | null;
|
|
18
|
-
export declare function annotations<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, valuePath: ValuePath, context: Context):
|
|
19
|
-
|
|
20
|
-
readonly
|
|
19
|
+
export declare function annotations<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, valuePath: ValuePath, context: Context): Annotations;
|
|
20
|
+
export declare function mergeAnnotations(a1: Annotations, a2: Annotations): {
|
|
21
|
+
readonly: boolean;
|
|
22
|
+
required: boolean;
|
|
21
23
|
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type AnnotatedValidator } from 'validation/validator';
|
|
2
|
+
export declare class OptionalValidatorProxy<V, V1 extends V, E, ValuePath extends string, Context> implements AnnotatedValidator<V, E, ValuePath, Context> {
|
|
3
|
+
private readonly proxied;
|
|
4
|
+
private readonly isRequired;
|
|
5
|
+
static createNullable<V, E, ValuePath extends string, Context>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>): OptionalValidatorProxy<V, NonNullable<V>, E, ValuePath, Context>;
|
|
6
|
+
static createNullableOrEmptyString<V extends string | null | undefined, E, ValuePath extends string, Context>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>): OptionalValidatorProxy<V, NonNullable<V>, E, ValuePath, Context>;
|
|
7
|
+
constructor(proxied: AnnotatedValidator<V1, E, ValuePath, Context>, isRequired: (v: V) => v is V1);
|
|
8
|
+
validate(v: V, valuePath: ValuePath, context: Context): E | null;
|
|
9
|
+
annotations(valuePath: ValuePath, context: Context): {
|
|
10
|
+
required: boolean;
|
|
11
|
+
readonly: boolean;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export class OptionalValidatorProxy {
|
|
2
|
+
proxied;
|
|
3
|
+
isRequired;
|
|
4
|
+
static createNullable(proxied) {
|
|
5
|
+
return new OptionalValidatorProxy(proxied, (v) => v != null);
|
|
6
|
+
}
|
|
7
|
+
static createNullableOrEmptyString(proxied) {
|
|
8
|
+
return new OptionalValidatorProxy(proxied, (v) => v != null && v !== '');
|
|
9
|
+
}
|
|
10
|
+
constructor(proxied, isRequired) {
|
|
11
|
+
this.proxied = proxied;
|
|
12
|
+
this.isRequired = isRequired;
|
|
13
|
+
}
|
|
14
|
+
validate(v, valuePath, context) {
|
|
15
|
+
if (this.isRequired(v)) {
|
|
16
|
+
return this.proxied.validate(v, valuePath, context);
|
|
17
|
+
}
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
annotations(valuePath, context) {
|
|
21
|
+
return {
|
|
22
|
+
...this.proxied.annotations(valuePath, context),
|
|
23
|
+
required: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type AnnotatedValidator } from 'validation/validator';
|
|
2
|
+
export declare const RegexpValidationErrorType = "regexp";
|
|
3
|
+
export type RegexpValidationError<Intent extends string> = {
|
|
4
|
+
type: typeof RegexpValidationErrorType;
|
|
5
|
+
intent: Intent;
|
|
6
|
+
};
|
|
7
|
+
export declare class RegexpValidator<Intent extends string> implements AnnotatedValidator<string, RegexpValidationError<Intent>, never, never> {
|
|
8
|
+
private readonly regexp;
|
|
9
|
+
private readonly intent;
|
|
10
|
+
/**
|
|
11
|
+
* Extremely permissive email validator
|
|
12
|
+
*/
|
|
13
|
+
static readonly email: RegexpValidator<"email">;
|
|
14
|
+
/**
|
|
15
|
+
* Extremely permissive phone number validator
|
|
16
|
+
*/
|
|
17
|
+
static readonly phone: RegexpValidator<"phone">;
|
|
18
|
+
private readonly negate;
|
|
19
|
+
private readonly required;
|
|
20
|
+
constructor(regexp: RegExp, intent: Intent, { negate, required, }?: {
|
|
21
|
+
negate?: boolean;
|
|
22
|
+
required?: boolean;
|
|
23
|
+
});
|
|
24
|
+
validate(value: string): RegexpValidationError<Intent> | null;
|
|
25
|
+
annotations(): {
|
|
26
|
+
required: boolean;
|
|
27
|
+
readonly: boolean;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const RegexpValidationErrorType = 'regexp';
|
|
2
|
+
export class RegexpValidator {
|
|
3
|
+
regexp;
|
|
4
|
+
intent;
|
|
5
|
+
/**
|
|
6
|
+
* Extremely permissive email validator
|
|
7
|
+
*/
|
|
8
|
+
static email = new RegexpValidator(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, 'email');
|
|
9
|
+
/**
|
|
10
|
+
* Extremely permissive phone number validator
|
|
11
|
+
*/
|
|
12
|
+
static phone = new RegexpValidator(/^(\+\d{1,4}[\s]*)?(((\([\d\s-]{1,6}\))|\d)[\s-]*){3,14}(\d|(\(\d+\)))$/, 'phone');
|
|
13
|
+
negate;
|
|
14
|
+
required;
|
|
15
|
+
constructor(regexp, intent, { negate = false, required = false, } = {}) {
|
|
16
|
+
this.regexp = regexp;
|
|
17
|
+
this.intent = intent;
|
|
18
|
+
this.negate = negate;
|
|
19
|
+
this.required = required;
|
|
20
|
+
}
|
|
21
|
+
validate(value) {
|
|
22
|
+
const passes = this.regexp.test(value);
|
|
23
|
+
if (!passes && !this.negate || passes && this.negate) {
|
|
24
|
+
return {
|
|
25
|
+
type: RegexpValidationErrorType,
|
|
26
|
+
intent: this.intent,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
annotations() {
|
|
32
|
+
return {
|
|
33
|
+
required: this.required,
|
|
34
|
+
readonly: false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { MinimumStringLengthValidationErrorType, MinimumStringLengthValidator, } from 'validation/validators/minimum_string_length_validator';
|
|
2
|
+
describe('MinimumStringLengthValidator', () => {
|
|
3
|
+
describe('required', () => {
|
|
4
|
+
it.each([
|
|
5
|
+
1,
|
|
6
|
+
2,
|
|
7
|
+
100,
|
|
8
|
+
])('is required when the string length is %s', (minimumLength) => {
|
|
9
|
+
const validator = new MinimumStringLengthValidator(minimumLength);
|
|
10
|
+
expect(validator.annotations().required).toBeTruthy();
|
|
11
|
+
});
|
|
12
|
+
it('is not required when the string length is zero', () => {
|
|
13
|
+
const validator = new MinimumStringLengthValidator(0);
|
|
14
|
+
expect(validator.annotations().required).toBeFalsy();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
describe('validation', () => {
|
|
18
|
+
it.each([
|
|
19
|
+
[
|
|
20
|
+
1,
|
|
21
|
+
'a',
|
|
22
|
+
],
|
|
23
|
+
[
|
|
24
|
+
2,
|
|
25
|
+
'asdf',
|
|
26
|
+
],
|
|
27
|
+
[
|
|
28
|
+
0,
|
|
29
|
+
'',
|
|
30
|
+
],
|
|
31
|
+
[
|
|
32
|
+
20,
|
|
33
|
+
'12345678901234567890',
|
|
34
|
+
],
|
|
35
|
+
])('passes validation with minimum length %s and value "%s"', (minimumLength, value) => {
|
|
36
|
+
const validator = new MinimumStringLengthValidator(minimumLength);
|
|
37
|
+
expect(validator.validate(value)).toBeNull();
|
|
38
|
+
});
|
|
39
|
+
it.each([
|
|
40
|
+
[
|
|
41
|
+
1,
|
|
42
|
+
'',
|
|
43
|
+
0,
|
|
44
|
+
],
|
|
45
|
+
[
|
|
46
|
+
2,
|
|
47
|
+
'a',
|
|
48
|
+
1,
|
|
49
|
+
],
|
|
50
|
+
[
|
|
51
|
+
20,
|
|
52
|
+
'1234567890123456789',
|
|
53
|
+
19,
|
|
54
|
+
],
|
|
55
|
+
[
|
|
56
|
+
100,
|
|
57
|
+
'',
|
|
58
|
+
0,
|
|
59
|
+
],
|
|
60
|
+
])('fails validation with minimum length %s and value "%s', (minimumLength, value, receivedLength) => {
|
|
61
|
+
const validator = new MinimumStringLengthValidator(minimumLength);
|
|
62
|
+
expect(validator.validate(value)).toEqual({
|
|
63
|
+
type: MinimumStringLengthValidationErrorType,
|
|
64
|
+
minimumLength,
|
|
65
|
+
receivedLength,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { RegexpValidationErrorType, RegexpValidator, } from 'validation/validators/regexp_validator';
|
|
2
|
+
describe('RegexpValidator', () => {
|
|
3
|
+
describe('phone', () => {
|
|
4
|
+
it.each([
|
|
5
|
+
'0403545000',
|
|
6
|
+
'+12 (1800) 000 000',
|
|
7
|
+
'1-2-3-4-5-6',
|
|
8
|
+
'+1 394 3234 000 (2)',
|
|
9
|
+
'1234',
|
|
10
|
+
])('accepts "%s"', (phoneNumber) => {
|
|
11
|
+
expect(RegexpValidator.phone.validate(phoneNumber)).toBeNull();
|
|
12
|
+
});
|
|
13
|
+
it.each([
|
|
14
|
+
'',
|
|
15
|
+
'ABC',
|
|
16
|
+
'1',
|
|
17
|
+
' 0412345678',
|
|
18
|
+
'----1245456',
|
|
19
|
+
'1 2',
|
|
20
|
+
'234234+234',
|
|
21
|
+
])('rejects "%s"', (phoneNumber) => {
|
|
22
|
+
expect(RegexpValidator.phone.validate(phoneNumber)).toEqual({
|
|
23
|
+
type: RegexpValidationErrorType,
|
|
24
|
+
intent: 'phone',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('email', () => {
|
|
29
|
+
it.each([
|
|
30
|
+
'x@y.z',
|
|
31
|
+
'support@company.com',
|
|
32
|
+
'...@......',
|
|
33
|
+
'1234@3454.23',
|
|
34
|
+
])('accepts "%s"', (email) => {
|
|
35
|
+
expect(RegexpValidator.email.validate(email)).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
it.each([
|
|
38
|
+
'',
|
|
39
|
+
'@@@@',
|
|
40
|
+
'email',
|
|
41
|
+
'@bee.com',
|
|
42
|
+
'aaa@bbb',
|
|
43
|
+
'a a@b b.c c',
|
|
44
|
+
])('rejects "%s"', (email) => {
|
|
45
|
+
expect(RegexpValidator.email.validate(email)).toEqual({
|
|
46
|
+
type: RegexpValidationErrorType,
|
|
47
|
+
intent: 'email',
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('required', () => {
|
|
52
|
+
it.each([
|
|
53
|
+
true,
|
|
54
|
+
false,
|
|
55
|
+
])('exposes required %s', (required) => {
|
|
56
|
+
const validator = new RegexpValidator(/a/, 'test', {
|
|
57
|
+
required,
|
|
58
|
+
});
|
|
59
|
+
expect(validator.annotations().required).toBe(required);
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('general', () => {
|
|
63
|
+
// not here to test if regexp works
|
|
64
|
+
it.each([
|
|
65
|
+
[
|
|
66
|
+
'^a$',
|
|
67
|
+
'a',
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
'^\\w+$',
|
|
71
|
+
'asdf',
|
|
72
|
+
],
|
|
73
|
+
])('passes validation with regexp "%s" and value "%s"', (regexpString, value) => {
|
|
74
|
+
const regexp = new RegExp(regexpString);
|
|
75
|
+
const validator = new RegexpValidator(regexp, 'test');
|
|
76
|
+
expect(validator.validate(value)).toBeNull();
|
|
77
|
+
});
|
|
78
|
+
it.each([
|
|
79
|
+
[
|
|
80
|
+
'^$',
|
|
81
|
+
'something',
|
|
82
|
+
'empty',
|
|
83
|
+
],
|
|
84
|
+
[
|
|
85
|
+
'^\\w+$',
|
|
86
|
+
'',
|
|
87
|
+
'non-empty',
|
|
88
|
+
],
|
|
89
|
+
])('fails validation with regexp "%s" and value "%s', (regexpString, value, intent) => {
|
|
90
|
+
const regexp = new RegExp(regexpString);
|
|
91
|
+
const validator = new RegexpValidator(regexp, intent);
|
|
92
|
+
expect(validator.validate(value)).toEqual({
|
|
93
|
+
type: RegexpValidationErrorType,
|
|
94
|
+
intent,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -7,12 +7,12 @@ $ tsup
|
|
|
7
7
|
[34mCLI[39m Target: esnext
|
|
8
8
|
[34mCJS[39m Build start
|
|
9
9
|
[34mESM[39m Build start
|
|
10
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
11
|
-
[32mCJS[39m ⚡️ Build success in
|
|
12
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
13
|
-
[32mESM[39m ⚡️ Build success in
|
|
10
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m29.06 KB[39m
|
|
11
|
+
[32mCJS[39m ⚡️ Build success in 57ms
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m26.67 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 62ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[
|
|
17
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
18
|
-
Done in 6.
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 5302ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m40.10 KB[39m
|
|
17
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m40.10 KB[39m
|
|
18
|
+
Done in 6.22s.
|
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,9 @@ __export(index_exports, {
|
|
|
23
23
|
DefinedValidator: () => DefinedValidator,
|
|
24
24
|
MinimumStringLengthValidationErrorType: () => MinimumStringLengthValidationErrorType,
|
|
25
25
|
MinimumStringLengthValidator: () => MinimumStringLengthValidator,
|
|
26
|
+
OptionalValidatorProxy: () => OptionalValidatorProxy,
|
|
27
|
+
RegexpValidationErrorType: () => RegexpValidationErrorType,
|
|
28
|
+
RegexpValidator: () => RegexpValidator,
|
|
26
29
|
TypeDefType: () => TypeDefType,
|
|
27
30
|
annotations: () => annotations,
|
|
28
31
|
booleanType: () => booleanType,
|
|
@@ -40,6 +43,7 @@ __export(index_exports, {
|
|
|
40
43
|
jsonPathPop: () => jsonPathPop,
|
|
41
44
|
list: () => list,
|
|
42
45
|
literal: () => literal,
|
|
46
|
+
mergeAnnotations: () => mergeAnnotations,
|
|
43
47
|
mobxCopy: () => mobxCopy,
|
|
44
48
|
nullType: () => nullType,
|
|
45
49
|
nullable: () => nullable,
|
|
@@ -430,7 +434,7 @@ function flattenTypeTo({ definition }, mapper2) {
|
|
|
430
434
|
return (0, import_base4.reduce)(
|
|
431
435
|
typeDefs,
|
|
432
436
|
function(acc, key, typeDef) {
|
|
433
|
-
acc[key] = mapper2(typeDef);
|
|
437
|
+
acc[key] = mapper2(typeDef, key);
|
|
434
438
|
return acc;
|
|
435
439
|
},
|
|
436
440
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
@@ -695,6 +699,12 @@ function annotations(validator, valuePath, context) {
|
|
|
695
699
|
};
|
|
696
700
|
}
|
|
697
701
|
}
|
|
702
|
+
function mergeAnnotations(a1, a2) {
|
|
703
|
+
return {
|
|
704
|
+
readonly: a1.readonly || a2.readonly,
|
|
705
|
+
required: a1.required || a2.required
|
|
706
|
+
};
|
|
707
|
+
}
|
|
698
708
|
|
|
699
709
|
// types/type_of_type.ts
|
|
700
710
|
var import_base6 = require("@strictly/base");
|
|
@@ -786,7 +796,7 @@ var TypeDefBuilder = class _TypeDefBuilder {
|
|
|
786
796
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
787
797
|
{
|
|
788
798
|
...this.definition,
|
|
789
|
-
...isAnnotatedValidator(rule) ? rule.annotations(null, null) : {},
|
|
799
|
+
...isAnnotatedValidator(rule) ? mergeAnnotations(rule.annotations(null, null), this.definition) : {},
|
|
790
800
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
791
801
|
rule: (value) => {
|
|
792
802
|
return this.definition.rule(value) ?? validate(rule, value, null, null);
|
|
@@ -795,15 +805,11 @@ var TypeDefBuilder = class _TypeDefBuilder {
|
|
|
795
805
|
}
|
|
796
806
|
);
|
|
797
807
|
}
|
|
798
|
-
required(
|
|
808
|
+
required() {
|
|
799
809
|
return new _TypeDefBuilder(
|
|
800
810
|
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
|
|
801
811
|
{
|
|
802
812
|
...this.definition,
|
|
803
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
804
|
-
rule: (v) => {
|
|
805
|
-
return this.definition.rule(v) || rule?.(v);
|
|
806
|
-
},
|
|
807
813
|
required: true
|
|
808
814
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
809
815
|
}
|
|
@@ -1045,7 +1051,76 @@ var MinimumStringLengthValidator = class {
|
|
|
1045
1051
|
}
|
|
1046
1052
|
annotations() {
|
|
1047
1053
|
return {
|
|
1048
|
-
required:
|
|
1054
|
+
required: this.minimumLength > 0,
|
|
1055
|
+
readonly: false
|
|
1056
|
+
};
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
|
|
1060
|
+
// validation/validators/optional_validator_proxy.ts
|
|
1061
|
+
var OptionalValidatorProxy = class _OptionalValidatorProxy {
|
|
1062
|
+
constructor(proxied, isRequired) {
|
|
1063
|
+
this.proxied = proxied;
|
|
1064
|
+
this.isRequired = isRequired;
|
|
1065
|
+
}
|
|
1066
|
+
static createNullable(proxied) {
|
|
1067
|
+
return new _OptionalValidatorProxy(proxied, (v) => v != null);
|
|
1068
|
+
}
|
|
1069
|
+
static createNullableOrEmptyString(proxied) {
|
|
1070
|
+
return new _OptionalValidatorProxy(proxied, (v) => v != null && v !== "");
|
|
1071
|
+
}
|
|
1072
|
+
validate(v, valuePath, context) {
|
|
1073
|
+
if (this.isRequired(v)) {
|
|
1074
|
+
return this.proxied.validate(v, valuePath, context);
|
|
1075
|
+
}
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
annotations(valuePath, context) {
|
|
1079
|
+
return {
|
|
1080
|
+
...this.proxied.annotations(valuePath, context),
|
|
1081
|
+
required: false
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
|
|
1086
|
+
// validation/validators/regexp_validator.ts
|
|
1087
|
+
var RegexpValidationErrorType = "regexp";
|
|
1088
|
+
var RegexpValidator = class _RegexpValidator {
|
|
1089
|
+
constructor(regexp, intent, {
|
|
1090
|
+
negate = false,
|
|
1091
|
+
required = false
|
|
1092
|
+
} = {}) {
|
|
1093
|
+
this.regexp = regexp;
|
|
1094
|
+
this.intent = intent;
|
|
1095
|
+
this.negate = negate;
|
|
1096
|
+
this.required = required;
|
|
1097
|
+
}
|
|
1098
|
+
/**
|
|
1099
|
+
* Extremely permissive email validator
|
|
1100
|
+
*/
|
|
1101
|
+
static email = new _RegexpValidator(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, "email");
|
|
1102
|
+
/**
|
|
1103
|
+
* Extremely permissive phone number validator
|
|
1104
|
+
*/
|
|
1105
|
+
static phone = new _RegexpValidator(
|
|
1106
|
+
/^(\+\d{1,4}[\s]*)?(((\([\d\s-]{1,6}\))|\d)[\s-]*){3,14}(\d|(\(\d+\)))$/,
|
|
1107
|
+
"phone"
|
|
1108
|
+
);
|
|
1109
|
+
negate;
|
|
1110
|
+
required;
|
|
1111
|
+
validate(value) {
|
|
1112
|
+
const passes = this.regexp.test(value);
|
|
1113
|
+
if (!passes && !this.negate || passes && this.negate) {
|
|
1114
|
+
return {
|
|
1115
|
+
type: RegexpValidationErrorType,
|
|
1116
|
+
intent: this.intent
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
return null;
|
|
1120
|
+
}
|
|
1121
|
+
annotations() {
|
|
1122
|
+
return {
|
|
1123
|
+
required: this.required,
|
|
1049
1124
|
readonly: false
|
|
1050
1125
|
};
|
|
1051
1126
|
}
|
|
@@ -1055,6 +1130,9 @@ var MinimumStringLengthValidator = class {
|
|
|
1055
1130
|
DefinedValidator,
|
|
1056
1131
|
MinimumStringLengthValidationErrorType,
|
|
1057
1132
|
MinimumStringLengthValidator,
|
|
1133
|
+
OptionalValidatorProxy,
|
|
1134
|
+
RegexpValidationErrorType,
|
|
1135
|
+
RegexpValidator,
|
|
1058
1136
|
TypeDefType,
|
|
1059
1137
|
annotations,
|
|
1060
1138
|
booleanType,
|
|
@@ -1072,6 +1150,7 @@ var MinimumStringLengthValidator = class {
|
|
|
1072
1150
|
jsonPathPop,
|
|
1073
1151
|
list,
|
|
1074
1152
|
literal,
|
|
1153
|
+
mergeAnnotations,
|
|
1075
1154
|
mobxCopy,
|
|
1076
1155
|
nullType,
|
|
1077
1156
|
nullable,
|
package/dist/index.d.cts
CHANGED
|
@@ -266,13 +266,14 @@ type InternalFlattenedTypeDefsOfUnionChildren<T extends ValidatingUnionTypeDef,
|
|
|
266
266
|
readonly [K in keyof Unions]: InternalFlattenedTypeDefsOfChildren<Unions[K], SegmentOverride, Path, `${Qualifier}${K}:`, Depth>;
|
|
267
267
|
}[keyof Unions]> : never : never;
|
|
268
268
|
|
|
269
|
+
type Annotations = {
|
|
270
|
+
readonly required: boolean;
|
|
271
|
+
readonly readonly: boolean;
|
|
272
|
+
};
|
|
269
273
|
type FunctionalValidator<V = any, E = any, ValuePath extends string = any, Context = any> = (v: V, valuePath: ValuePath, context: Context) => E | null;
|
|
270
274
|
type AnnotatedValidator<V = any, E = any, ValuePath extends string = any, Context = any> = {
|
|
271
275
|
readonly validate: (v: V, valuePath: ValuePath, context: Context) => E | null;
|
|
272
|
-
readonly annotations: (valuePath: ValuePath, context: Context) =>
|
|
273
|
-
readonly required: boolean;
|
|
274
|
-
readonly readonly: boolean;
|
|
275
|
-
};
|
|
276
|
+
readonly annotations: (valuePath: ValuePath, context: Context) => Annotations;
|
|
276
277
|
};
|
|
277
278
|
type Validator<V = any, E = any, ValuePath extends string = any, Context = any> = FunctionalValidator<V, E, ValuePath, Context> | AnnotatedValidator<V, E, ValuePath, Context>;
|
|
278
279
|
type ErrorOfValidator<V extends Validator> = V extends Validator<infer _V, infer E> ? E : never;
|
|
@@ -282,9 +283,10 @@ type ValidationError<Type extends string, Data = {}> = Simplify<{
|
|
|
282
283
|
declare function isFunctionalValidator<V, E, ValuePath extends string, Context>(v: Validator<V, E, ValuePath, Context>): v is FunctionalValidator<V, E, ValuePath, Context>;
|
|
283
284
|
declare function isAnnotatedValidator<V, E, ValuePath extends string, Context>(v: Validator<V, E, ValuePath, Context>): v is AnnotatedValidator<V, E, ValuePath, Context>;
|
|
284
285
|
declare function validate<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, v: V, valuePath: ValuePath, context: Context): E | null;
|
|
285
|
-
declare function annotations<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, valuePath: ValuePath, context: Context):
|
|
286
|
-
|
|
287
|
-
readonly
|
|
286
|
+
declare function annotations<V = any, E = any, ValuePath extends string = any, Context = any>(validator: Validator<V, E, ValuePath, Context>, valuePath: ValuePath, context: Context): Annotations;
|
|
287
|
+
declare function mergeAnnotations(a1: Annotations, a2: Annotations): {
|
|
288
|
+
readonly: boolean;
|
|
289
|
+
required: boolean;
|
|
288
290
|
};
|
|
289
291
|
|
|
290
292
|
type ValidatorOfValidatingType<T extends ValidatingTypeDef, ValuePath extends string, Context> = T extends ValidatingTypeDef<infer E> ? Validator<ValueOfTypeDef<ReadonlyOfTypeDef<T>>, E, ValuePath, Context> : never;
|
|
@@ -403,8 +405,7 @@ declare class TypeDefBuilder<T extends ValidatingTypeDef> implements ValidatingT
|
|
|
403
405
|
readonly definition: T;
|
|
404
406
|
constructor(definition: T);
|
|
405
407
|
enforce<E2>(rule: Rule<E2, ValueOfType<Type<T>>> | Validator<ValueOfType<Type<T>>, E2, never, never>): TypeDefBuilder<ValidatingTypeDefWithError<T, E2>>;
|
|
406
|
-
required(): TypeDefBuilder<
|
|
407
|
-
required<RequiredError>(rule: Rule<RequiredError, ValueOfType<typeof this.narrow>>): TypeDefBuilder<ValidatingTypeDefWithError<T, RequiredError>>;
|
|
408
|
+
required(): TypeDefBuilder<T>;
|
|
408
409
|
readonly(): TypeDefBuilder<T>;
|
|
409
410
|
get _type(): TypeOfType<Type<T>>;
|
|
410
411
|
get narrow(): ValidatingType<T>;
|
|
@@ -545,8 +546,50 @@ declare class MinimumStringLengthValidator implements AnnotatedValidator<string,
|
|
|
545
546
|
};
|
|
546
547
|
}
|
|
547
548
|
|
|
549
|
+
declare class OptionalValidatorProxy<V, V1 extends V, E, ValuePath extends string, Context> implements AnnotatedValidator<V, E, ValuePath, Context> {
|
|
550
|
+
private readonly proxied;
|
|
551
|
+
private readonly isRequired;
|
|
552
|
+
static createNullable<V, E, ValuePath extends string, Context>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>): OptionalValidatorProxy<V, NonNullable<V>, E, ValuePath, Context>;
|
|
553
|
+
static createNullableOrEmptyString<V extends string | null | undefined, E, ValuePath extends string, Context>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>): OptionalValidatorProxy<V, NonNullable<V>, E, ValuePath, Context>;
|
|
554
|
+
constructor(proxied: AnnotatedValidator<V1, E, ValuePath, Context>, isRequired: (v: V) => v is V1);
|
|
555
|
+
validate(v: V, valuePath: ValuePath, context: Context): E | null;
|
|
556
|
+
annotations(valuePath: ValuePath, context: Context): {
|
|
557
|
+
required: boolean;
|
|
558
|
+
readonly: boolean;
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
declare const RegexpValidationErrorType = "regexp";
|
|
563
|
+
type RegexpValidationError<Intent extends string> = {
|
|
564
|
+
type: typeof RegexpValidationErrorType;
|
|
565
|
+
intent: Intent;
|
|
566
|
+
};
|
|
567
|
+
declare class RegexpValidator<Intent extends string> implements AnnotatedValidator<string, RegexpValidationError<Intent>, never, never> {
|
|
568
|
+
private readonly regexp;
|
|
569
|
+
private readonly intent;
|
|
570
|
+
/**
|
|
571
|
+
* Extremely permissive email validator
|
|
572
|
+
*/
|
|
573
|
+
static readonly email: RegexpValidator<"email">;
|
|
574
|
+
/**
|
|
575
|
+
* Extremely permissive phone number validator
|
|
576
|
+
*/
|
|
577
|
+
static readonly phone: RegexpValidator<"phone">;
|
|
578
|
+
private readonly negate;
|
|
579
|
+
private readonly required;
|
|
580
|
+
constructor(regexp: RegExp, intent: Intent, { negate, required, }?: {
|
|
581
|
+
negate?: boolean;
|
|
582
|
+
required?: boolean;
|
|
583
|
+
});
|
|
584
|
+
validate(value: string): RegexpValidationError<Intent> | null;
|
|
585
|
+
annotations(): {
|
|
586
|
+
required: boolean;
|
|
587
|
+
readonly: boolean;
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
548
591
|
type ValidatorsOfValues<FlattenedValues extends Readonly<Record<string, any>>, TypePathsToValuePaths extends Readonly<Record<keyof FlattenedValues, string>> = Readonly<Record<keyof FlattenedValues, any>>, Context = any> = {
|
|
549
592
|
readonly [K in keyof FlattenedValues]: Validator<FlattenedValues[K], any, TypePathsToValuePaths[K], Context>;
|
|
550
593
|
};
|
|
551
594
|
|
|
552
|
-
export { type Accessor, type AnnotatedValidator, type AnyValueType, DefinedValidator, type ErrorOfValidator, type FlattenedAccessorsOfType, type FlattenedTypesOfType, type FlattenedValuesOfType, type FunctionalValidator, type InternalJsonPathsOf, type IsStrictUnion, type ListTypeDef, type LiteralTypeDef, type Mapper, type MinimumStringLengthValidationError, MinimumStringLengthValidationErrorType, MinimumStringLengthValidator, type MobxObservable, type MobxValueOfType, type NonMobxObservable, type ObjectFieldKey, type ObjectTypeDef, type ObjectTypeDefFields, type PathsOfType, type ReadonlyOfTypeDef, type ReadonlyTypeOfType, type RecordKeyType, type RecordTypeDef, type Setter, type StrictListTypeDef, type StrictLiteralTypeDef, type StrictObjectTypeDef, type StrictObjectTypeDefFields, type StrictRecordTypeDef, type StrictType, type StrictTypeDef, type StrictUnionTypeDef, type Type, type TypeDef, TypeDefType, type UnionKey, type UnionTypeDef, type ValidationError, type Validator, type ValidatorsOfValues, type ValueOfType, type ValueOfTypeDef, type ValueToTypePathsOfType, type ValueTypesOfDiscriminatedUnion, annotations, booleanType, copy, flattenAccessorsOfType, flattenJsonValueToTypePathsOf, flattenTypesOfType, flattenValidatorsOfValidatingType, flattenValueTo, flattenValuesOfType, getUnionTypeDef, isAnnotatedValidator, isFunctionalValidator, jsonPath, jsonPathPop, list, literal, mobxCopy, nullType, nullable, numberType, object, record, stringType, union, validate, valuePathToTypePath };
|
|
595
|
+
export { type Accessor, type AnnotatedValidator, type Annotations, type AnyValueType, DefinedValidator, type ErrorOfValidator, type FlattenedAccessorsOfType, type FlattenedTypesOfType, type FlattenedValuesOfType, type FunctionalValidator, type InternalJsonPathsOf, type IsStrictUnion, type ListTypeDef, type LiteralTypeDef, type Mapper, type MinimumStringLengthValidationError, MinimumStringLengthValidationErrorType, MinimumStringLengthValidator, type MobxObservable, type MobxValueOfType, type NonMobxObservable, type ObjectFieldKey, type ObjectTypeDef, type ObjectTypeDefFields, OptionalValidatorProxy, type PathsOfType, type ReadonlyOfTypeDef, type ReadonlyTypeOfType, type RecordKeyType, type RecordTypeDef, type RegexpValidationError, RegexpValidationErrorType, RegexpValidator, type Setter, type StrictListTypeDef, type StrictLiteralTypeDef, type StrictObjectTypeDef, type StrictObjectTypeDefFields, type StrictRecordTypeDef, type StrictType, type StrictTypeDef, type StrictUnionTypeDef, type Type, type TypeDef, TypeDefType, type UnionKey, type UnionTypeDef, type ValidationError, type Validator, type ValidatorsOfValues, type ValueOfType, type ValueOfTypeDef, type ValueToTypePathsOfType, type ValueTypesOfDiscriminatedUnion, annotations, booleanType, copy, flattenAccessorsOfType, flattenJsonValueToTypePathsOf, flattenTypesOfType, flattenValidatorsOfValidatingType, flattenValueTo, flattenValuesOfType, getUnionTypeDef, isAnnotatedValidator, isFunctionalValidator, jsonPath, jsonPathPop, list, literal, mergeAnnotations, mobxCopy, nullType, nullable, numberType, object, record, stringType, union, validate, valuePathToTypePath };
|