codpro-validator 1.0.0
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 +282 -0
- package/dist/index.cjs +212 -0
- package/dist/index.d.cts +177 -0
- package/dist/index.d.ts +177 -0
- package/dist/index.js +180 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# codpro-validator
|
|
2
|
+
|
|
3
|
+
🚀 A lightweight, powerful and flexible React validation hook.
|
|
4
|
+
|
|
5
|
+
Built for developers who want full control without heavy form libraries.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- Lightweight & zero external dependencies
|
|
12
|
+
- Custom validation rules
|
|
13
|
+
- Cross-field validation (match passwords)
|
|
14
|
+
- Multiple validation modes
|
|
15
|
+
- `onSubmit`
|
|
16
|
+
- `onBlur`
|
|
17
|
+
- `onChange`
|
|
18
|
+
- `isDirty` tracking
|
|
19
|
+
- `touchedFields` tracking
|
|
20
|
+
- `isSubmitting` state
|
|
21
|
+
- `isValid` state
|
|
22
|
+
- `reset()` support
|
|
23
|
+
- Async submit support
|
|
24
|
+
- Fully extensible
|
|
25
|
+
- TypeScript friendly
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 📦 Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install codpro-validator
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
or
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
yarn add codpro-validator
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 🚀 Basic Usage
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import {
|
|
47
|
+
useValidator,
|
|
48
|
+
required,
|
|
49
|
+
email,
|
|
50
|
+
min,
|
|
51
|
+
match
|
|
52
|
+
} from "codpro-validator"
|
|
53
|
+
|
|
54
|
+
function RegisterForm() {
|
|
55
|
+
const {
|
|
56
|
+
register,
|
|
57
|
+
handleSubmit,
|
|
58
|
+
errors,
|
|
59
|
+
isDirty,
|
|
60
|
+
isSubmitting,
|
|
61
|
+
isValid,
|
|
62
|
+
touchedFields,
|
|
63
|
+
reset
|
|
64
|
+
} = useValidator(
|
|
65
|
+
{
|
|
66
|
+
email: {
|
|
67
|
+
rules: [required(), email()]
|
|
68
|
+
},
|
|
69
|
+
password: {
|
|
70
|
+
rules: [required(), min(6)]
|
|
71
|
+
},
|
|
72
|
+
confirmPassword: {
|
|
73
|
+
rules: [
|
|
74
|
+
required(),
|
|
75
|
+
match("password", "Passwords must match")
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
{ mode: "onSubmit" }
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
const onSubmit = async (data) => {
|
|
83
|
+
await new Promise((r) => setTimeout(r, 1000))
|
|
84
|
+
console.log(data)
|
|
85
|
+
reset()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
90
|
+
<input {...register("email")} placeholder="Email" />
|
|
91
|
+
{errors.email && <p>{errors.email}</p>}
|
|
92
|
+
|
|
93
|
+
<input type="password" {...register("password")} placeholder="Password" />
|
|
94
|
+
{errors.password && <p>{errors.password}</p>}
|
|
95
|
+
|
|
96
|
+
<input type="password" {...register("confirmPassword")} placeholder="Confirm Password" />
|
|
97
|
+
{errors.confirmPassword && <p>{errors.confirmPassword}</p>}
|
|
98
|
+
|
|
99
|
+
<button disabled={!isDirty || isSubmitting}>
|
|
100
|
+
{isSubmitting ? "Submitting..." : "Submit"}
|
|
101
|
+
</button>
|
|
102
|
+
</form>
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
## 🧠 Validation Modes
|
|
110
|
+
|
|
111
|
+
### onSubmit (Default)
|
|
112
|
+
|
|
113
|
+
Validation runs when form is submitted.
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
useValidator(schema, { mode: "onSubmit" })
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### onBlur
|
|
122
|
+
|
|
123
|
+
Validation runs when input loses focus.
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
useValidator(schema, { mode: "onBlur" })
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
### onChange
|
|
132
|
+
|
|
133
|
+
Validation runs on every keystroke.
|
|
134
|
+
|
|
135
|
+
```js
|
|
136
|
+
useValidator(schema, { mode: "onChange" })
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 📘 API
|
|
142
|
+
|
|
143
|
+
### useValidator(schema, options)
|
|
144
|
+
|
|
145
|
+
#### Parameters
|
|
146
|
+
|
|
147
|
+
| Name | Type | Description |
|
|
148
|
+
|---------|--------|------------|
|
|
149
|
+
| schema | object | Validation schema |
|
|
150
|
+
| options | object | Config options |
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
### Returned Properties
|
|
155
|
+
|
|
156
|
+
| Property | Description |
|
|
157
|
+
|----------------|------------|
|
|
158
|
+
| register | Connect input field |
|
|
159
|
+
| handleSubmit | Wrap submit handler |
|
|
160
|
+
| errors | Validation errors |
|
|
161
|
+
| isDirty | True if any field changed |
|
|
162
|
+
| isSubmitting | True during submit |
|
|
163
|
+
| touchedFields | Tracks touched inputs |
|
|
164
|
+
| isValid | True if no errors |
|
|
165
|
+
| reset | Reset form |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## 🛠 Built-in Validators
|
|
170
|
+
|
|
171
|
+
### required(message?)
|
|
172
|
+
|
|
173
|
+
```js
|
|
174
|
+
required("This field is required")
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
### email(message?)
|
|
180
|
+
|
|
181
|
+
```js
|
|
182
|
+
email("Invalid email address")
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
### min(length, message?)
|
|
188
|
+
|
|
189
|
+
```js
|
|
190
|
+
min(6, "Minimum 6 characters required")
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
### max(length, message?)
|
|
196
|
+
|
|
197
|
+
```js
|
|
198
|
+
max(20, "Maximum 20 characters allowed")
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
### match(fieldName, message?)
|
|
204
|
+
|
|
205
|
+
```js
|
|
206
|
+
match("password", "Passwords must match")
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## 🔁 Reset Form
|
|
212
|
+
|
|
213
|
+
```js
|
|
214
|
+
reset()
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
Resets form back to initial values.
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## 🧩 Example Project Structure
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
src/
|
|
225
|
+
├── guards/
|
|
226
|
+
│ ├── required.js
|
|
227
|
+
│ ├── email.js
|
|
228
|
+
│ ├── min.js
|
|
229
|
+
│ ├── max.js
|
|
230
|
+
│ └── match.js
|
|
231
|
+
│
|
|
232
|
+
├── hooks/
|
|
233
|
+
│ └── useValidator.js
|
|
234
|
+
│
|
|
235
|
+
└── index.js
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
---
|
|
239
|
+
|
|
240
|
+
## 🔥 Why codpro-validator?
|
|
241
|
+
|
|
242
|
+
- Smaller than heavy form libraries
|
|
243
|
+
- Easy to understand
|
|
244
|
+
- Fully customizable
|
|
245
|
+
- Great for learning & production
|
|
246
|
+
- Clean API design
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 🗺 Roadmap
|
|
251
|
+
|
|
252
|
+
- Async validation rules
|
|
253
|
+
- Debounced validation
|
|
254
|
+
- Nested object validation
|
|
255
|
+
- Field arrays support
|
|
256
|
+
- DevTools integration
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
## 🏷 Keywords
|
|
261
|
+
|
|
262
|
+
react validation hook, react form validation, lightweight form library, custom validation, react validator hook, form state management
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## 📄 License
|
|
267
|
+
|
|
268
|
+
MIT © CodPro Sui
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
## ⭐ Support
|
|
273
|
+
|
|
274
|
+
If you like this project:
|
|
275
|
+
|
|
276
|
+
- Star the repository
|
|
277
|
+
- Share it with developers
|
|
278
|
+
- Contribute improvements
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
Built with ❤️ by CodPro Sui
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/index.js
|
|
20
|
+
var index_exports = {};
|
|
21
|
+
__export(index_exports, {
|
|
22
|
+
default: () => index_default,
|
|
23
|
+
email: () => email_default,
|
|
24
|
+
match: () => match_default,
|
|
25
|
+
max: () => max_default,
|
|
26
|
+
min: () => min_default,
|
|
27
|
+
password: () => password_default,
|
|
28
|
+
required: () => required_default,
|
|
29
|
+
useValidator: () => useValidator_default
|
|
30
|
+
});
|
|
31
|
+
module.exports = __toCommonJS(index_exports);
|
|
32
|
+
|
|
33
|
+
// src/guards/email.js
|
|
34
|
+
var email = (reg, mess = "Invalid email") => {
|
|
35
|
+
return (value) => {
|
|
36
|
+
if (!value || value.trim() === "admin@gmail.com") return mess;
|
|
37
|
+
let regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z.-]{2,}\.[a-zA-Z]{2,}$/;
|
|
38
|
+
if (reg) {
|
|
39
|
+
regex = reg;
|
|
40
|
+
}
|
|
41
|
+
return regex.test(value) ? null : mess;
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
var email_default = email;
|
|
45
|
+
|
|
46
|
+
// src/guards/match.js
|
|
47
|
+
var match = (fieldName, mess = "No Match!") => {
|
|
48
|
+
return (value, values) => {
|
|
49
|
+
return value === values[fieldName] ? null : mess;
|
|
50
|
+
};
|
|
51
|
+
};
|
|
52
|
+
var match_default = match;
|
|
53
|
+
|
|
54
|
+
// src/guards/max.js
|
|
55
|
+
var max = (length, mess) => {
|
|
56
|
+
return (value) => {
|
|
57
|
+
if (!value.trim() || typeof value !== "string") return null;
|
|
58
|
+
if (value.trim().length > length) {
|
|
59
|
+
return mess || `The size must be less than ${length}`;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
var max_default = max;
|
|
65
|
+
|
|
66
|
+
// src/guards/min.js
|
|
67
|
+
var min = (length, mess) => {
|
|
68
|
+
return (value) => {
|
|
69
|
+
if (!value || typeof value !== "string") return null;
|
|
70
|
+
if (value.trim().length < length)
|
|
71
|
+
return mess || `The size must be at least ${length}`;
|
|
72
|
+
return null;
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
var min_default = min;
|
|
76
|
+
|
|
77
|
+
// src/guards/password.js
|
|
78
|
+
var password = (regex, mess = "Password must include at least one uppercase letter, one lowercase letter, one number, one special character (@$!%*?&) and be at least 8 characters long.") => {
|
|
79
|
+
return (value) => {
|
|
80
|
+
if (!value || typeof value !== "string") return null;
|
|
81
|
+
let reg = regex || /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
|
82
|
+
return reg.test(value.trim()) ? null : mess;
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
var password_default = password;
|
|
86
|
+
|
|
87
|
+
// src/guards/required.js
|
|
88
|
+
var required = (mess = "This is required") => {
|
|
89
|
+
return (value) => {
|
|
90
|
+
if (typeof value === "string" && value.trim() === "" || value === null || value === void 0)
|
|
91
|
+
return mess;
|
|
92
|
+
return null;
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
var required_default = required;
|
|
96
|
+
|
|
97
|
+
// src/hooks/useValidator.js
|
|
98
|
+
var import_react = require("react");
|
|
99
|
+
|
|
100
|
+
// src/engine/runRules.js
|
|
101
|
+
var runRules = async (value, rules = [], values = {}) => {
|
|
102
|
+
for (let rule of rules) {
|
|
103
|
+
let res = await rule(value, values);
|
|
104
|
+
if (res) return res;
|
|
105
|
+
}
|
|
106
|
+
return null;
|
|
107
|
+
};
|
|
108
|
+
var runRules_default = runRules;
|
|
109
|
+
|
|
110
|
+
// src/hooks/useValidator.js
|
|
111
|
+
var useValidator = (schema = {}, options = {}) => {
|
|
112
|
+
const [values, setValues] = (0, import_react.useState)({});
|
|
113
|
+
const [errors, setErrors] = (0, import_react.useState)({});
|
|
114
|
+
const [isSubmitting, setIsSubmitting] = (0, import_react.useState)(false);
|
|
115
|
+
const [touch, setTouch] = (0, import_react.useState)({});
|
|
116
|
+
const [isDirty, setIsDirty] = (0, import_react.useState)({});
|
|
117
|
+
let mode = options.mode || "onSubmit";
|
|
118
|
+
for (let field in schema) {
|
|
119
|
+
setIsDirty((pre) => ({ ...pre, [field]: "" }));
|
|
120
|
+
}
|
|
121
|
+
const register = (name) => {
|
|
122
|
+
if (typeof name !== "string") {
|
|
123
|
+
console.warn("In the register must be a string");
|
|
124
|
+
return {};
|
|
125
|
+
}
|
|
126
|
+
let config = schema[name] || {};
|
|
127
|
+
return {
|
|
128
|
+
name,
|
|
129
|
+
value: values[name] || "",
|
|
130
|
+
disabled: config.disabled || false,
|
|
131
|
+
onChange: async (e) => {
|
|
132
|
+
setValues((pre) => ({ ...pre, [name]: e.target.value }));
|
|
133
|
+
setIsDirty((pre) => ({ ...pre, [name]: e.target.value !== isDirty[name] }));
|
|
134
|
+
if (mode === "onChange") {
|
|
135
|
+
let res = await runRules_default(e.target.value, config.rules, values);
|
|
136
|
+
setErrors((pre) => ({ ...pre, [name]: res }));
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
onBlur: async () => {
|
|
140
|
+
setTouch((pre) => ({ ...pre, [name]: true }));
|
|
141
|
+
if (mode === "onBlur") {
|
|
142
|
+
let res = await runRules_default(values[name], config.rules, values);
|
|
143
|
+
setErrors((pre) => ({ ...pre, [name]: res }));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
async function isValid() {
|
|
149
|
+
let error = {};
|
|
150
|
+
for (let field in schema) {
|
|
151
|
+
let config = schema[field];
|
|
152
|
+
if (config.disabled) continue;
|
|
153
|
+
let res = await runRules_default(values[field], config.rules, values);
|
|
154
|
+
if (res) error[field] = res;
|
|
155
|
+
}
|
|
156
|
+
setErrors(error);
|
|
157
|
+
return Object.keys(error).length === 0;
|
|
158
|
+
}
|
|
159
|
+
;
|
|
160
|
+
const handleSubmit = (onSuccess, onError = () => {
|
|
161
|
+
}) => {
|
|
162
|
+
return async (e) => {
|
|
163
|
+
e.preventDefault();
|
|
164
|
+
setIsSubmitting(true);
|
|
165
|
+
try {
|
|
166
|
+
const valid = await isValid();
|
|
167
|
+
if (!valid) {
|
|
168
|
+
onError(errors);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
await onSuccess(values);
|
|
172
|
+
} catch (err) {
|
|
173
|
+
console.error("Submit Error:", err);
|
|
174
|
+
} finally {
|
|
175
|
+
setIsSubmitting(false);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
};
|
|
179
|
+
const reset = () => {
|
|
180
|
+
let resetObj = {};
|
|
181
|
+
for (let field in schema) {
|
|
182
|
+
resetObj[field] = "";
|
|
183
|
+
}
|
|
184
|
+
setValues(resetObj);
|
|
185
|
+
setErrors({});
|
|
186
|
+
};
|
|
187
|
+
return {
|
|
188
|
+
values,
|
|
189
|
+
errors,
|
|
190
|
+
register,
|
|
191
|
+
isValid,
|
|
192
|
+
handleSubmit,
|
|
193
|
+
reset,
|
|
194
|
+
isSubmitting,
|
|
195
|
+
isDirty,
|
|
196
|
+
touch
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
var useValidator_default = useValidator;
|
|
200
|
+
|
|
201
|
+
// src/index.js
|
|
202
|
+
var index_default = useValidator_default;
|
|
203
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
204
|
+
0 && (module.exports = {
|
|
205
|
+
email,
|
|
206
|
+
match,
|
|
207
|
+
max,
|
|
208
|
+
min,
|
|
209
|
+
password,
|
|
210
|
+
required,
|
|
211
|
+
useValidator
|
|
212
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const email = (reg, mess = "Invalid email") => {
|
|
4
|
+
return (value) => {
|
|
5
|
+
if (!value || value.trim() === "admin@gmail.com") return mess;
|
|
6
|
+
let regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z.-]{2,}\.[a-zA-Z]{2,}$/;
|
|
7
|
+
if (reg) {
|
|
8
|
+
regex = reg;
|
|
9
|
+
}
|
|
10
|
+
return regex.test(value) ? null : mess;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const match = (fieldName,mess="No Match!") =>{
|
|
15
|
+
return (value,values) =>{
|
|
16
|
+
return value === values[fieldName]?null:mess;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const max = (length, mess) => {
|
|
21
|
+
return (value) => {
|
|
22
|
+
if (!value.trim() || typeof value !== "string") return null;
|
|
23
|
+
if (value.trim().length > length) {
|
|
24
|
+
return mess || `The size must be less than ${length}`;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const min = (length, mess) => {
|
|
31
|
+
return (value) => {
|
|
32
|
+
if (!value || typeof value !== "string") return null;
|
|
33
|
+
if (value.trim().length < length)
|
|
34
|
+
return mess || `The size must be at least ${length}`;
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const password = (
|
|
40
|
+
regex,
|
|
41
|
+
mess = "Password must include at least one uppercase letter, one lowercase letter, one number, one special character (@$!%*?&) and be at least 8 characters long.",
|
|
42
|
+
) => {
|
|
43
|
+
return (value) => {
|
|
44
|
+
if (!value || typeof value !== "string") return null;
|
|
45
|
+
let reg = regex || /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
|
46
|
+
|
|
47
|
+
return reg.test(value.trim()) ? null : mess;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const required = (mess = "This is required") => {
|
|
52
|
+
return (value) => {
|
|
53
|
+
if ((typeof value === "string" && value.trim() === "") || value === null || value === undefined)
|
|
54
|
+
return mess;
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const runRules = async (value,rules = [],values = {}) =>{
|
|
60
|
+
for(let rule of rules){
|
|
61
|
+
let res = await rule(value,values);
|
|
62
|
+
if(res) return res;
|
|
63
|
+
}
|
|
64
|
+
return null
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const useValidator = (schema = {}, options = {}) => {
|
|
68
|
+
const [values, setValues] = useState({});
|
|
69
|
+
const [errors, setErrors] = useState({});
|
|
70
|
+
const [isSubmitting,setIsSubmitting] = useState(false);
|
|
71
|
+
const [touch, setTouch] = useState({});
|
|
72
|
+
const [isDirty,setIsDirty] = useState({});
|
|
73
|
+
//define default
|
|
74
|
+
let mode = options.mode || "onSubmit"; // onSubmit, onBlur, onChange
|
|
75
|
+
|
|
76
|
+
//dirty
|
|
77
|
+
for(let field in schema){
|
|
78
|
+
setIsDirty(pre => ({...pre,[field]: ""}));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// register for input
|
|
82
|
+
const register = (name) => {
|
|
83
|
+
if (typeof name !== "string") {
|
|
84
|
+
console.warn("In the register must be a string");
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
let config = schema[name] || {};
|
|
88
|
+
return {
|
|
89
|
+
name,
|
|
90
|
+
value: values[name] || "",
|
|
91
|
+
disabled: config.disabled || false,
|
|
92
|
+
onChange: async (e) => {
|
|
93
|
+
setValues((pre) => ({ ...pre, [name]: e.target.value }));
|
|
94
|
+
|
|
95
|
+
//dirty field
|
|
96
|
+
setIsDirty(pre => ({...pre,[name]:e.target.value !== isDirty[name]}));
|
|
97
|
+
|
|
98
|
+
if (mode === "onChange") {
|
|
99
|
+
let res = await runRules(e.target.value, config.rules,values);
|
|
100
|
+
setErrors((pre) => ({ ...pre, [name]: res }));
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
onBlur: async () => {
|
|
104
|
+
setTouch(pre => ({...pre,[name]:true}));
|
|
105
|
+
if (mode === "onBlur") {
|
|
106
|
+
let res = await runRules(values[name], config.rules,values);
|
|
107
|
+
setErrors((pre) => ({ ...pre, [name]: res }));
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
//checker
|
|
114
|
+
async function isValid(){
|
|
115
|
+
let error = {};
|
|
116
|
+
for (let field in schema) {
|
|
117
|
+
let config = schema[field];
|
|
118
|
+
if (config.disabled) continue;
|
|
119
|
+
let res = await runRules(values[field], config.rules,values);
|
|
120
|
+
if (res) error[field] = res;
|
|
121
|
+
}
|
|
122
|
+
setErrors(error);
|
|
123
|
+
return Object.keys(error).length === 0;
|
|
124
|
+
}
|
|
125
|
+
//submit data
|
|
126
|
+
|
|
127
|
+
const handleSubmit = (onSuccess, onError = () => {}) => {
|
|
128
|
+
return async (e) => {
|
|
129
|
+
e.preventDefault();
|
|
130
|
+
|
|
131
|
+
setIsSubmitting(true);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const valid = await isValid();
|
|
135
|
+
|
|
136
|
+
if (!valid) {
|
|
137
|
+
onError(errors);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await onSuccess(values);
|
|
142
|
+
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error("Submit Error:", err);
|
|
145
|
+
} finally {
|
|
146
|
+
setIsSubmitting(false);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
//reset data
|
|
152
|
+
|
|
153
|
+
const reset = () => {
|
|
154
|
+
let resetObj = {};
|
|
155
|
+
for(let field in schema){
|
|
156
|
+
resetObj[field] = "";
|
|
157
|
+
}
|
|
158
|
+
setValues(resetObj);
|
|
159
|
+
setErrors({});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
values,
|
|
166
|
+
errors,
|
|
167
|
+
register,
|
|
168
|
+
isValid,
|
|
169
|
+
handleSubmit,
|
|
170
|
+
reset,
|
|
171
|
+
isSubmitting,
|
|
172
|
+
isDirty,
|
|
173
|
+
touch
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export { useValidator as default, email, match, max, min, password, required, useValidator };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
|
|
3
|
+
const email = (reg, mess = "Invalid email") => {
|
|
4
|
+
return (value) => {
|
|
5
|
+
if (!value || value.trim() === "admin@gmail.com") return mess;
|
|
6
|
+
let regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z.-]{2,}\.[a-zA-Z]{2,}$/;
|
|
7
|
+
if (reg) {
|
|
8
|
+
regex = reg;
|
|
9
|
+
}
|
|
10
|
+
return regex.test(value) ? null : mess;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const match = (fieldName,mess="No Match!") =>{
|
|
15
|
+
return (value,values) =>{
|
|
16
|
+
return value === values[fieldName]?null:mess;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const max = (length, mess) => {
|
|
21
|
+
return (value) => {
|
|
22
|
+
if (!value.trim() || typeof value !== "string") return null;
|
|
23
|
+
if (value.trim().length > length) {
|
|
24
|
+
return mess || `The size must be less than ${length}`;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const min = (length, mess) => {
|
|
31
|
+
return (value) => {
|
|
32
|
+
if (!value || typeof value !== "string") return null;
|
|
33
|
+
if (value.trim().length < length)
|
|
34
|
+
return mess || `The size must be at least ${length}`;
|
|
35
|
+
return null;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const password = (
|
|
40
|
+
regex,
|
|
41
|
+
mess = "Password must include at least one uppercase letter, one lowercase letter, one number, one special character (@$!%*?&) and be at least 8 characters long.",
|
|
42
|
+
) => {
|
|
43
|
+
return (value) => {
|
|
44
|
+
if (!value || typeof value !== "string") return null;
|
|
45
|
+
let reg = regex || /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
|
46
|
+
|
|
47
|
+
return reg.test(value.trim()) ? null : mess;
|
|
48
|
+
};
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const required = (mess = "This is required") => {
|
|
52
|
+
return (value) => {
|
|
53
|
+
if ((typeof value === "string" && value.trim() === "") || value === null || value === undefined)
|
|
54
|
+
return mess;
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const runRules = async (value,rules = [],values = {}) =>{
|
|
60
|
+
for(let rule of rules){
|
|
61
|
+
let res = await rule(value,values);
|
|
62
|
+
if(res) return res;
|
|
63
|
+
}
|
|
64
|
+
return null
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const useValidator = (schema = {}, options = {}) => {
|
|
68
|
+
const [values, setValues] = useState({});
|
|
69
|
+
const [errors, setErrors] = useState({});
|
|
70
|
+
const [isSubmitting,setIsSubmitting] = useState(false);
|
|
71
|
+
const [touch, setTouch] = useState({});
|
|
72
|
+
const [isDirty,setIsDirty] = useState({});
|
|
73
|
+
//define default
|
|
74
|
+
let mode = options.mode || "onSubmit"; // onSubmit, onBlur, onChange
|
|
75
|
+
|
|
76
|
+
//dirty
|
|
77
|
+
for(let field in schema){
|
|
78
|
+
setIsDirty(pre => ({...pre,[field]: ""}));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// register for input
|
|
82
|
+
const register = (name) => {
|
|
83
|
+
if (typeof name !== "string") {
|
|
84
|
+
console.warn("In the register must be a string");
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
let config = schema[name] || {};
|
|
88
|
+
return {
|
|
89
|
+
name,
|
|
90
|
+
value: values[name] || "",
|
|
91
|
+
disabled: config.disabled || false,
|
|
92
|
+
onChange: async (e) => {
|
|
93
|
+
setValues((pre) => ({ ...pre, [name]: e.target.value }));
|
|
94
|
+
|
|
95
|
+
//dirty field
|
|
96
|
+
setIsDirty(pre => ({...pre,[name]:e.target.value !== isDirty[name]}));
|
|
97
|
+
|
|
98
|
+
if (mode === "onChange") {
|
|
99
|
+
let res = await runRules(e.target.value, config.rules,values);
|
|
100
|
+
setErrors((pre) => ({ ...pre, [name]: res }));
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
onBlur: async () => {
|
|
104
|
+
setTouch(pre => ({...pre,[name]:true}));
|
|
105
|
+
if (mode === "onBlur") {
|
|
106
|
+
let res = await runRules(values[name], config.rules,values);
|
|
107
|
+
setErrors((pre) => ({ ...pre, [name]: res }));
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
//checker
|
|
114
|
+
async function isValid(){
|
|
115
|
+
let error = {};
|
|
116
|
+
for (let field in schema) {
|
|
117
|
+
let config = schema[field];
|
|
118
|
+
if (config.disabled) continue;
|
|
119
|
+
let res = await runRules(values[field], config.rules,values);
|
|
120
|
+
if (res) error[field] = res;
|
|
121
|
+
}
|
|
122
|
+
setErrors(error);
|
|
123
|
+
return Object.keys(error).length === 0;
|
|
124
|
+
}
|
|
125
|
+
//submit data
|
|
126
|
+
|
|
127
|
+
const handleSubmit = (onSuccess, onError = () => {}) => {
|
|
128
|
+
return async (e) => {
|
|
129
|
+
e.preventDefault();
|
|
130
|
+
|
|
131
|
+
setIsSubmitting(true);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
const valid = await isValid();
|
|
135
|
+
|
|
136
|
+
if (!valid) {
|
|
137
|
+
onError(errors);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
await onSuccess(values);
|
|
142
|
+
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error("Submit Error:", err);
|
|
145
|
+
} finally {
|
|
146
|
+
setIsSubmitting(false);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
//reset data
|
|
152
|
+
|
|
153
|
+
const reset = () => {
|
|
154
|
+
let resetObj = {};
|
|
155
|
+
for(let field in schema){
|
|
156
|
+
resetObj[field] = "";
|
|
157
|
+
}
|
|
158
|
+
setValues(resetObj);
|
|
159
|
+
setErrors({});
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
values,
|
|
166
|
+
errors,
|
|
167
|
+
register,
|
|
168
|
+
isValid,
|
|
169
|
+
handleSubmit,
|
|
170
|
+
reset,
|
|
171
|
+
isSubmitting,
|
|
172
|
+
isDirty,
|
|
173
|
+
touch
|
|
174
|
+
};
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export { useValidator as default, email, match, max, min, password, required, useValidator };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// src/guards/email.js
|
|
2
|
+
var email = (reg, mess = "Invalid email") => {
|
|
3
|
+
return (value) => {
|
|
4
|
+
if (!value || value.trim() === "admin@gmail.com") return mess;
|
|
5
|
+
let regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z.-]{2,}\.[a-zA-Z]{2,}$/;
|
|
6
|
+
if (reg) {
|
|
7
|
+
regex = reg;
|
|
8
|
+
}
|
|
9
|
+
return regex.test(value) ? null : mess;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
var email_default = email;
|
|
13
|
+
|
|
14
|
+
// src/guards/match.js
|
|
15
|
+
var match = (fieldName, mess = "No Match!") => {
|
|
16
|
+
return (value, values) => {
|
|
17
|
+
return value === values[fieldName] ? null : mess;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
var match_default = match;
|
|
21
|
+
|
|
22
|
+
// src/guards/max.js
|
|
23
|
+
var max = (length, mess) => {
|
|
24
|
+
return (value) => {
|
|
25
|
+
if (!value.trim() || typeof value !== "string") return null;
|
|
26
|
+
if (value.trim().length > length) {
|
|
27
|
+
return mess || `The size must be less than ${length}`;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
var max_default = max;
|
|
33
|
+
|
|
34
|
+
// src/guards/min.js
|
|
35
|
+
var min = (length, mess) => {
|
|
36
|
+
return (value) => {
|
|
37
|
+
if (!value || typeof value !== "string") return null;
|
|
38
|
+
if (value.trim().length < length)
|
|
39
|
+
return mess || `The size must be at least ${length}`;
|
|
40
|
+
return null;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
var min_default = min;
|
|
44
|
+
|
|
45
|
+
// src/guards/password.js
|
|
46
|
+
var password = (regex, mess = "Password must include at least one uppercase letter, one lowercase letter, one number, one special character (@$!%*?&) and be at least 8 characters long.") => {
|
|
47
|
+
return (value) => {
|
|
48
|
+
if (!value || typeof value !== "string") return null;
|
|
49
|
+
let reg = regex || /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
|
50
|
+
return reg.test(value.trim()) ? null : mess;
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
var password_default = password;
|
|
54
|
+
|
|
55
|
+
// src/guards/required.js
|
|
56
|
+
var required = (mess = "This is required") => {
|
|
57
|
+
return (value) => {
|
|
58
|
+
if (typeof value === "string" && value.trim() === "" || value === null || value === void 0)
|
|
59
|
+
return mess;
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
var required_default = required;
|
|
64
|
+
|
|
65
|
+
// src/hooks/useValidator.js
|
|
66
|
+
import { useRef, useState } from "react";
|
|
67
|
+
|
|
68
|
+
// src/engine/runRules.js
|
|
69
|
+
var runRules = async (value, rules = [], values = {}) => {
|
|
70
|
+
for (let rule of rules) {
|
|
71
|
+
let res = await rule(value, values);
|
|
72
|
+
if (res) return res;
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
};
|
|
76
|
+
var runRules_default = runRules;
|
|
77
|
+
|
|
78
|
+
// src/hooks/useValidator.js
|
|
79
|
+
var useValidator = (schema = {}, options = {}) => {
|
|
80
|
+
const [values, setValues] = useState({});
|
|
81
|
+
const [errors, setErrors] = useState({});
|
|
82
|
+
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
83
|
+
const [touch, setTouch] = useState({});
|
|
84
|
+
const [isDirty, setIsDirty] = useState({});
|
|
85
|
+
let mode = options.mode || "onSubmit";
|
|
86
|
+
for (let field in schema) {
|
|
87
|
+
setIsDirty((pre) => ({ ...pre, [field]: "" }));
|
|
88
|
+
}
|
|
89
|
+
const register = (name) => {
|
|
90
|
+
if (typeof name !== "string") {
|
|
91
|
+
console.warn("In the register must be a string");
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
let config = schema[name] || {};
|
|
95
|
+
return {
|
|
96
|
+
name,
|
|
97
|
+
value: values[name] || "",
|
|
98
|
+
disabled: config.disabled || false,
|
|
99
|
+
onChange: async (e) => {
|
|
100
|
+
setValues((pre) => ({ ...pre, [name]: e.target.value }));
|
|
101
|
+
setIsDirty((pre) => ({ ...pre, [name]: e.target.value !== isDirty[name] }));
|
|
102
|
+
if (mode === "onChange") {
|
|
103
|
+
let res = await runRules_default(e.target.value, config.rules, values);
|
|
104
|
+
setErrors((pre) => ({ ...pre, [name]: res }));
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
onBlur: async () => {
|
|
108
|
+
setTouch((pre) => ({ ...pre, [name]: true }));
|
|
109
|
+
if (mode === "onBlur") {
|
|
110
|
+
let res = await runRules_default(values[name], config.rules, values);
|
|
111
|
+
setErrors((pre) => ({ ...pre, [name]: res }));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
async function isValid() {
|
|
117
|
+
let error = {};
|
|
118
|
+
for (let field in schema) {
|
|
119
|
+
let config = schema[field];
|
|
120
|
+
if (config.disabled) continue;
|
|
121
|
+
let res = await runRules_default(values[field], config.rules, values);
|
|
122
|
+
if (res) error[field] = res;
|
|
123
|
+
}
|
|
124
|
+
setErrors(error);
|
|
125
|
+
return Object.keys(error).length === 0;
|
|
126
|
+
}
|
|
127
|
+
;
|
|
128
|
+
const handleSubmit = (onSuccess, onError = () => {
|
|
129
|
+
}) => {
|
|
130
|
+
return async (e) => {
|
|
131
|
+
e.preventDefault();
|
|
132
|
+
setIsSubmitting(true);
|
|
133
|
+
try {
|
|
134
|
+
const valid = await isValid();
|
|
135
|
+
if (!valid) {
|
|
136
|
+
onError(errors);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
await onSuccess(values);
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error("Submit Error:", err);
|
|
142
|
+
} finally {
|
|
143
|
+
setIsSubmitting(false);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
};
|
|
147
|
+
const reset = () => {
|
|
148
|
+
let resetObj = {};
|
|
149
|
+
for (let field in schema) {
|
|
150
|
+
resetObj[field] = "";
|
|
151
|
+
}
|
|
152
|
+
setValues(resetObj);
|
|
153
|
+
setErrors({});
|
|
154
|
+
};
|
|
155
|
+
return {
|
|
156
|
+
values,
|
|
157
|
+
errors,
|
|
158
|
+
register,
|
|
159
|
+
isValid,
|
|
160
|
+
handleSubmit,
|
|
161
|
+
reset,
|
|
162
|
+
isSubmitting,
|
|
163
|
+
isDirty,
|
|
164
|
+
touch
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
var useValidator_default = useValidator;
|
|
168
|
+
|
|
169
|
+
// src/index.js
|
|
170
|
+
var index_default = useValidator_default;
|
|
171
|
+
export {
|
|
172
|
+
index_default as default,
|
|
173
|
+
email_default as email,
|
|
174
|
+
match_default as match,
|
|
175
|
+
max_default as max,
|
|
176
|
+
min_default as min,
|
|
177
|
+
password_default as password,
|
|
178
|
+
required_default as required,
|
|
179
|
+
useValidator_default as useValidator
|
|
180
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "codpro-validator",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A lightweight, flexible React validation hook with cross-field validation and powerful rule engine",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
9
|
+
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup src/index.js --format esm,cjs --dts",
|
|
25
|
+
"dev": "tsup src/index.js --watch",
|
|
26
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
"keywords": [
|
|
30
|
+
"react",
|
|
31
|
+
"react-hook",
|
|
32
|
+
"form-validation",
|
|
33
|
+
"react-form",
|
|
34
|
+
"validation",
|
|
35
|
+
"react-validation",
|
|
36
|
+
"custom-hook",
|
|
37
|
+
"form-library",
|
|
38
|
+
"cross-field-validation",
|
|
39
|
+
"lightweight-form"
|
|
40
|
+
],
|
|
41
|
+
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"react": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/CodPro-Sui/codpro-validator.git"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/CodPro-Sui/codpro-validator#readme",
|
|
50
|
+
|
|
51
|
+
"author": "CodPro Sui",
|
|
52
|
+
"license": "MIT",
|
|
53
|
+
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"tsup": "^8.5.1",
|
|
56
|
+
"typescript": "^5.9.3"
|
|
57
|
+
}
|
|
58
|
+
}
|