@stacksjs/validation 0.58.48 → 0.58.50
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/package.json +3 -2
- package/src/index.ts +4 -0
- package/src/is.ts +111 -0
- package/src/rules.ts +126 -0
- package/src/types/index.ts +7 -0
- package/src/types/money.ts +17 -0
- package/src/validate.ts +33 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stacksjs/validation",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.58.
|
|
4
|
+
"version": "0.58.50",
|
|
5
5
|
"description": "The Stacks Validation ways.",
|
|
6
6
|
"author": "Chris Breuer",
|
|
7
7
|
"license": "MIT",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
],
|
|
40
40
|
"files": [
|
|
41
41
|
"README.md",
|
|
42
|
-
"dist"
|
|
42
|
+
"dist",
|
|
43
|
+
"src"
|
|
43
44
|
],
|
|
44
45
|
"scripts": {
|
|
45
46
|
"build": "bun --bun build.ts",
|
package/src/index.ts
ADDED
package/src/is.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { toString } from '@stacksjs/strings'
|
|
2
|
+
import { getTypeName } from '@stacksjs/types'
|
|
3
|
+
|
|
4
|
+
export function isDef<T = any>(val?: T): val is T {
|
|
5
|
+
return typeof val !== 'undefined'
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function isBoolean(val: any): val is boolean {
|
|
9
|
+
return typeof val === 'boolean'
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function isFunction<T extends Function>(val: any): val is T {
|
|
13
|
+
return typeof val === 'function'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isNumber(val: any): val is number {
|
|
17
|
+
return typeof val === 'number'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function isString(val: unknown): val is string {
|
|
21
|
+
return typeof val === 'string'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isObject(val: any): val is object {
|
|
25
|
+
return toString(val) === '[object Object]'
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function isWindow(val: any): boolean {
|
|
29
|
+
return typeof window !== 'undefined' && toString(val) === '[object Window]'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const isBrowser = typeof window !== 'undefined'
|
|
33
|
+
|
|
34
|
+
export const isServer = typeof document === 'undefined' // https://remix.run/docs/en/v1/pages/gotchas#typeof-window-checks
|
|
35
|
+
|
|
36
|
+
export function isMap(val: any): val is Map<any, any> {
|
|
37
|
+
return toString(val) === '[object Map]'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isSet(val: any): val is Set<any> {
|
|
41
|
+
return toString(val) === '[object Set]'
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function isPromise<T = any>(val: any): val is Promise<T> {
|
|
45
|
+
return toString(val) === '[object Promise]'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isUndefined(v: any) {
|
|
49
|
+
return getTypeName(v) === 'undefined'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function isNull(v: any) {
|
|
53
|
+
return getTypeName(v) === 'null'
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function isSymbol(v: any) {
|
|
57
|
+
return getTypeName(v) === 'symbol'
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isDate(v: any) {
|
|
61
|
+
return getTypeName(v) === 'date'
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isRegExp(v: any) {
|
|
65
|
+
return getTypeName(v) === 'regexp'
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function isArray(v: any) {
|
|
69
|
+
return getTypeName(v) === 'array'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function isPrimitive(v: any) {
|
|
73
|
+
const type = getTypeName(v)
|
|
74
|
+
return type === 'null' || type === 'undefined' || type === 'string' || type === 'number' || type === 'boolean' || type === 'symbol'
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export function isInteger(v: any) {
|
|
78
|
+
return isNumber(v) && Number.isInteger(v)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function isFloat(v: any) {
|
|
82
|
+
return isNumber(v) && !Number.isInteger(v)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function isPositive(v: any) {
|
|
86
|
+
return isNumber(v) && v > 0
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function isNegative(v: any) {
|
|
90
|
+
return isNumber(v) && v < 0
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function isEven(v: any) {
|
|
94
|
+
return isNumber(v) && v % 2 === 0
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function isOdd(v: any) {
|
|
98
|
+
return isNumber(v) && v % 2 !== 0
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function isEvenOrOdd(v: any) {
|
|
102
|
+
return isNumber(v) && (v % 2 === 0 ? 'even' : 'odd')
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export function isPositiveOrNegative(v: any) {
|
|
106
|
+
return isNumber(v) && (v > 0 ? 'positive' : 'negative')
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function isIntegerOrFloat(v: any) {
|
|
110
|
+
return isNumber(v) && (Number.isInteger(v) ? 'integer' : 'float')
|
|
111
|
+
}
|
package/src/rules.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { USD } from '@dinero.js/currencies'
|
|
2
|
+
import { dinero as currency } from 'dinero.js'
|
|
3
|
+
import { validator } from './validate'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Thanks to VineJS for the following types:
|
|
7
|
+
*
|
|
8
|
+
* The context shared with the entire validation pipeline.
|
|
9
|
+
* Each field gets its own context object.
|
|
10
|
+
*/
|
|
11
|
+
export interface FieldContext {
|
|
12
|
+
/**
|
|
13
|
+
* Field value
|
|
14
|
+
*/
|
|
15
|
+
value: unknown
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The data property is the top-level object under validation.
|
|
19
|
+
*/
|
|
20
|
+
data: any
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Shared metadata across the entire validation lifecycle. It can be
|
|
24
|
+
* used to pass data between validation rules
|
|
25
|
+
*/
|
|
26
|
+
meta: Record<string, any>
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Mutate the value of field under validation.
|
|
30
|
+
*/
|
|
31
|
+
mutate(newValue: any, field: FieldContext): void
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Report error to the error reporter
|
|
35
|
+
*/
|
|
36
|
+
report: ErrorReporterContract['report']
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Is this field valid. Default: true
|
|
40
|
+
*/
|
|
41
|
+
isValid: boolean
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Is this field has value defined.
|
|
45
|
+
*/
|
|
46
|
+
isDefined: boolean
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Wildcard path for the field. The value is a nested
|
|
50
|
+
* pointer to the field under validation.
|
|
51
|
+
*
|
|
52
|
+
* In case of arrays, the `*` wildcard is used.
|
|
53
|
+
*/
|
|
54
|
+
wildCardPath: string
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The parent property is the parent of the field. It could be an
|
|
58
|
+
* array or an object.
|
|
59
|
+
*/
|
|
60
|
+
parent: any
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Name of the field under validation. In case of an array, the field
|
|
64
|
+
* name will be a number
|
|
65
|
+
*/
|
|
66
|
+
name: string | number
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Is this field an array member
|
|
70
|
+
*/
|
|
71
|
+
isArrayMember: boolean
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Thanks to VineJS for the following types:
|
|
76
|
+
*
|
|
77
|
+
* The error reporter is used to report errors during the validation
|
|
78
|
+
* process.
|
|
79
|
+
*/
|
|
80
|
+
export interface ErrorReporterContract {
|
|
81
|
+
/**
|
|
82
|
+
* A boolean to known if there are one or more
|
|
83
|
+
* errors.
|
|
84
|
+
*/
|
|
85
|
+
hasErrors: boolean
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Creates an instance of an exception to throw
|
|
89
|
+
*/
|
|
90
|
+
createError(): Error
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Report error for a field
|
|
94
|
+
*/
|
|
95
|
+
report(message: string, rule: string, field: FieldContext, args?: Record<string, any>): any
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export const isMoney = validator.createRule((value: unknown, _, field: FieldContext) => {
|
|
99
|
+
/**
|
|
100
|
+
* Convert string representation of a number to a JavaScript
|
|
101
|
+
* Number data type.
|
|
102
|
+
*/
|
|
103
|
+
const numericValue = validator.helpers.asNumber(value)
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Report error, if the value is NaN post-conversion
|
|
107
|
+
*/
|
|
108
|
+
if (Number.isNaN(numericValue)) {
|
|
109
|
+
field.report(
|
|
110
|
+
'The {{ field }} field value must be a number',
|
|
111
|
+
'money',
|
|
112
|
+
field,
|
|
113
|
+
)
|
|
114
|
+
return
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create amount type
|
|
119
|
+
*/
|
|
120
|
+
const amount = currency({ amount: numericValue, currency: USD })
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Mutate the field's value
|
|
124
|
+
*/
|
|
125
|
+
field.mutate(amount, field)
|
|
126
|
+
}) as any
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Money } from '@stacksjs/types'
|
|
2
|
+
import { BaseLiteralType } from '@vinejs/vine'
|
|
3
|
+
import type { FieldOptions, Validation } from '@vinejs/vine/types'
|
|
4
|
+
import { isMoney } from '../rules'
|
|
5
|
+
|
|
6
|
+
export class MoneyValidator extends BaseLiteralType<Money, Money> {
|
|
7
|
+
constructor(options?: FieldOptions, validations?: Validation<any>[]) {
|
|
8
|
+
super(options, validations || [isMoney()])
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
clone() {
|
|
12
|
+
return new MoneyValidator(
|
|
13
|
+
this.cloneOptions(),
|
|
14
|
+
this.cloneValidations(),
|
|
15
|
+
) as this
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/validate.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import v, { Vine as Validator } from '@vinejs/vine'
|
|
2
|
+
import { MoneyValidator } from './types/money'
|
|
3
|
+
|
|
4
|
+
Validator.macro('money', () => {
|
|
5
|
+
return new MoneyValidator()
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
export const validator: Validator = v
|
|
9
|
+
// validator.string().email()
|
|
10
|
+
type SchemaString = string
|
|
11
|
+
type SchemaNumber = number
|
|
12
|
+
type SchemaBoolean = boolean
|
|
13
|
+
type SchemaEnum = string[]
|
|
14
|
+
|
|
15
|
+
export type SchemaType = SchemaString | SchemaNumber | SchemaBoolean | SchemaEnum
|
|
16
|
+
|
|
17
|
+
export const validate = {
|
|
18
|
+
string: (defaultValue: string = ''): SchemaString => defaultValue,
|
|
19
|
+
number: (defaultValue: number = 1): SchemaNumber => defaultValue,
|
|
20
|
+
boolean: (defaultValue: boolean = true): SchemaBoolean => defaultValue,
|
|
21
|
+
enum: (values: string[]): SchemaEnum => values,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const RuleString: Validator['string'] = (...args) => validator.string(...args)
|
|
25
|
+
export const RuleNumber: Validator['number'] = (...args) => validator.number(...args)
|
|
26
|
+
export const RuleBoolean: Validator['boolean'] = (...args) => validator.boolean(...args)
|
|
27
|
+
export const RuleArray: Validator['array'] = (...args) => validator.array(...args)
|
|
28
|
+
export const RuleObject: Validator['object'] = (...args) => validator.object(...args)
|
|
29
|
+
export const RuleAny: Validator['any'] = (...args) => validator.any(...args)
|
|
30
|
+
// const email = () => validator.string().email()
|
|
31
|
+
|
|
32
|
+
export type { Infer } from '@vinejs/vine/types'
|
|
33
|
+
export { VineString, VineBoolean, VineEnum, VineNumber } from '@vinejs/vine'
|