vuelidify 1.0.1 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +159 -96
- package/dist/index.d.ts +172 -132
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
# Vuelidify
|
|
2
2
|
|
|
3
|
-
|
|
4
3
|
[Installation](#installation)
|
|
5
4
|
[Types](#types)
|
|
6
5
|
[Examples](#examples)
|
|
6
|
+
[Technical Details](#technical-details)
|
|
7
|
+
[Custom Validators](#custom-validators)
|
|
7
8
|
---
|
|
8
|
-
*
|
|
9
|
+
*Vuelidify is a Vue 3 model-based validation library that makes complex form validation easy.*
|
|
9
10
|
|
|
10
|
-
This library was inspired by Vuelidate
|
|
11
|
+
This library was inspired by Vuelidate and sought to solve some of its biggest problems. This library does NOT support Vue2, and does NOT support commonJS. Technology must move forward.
|
|
11
12
|
|
|
12
|
-
**✨
|
|
13
|
+
**✨ Powerful** because it handles complex validation scenarios easily and efficiently.
|
|
13
14
|
|
|
14
|
-
**🪶 Lightweight** because the .mjs is <9KB (uncompressed), and ~3KB gzipped.
|
|
15
|
+
**🪶 Lightweight** because the .mjs is <9KB (uncompressed), and ~3KB gzipped; no bloat or useless dependencies.
|
|
15
16
|
|
|
16
|
-
**📝 Model
|
|
17
|
+
**📝 Model-based** refers to validation being done in your script alongside your data instead of in your templates.
|
|
17
18
|
|
|
18
|
-
**💪 Strong types** makes
|
|
19
|
+
**💪 Strong types** makes setup intuitive for developers. No more, "wait, how do I do that again?"
|
|
19
20
|
|
|
20
|
-
Too many validation libraries for Vue lack good type support
|
|
21
|
+
Too many validation libraries for Vue lack good type support, which makes maintaining codebases harder over time. When your data models change, there's often no clear signal that your validation needs updated as well. Vuelidify was built to solve this problem.
|
|
21
22
|
|
|
22
23
|
---
|
|
23
24
|
|
|
@@ -37,7 +38,7 @@ pnpm add vuelidify
|
|
|
37
38
|
|
|
38
39
|
## Types
|
|
39
40
|
|
|
40
|
-
This was created for use in ```<script setup lang="ts"
|
|
41
|
+
This was created for use in ```<script setup lang="ts">```. You need TypeScript in order to get the full benefits of this library.
|
|
41
42
|
|
|
42
43
|
**useValidation()** is the starting point for validating your models.
|
|
43
44
|
|
|
@@ -52,62 +53,84 @@ This was created for use in ```<script setup lang="ts">```, meaning you need Typ
|
|
|
52
53
|
Here is a breakdown of the configuration object the composable expects.
|
|
53
54
|
```ts
|
|
54
55
|
{
|
|
55
|
-
|
|
56
|
+
model: T, // The ref, computed, or reactive object you want to validate.
|
|
56
57
|
validation: Validation<T>, // Describes how to validate your object.
|
|
57
|
-
args: A = undefined, // Can be anything
|
|
58
|
+
args: A = undefined, // Can be anything and will be passed into every validator.
|
|
58
59
|
delayReactiveValidation: boolean, // Should reactive validation be active immediately or only after calling validate()?
|
|
59
60
|
}
|
|
60
61
|
```
|
|
61
62
|
That's it, super simple!
|
|
62
63
|
|
|
63
|
-
Just kidding, ```validation: Validation<T>``` isn't the full picture. The type here is quite complicated, but easy to use. Here's what you need to know
|
|
64
|
+
Just kidding, ```validation: Validation<T>``` isn't the full picture. The type here is quite complicated, but easy to use. Here's what you need to know:
|
|
64
65
|
|
|
65
|
-
1. ```Validation<T>``` will copy the
|
|
66
|
-
2.
|
|
66
|
+
1. ```Validation<T>``` will copy the layout of T's properties. Nested objects will also be copied. This type is recursive!
|
|
67
|
+
2. Types which can be validated will have some unique properties available:
|
|
67
68
|
```ts
|
|
69
|
+
|
|
70
|
+
// foo is a string
|
|
71
|
+
foo: {
|
|
72
|
+
$reactive?: [],
|
|
73
|
+
$lazy?: []
|
|
74
|
+
}
|
|
75
|
+
// bar is an array
|
|
76
|
+
bar: {
|
|
77
|
+
$reactive?: [],
|
|
78
|
+
$lazy?: []
|
|
79
|
+
$each?: {},
|
|
80
|
+
}
|
|
81
|
+
// zaa is an object containing { foo: string, bar: array }
|
|
68
82
|
{
|
|
69
|
-
|
|
70
|
-
|
|
83
|
+
$reactive?: [],
|
|
84
|
+
$lazy?: [],
|
|
85
|
+
bar: {
|
|
71
86
|
$reactive?: [],
|
|
72
87
|
$lazy?: []
|
|
73
|
-
}
|
|
74
|
-
// bar is an array
|
|
75
|
-
bar: {
|
|
76
88
|
$each?: {},
|
|
89
|
+
},
|
|
90
|
+
foo: {
|
|
77
91
|
$reactive?: [],
|
|
78
92
|
$lazy?: []
|
|
79
93
|
}
|
|
80
94
|
}
|
|
95
|
+
|
|
81
96
|
```
|
|
82
|
-
3.
|
|
83
|
-
4. ```$reactive``` is an array of validators that should be performed on that property
|
|
84
|
-
5. ```$lazy``` is an array of validators that should be performed on that property whenever ```validate()``` is called.
|
|
97
|
+
3. ```$each``` is the same type as ```Validation<U>``` where ```U``` is the type of each object in the array you are validating.
|
|
98
|
+
4. ```$reactive``` is an array of validators that should be performed reactively on that property. [See technical details](#technical-details) for more information.
|
|
99
|
+
5. ```$lazy``` is an array of validators that should be performed on that property whenever ```validate()``` is called. [See technical details](#technical-details) for more information.
|
|
85
100
|
|
|
86
|
-
Here is the breakdown of the return
|
|
101
|
+
Here is the breakdown of the composable's return type
|
|
87
102
|
```ts
|
|
88
103
|
{
|
|
104
|
+
// true after validate() is invoked once
|
|
89
105
|
hasValidated: boolean,
|
|
90
|
-
//
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
isValid: boolean,
|
|
106
|
+
// invokes every validator defined in the validation rules.
|
|
107
|
+
// returns whether or not they all passed
|
|
108
|
+
validate: () => Promise<boolean>
|
|
94
109
|
isValidating: boolean,
|
|
95
|
-
// Access the results of validation.
|
|
96
|
-
//
|
|
110
|
+
// Access the results of validation.
|
|
111
|
+
// This type is explained below.
|
|
97
112
|
state: ValidationState<T>,
|
|
113
|
+
// true only if every validator passed
|
|
114
|
+
isValid: boolean,
|
|
115
|
+
// true if any validator failed
|
|
116
|
+
isErrored: boolean,
|
|
98
117
|
// Set the comparison object for determining dirty state.
|
|
99
|
-
// If your object must
|
|
118
|
+
// If your object must load in asynchronously,
|
|
100
119
|
// use this function to set the reference once it has loaded.
|
|
101
|
-
setReference: (reference: T) => void,
|
|
102
|
-
|
|
120
|
+
setReference: (reference: T) => void,
|
|
121
|
+
// True if your object has changed from the reference.
|
|
122
|
+
// Useful for enabling save buttons after changes have been made
|
|
123
|
+
isDirty: boolean,
|
|
103
124
|
}
|
|
104
125
|
```
|
|
105
|
-
|
|
126
|
+
Validation state also copies the layout of the model you provide. However, instead of providing validators, you now get access to `$state` and `$arrayState`. `$arrayState` is just an array of state objects created by validating an array object.
|
|
127
|
+
|
|
128
|
+
Here is the breakdown of `$state`
|
|
106
129
|
```ts
|
|
107
130
|
{
|
|
108
131
|
// The collected error messages returned from all the validators
|
|
109
132
|
errorMessages: string[],
|
|
110
|
-
// True if any validators
|
|
133
|
+
// True if any validators failed.
|
|
111
134
|
// Not equivalent to !isValid, because !isValid is true even
|
|
112
135
|
// when validation has not been ran yet.
|
|
113
136
|
// Can be false when there are lazy validators that still need executed.
|
|
@@ -116,27 +139,29 @@ Here is the breakdown of the validation state
|
|
|
116
139
|
isValid: boolean,
|
|
117
140
|
isValidating: boolean,
|
|
118
141
|
// A map for easily accessing named validation results.
|
|
119
|
-
// This is the one spot without type support
|
|
142
|
+
// This is the one spot without good type support.
|
|
120
143
|
results: { [key: string]: BaseValidationReturn<F> }
|
|
121
|
-
// A
|
|
144
|
+
// A collection of validator responses.
|
|
122
145
|
resultsArray: Array<BaseValidationReturn<F>>
|
|
123
146
|
}
|
|
124
147
|
```
|
|
125
|
-
|
|
148
|
+
Validators must return one of the following:
|
|
126
149
|
```ts
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
150
|
+
BaseValidationReturn<F> | Validator[] | undefined
|
|
151
|
+
```
|
|
152
|
+
Here is the breakdown of the `BaseValidationReturn<F>`
|
|
153
|
+
```ts
|
|
154
|
+
type BaseValidationReturn<F> = {
|
|
130
155
|
// Name the result of this validator. This will put the validation result
|
|
131
156
|
// into the results map in the validation state of this property.
|
|
132
157
|
// Make sure your names are unique between your validators.
|
|
133
158
|
name?: string,
|
|
134
|
-
// the unique identifier for this validation result. Assigned
|
|
159
|
+
// the unique identifier for this validation result. Assigned internally.
|
|
135
160
|
// you can use this ID to identify your DOM elements that display error messages.
|
|
136
161
|
id? string,
|
|
137
162
|
// required for determining whether or not this validator passed
|
|
138
163
|
isValid: boolean,
|
|
139
|
-
|
|
164
|
+
message?: string,
|
|
140
165
|
// Sometimes a true or false is not enough information for end users.
|
|
141
166
|
// Use this to return any object to give additional information about the validation.
|
|
142
167
|
// In order to access this custom data easily, make sure you give the result a name
|
|
@@ -145,27 +170,24 @@ Here is the breakdown of the return type from validators.
|
|
|
145
170
|
```
|
|
146
171
|
Here is the breakdown of the parameters that are passed into validators
|
|
147
172
|
```ts
|
|
148
|
-
{
|
|
173
|
+
type ValidatorParams<T,P,V,A> = {
|
|
149
174
|
// The value of the property being validated
|
|
150
175
|
value: T,
|
|
151
176
|
// The top-most ancestor being validated. The object that was passed to the composable.
|
|
152
177
|
parent: P,
|
|
153
178
|
// The args that were specified in the composable configuration.
|
|
154
|
-
// This type will only appear for you when args is NOT undefined.
|
|
155
179
|
args: V,
|
|
156
|
-
// Will only appear for properties nested in some array.
|
|
157
180
|
// The type will be an ordered array of strongly typed objects.
|
|
158
|
-
// Each
|
|
159
|
-
// Index 0 will
|
|
160
|
-
//
|
|
161
|
-
|
|
162
|
-
// rather than the top-most parent object which contains the array.
|
|
163
|
-
arrayParents: A
|
|
181
|
+
// Each index is an ancestor to what you're validating.
|
|
182
|
+
// Index 0 will appear when you're 1 array deep, and index 1 will appear 2 arrays deep, etc.
|
|
183
|
+
// Extremely useful for complex validation.
|
|
184
|
+
arrayAncestors: A
|
|
164
185
|
}
|
|
165
186
|
```
|
|
166
187
|
|
|
167
188
|
## Examples
|
|
168
|
-
|
|
189
|
+
### Validation Rules
|
|
190
|
+
#### Validating Primitives
|
|
169
191
|
```ts
|
|
170
192
|
<script setup lang="ts">
|
|
171
193
|
import { ref } from 'vue';
|
|
@@ -173,29 +195,42 @@ Here is the breakdown of the parameters that are passed into validators
|
|
|
173
195
|
|
|
174
196
|
const string = ref("");
|
|
175
197
|
const v$ = useValidation({
|
|
176
|
-
|
|
198
|
+
model: string,
|
|
177
199
|
validation: {
|
|
178
200
|
$reactive: [minLength(10)] // Put as many validators as you want here
|
|
179
201
|
}
|
|
180
202
|
});
|
|
181
203
|
</script>
|
|
182
204
|
```
|
|
183
|
-
#### Simple Objects
|
|
205
|
+
#### Validating Simple Objects
|
|
184
206
|
```ts
|
|
185
207
|
<script setup lang="ts">
|
|
186
|
-
import { ref } from 'vue';
|
|
208
|
+
import { ref, type Ref } from 'vue';
|
|
187
209
|
import { minLength, useValidation, minNumber } from "vuelidify";
|
|
188
210
|
|
|
189
|
-
|
|
211
|
+
// Note this format may not have correctly typed validation when using strict TypeScript.
|
|
212
|
+
// const obj = ref({
|
|
213
|
+
// foo: "string",
|
|
214
|
+
// bar: true,
|
|
215
|
+
// zaa: 1
|
|
216
|
+
// });
|
|
217
|
+
|
|
218
|
+
type SimpleObject = {
|
|
219
|
+
foo: name;
|
|
220
|
+
bar: boolean;
|
|
221
|
+
zaa: number;
|
|
222
|
+
}
|
|
223
|
+
const obj: Ref<SimpleObject> = ref({
|
|
190
224
|
foo: "string",
|
|
191
225
|
bar: true,
|
|
192
226
|
zaa: 1
|
|
193
|
-
})
|
|
227
|
+
})
|
|
228
|
+
|
|
194
229
|
const v$ = useValidation({
|
|
195
|
-
|
|
230
|
+
model: obj,
|
|
196
231
|
validation: {
|
|
197
232
|
foo: {
|
|
198
|
-
// Validate foo when v$.validate is
|
|
233
|
+
// Validate foo when v$.validate() is invoked.
|
|
199
234
|
$lazy: [minLength(10)]
|
|
200
235
|
},
|
|
201
236
|
bar: {
|
|
@@ -207,15 +242,15 @@ Here is the breakdown of the parameters that are passed into validators
|
|
|
207
242
|
}]
|
|
208
243
|
},
|
|
209
244
|
zaa: {
|
|
210
|
-
// Validate zaa reactively and when v$.validate is
|
|
211
|
-
// Notice how
|
|
245
|
+
// Validate zaa reactively and when v$.validate() is invoked.
|
|
246
|
+
// Notice how you can validate using other properties in the model.
|
|
212
247
|
$reactive: [minNumber(10)],
|
|
213
248
|
$lazy: [
|
|
214
249
|
(params) => {
|
|
215
|
-
const isBar = params.
|
|
250
|
+
const isBar = params.model.bar;
|
|
216
251
|
return {
|
|
217
252
|
isValid: isBar ? params.value > 100 : true,
|
|
218
|
-
|
|
253
|
+
message: "Must be greater than 100 when bar is true"
|
|
219
254
|
}
|
|
220
255
|
}
|
|
221
256
|
]
|
|
@@ -224,7 +259,7 @@ Here is the breakdown of the parameters that are passed into validators
|
|
|
224
259
|
});
|
|
225
260
|
</script>
|
|
226
261
|
```
|
|
227
|
-
#### Arrays
|
|
262
|
+
#### Validating Arrays
|
|
228
263
|
```ts
|
|
229
264
|
<script setup lang="ts">
|
|
230
265
|
import { ref } from 'vue';
|
|
@@ -235,9 +270,9 @@ Here is the breakdown of the parameters that are passed into validators
|
|
|
235
270
|
isActive: boolean;
|
|
236
271
|
}
|
|
237
272
|
|
|
238
|
-
const array
|
|
273
|
+
const array: Ref<FooBar[]> = ref([]);
|
|
239
274
|
const v$ = useValidation({
|
|
240
|
-
|
|
275
|
+
model: array,
|
|
241
276
|
validation: {
|
|
242
277
|
// Validate each object in the array.
|
|
243
278
|
$each: {
|
|
@@ -246,11 +281,12 @@ Here is the breakdown of the parameters that are passed into validators
|
|
|
246
281
|
$reactive: [
|
|
247
282
|
// Validate the length of the name only if the object's isActive property is true.
|
|
248
283
|
(params) => {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
//
|
|
284
|
+
// arrayAncestors[0] is an object with ancestor, index, and the array it is in.
|
|
285
|
+
if (params.arrayAncestors[0].ancestor.isActive === false) {
|
|
286
|
+
// Return undefined to ignore this validator
|
|
252
287
|
return;
|
|
253
288
|
}
|
|
289
|
+
// Return an array of validators which are immediately invoked. Useful for conditional validation.
|
|
254
290
|
return [minLength(10)]
|
|
255
291
|
}
|
|
256
292
|
]
|
|
@@ -260,26 +296,26 @@ Here is the breakdown of the parameters that are passed into validators
|
|
|
260
296
|
});
|
|
261
297
|
</script>
|
|
262
298
|
```
|
|
263
|
-
#### Complex Objects
|
|
264
|
-
Sometimes your objects will contain
|
|
299
|
+
#### Validating Complex Objects
|
|
300
|
+
Sometimes your objects will contain objects and arrays.
|
|
265
301
|
```ts
|
|
266
302
|
<script setup lang="ts">
|
|
267
303
|
import { ref } from 'vue';
|
|
268
304
|
import { minLength, minNumber, useValidation } from "vuelidify";
|
|
269
305
|
|
|
270
|
-
type
|
|
306
|
+
type Example = {
|
|
271
307
|
a: Person,
|
|
272
308
|
b: Person[]
|
|
273
309
|
}
|
|
274
|
-
|
|
310
|
+
|
|
275
311
|
type Person = {
|
|
276
312
|
name: string;
|
|
277
313
|
age: number;
|
|
278
314
|
}
|
|
279
315
|
|
|
280
|
-
const complexObj = ref
|
|
316
|
+
const complexObj: Ref<Example | undefined> = ref();
|
|
281
317
|
const v$ = useValidation({
|
|
282
|
-
|
|
318
|
+
model: complexObj,
|
|
283
319
|
validation: {
|
|
284
320
|
a: {
|
|
285
321
|
// Validate person a's age reactively
|
|
@@ -296,8 +332,9 @@ Sometimes your objects will contain other objects and arrays.
|
|
|
296
332
|
// Make sure each person in the array is younger than person a
|
|
297
333
|
(params) => {
|
|
298
334
|
return {
|
|
299
|
-
isValid: params.
|
|
300
|
-
|
|
335
|
+
isValid: params.model?.a.age != undefined
|
|
336
|
+
&& params.value < params.model.a.age,
|
|
337
|
+
message: "Must be younger than person a."
|
|
301
338
|
}
|
|
302
339
|
}
|
|
303
340
|
],
|
|
@@ -312,20 +349,34 @@ Sometimes your objects will contain other objects and arrays.
|
|
|
312
349
|
});
|
|
313
350
|
</script>
|
|
314
351
|
```
|
|
352
|
+
### Validation State
|
|
353
|
+
All of the validation types present their results similarly. Just access `state` to get started! As long as you have TypeScript enabled in your workspace, you should have no problem understanding its layout. Here's a short example on what you can do with the state object.
|
|
354
|
+
```ts
|
|
355
|
+
const v$ = useValidation({
|
|
356
|
+
// ... complex object validation provided earlier ...
|
|
357
|
+
})
|
|
358
|
+
// The error messages present on person a's age
|
|
359
|
+
const aAgeErrors: string[] | undefined = v$.state.a?.age?.$state?.errorMessages;
|
|
360
|
+
// An array of validation state objects on each person of property b.
|
|
361
|
+
const bErrors = v$.state.b?.$arrayState;
|
|
362
|
+
```
|
|
363
|
+
Note, properties may show up in the intellisense, but they are undefinable *on purpose*. If validation rules are not provided for a property, its state object will not exist.
|
|
315
364
|
|
|
316
365
|
## Technical Details
|
|
317
366
|
For those interested in the inner workings of the library without looking at the code:
|
|
318
367
|
|
|
319
|
-
- Reactive validation is performed by a deep watcher on the
|
|
320
|
-
- Lazy validation is only performed only when the validate() function is called. However, validate() will also invoke all reactive validators to guarantee all validation results are up-to-date with the model. Properties or the
|
|
321
|
-
- Async validators can be mixed with sync validators, so there is no way to distinguish them upon initialization. However, once they are invoked for the first time, it is possible to distinguish them. Optimizations can then be made on the sync and async validators to improve validation behavior and performance. Sync validators will be wrapped in a computed function which has the benefit of determining reactive dependencies and caching the result. This counteracts the downside of using a deep watcher discussed previously. Synchronous validators will not be needlessly reevaluated every time a character changes in an unrelated property because the computed determines it doesn't rely on it. Async validators will be optimized based on how long they take to return. If they return faster than 250ms, they will not be given any optimization; if they return less than 500ms they will be given a throttle of 250ms; if they return longer than that they will be given a buffer. Details of the throttles are below.
|
|
322
|
-
-
|
|
323
|
-
-
|
|
368
|
+
- Reactive validation is performed by a deep watcher on the provided model. This was done because of inter-property dependence. When a validator for one property relies on another property in the object, it needs to be reevaluated. This does come with the technical debt of running *every* reactive validator *every* time the model is changed. However, the problem is mediated by validator optimizations which is discussed later.
|
|
369
|
+
- Lazy validation is only performed only when the `validate()` function is called. However, `validate()` will also invoke all reactive validators to guarantee all validation results are up-to-date with the model. Properties or the model itself may be valid before ever calling `validate()` if there were no lazy validators provided, and all reactive validators were true (or again none specified).
|
|
370
|
+
- Async validators can be mixed with sync validators, so there is no way to distinguish them upon initialization. However, once they are invoked for the first time, it is possible to distinguish them. Optimizations can then be made on the sync and async validators to improve validation behavior and performance. Sync validators will be wrapped in a computed function which has the benefit of determining reactive dependencies and caching the result. This counteracts the downside of using a deep watcher discussed previously. Synchronous validators will not be needlessly reevaluated every time a character changes in an unrelated property because the computed determines it doesn't rely on it. Async validators will be optimized based on how long they take to return. If they return faster than 250ms, they will not be given any optimization; if they return in less than 500ms, they will be given a throttle of 250ms; if they return longer than that they will be given a buffer. Details of the throttles are below.
|
|
371
|
+
- `throttleAsync` is an async throttler that preserves your function’s signature and always returns a Promise of its result. It executes immediately when idle; if called during the throttle interval, it buffers only the latest call and runs it once the interval elapses—earlier buffered calls resolve to `undefined`. This lets you schedule non-blocking, promise-based throttling without overlapping executions and guarantees you always call your function with the latest arguments. Note, you are unable to distinguish if your function returned `undefined` or `throttleAsync` returned `undefined`.
|
|
372
|
+
- `bufferAsync` is another custom function exported by this library that provides a more aggressive throttling behavior than `throttleAsync`. `bufferAsync` preserves the original function’s signature and returns a promise resolving to its result. It only remembers the latest invocation while the provided function is still executing. Once the current execution completes, only the remembered call will be invoked — all intermediate calls will return `undefined`. This mechanism prevents overlapping executions and reduces redundant work, making it ideal for expensive async operations where only the most recent intent should be executed. Note, you are unable to distinguish if your function returned `undefined` or `bufferAsync` returned `undefined`.
|
|
373
|
+
- Returning arrays of validators from within other validators is powerful but complex. Initially, we aimed to optimize these nested validators, but their dynamic nature--varying instances, order, and presence between iterations--made this unreliable. Since their results merge with all other validators, Vuelidify tracks and removes outdated results when the "parent" validator is invoked again and the same results are not returned.
|
|
374
|
+
- This library uses `unknown` instead of `any` to align with Deno and strict TypeScript standards. While `Args` and `Ancestors` are logically `undefined` by default, using `undefined` as a type causes issues—`unknown` can't be assigned to `undefined`. This distinction is why some of Vuelidify’s types may seem unusual, especially when creating generic validators meant to work universally. Additionally, the default type of `Return` is `any` because it truly *can be* anything. If it was unknown, you would be unable to access it for anything. `Return` is the only part of Vuelidify that is not strongly-typed. Be sure to cast it to what you expect before using it.
|
|
324
375
|
|
|
325
|
-
#
|
|
326
|
-
There aren't many validators provided by this library on purpose. Mostly because I
|
|
376
|
+
# Custom Validators
|
|
377
|
+
There aren't many validators provided by this library on purpose. Mostly because I would rather rely on feedback for useful validators. Feel free to give me feedback on the github repo.
|
|
327
378
|
|
|
328
|
-
I highly encourage you to understand the types enough to create your own validators. It isn't too difficult, and you should be able to base
|
|
379
|
+
I highly encourage you to understand the types enough to create your own validators. It isn't too difficult, and you should be able to base your validator off of existing ones.
|
|
329
380
|
|
|
330
381
|
Here is a breakdown of one of the validators exported by this library (expanded to make comments more readable):
|
|
331
382
|
|
|
@@ -333,16 +384,13 @@ Here is a breakdown of one of the validators exported by this library (expanded
|
|
|
333
384
|
// always provide a header comment to explain what the validator does!
|
|
334
385
|
/**
|
|
335
386
|
* Checks if the string value is a valid looking email using RegEx.
|
|
336
|
-
*
|
|
337
|
-
* The RegEx was taken from https://stackoverflow.com/questions/46155/how-can-i-validate-an-email-address-in-javascript, and may be updated in the future.
|
|
338
|
-
* @returns Synchronous validator
|
|
339
387
|
*/
|
|
340
388
|
export function isEmailSync<
|
|
341
389
|
// The type of the property you want to support validation for.
|
|
342
390
|
// adding | undefined | null is good practice for writing more robust code
|
|
343
|
-
// Furthermore, if you just did string here, it wouldn't work with string
|
|
344
|
-
T extends string | undefined | null,
|
|
345
|
-
// The type for the
|
|
391
|
+
// Furthermore, if you just did string here, it wouldn't work with string | undefined
|
|
392
|
+
T extends string | undefined | null,
|
|
393
|
+
// The type for the model parameter.
|
|
346
394
|
// Generally you don't put constraints on this.
|
|
347
395
|
P,
|
|
348
396
|
// The type for the args
|
|
@@ -355,8 +403,8 @@ export function isEmailSync<
|
|
|
355
403
|
// But you have to accept this generic in order to pass the type forward to not mess up outside types.
|
|
356
404
|
A
|
|
357
405
|
>(
|
|
358
|
-
// Specify any parameters you need here.
|
|
359
|
-
): SyncValidator<T, P, V, R, A> //
|
|
406
|
+
// Specify any parameters you need here. This is a function that returns a validator.
|
|
407
|
+
): SyncValidator<T, P, V, R, A> // Explicitly type the validator you'll be returning
|
|
360
408
|
{
|
|
361
409
|
// Return a validator function
|
|
362
410
|
return (
|
|
@@ -368,11 +416,26 @@ export function isEmailSync<
|
|
|
368
416
|
// In this case, we're checking the value of the property against an email regex.
|
|
369
417
|
return {
|
|
370
418
|
isValid: params.value ? RegExp(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/).test(params.value) : false,
|
|
371
|
-
|
|
419
|
+
message: "Invalid email format"
|
|
372
420
|
}
|
|
373
421
|
};
|
|
374
422
|
}
|
|
375
423
|
```
|
|
376
424
|
|
|
377
|
-
|
|
378
|
-
|
|
425
|
+
Because every generic has a default value in `SyncValidator`, we can greatly simplify this validator definition to only what is required for constraints:
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
/**
|
|
429
|
+
* Validates a string is a valid looking email using RegEx.
|
|
430
|
+
*/
|
|
431
|
+
export function isEmailSync<T extends string | undefined | null>(): SyncValidator<T> {
|
|
432
|
+
return (params: ValidatorParams<T>) => ({
|
|
433
|
+
isValid: params.value ? RegExp(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/).test(params.value) : false,
|
|
434
|
+
message: "Invalid email format"
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
This validator is effectively: `SyncValidator<string | undefined | null, unknown, unknown, unknown, unknown>`
|
|
440
|
+
|
|
441
|
+
Feel free to post issues you may have with the package on the git repo! Happy validating!
|
package/dist/index.d.ts
CHANGED
|
@@ -1,144 +1,162 @@
|
|
|
1
|
-
import { Ref } from 'vue';
|
|
1
|
+
import { Ref, Reactive, ComputedRef } from 'vue';
|
|
2
2
|
|
|
3
3
|
/** Shorthand union of the primitive types */
|
|
4
4
|
type Primitive = string | number | boolean;
|
|
5
|
-
/**
|
|
6
|
-
type ValidationState<T,
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
/** Defines the layout of validation results. Copies the format of the object being validated. */
|
|
6
|
+
type ValidationState<T, Return = any> = T extends Array<infer U> ? ArrayValidationState<U, Return> : T extends IndexableRecord ? RecursiveValidationState<T, Return> : T extends Primitive ? PrimitiveValidationState<Return> : never;
|
|
7
|
+
/** Determines the validation state available to objects. */
|
|
8
|
+
type RecursiveValidationState<T extends IndexableRecord, Return = any> = BaseValidationState<Return> & {
|
|
9
|
+
[key in keyof T]?: ValidationState<T[key], Return>;
|
|
9
10
|
};
|
|
10
|
-
/**
|
|
11
|
-
type
|
|
12
|
-
/** True if all the validators defined for this property have passed. False otherwise. */
|
|
13
|
-
isValid: boolean;
|
|
14
|
-
isValidating: boolean;
|
|
11
|
+
/** Describes the Vuelidify validation state. */
|
|
12
|
+
type BaseValidationState<Return = any> = {
|
|
15
13
|
/**
|
|
16
|
-
*
|
|
14
|
+
* The validation state for this object.
|
|
17
15
|
*
|
|
18
|
-
*
|
|
19
|
-
* This will only ever be true when validation results have returned.
|
|
16
|
+
* Is named this way to avoid naming conflicts with existing object properties.
|
|
20
17
|
*/
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
$state?: {
|
|
19
|
+
/** True if all the validators defined have passed. False otherwise. */
|
|
20
|
+
isValid: boolean;
|
|
21
|
+
isValidating: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* True if there are any results that failed validation.
|
|
24
|
+
*
|
|
25
|
+
* Not always equal to `!isValid` because `!isValid` can be true when validators haven't been invoked.
|
|
26
|
+
*/
|
|
27
|
+
isErrored: boolean;
|
|
28
|
+
/** Collection of the error messages from validators */
|
|
29
|
+
errorMessages: string[];
|
|
30
|
+
/**
|
|
31
|
+
* An indexable object of the validators that returned with names.
|
|
32
|
+
*
|
|
33
|
+
* Useful for validators which return data you want to use.
|
|
34
|
+
*/
|
|
35
|
+
results: {
|
|
36
|
+
[key: string]: BaseValidationReturn<Return> | undefined;
|
|
37
|
+
};
|
|
38
|
+
resultsArray: BaseValidationReturn<Return>[];
|
|
26
39
|
};
|
|
27
|
-
resultsArray: BaseValidationReturn<FValidationReturn>[];
|
|
28
40
|
};
|
|
29
|
-
|
|
41
|
+
/** Defines the validation state for a primitive value. */
|
|
42
|
+
type PrimitiveValidationState<Return = any> = BaseValidationState<Return>;
|
|
43
|
+
/** Defines the validation state for an array. */
|
|
44
|
+
type ArrayValidationState<U, Return = any> = BaseValidationState<Return> & {
|
|
30
45
|
/**
|
|
31
46
|
* Contains the validation state for each element in the array.
|
|
32
47
|
*
|
|
33
48
|
* Maps 1:1 to the array which was validated.
|
|
34
49
|
*/
|
|
35
|
-
arrayState: ValidationState<U,
|
|
36
|
-
};
|
|
37
|
-
type RecursiveValidation<T extends IndexableObject, KParent, ValidationArgs, FValidationReturn, ArrParent, NLevel extends number> = {
|
|
38
|
-
[key in keyof Partial<T>]: Validation<T[key], ValidationArgs, FValidationReturn, KParent, ArrParent, NLevel>;
|
|
50
|
+
$arrayState: ValidationState<U, Return>[];
|
|
39
51
|
};
|
|
40
|
-
|
|
41
|
-
type
|
|
42
|
-
[key
|
|
52
|
+
/** Defines validation rules for records. */
|
|
53
|
+
type RecursiveValidation<T, KModel, ValidationArgs, Return, Ancestors, NLevel extends number> = ObjectValidationTypes<T, KModel, ValidationArgs, Return, Ancestors> & {
|
|
54
|
+
[key in keyof Partial<T>]: Validation<T[key], ValidationArgs, Return, KModel, Ancestors, NLevel>;
|
|
43
55
|
};
|
|
44
|
-
/**
|
|
45
|
-
type
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
/** Defines the validation rules for records */
|
|
57
|
+
type ObjectValidationTypes<T = unknown, KModel = unknown, Args = unknown, Return = any, Ancestors = unknown> = BaseValidation<T, KModel, Args, Return, Ancestors>;
|
|
58
|
+
type IndexableRecord = Record<string, unknown>;
|
|
59
|
+
/** Defines the validation rules for all supported objects. */
|
|
60
|
+
type BaseValidation<T = unknown, KModel = unknown, Args = unknown, Return = any, Ancestors = unknown> = {
|
|
61
|
+
/** Validators invoked whenever the model is changed. */
|
|
62
|
+
$reactive?: Validator<T, KModel, Args, Return, Ancestors>[];
|
|
63
|
+
/** Validators invoked only after {@link UseValidationReturn.validate | validate()} is invoked. */
|
|
64
|
+
$lazy?: Validator<T, KModel, Args, Return, Ancestors>[];
|
|
50
65
|
};
|
|
51
|
-
|
|
66
|
+
/** Defines the validation rules for an array. */
|
|
67
|
+
type ArrayValidation<U, T = U[], KModel = unknown, Args = unknown, Return = any, Ancestors = unknown, NLevel extends number = number> = BaseValidation<T, KModel, Args, Return, Ancestors> & {
|
|
52
68
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* Defines the validation that should be performed on each element of the array.
|
|
69
|
+
* Defines the validation rules for each element of an array.
|
|
56
70
|
*
|
|
57
|
-
*
|
|
71
|
+
* Works best with arrays of objects; see documentation for details.
|
|
58
72
|
*/
|
|
59
|
-
$each?: Validation<U, Args,
|
|
60
|
-
[key in NLevel]: U
|
|
61
|
-
} :
|
|
62
|
-
[key in NLevel]: U
|
|
73
|
+
$each?: Validation<U, Args, Return, KModel, Ancestors extends undefined ? {
|
|
74
|
+
[key in NLevel]: ArrayAncestor<U, T>;
|
|
75
|
+
} : Ancestors & {
|
|
76
|
+
[key in NLevel]: ArrayAncestor<U, T>;
|
|
63
77
|
}, Increment<NLevel>>;
|
|
64
|
-
/** The validators for the array that are invoked whenever the form is changed. */
|
|
65
|
-
$reactive?: Validator<T, KParent, Args, FValidationReturn, ArrParent>[];
|
|
66
|
-
/** The validators for the array that are invoked only when {@link validate()} is called. */
|
|
67
|
-
$lazy?: Validator<T, KParent, Args, FValidationReturn, ArrParent>[];
|
|
68
78
|
};
|
|
79
|
+
type ArrayAncestor<U = unknown, // the type of T's elements
|
|
80
|
+
T = unknown> = Readonly<{
|
|
81
|
+
/** The index this ancestor is at in `array` */
|
|
82
|
+
index: number;
|
|
83
|
+
/** The array which contains the ancestor. Useful for referencing this ancestor's siblings. */
|
|
84
|
+
array: T;
|
|
85
|
+
/** An object which contains the value you are validating. */
|
|
86
|
+
ancestor: U;
|
|
87
|
+
}>;
|
|
69
88
|
/** A synchronous or asynchronous validator. */
|
|
70
|
-
type Validator<T,
|
|
71
|
-
|
|
72
|
-
type BaseValidator<T, Parent, Args, Return,
|
|
73
|
-
|
|
74
|
-
type
|
|
75
|
-
|
|
89
|
+
type Validator<T = unknown, KModel = unknown, Args = unknown, Return = any, Ancestors = unknown> = SyncValidator<T, KModel, Args, Return, Ancestors> | AsyncValidator<T, KModel, Args, Return, Ancestors>;
|
|
90
|
+
/** Defines a validator function */
|
|
91
|
+
type BaseValidator<T, Parent, Args, Return, Ancestors> = (input: ValidatorParams<T, Parent, Args, Ancestors>) => Return;
|
|
92
|
+
/** Defines a validator which always runs synchronously */
|
|
93
|
+
type SyncValidator<T = unknown, Parent = unknown, Args = unknown, Return = any, Ancestors = unknown> = BaseValidator<T, Parent, Args, BaseValidationReturn<Return> | Array<Validator<T, Parent, Args, Return, Ancestors>> | undefined, Ancestors>;
|
|
94
|
+
/** Defines a validator which returns a promise */
|
|
95
|
+
type AsyncValidator<T = unknown, Parent = unknown, Args = unknown, Return = any, Ancestors = unknown> = BaseValidator<T, Parent, Args, Promise<BaseValidationReturn<Return> | Array<Validator<T, Parent, Args, Return, Ancestors>> | undefined>, Ancestors>;
|
|
96
|
+
/** Defines the return value of validators */
|
|
97
|
+
type BaseValidationReturn<F = unknown> = {
|
|
76
98
|
/**
|
|
77
|
-
*
|
|
78
|
-
* The result will
|
|
79
|
-
* so you can easily access the result.
|
|
99
|
+
* The validation result's name.
|
|
100
|
+
* The result will be added to a record using the name as the key
|
|
80
101
|
*
|
|
81
|
-
* Note, the
|
|
102
|
+
* Note, the entry will not exist until this validator has been ran once, so account for undefined.
|
|
82
103
|
*/
|
|
83
104
|
name?: string;
|
|
84
105
|
/**
|
|
85
106
|
* The unique identifier for this validation result.
|
|
86
107
|
*
|
|
87
|
-
* Assigned and used internally, but can be used as
|
|
108
|
+
* Assigned and used internally, but can be used as an element's ID or key.
|
|
88
109
|
*/
|
|
89
110
|
id?: string;
|
|
90
|
-
/**
|
|
111
|
+
/** Determines if this validator passed. */
|
|
91
112
|
isValid: boolean;
|
|
92
|
-
/** The message
|
|
93
|
-
|
|
113
|
+
/** The error message for this validator. */
|
|
114
|
+
message?: string;
|
|
94
115
|
/**
|
|
95
|
-
* Return
|
|
116
|
+
* Return any extra data you want from this validator.
|
|
96
117
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* i.e. password strength, severity levels, functions, etc.
|
|
118
|
+
* Meant for more sophisticated validation which returns data instead of just an error message (e.g. password strength, severity levels, or arrays).
|
|
100
119
|
*/
|
|
101
120
|
custom?: F;
|
|
102
121
|
};
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
type
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
validation: Validation<T, Args, FValidationReturn, T>;
|
|
122
|
+
/**
|
|
123
|
+
* The entry point for validation with Vuelidify.
|
|
124
|
+
*
|
|
125
|
+
* Defines the validation rules for all the supported object types.
|
|
126
|
+
*/
|
|
127
|
+
type Validation<T, Args = unknown, Return = any, KModel = T, Ancestors = unknown, NLevel extends number = 0> = [
|
|
128
|
+
NonNullable<T>
|
|
129
|
+
] extends [Array<infer U>] ? ArrayValidation<U, T, KModel, Args, Return, Ancestors, NLevel> : [NonNullable<T>] extends [IndexableRecord] ? RecursiveValidation<T, KModel, Args, Return, Ancestors, NLevel> : [NonNullable<T>] extends [Primitive] ? BaseValidation<T, KModel, Args, Return, Ancestors> : never;
|
|
130
|
+
/** Defines the configuration for the {@link useValidation | useValidation() } composable */
|
|
131
|
+
type ValidationConfig<T = unknown, Args = unknown, Return = any> = {
|
|
132
|
+
/** The object to validate */
|
|
133
|
+
model: Ref<T>;
|
|
134
|
+
/** Configures the validation on the model. */
|
|
135
|
+
validation: Validation<T, Args, Return, T>;
|
|
118
136
|
/**
|
|
119
|
-
*
|
|
137
|
+
* Controls when reactive validation is triggered.
|
|
120
138
|
*
|
|
121
|
-
*
|
|
139
|
+
* - `false`: Reactive validation is always active.
|
|
140
|
+
* - `true`: Reactive validation starts after the first invocation of {@link validate()}.
|
|
122
141
|
*
|
|
123
|
-
* Defaults to true
|
|
142
|
+
* Defaults to `true`.
|
|
124
143
|
*/
|
|
125
144
|
delayReactiveValidation?: boolean;
|
|
126
145
|
/**
|
|
127
|
-
* Provide
|
|
128
|
-
*
|
|
146
|
+
* Provide anything you want your validators to have access to.
|
|
147
|
+
*
|
|
148
|
+
* Particularly useful when defining validation in separate files and you want to reference local variables.
|
|
129
149
|
*/
|
|
130
150
|
args?: Args;
|
|
131
151
|
};
|
|
132
|
-
/**
|
|
133
|
-
type ValidatorParams<T,
|
|
152
|
+
/** Defines the parameters passed into every validator */
|
|
153
|
+
type ValidatorParams<T = unknown, KModel = unknown, Args = unknown, Ancestors = unknown> = {
|
|
134
154
|
/** The current value of the property */
|
|
135
155
|
value: T;
|
|
136
156
|
/** The entire object that was passed into the useValidation() composable to be validated. */
|
|
137
|
-
|
|
138
|
-
} & (Args extends undefined ? unknown : {
|
|
157
|
+
model: KModel;
|
|
139
158
|
/** The args passed in to the useValidation() composable configuration. */
|
|
140
159
|
args: Args;
|
|
141
|
-
}) & (ArrParent extends undefined ? unknown : {
|
|
142
160
|
/**
|
|
143
161
|
* An ordered list of objects that were traversed through while navigating to this validator.
|
|
144
162
|
*
|
|
@@ -146,9 +164,9 @@ type ValidatorParams<T, KParent, Args, ArrParent> = {
|
|
|
146
164
|
*
|
|
147
165
|
* Useful for inter-property dependence when validating arrays of complex objects.
|
|
148
166
|
*/
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
/**
|
|
167
|
+
arrayAncestors: Ancestors;
|
|
168
|
+
};
|
|
169
|
+
/** Increments a provided integer. Only works for 0 through 19, inclusive. */
|
|
152
170
|
type Increment<N extends number> = [
|
|
153
171
|
1,
|
|
154
172
|
2,
|
|
@@ -174,10 +192,18 @@ type Increment<N extends number> = [
|
|
|
174
192
|
][N];
|
|
175
193
|
|
|
176
194
|
/**
|
|
177
|
-
* Returns a function that will
|
|
178
|
-
* with the
|
|
195
|
+
* Returns a function that will execute the provided function
|
|
196
|
+
* with the latest params only if a previously created promise does not exist.
|
|
197
|
+
* ```ts
|
|
198
|
+
* async function test(): Promise<boolean> {}
|
|
199
|
+
* // Call this constant instead of the function to get the buffer benefits
|
|
200
|
+
* const bufferedTest = bufferAsync<
|
|
201
|
+
* typeof test, // this type makes the return the same signature as test()
|
|
202
|
+
* Awaited<ReturnType<typeof test>> // this type makes the returned function have the same return type
|
|
203
|
+
* >(test);
|
|
204
|
+
* ```
|
|
179
205
|
*/
|
|
180
|
-
declare function bufferAsync<F extends (...args: any) => any, K>(func: (...params: Parameters<F>) => Promise<K>): (...params: Parameters<typeof func>) => Promise<K | undefined>;
|
|
206
|
+
declare function bufferAsync<F extends (...args: any[]) => any, K>(func: (...params: Parameters<F>) => K | Promise<K>): (...params: Parameters<typeof func>) => Promise<K | undefined>;
|
|
181
207
|
/**
|
|
182
208
|
* Guarantees delay between invocations of the given function.
|
|
183
209
|
*
|
|
@@ -186,66 +212,80 @@ declare function bufferAsync<F extends (...args: any) => any, K>(func: (...param
|
|
|
186
212
|
* Subsequent invocations during the cool down return a promise to invoke the function after the remaining delay has passed.
|
|
187
213
|
*
|
|
188
214
|
* Once the interval has passed, all queued promises are executed, but only the latest promise will execute the function. The others will return undefined.
|
|
215
|
+
* ```ts
|
|
216
|
+
* async function test(): Promise<boolean> {}
|
|
217
|
+
* // Call this constant instead of the function to get the throttle benefits
|
|
218
|
+
* const throttledTest = throttleQueueAsync<
|
|
219
|
+
* typeof test, // this type makes the return the same signature as test()
|
|
220
|
+
* Awaited<ReturnType<typeof test>> // this type makes the returned function have the same return type
|
|
221
|
+
* >(test);
|
|
222
|
+
* ```
|
|
189
223
|
* @param func the function to throttle
|
|
190
224
|
* @param delay milliseconds required between invocations of the function.
|
|
191
225
|
*/
|
|
192
|
-
declare function throttleQueueAsync<F extends (...args: any) => any, K>(func: (...params: Parameters<F>) => K | Promise<K>, delay: number): (...params: Parameters<typeof func>) => Promise<K | undefined>;
|
|
226
|
+
declare function throttleQueueAsync<F extends (...args: any[]) => any, K>(func: (...params: Parameters<F>) => K | Promise<K>, delay: number): (...params: Parameters<typeof func>) => Promise<K | undefined>;
|
|
193
227
|
|
|
194
228
|
/**
|
|
195
|
-
*
|
|
196
|
-
* @param value
|
|
197
|
-
* @returns Synchronous validator
|
|
229
|
+
* Validates the object is not loosely undefined.
|
|
198
230
|
*/
|
|
199
|
-
declare function required
|
|
231
|
+
declare function required(): SyncValidator;
|
|
200
232
|
/**
|
|
201
|
-
*
|
|
233
|
+
* Validates a string or number has a length >= to the provided length. Undefined and null are 0 length.
|
|
202
234
|
* @param minLength
|
|
203
|
-
* @returns Synchronous validator
|
|
204
235
|
*/
|
|
205
|
-
declare function minLength<T extends string | undefined | null
|
|
236
|
+
declare function minLength<T extends string | number | undefined | null>(minLength: number): SyncValidator<T>;
|
|
206
237
|
/**
|
|
207
|
-
*
|
|
208
|
-
* @param maxLength
|
|
209
|
-
* @return Synchronous validator
|
|
238
|
+
* Validates a string or number's length. Undefined and null are 0 length.
|
|
239
|
+
* @param maxLength the maximum length of the string or number
|
|
210
240
|
*/
|
|
211
|
-
declare function maxLength<T extends string | number | undefined | null
|
|
241
|
+
declare function maxLength<T extends string | number | undefined | null>(maxLength: number): SyncValidator<T>;
|
|
212
242
|
/**
|
|
213
|
-
*
|
|
214
|
-
* @param minNumber
|
|
215
|
-
* @returns Synchronous validator
|
|
243
|
+
* Validates a number is defined and is at least some value.
|
|
244
|
+
* @param minNumber the minimum number the value can be
|
|
216
245
|
*/
|
|
217
|
-
declare function minNumber<T extends number | undefined | null
|
|
246
|
+
declare function minNumber<T extends number | undefined | null>(minNumber: number): SyncValidator<T>;
|
|
218
247
|
/**
|
|
219
|
-
*
|
|
220
|
-
* @param maxNumber
|
|
221
|
-
* @returns Synchronous validator
|
|
248
|
+
* Validates a number is defined and is at most some value.
|
|
249
|
+
* @param maxNumber the maximum number the value can be
|
|
222
250
|
*/
|
|
223
|
-
declare function maxNumber<T extends number | undefined | null
|
|
251
|
+
declare function maxNumber<T extends number | undefined | null>(maxNumber: number): SyncValidator<T>;
|
|
224
252
|
/**
|
|
225
|
-
*
|
|
253
|
+
* Validate the provided predicate function.
|
|
254
|
+
* @param fn predicate that returns true if the value is valid.
|
|
255
|
+
* @param errorMessage the message to display when the values are not equal.
|
|
226
256
|
*/
|
|
227
|
-
declare function
|
|
257
|
+
declare function must<T, K, V, R, A>(fn: (params: ValidatorParams<T, K, V, A>) => boolean, errorMessage: string): SyncValidator<T, K, V, R, A>;
|
|
228
258
|
/**
|
|
229
|
-
*
|
|
259
|
+
* Validates a string is a valid looking email using RegEx.
|
|
230
260
|
*
|
|
231
261
|
* The RegEx was taken from https://stackoverflow.com/questions/46155/how-can-i-validate-an-email-address-in-javascript, and may be updated in the future.
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
declare function isEmailSync<T extends string | undefined | null, P, V, R, A>(): SyncValidator<T, P, V, R, A>;
|
|
262
|
+
s */
|
|
263
|
+
declare function isEmailSync<T extends string | undefined | null>(): SyncValidator<T>;
|
|
235
264
|
|
|
265
|
+
type UseValidationReturn<T = unknown, Return = any> = {
|
|
266
|
+
hasValidated: Ref<boolean>;
|
|
267
|
+
validate: () => Promise<boolean>;
|
|
268
|
+
isValidating: ComputedRef<boolean>;
|
|
269
|
+
/** Stores the results of validation */
|
|
270
|
+
state: ComputedRef<ValidationState<T, Return>>;
|
|
271
|
+
/** True only if all validators passed. */
|
|
272
|
+
isValid: ComputedRef<boolean>;
|
|
273
|
+
/** True if any of the validators failed. */
|
|
274
|
+
isErrored: ComputedRef<boolean>;
|
|
275
|
+
/** Sets the internal reference object for determining {@link isDirty} */
|
|
276
|
+
setReference: (reference: T) => void;
|
|
277
|
+
/**
|
|
278
|
+
* Reactively determines if the object being validated has changed from the reference state.
|
|
279
|
+
*
|
|
280
|
+
* The reference state can be changed using {@link setReference()}.
|
|
281
|
+
*/
|
|
282
|
+
isDirty: ComputedRef<boolean>;
|
|
283
|
+
};
|
|
236
284
|
/**
|
|
237
|
-
*
|
|
285
|
+
* The starting point for validation with Vuelidify.
|
|
238
286
|
*
|
|
239
287
|
* @author Daniel Walbolt
|
|
240
288
|
*/
|
|
241
|
-
declare function useValidation<T, Args =
|
|
242
|
-
hasValidated: boolean;
|
|
243
|
-
validate: () => Promise<boolean>;
|
|
244
|
-
isValidating: boolean;
|
|
245
|
-
propertyState: ValidationState<T, FValidationReturn>;
|
|
246
|
-
isValid: boolean;
|
|
247
|
-
setReference: (reference: T) => void;
|
|
248
|
-
isDirty: boolean;
|
|
249
|
-
};
|
|
289
|
+
declare function useValidation<T, Args = unknown, Return = any>(validationConfig: ValidationConfig<T, Args, Return>): Reactive<UseValidationReturn<T, Return>>;
|
|
250
290
|
|
|
251
|
-
export {
|
|
291
|
+
export { ArrayAncestor, ArrayValidation, ArrayValidationState, AsyncValidator, BaseValidation, BaseValidationReturn, BaseValidationState, BaseValidator, ObjectValidationTypes, Primitive, PrimitiveValidationState, RecursiveValidation, RecursiveValidationState, SyncValidator, Validation, ValidationConfig, ValidationState, Validator, ValidatorParams, bufferAsync, isEmailSync, maxLength, maxNumber, minLength, minNumber, must, required, throttleQueueAsync, useValidation };
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
function x(e){let t=0,i;return(...r)=>{let o=++t;return i=(i==null?void 0:i.then(()=>{if(t===o)return i=new Promise(n=>n(e(...r))).then(n=>(i=void 0,n)),i}))??new Promise(n=>n(e(...r))).then(n=>(i=void 0,n)),i}}function S(e,t){let i=0,r;return(...o)=>new Promise(n=>{let l=++i,f=new Date().getTime();r??(r=f-t);let d=f-r-t;d<0?new Promise(u=>setTimeout(u,-1*d)).then(()=>{r=new Date().getTime(),l===i&&n(e(...o)),n(void 0)}):(r=f,n(e(...o)))})}function I(e,t=i=>i){return e.reduce((i,r)=>{if(r!==void 0){let o=t(r);o!==void 0&&i.push(o)}return i},[])}function X(){return e=>({isValid:e.value!=null,message:"This field is required"})}function Y(e){return t=>{let i=String(t.value??"");return{isValid:i.length>=e,message:`Too short (${i.length} / ${e})`}}}function _(e){return t=>{let i=String(t.value??"");return{isValid:i.length<=e,message:`Too long (${i.length} / ${e})`}}}function ee(e){return t=>({isValid:t.value!=null&&t.value>=e,message:`The minimum value is ${e}`})}function ae(e){return t=>({isValid:t.value!=null&&t.value<=e,message:`The maximum value is ${e}`})}function te(e,t){return i=>({isValid:e(i),message:t})}function ie(){return e=>({isValid:e.value?RegExp(/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/).test(e.value):!1,message:"Invalid email format"})}import{computed as b,reactive as q,ref as N,watch as Z}from"vue";import{computed as U,toValue as G}from"vue";import{computed as R,reactive as O,ref as P,toValue as M}from"vue";function L(){return`${Date.now()}-${Math.floor(Math.random()*1e3)}`}function C(e,t){return D(e,t)}function A(e,t,i){let r=[],o=()=>`${t?"reactive":"lazy"}-${L()}`;i!=null&&(o=n=>`${i}-${n}`);for(let[n,l]of e.entries())r.push({validatorId:o(n),validator:l,optimized:!1,isReactive:t,previouslyReturnedValidators:!1,previouslySpawnedValidators:{},spawnedValidators:{}});return r}function K(e,t,i=[]){var u;let r=O({$state:{isValid:R(()=>{let s=d.isLazyValid.value??!1,c=d.isReactiveValid.value??!1;return s&&c}),isValidating:R(()=>d.isValidatingReactive.value||d.isValidatingLazy.value),isErrored:R(()=>{var s;return((s=r.$state)==null?void 0:s.resultsArray.some(c=>c.isValid===!1))??!1}),errorMessages:R(()=>{var s;return I(((s=r.$state)==null?void 0:s.resultsArray)??[],c=>c.isValid?void 0:c.message)}),results:R(()=>d.namedValidationResults.value),resultsArray:R(()=>d.validationResults.value)},$arrayState:R(()=>{if(Array.isArray(e.value)===!1||d.elementValidation===void 0)return[];let s=e.value,c=d.elementValidation,a=d.arrayConfigMap,v=[],m={},y,V=[];for(let p=0;p<s.length;p++){let T=s[p]!==void 0&&typeof s[p]=="object";if(T?(s[p].$ffId===void 0&&Object.defineProperty(s[p],"$ffId",{value:`${d.id}-${d.elementId++}`,writable:!1,configurable:!1,enumerable:!1}),y=s[p].$ffId):s[p]!==void 0&&(y=p),V.push(y),a[y]){v.push(a[y].validationState),m[y]=a[y];continue}let h=R(()=>s[p]),z=[...d.arrayAncestors];T&&z.push({ancestor:h,array:s,index:p});let k=D(h,c,z);a[y]={validationConfigs:k.validationConfigs,validationState:k.state},v.push(a[y].validationState),m[y]=a[y]}return d.arrayConfigMap=m,v})}),o=!0,n=!0,l=[],f=[];t.$reactive&&t.$reactive.length>0&&(n=!1,l=A(t.$reactive,!0)),t.$lazy&&((u=t.$lazy)==null?void 0:u.length)>0&&(o=!1,f=A(t.$lazy,!1));let d={id:L(),reactiveIterationId:0,lazyIterationId:0,isReactiveValid:P(n),isValidatingReactive:P(!1),reactiveProcessedValidators:l,isLazyValid:P(o),isValidatingLazy:P(!1),lazyProcessedValidators:f,target:e,validation:t,validationState:r,validationResults:P([]),namedValidationResults:P({}),arrayConfigMap:{},elementId:0,elementValidation:t.$each,arrayAncestors:O(i)};return d}function D(e,t,i=[]){let r=[],o={};if(j(t)){let l=R(()=>M(e)),f=K(l,t,i);r.push(f),o=f.validationState}t!=null&&n(e,t,o);function n(l,f,d){if($(f)!==!1)for(let u in f){if(u==="$reactive"||u==="$lazy"||u==="$each")continue;let s=R(()=>{let a=M(l);return $(a)?a[u]:(console.error(`Vuelidify Error: validation could not be setup correctly on ${a} because ${l} is not enumerable.`),null)}),c=f[u];if(j(c)){let a=K(s,c,i);r.push(a),d[u]=a.validationState}if($(c)){let a=d[u]??{};d[u]=a,n(s,c,a)}}}return{validationConfigs:r,state:o}}function j(e){return Array.isArray(e==null?void 0:e.$reactive)||Array.isArray(e==null?void 0:e.$lazy)||(e==null?void 0:e.$each)!==void 0}function $(e){return typeof e=="object"&&e!==null&&!Array.isArray(e)}var w=250;async function B(e,t,i,r,o,n){let l=!0,f=(s,c,a)=>{if(r!==G(o))return;a.isValid===!1&&(l=!1),a.id=c.validatorId;let v=s.validationResults.value.find(m=>m.id===a.id);v!==void 0?(Object.assign(v,a),a.name!==void 0&&s.namedValidationResults.value[a.name]!==void 0&&Object.assign(s.namedValidationResults.value[a.name],a)):(s.validationResults.value.push(a),a.name!==void 0&&(s.namedValidationResults.value[a.name]=a))},{asyncPromises:d,validatorsWhichPreviouslyReturnedValidators:u}=F(e,t,i,r,n,f,!0,1);if(await Promise.all(d),r===G(o)){for(let s of u)for(let c of Object.keys(s.previouslySpawnedValidators))if(s.spawnedValidators[c]==null){let a=e.validationResults.value.findIndex(v=>v.id===c);a!==-1&&e.validationResults.value.splice(a)}}return l}function F(e,t,i,r,o,n,l,f){let d=e.target.value,u=[],s=[],c=[];for(let a of o){let v=!1;a.previouslyReturnedValidators&&(a.previouslySpawnedValidators=a.spawnedValidators,a.spawnedValidators={},v=!0),a.previouslyReturnedValidators=!1;let m;if(a.computedValidator===void 0){let y={value:d,model:t,args:i,arrayAncestors:e.arrayAncestors};m=a.validator(y)}else m=a.computedValidator.value;if(m instanceof Promise){let y=Date.now();u.push(m.then(async V=>{if(V===void 0){v&&c.push(a);return}let p=Date.now()-y;if(l&&p>w&&a.optimized===!1&&(a.optimized=!0,p>w&&p<2*w?a.validator=S(a.validator,w):a.validator=x(a.validator)),Array.isArray(V)){let{asyncPromises:T,syncResults:h}=E(e,t,i,r,n,a,V,f);s.push(...h),await Promise.all(T);return}else v&&c.push(a);n(e,a,V)}))}else if(Array.isArray(m)){let{asyncPromises:y,syncResults:V}=E(e,t,i,r,n,a,m,f);u.push(...y),s.push(...V)}else if(v&&c.push(a),m!==void 0){if(l&&a.optimized===!1){let y=a.validator;a.computedValidator=U(()=>{let V={value:e.target.value,model:t,args:i,arrayAncestors:e.arrayAncestors};return y(V)}),a.optimized=!0,a.validator=()=>{var V;return(V=a.computedValidator)==null?void 0:V.value}}s.push(m),n(e,a,m)}}return{asyncPromises:u,syncResults:s,validatorsWhichPreviouslyReturnedValidators:c}}function E(e,t,i,r,o,n,l,f){let d=A(l,n.isReactive,n.validatorId),u=F(e,t,i,r,d,o,!1,++f),s={};for(let c of d)s[c.validatorId]=c;return n.spawnedValidators=s,n.previouslyReturnedValidators=!0,u}async function J(e,t,i,r,o){e.isValidatingReactive.value=!0;let n=await B(e,t,i,r,o,e.reactiveProcessedValidators);return r===G(o)&&(e.isReactiveValid.value=n,e.isValidatingReactive.value=!1),e.isReactiveValid.value??!1}async function Q(e,t,i,r,o){e.isValidatingLazy.value=!0;let n=await B(e,t,i,r,o,e.lazyProcessedValidators);return r===G(o)&&(e.isLazyValid.value=n,e.isValidatingLazy.value=!1),e.isLazyValid.value??!1}function g(e,t,i,r,o){let n=[];for(let l of e)if(r&&l.reactiveProcessedValidators.length>0&&n.push(J(l,t.value,i,++l.reactiveIterationId,()=>l.reactiveIterationId)),o&&l.lazyProcessedValidators.length>0&&n.push(Q(l,t.value,i,++l.lazyIterationId,()=>l.lazyIterationId)),Array.isArray(l.validationState.$arrayState)){let f=[];for(let d in l.arrayConfigMap)f.push(...l.arrayConfigMap[d].validationConfigs);n.push(g(f,t,i,r,o))}return Promise.all(n).then(l=>l.every(f=>f===!0))}function W(e){e.delayReactiveValidation??(e.delayReactiveValidation=!0);let{model:t,validation:i,delayReactiveValidation:r,args:o}=e,n=N(!1),l=b(()=>u.some(V=>V.isValidatingLazy.value||V.isValidatingReactive.value)),f=b(()=>u.some(V=>V.validationResults.value.some(p=>p.isValid===!1))),d=b(()=>u.every(p=>p.isReactiveValid.value&&p.isLazyValid.value)),u=[],s=N(JSON.stringify(e.model.value)),c=b(()=>s.value!==JSON.stringify(e.model.value)),a=C(t,i),v=a.state;u=a.validationConfigs,Z(e.model,()=>{r?n.value===!0&&g(u,t,o,!0,!1):g(u,t,o,!0,!1)},{deep:!0});async function m(){let V=await g(u,t,o,!0,!0);return n.value=!0,V}function y(V){s.value=JSON.stringify(V)}return q({hasValidated:n,validate:m,isValidating:l,state:b(()=>v),isValid:d,isErrored:f,setReference:y,isDirty:c})}export{x as bufferAsync,ie as isEmailSync,_ as maxLength,ae as maxNumber,Y as minLength,ee as minNumber,te as must,X as required,S as throttleQueueAsync,W as useValidation};
|