@strictly/define 0.0.7 → 0.0.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.
- 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 +3 -3
- package/.out/types/builders.js +4 -6
- package/.out/validation/validator.d.ts +11 -9
- package/.out/validation/validator.js +6 -1
- package/.out/validation/validators/defined_validator.d.ts +1 -1
- package/.out/validation/validators/minimum_string_length_validator.d.ts +1 -1
- 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 +113 -33
- package/dist/index.d.cts +58 -15
- package/dist/index.d.ts +58 -15
- package/dist/index.js +109 -33
- package/index.ts +2 -0
- package/package.json +1 -1
- package/transformers/flatteners/flatten_type_to.ts +2 -2
- package/types/builders.ts +11 -14
- package/validation/validator.ts +26 -8
- package/validation/validators/defined_validator.ts +1 -3
- package/validation/validators/minimum_string_length_validator.ts +3 -3
- 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
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type AnnotatedValidator } from 'validation/validator'
|
|
2
|
+
|
|
3
|
+
export class OptionalValidatorProxy<
|
|
4
|
+
V,
|
|
5
|
+
V1 extends V,
|
|
6
|
+
E,
|
|
7
|
+
ValuePath extends string,
|
|
8
|
+
Context,
|
|
9
|
+
> implements AnnotatedValidator<
|
|
10
|
+
V,
|
|
11
|
+
E,
|
|
12
|
+
ValuePath,
|
|
13
|
+
Context
|
|
14
|
+
> {
|
|
15
|
+
static createNullable<
|
|
16
|
+
V,
|
|
17
|
+
E,
|
|
18
|
+
ValuePath extends string,
|
|
19
|
+
Context,
|
|
20
|
+
>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>) {
|
|
21
|
+
return new OptionalValidatorProxy(proxied, (v: V) => v != null)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static createNullableOrEmptyString<
|
|
25
|
+
V extends string | null | undefined,
|
|
26
|
+
E,
|
|
27
|
+
ValuePath extends string,
|
|
28
|
+
Context,
|
|
29
|
+
>(proxied: AnnotatedValidator<NonNullable<V>, E, ValuePath, Context>) {
|
|
30
|
+
return new OptionalValidatorProxy(proxied, (v: V): v is NonNullable<V> => v != null && v !== '')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
constructor(
|
|
34
|
+
private readonly proxied: AnnotatedValidator<V1, E, ValuePath, Context>,
|
|
35
|
+
private readonly isRequired: (v: V) => v is V1,
|
|
36
|
+
) {
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
validate(v: V, valuePath: ValuePath, context: Context): E | null {
|
|
40
|
+
if (this.isRequired(v)) {
|
|
41
|
+
return this.proxied.validate(v, valuePath, context)
|
|
42
|
+
}
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
annotations(valuePath: ValuePath, context: Context) {
|
|
47
|
+
return {
|
|
48
|
+
...this.proxied.annotations(valuePath, context),
|
|
49
|
+
required: false,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AnnotatedValidator,
|
|
3
|
+
} from 'validation/validator'
|
|
4
|
+
|
|
5
|
+
export const RegexpValidationErrorType = 'regexp'
|
|
6
|
+
|
|
7
|
+
export type RegexpValidationError<Intent extends string> = {
|
|
8
|
+
type: typeof RegexpValidationErrorType,
|
|
9
|
+
intent: Intent,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export class RegexpValidator<Intent extends string>
|
|
13
|
+
implements AnnotatedValidator<string, RegexpValidationError<Intent>, never, never>
|
|
14
|
+
{
|
|
15
|
+
/**
|
|
16
|
+
* Extremely permissive email validator
|
|
17
|
+
*/
|
|
18
|
+
static readonly email = new RegexpValidator(/^[^\s@]+@[^\s@]+\.[^\s@]+$/, 'email')
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Extremely permissive phone number validator
|
|
22
|
+
*/
|
|
23
|
+
static readonly phone = new RegexpValidator(/^(\+\d{1,4}[\s]*)?(((\([\d\s-]{1,6}\))|\d)[\s-]*){3,14}(\d|(\(\d+\)))$/,
|
|
24
|
+
'phone')
|
|
25
|
+
|
|
26
|
+
private readonly negate: boolean
|
|
27
|
+
private readonly required: boolean
|
|
28
|
+
|
|
29
|
+
constructor(
|
|
30
|
+
private readonly regexp: RegExp,
|
|
31
|
+
private readonly intent: Intent,
|
|
32
|
+
{
|
|
33
|
+
negate = false,
|
|
34
|
+
required = false,
|
|
35
|
+
}: {
|
|
36
|
+
negate?: boolean,
|
|
37
|
+
required?: boolean,
|
|
38
|
+
} = {},
|
|
39
|
+
) {
|
|
40
|
+
this.negate = negate
|
|
41
|
+
this.required = required
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
validate(value: string): RegexpValidationError<Intent> | null {
|
|
45
|
+
const passes = this.regexp.test(value)
|
|
46
|
+
if (!passes && !this.negate || passes && this.negate) {
|
|
47
|
+
return {
|
|
48
|
+
type: RegexpValidationErrorType,
|
|
49
|
+
intent: this.intent,
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return null
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
annotations() {
|
|
56
|
+
return {
|
|
57
|
+
required: this.required,
|
|
58
|
+
readonly: false,
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MinimumStringLengthValidationErrorType,
|
|
3
|
+
MinimumStringLengthValidator,
|
|
4
|
+
} from 'validation/validators/minimum_string_length_validator'
|
|
5
|
+
|
|
6
|
+
describe('MinimumStringLengthValidator', () => {
|
|
7
|
+
describe('required', () => {
|
|
8
|
+
it.each([
|
|
9
|
+
1,
|
|
10
|
+
2,
|
|
11
|
+
100,
|
|
12
|
+
])('is required when the string length is %s', (minimumLength) => {
|
|
13
|
+
const validator = new MinimumStringLengthValidator(minimumLength)
|
|
14
|
+
expect(validator.annotations().required).toBeTruthy()
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
it('is not required when the string length is zero', () => {
|
|
18
|
+
const validator = new MinimumStringLengthValidator(0)
|
|
19
|
+
expect(validator.annotations().required).toBeFalsy()
|
|
20
|
+
})
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
describe('validation', () => {
|
|
24
|
+
it.each([
|
|
25
|
+
[
|
|
26
|
+
1,
|
|
27
|
+
'a',
|
|
28
|
+
],
|
|
29
|
+
[
|
|
30
|
+
2,
|
|
31
|
+
'asdf',
|
|
32
|
+
],
|
|
33
|
+
[
|
|
34
|
+
0,
|
|
35
|
+
'',
|
|
36
|
+
],
|
|
37
|
+
[
|
|
38
|
+
20,
|
|
39
|
+
'12345678901234567890',
|
|
40
|
+
],
|
|
41
|
+
])('passes validation with minimum length %s and value "%s"', (minimumLength, value) => {
|
|
42
|
+
const validator = new MinimumStringLengthValidator(minimumLength)
|
|
43
|
+
expect(validator.validate(value)).toBeNull()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it.each([
|
|
47
|
+
[
|
|
48
|
+
1,
|
|
49
|
+
'',
|
|
50
|
+
0,
|
|
51
|
+
],
|
|
52
|
+
[
|
|
53
|
+
2,
|
|
54
|
+
'a',
|
|
55
|
+
1,
|
|
56
|
+
],
|
|
57
|
+
[
|
|
58
|
+
20,
|
|
59
|
+
'1234567890123456789',
|
|
60
|
+
19,
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
100,
|
|
64
|
+
'',
|
|
65
|
+
0,
|
|
66
|
+
],
|
|
67
|
+
])('fails validation with minimum length %s and value "%s', (minimumLength, value, receivedLength) => {
|
|
68
|
+
const validator = new MinimumStringLengthValidator(minimumLength)
|
|
69
|
+
expect(validator.validate(value)).toEqual({
|
|
70
|
+
type: MinimumStringLengthValidationErrorType,
|
|
71
|
+
minimumLength,
|
|
72
|
+
receivedLength,
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
})
|
|
76
|
+
})
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RegexpValidationErrorType,
|
|
3
|
+
RegexpValidator,
|
|
4
|
+
} from 'validation/validators/regexp_validator'
|
|
5
|
+
|
|
6
|
+
describe('RegexpValidator', () => {
|
|
7
|
+
describe('phone', () => {
|
|
8
|
+
it.each([
|
|
9
|
+
'0403545000',
|
|
10
|
+
'+12 (1800) 000 000',
|
|
11
|
+
'1-2-3-4-5-6',
|
|
12
|
+
'+1 394 3234 000 (2)',
|
|
13
|
+
'1234',
|
|
14
|
+
])('accepts "%s"', (phoneNumber) => {
|
|
15
|
+
expect(RegexpValidator.phone.validate(phoneNumber)).toBeNull()
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it.each([
|
|
19
|
+
'',
|
|
20
|
+
'ABC',
|
|
21
|
+
'1',
|
|
22
|
+
' 0412345678',
|
|
23
|
+
'----1245456',
|
|
24
|
+
'1 2',
|
|
25
|
+
'234234+234',
|
|
26
|
+
])('rejects "%s"', (phoneNumber) => {
|
|
27
|
+
expect(RegexpValidator.phone.validate(phoneNumber)).toEqual({
|
|
28
|
+
type: RegexpValidationErrorType,
|
|
29
|
+
intent: 'phone',
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
describe('email', () => {
|
|
35
|
+
it.each([
|
|
36
|
+
'x@y.z',
|
|
37
|
+
'support@company.com',
|
|
38
|
+
'...@......',
|
|
39
|
+
'1234@3454.23',
|
|
40
|
+
])('accepts "%s"', (email) => {
|
|
41
|
+
expect(RegexpValidator.email.validate(email)).toBeNull()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it.each([
|
|
45
|
+
'',
|
|
46
|
+
'@@@@',
|
|
47
|
+
'email',
|
|
48
|
+
'@bee.com',
|
|
49
|
+
'aaa@bbb',
|
|
50
|
+
'a a@b b.c c',
|
|
51
|
+
])('rejects "%s"', (email) => {
|
|
52
|
+
expect(RegexpValidator.email.validate(email)).toEqual({
|
|
53
|
+
type: RegexpValidationErrorType,
|
|
54
|
+
intent: 'email',
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
describe('required', () => {
|
|
60
|
+
it.each([
|
|
61
|
+
true,
|
|
62
|
+
false,
|
|
63
|
+
])('exposes required %s', (required) => {
|
|
64
|
+
const validator = new RegexpValidator(/a/, 'test', {
|
|
65
|
+
required,
|
|
66
|
+
})
|
|
67
|
+
expect(validator.annotations().required).toBe(required)
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
describe('general', () => {
|
|
72
|
+
// not here to test if regexp works
|
|
73
|
+
it.each([
|
|
74
|
+
[
|
|
75
|
+
'^a$',
|
|
76
|
+
'a',
|
|
77
|
+
],
|
|
78
|
+
[
|
|
79
|
+
'^\\w+$',
|
|
80
|
+
'asdf',
|
|
81
|
+
],
|
|
82
|
+
])('passes validation with regexp "%s" and value "%s"', (regexpString, value) => {
|
|
83
|
+
const regexp = new RegExp(regexpString)
|
|
84
|
+
const validator = new RegexpValidator(regexp, 'test')
|
|
85
|
+
expect(validator.validate(value)).toBeNull()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it.each([
|
|
89
|
+
[
|
|
90
|
+
'^$',
|
|
91
|
+
'something',
|
|
92
|
+
'empty',
|
|
93
|
+
],
|
|
94
|
+
[
|
|
95
|
+
'^\\w+$',
|
|
96
|
+
'',
|
|
97
|
+
'non-empty',
|
|
98
|
+
],
|
|
99
|
+
])('fails validation with regexp "%s" and value "%s', (regexpString, value, intent) => {
|
|
100
|
+
const regexp = new RegExp(regexpString)
|
|
101
|
+
const validator = new RegexpValidator(regexp, intent)
|
|
102
|
+
expect(validator.validate(value)).toEqual({
|
|
103
|
+
type: RegexpValidationErrorType,
|
|
104
|
+
intent,
|
|
105
|
+
})
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
})
|