@lytjs/plugin-validation 6.5.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 ADDED
@@ -0,0 +1,221 @@
1
+ # @lytjs/plugin-validation
2
+
3
+ LytJS 官方表单验证插件,提供类型安全的表单验证能力。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install @lytjs/plugin-validation
9
+ # 或者
10
+ yarn add @lytjs/plugin-validation
11
+ # 或者
12
+ pnpm add @lytjs/plugin-validation
13
+ ```
14
+
15
+ ## 快速开始
16
+
17
+ ### 作为插件使用
18
+
19
+ ```typescript
20
+ import { createApp } from '@lytjs/core';
21
+ import pluginValidation from '@lytjs/plugin-validation';
22
+
23
+ const app = createApp();
24
+ app.use(pluginValidation, {
25
+ stopOnFirstError: false,
26
+ });
27
+ ```
28
+
29
+ ### 独立使用
30
+
31
+ ```typescript
32
+ import { createValidationInstance } from '@lytjs/plugin-validation';
33
+
34
+ const validation = createValidationInstance();
35
+
36
+ // 验证单个字段
37
+ const result = await validation.validateField('email', 'test@example.com', [
38
+ { type: 'required' },
39
+ { type: 'email' },
40
+ ]);
41
+
42
+ if (result.valid) {
43
+ console.log('验证通过!');
44
+ } else {
45
+ console.log('验证失败:', result.errors);
46
+ }
47
+
48
+ // 验证整个对象
49
+ const schema = {
50
+ username: {
51
+ rules: [{ type: 'required' }],
52
+ label: '用户名',
53
+ },
54
+ email: {
55
+ rules: [{ type: 'required' }, { type: 'email' }],
56
+ },
57
+ password: {
58
+ rules: [{ type: 'required' }, { type: 'minLength', value: 8 }],
59
+ },
60
+ };
61
+
62
+ const formData = {
63
+ username: 'testuser',
64
+ email: 'test@example.com',
65
+ password: 'password123',
66
+ };
67
+
68
+ const validationResult = await validation.validate(schema, formData);
69
+ console.log(validationResult.valid); // true 或 false
70
+ ```
71
+
72
+ ## 验证规则
73
+
74
+ ### 内置规则
75
+
76
+ | 规则类型 | 说明 | 参数 |
77
+ | ----------- | -------------- | ------------------------- |
78
+ | `required` | 必填验证 | - |
79
+ | `email` | 邮箱格式验证 | - |
80
+ | `phone` | 手机号验证 | - |
81
+ | `number` | 数字验证 | - |
82
+ | `min` | 最小值验证 | `value: number` |
83
+ | `max` | 最大值验证 | `value: number` |
84
+ | `minLength` | 最小长度验证 | `value: number` |
85
+ | `maxLength` | 最大长度验证 | `value: number` |
86
+ | `length` | 精确长度验证 | `value: number` |
87
+ | `pattern` | 正则表达式验证 | `value: RegExp \| string` |
88
+ | `url` | URL 格式验证 | - |
89
+ | `uuid` | UUID 格式验证 | - |
90
+ | `date` | 日期格式验证 | - |
91
+ | `custom` | 自定义验证 | `validator: Function` |
92
+
93
+ ### 规则示例
94
+
95
+ ```typescript
96
+ const rules = [
97
+ // 必填
98
+ { type: 'required' },
99
+
100
+ // 邮箱
101
+ { type: 'email' },
102
+
103
+ // 手机号
104
+ { type: 'phone' },
105
+
106
+ // 数字
107
+ { type: 'number' },
108
+
109
+ // 范围
110
+ { type: 'min', value: 18 },
111
+ { type: 'max', value: 100 },
112
+
113
+ // 长度
114
+ { type: 'minLength', value: 6 },
115
+ { type: 'maxLength', value: 20 },
116
+ { type: 'length', value: 4 },
117
+
118
+ // 正则
119
+ { type: 'pattern', value: /^\d+$/ },
120
+
121
+ // URL
122
+ { type: 'url' },
123
+
124
+ // UUID
125
+ { type: 'uuid' },
126
+
127
+ // 日期
128
+ { type: 'date' },
129
+
130
+ // 自定义
131
+ {
132
+ type: 'custom',
133
+ validator: (value: unknown, allValues?: Record<string, unknown>) => {
134
+ return String(value).includes('test');
135
+ },
136
+ },
137
+
138
+ // 自定义错误消息
139
+ { type: 'required', message: '请输入用户名' },
140
+ ];
141
+ ```
142
+
143
+ ## API
144
+
145
+ ### ValidationInstance
146
+
147
+ #### `validate(schema: ValidationSchema, values: Record<string, unknown>): Promise<ValidationResult>`
148
+
149
+ 验证整个对象。
150
+
151
+ #### `validateField(field: string, value: unknown, rules: ValidationRule[], allValues?: Record<string, unknown>): Promise<ValidationResult>`
152
+
153
+ 验证单个字段。
154
+
155
+ #### `setMessages(messages: ValidationMessages): void`
156
+
157
+ 设置自定义错误消息。
158
+
159
+ #### `addRule(type: ValidationRuleType, validator: Validator, defaultMessage?: string): void`
160
+
161
+ 添加自定义验证规则。
162
+
163
+ ### ValidationResult
164
+
165
+ ```typescript
166
+ interface ValidationResult {
167
+ valid: boolean;
168
+ errors: string[];
169
+ }
170
+ ```
171
+
172
+ ## 自定义消息
173
+
174
+ ```typescript
175
+ const validation = createValidationInstance({
176
+ messages: {
177
+ required: '此字段为必填项',
178
+ email: '请输入有效的邮箱地址',
179
+ min: (value?: unknown, label?: string) => `${label || '值'}不能小于 ${value}`,
180
+ },
181
+ });
182
+
183
+ // 或者运行时设置
184
+ validation.setMessages({
185
+ required: '请填写这个字段',
186
+ });
187
+ ```
188
+
189
+ ## 自定义验证规则
190
+
191
+ ```typescript
192
+ validation.addRule(
193
+ 'startsWithLyt',
194
+ (value: unknown) => String(value).startsWith('lyt'),
195
+ '必须以 lyt 开头',
196
+ );
197
+
198
+ // 使用自定义规则
199
+ const result = await validation.validateField('name', 'lytjs', [{ type: 'startsWithLyt' as any }]);
200
+ ```
201
+
202
+ ## 与 @lytjs/plugin-form 配合使用
203
+
204
+ ```typescript
205
+ import { createFormManager } from '@lytjs/plugin-form';
206
+ import { createValidationInstance } from '@lytjs/plugin-validation';
207
+
208
+ const validation = createValidationInstance();
209
+
210
+ const form = createFormManager({
211
+ fields: {
212
+ email: {
213
+ rules: [{ type: 'required' }, { type: 'email' }],
214
+ },
215
+ },
216
+ });
217
+ ```
218
+
219
+ ## 许可证
220
+
221
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,274 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ createValidationInstance: () => createValidationInstance,
24
+ default: () => index_default,
25
+ defaultMessages: () => defaultMessages
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ var import_core = require("@lytjs/core");
29
+
30
+ // src/rules/required.ts
31
+ var validateRequired = (value) => {
32
+ if (value == null) return false;
33
+ if (typeof value === "string") return value.trim() !== "";
34
+ if (Array.isArray(value)) return value.length > 0;
35
+ if (typeof value === "object" && value !== null) {
36
+ return Object.keys(value).length > 0;
37
+ }
38
+ return true;
39
+ };
40
+
41
+ // src/rules/email.ts
42
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
43
+ var validateEmail = (value) => {
44
+ if (value == null || value === "") return true;
45
+ return EMAIL_REGEX.test(String(value));
46
+ };
47
+
48
+ // src/rules/phone.ts
49
+ var PHONE_REGEX = /^1[3-9]\d{9}$/;
50
+ var validatePhone = (value) => {
51
+ if (value == null || value === "") return true;
52
+ return PHONE_REGEX.test(String(value));
53
+ };
54
+
55
+ // src/rules/number.ts
56
+ var validateNumber = (value) => {
57
+ if (value == null || value === "") return true;
58
+ return !isNaN(Number(value)) && isFinite(Number(value));
59
+ };
60
+
61
+ // src/rules/range.ts
62
+ var validateMin = (value, ruleValue) => {
63
+ if (value == null || value === "") return true;
64
+ const num = Number(value);
65
+ const min = Number(ruleValue);
66
+ if (isNaN(num) || isNaN(min)) return true;
67
+ return num >= min;
68
+ };
69
+ var validateMax = (value, ruleValue) => {
70
+ if (value == null || value === "") return true;
71
+ const num = Number(value);
72
+ const max = Number(ruleValue);
73
+ if (isNaN(num) || isNaN(max)) return true;
74
+ return num <= max;
75
+ };
76
+
77
+ // src/rules/string.ts
78
+ var validateMinLength = (value, ruleValue) => {
79
+ if (value == null || value === "") return true;
80
+ const str = String(value);
81
+ const min = Number(ruleValue);
82
+ if (isNaN(min)) return true;
83
+ return str.length >= min;
84
+ };
85
+ var validateMaxLength = (value, ruleValue) => {
86
+ if (value == null || value === "") return true;
87
+ const str = String(value);
88
+ const max = Number(ruleValue);
89
+ if (isNaN(max)) return true;
90
+ return str.length <= max;
91
+ };
92
+ var validateLength = (value, ruleValue) => {
93
+ if (value == null || value === "") return true;
94
+ const str = String(value);
95
+ const len = Number(ruleValue);
96
+ if (isNaN(len)) return true;
97
+ return str.length === len;
98
+ };
99
+
100
+ // src/rules/pattern.ts
101
+ var validatePattern = (value, ruleValue) => {
102
+ if (value == null || value === "") return true;
103
+ const regex = typeof ruleValue === "string" ? new RegExp(ruleValue) : ruleValue;
104
+ if (!(regex instanceof RegExp)) return true;
105
+ return regex.test(String(value));
106
+ };
107
+
108
+ // src/rules/url.ts
109
+ var URL_REGEX = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/i;
110
+ var validateUrl = (value) => {
111
+ if (value == null || value === "") return true;
112
+ return URL_REGEX.test(String(value));
113
+ };
114
+
115
+ // src/rules/uuid.ts
116
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
117
+ var validateUuid = (value) => {
118
+ if (value == null || value === "") return true;
119
+ return UUID_REGEX.test(String(value));
120
+ };
121
+
122
+ // src/rules/date.ts
123
+ var validateDate = (value) => {
124
+ if (value == null || value === "") return true;
125
+ const date = new Date(value);
126
+ return !isNaN(date.getTime());
127
+ };
128
+
129
+ // src/rules/messages.ts
130
+ var defaultMessages = {
131
+ required: "\u6B64\u5B57\u6BB5\u4E3A\u5FC5\u586B\u9879",
132
+ email: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740",
133
+ phone: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u624B\u673A\u53F7\u7801",
134
+ number: "\u8BF7\u8F93\u5165\u6570\u5B57",
135
+ min: (value, label) => `${label || "\u503C"}\u4E0D\u80FD\u5C0F\u4E8E ${value}`,
136
+ max: (value, label) => `${label || "\u503C"}\u4E0D\u80FD\u5927\u4E8E ${value}`,
137
+ minLength: (value, label) => `${label || "\u957F\u5EA6"}\u4E0D\u80FD\u5C0F\u4E8E ${value}`,
138
+ maxLength: (value, label) => `${label || "\u957F\u5EA6"}\u4E0D\u80FD\u5927\u4E8E ${value}`,
139
+ length: (value, label) => `${label || "\u957F\u5EA6"}\u5FC5\u987B\u7B49\u4E8E ${value}`,
140
+ pattern: "\u683C\u5F0F\u4E0D\u6B63\u786E",
141
+ url: "\u8BF7\u8F93\u5165\u6709\u6548\u7684URL",
142
+ uuid: "\u8BF7\u8F93\u5165\u6709\u6548\u7684UUID",
143
+ date: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u65E5\u671F",
144
+ custom: "\u6821\u9A8C\u5931\u8D25"
145
+ };
146
+
147
+ // src/index.ts
148
+ var builtInValidators = {
149
+ required: validateRequired,
150
+ email: validateEmail,
151
+ phone: validatePhone,
152
+ number: validateNumber,
153
+ min: validateMin,
154
+ max: validateMax,
155
+ minLength: validateMinLength,
156
+ maxLength: validateMaxLength,
157
+ length: validateLength,
158
+ pattern: validatePattern,
159
+ url: validateUrl,
160
+ uuid: validateUuid,
161
+ date: validateDate,
162
+ custom: () => true
163
+ };
164
+ function getMessage(messages, type, ruleValue, label) {
165
+ const msg = messages[type];
166
+ if (typeof msg === "function") {
167
+ return msg(ruleValue, label);
168
+ }
169
+ return msg || "\u6821\u9A8C\u5931\u8D25";
170
+ }
171
+ async function validateRule(rule, value, allValues, validators, messages, label) {
172
+ let isValid;
173
+ if (rule.type === "custom" && rule.validator) {
174
+ isValid = await rule.validator(value, allValues);
175
+ } else {
176
+ const validator = validators[rule.type];
177
+ if (!validator) return null;
178
+ isValid = await validator(value, rule.value, allValues);
179
+ }
180
+ if (!isValid) {
181
+ return rule.message || getMessage(messages, rule.type, rule.value, label);
182
+ }
183
+ return null;
184
+ }
185
+ async function validateFieldInternal(value, config, allValues, validators, messages, stopOnFirstError = false) {
186
+ const errors = [];
187
+ for (const rule of config.rules) {
188
+ const error = await validateRule(rule, value, allValues, validators, messages, config.label);
189
+ if (error) {
190
+ errors.push(error);
191
+ if (stopOnFirstError) break;
192
+ }
193
+ }
194
+ return {
195
+ valid: errors.length === 0,
196
+ errors
197
+ };
198
+ }
199
+ function createValidationInstance(options) {
200
+ const { messages = {}, stopOnFirstError = false } = options || {};
201
+ const customMessages = { ...defaultMessages, ...messages };
202
+ const customValidators = {};
203
+ const instance = {
204
+ validateField: async (field, value, rules, allValues) => {
205
+ return await validateFieldInternal(
206
+ value,
207
+ { rules },
208
+ allValues || {},
209
+ { ...builtInValidators, ...customValidators },
210
+ customMessages,
211
+ stopOnFirstError
212
+ );
213
+ },
214
+ validate: async (schema, values) => {
215
+ const allErrors = [];
216
+ for (const [field, config] of Object.entries(schema)) {
217
+ const result = await validateFieldInternal(
218
+ values[field],
219
+ config,
220
+ values,
221
+ { ...builtInValidators, ...customValidators },
222
+ customMessages,
223
+ stopOnFirstError
224
+ );
225
+ if (!result.valid) {
226
+ allErrors.push(...result.errors);
227
+ }
228
+ }
229
+ return {
230
+ valid: allErrors.length === 0,
231
+ errors: allErrors
232
+ };
233
+ },
234
+ setMessages: (newMessages) => {
235
+ Object.assign(customMessages, newMessages);
236
+ },
237
+ addRule: (type, validator, defaultMessage) => {
238
+ customValidators[type] = validator;
239
+ if (defaultMessage) {
240
+ customMessages[type] = defaultMessage;
241
+ }
242
+ }
243
+ };
244
+ return instance;
245
+ }
246
+ var pluginValidation = (0, import_core.definePlugin)({
247
+ name: "validation",
248
+ version: "6.0.0",
249
+ description: "LytJS official validation plugin for type-safe form validation",
250
+ author: "LytJS Team",
251
+ keywords: ["lytjs", "validation", "form", "validator"],
252
+ schema: {
253
+ type: "object",
254
+ object: {
255
+ properties: {
256
+ messages: { type: "object", default: {} },
257
+ validateOnChange: { type: "boolean", default: false },
258
+ validateOnBlur: { type: "boolean", default: false },
259
+ stopOnFirstError: { type: "boolean", default: false }
260
+ }
261
+ }
262
+ },
263
+ install(app, options) {
264
+ const validation = createValidationInstance(options);
265
+ app.config.globalProperties.$validation = validation;
266
+ app.provide("lyt-validation", validation);
267
+ }
268
+ });
269
+ var index_default = pluginValidation;
270
+ // Annotate the CommonJS export names for ESM import in node:
271
+ 0 && (module.exports = {
272
+ createValidationInstance,
273
+ defaultMessages
274
+ });
@@ -0,0 +1,51 @@
1
+ import * as _lytjs_core from '@lytjs/core';
2
+
3
+ /**
4
+ * @lytjs/plugin-validation - 类型定义
5
+ */
6
+ type ValidationRuleType = 'required' | 'email' | 'phone' | 'number' | 'min' | 'max' | 'minLength' | 'maxLength' | 'length' | 'pattern' | 'url' | 'uuid' | 'date' | 'custom';
7
+ interface ValidationRule {
8
+ type: ValidationRuleType;
9
+ message?: string;
10
+ value?: unknown;
11
+ validator?: (value: unknown, allValues?: Record<string, unknown>) => boolean | Promise<boolean>;
12
+ }
13
+ interface Validator {
14
+ (value: unknown, ruleValue?: unknown, allValues?: Record<string, unknown>): boolean | Promise<boolean>;
15
+ }
16
+ interface ValidationResult {
17
+ valid: boolean;
18
+ errors: string[];
19
+ }
20
+ interface FieldValidationConfig {
21
+ rules: ValidationRule[];
22
+ label?: string;
23
+ }
24
+ interface ValidationSchema {
25
+ [field: string]: FieldValidationConfig;
26
+ }
27
+ interface ValidationMessages {
28
+ [key: string]: string | ((value?: unknown, label?: string) => string);
29
+ }
30
+ interface ValidationOptions {
31
+ messages?: ValidationMessages;
32
+ validateOnChange?: boolean;
33
+ validateOnBlur?: boolean;
34
+ stopOnFirstError?: boolean;
35
+ }
36
+ interface ValidationInstance {
37
+ validate: (schema: ValidationSchema, values: Record<string, unknown>) => Promise<ValidationResult>;
38
+ validateField: (field: string, value: unknown, rules: ValidationRule[], allValues?: Record<string, unknown>) => Promise<ValidationResult>;
39
+ setMessages: (messages: ValidationMessages) => void;
40
+ addRule: (type: ValidationRuleType, validator: Validator, defaultMessage?: string) => void;
41
+ }
42
+ interface ValidationPluginOptions extends ValidationOptions {
43
+ name?: string;
44
+ }
45
+
46
+ declare const defaultMessages: ValidationMessages;
47
+
48
+ declare function createValidationInstance(options?: ValidationPluginOptions): ValidationInstance;
49
+ declare const pluginValidation: _lytjs_core.PluginDefinition<unknown>;
50
+
51
+ export { type FieldValidationConfig, type ValidationInstance, type ValidationMessages, type ValidationOptions, type ValidationPluginOptions, type ValidationResult, type ValidationRule, type ValidationRuleType, type ValidationSchema, type Validator, createValidationInstance, pluginValidation as default, defaultMessages };
@@ -0,0 +1,51 @@
1
+ import * as _lytjs_core from '@lytjs/core';
2
+
3
+ /**
4
+ * @lytjs/plugin-validation - 类型定义
5
+ */
6
+ type ValidationRuleType = 'required' | 'email' | 'phone' | 'number' | 'min' | 'max' | 'minLength' | 'maxLength' | 'length' | 'pattern' | 'url' | 'uuid' | 'date' | 'custom';
7
+ interface ValidationRule {
8
+ type: ValidationRuleType;
9
+ message?: string;
10
+ value?: unknown;
11
+ validator?: (value: unknown, allValues?: Record<string, unknown>) => boolean | Promise<boolean>;
12
+ }
13
+ interface Validator {
14
+ (value: unknown, ruleValue?: unknown, allValues?: Record<string, unknown>): boolean | Promise<boolean>;
15
+ }
16
+ interface ValidationResult {
17
+ valid: boolean;
18
+ errors: string[];
19
+ }
20
+ interface FieldValidationConfig {
21
+ rules: ValidationRule[];
22
+ label?: string;
23
+ }
24
+ interface ValidationSchema {
25
+ [field: string]: FieldValidationConfig;
26
+ }
27
+ interface ValidationMessages {
28
+ [key: string]: string | ((value?: unknown, label?: string) => string);
29
+ }
30
+ interface ValidationOptions {
31
+ messages?: ValidationMessages;
32
+ validateOnChange?: boolean;
33
+ validateOnBlur?: boolean;
34
+ stopOnFirstError?: boolean;
35
+ }
36
+ interface ValidationInstance {
37
+ validate: (schema: ValidationSchema, values: Record<string, unknown>) => Promise<ValidationResult>;
38
+ validateField: (field: string, value: unknown, rules: ValidationRule[], allValues?: Record<string, unknown>) => Promise<ValidationResult>;
39
+ setMessages: (messages: ValidationMessages) => void;
40
+ addRule: (type: ValidationRuleType, validator: Validator, defaultMessage?: string) => void;
41
+ }
42
+ interface ValidationPluginOptions extends ValidationOptions {
43
+ name?: string;
44
+ }
45
+
46
+ declare const defaultMessages: ValidationMessages;
47
+
48
+ declare function createValidationInstance(options?: ValidationPluginOptions): ValidationInstance;
49
+ declare const pluginValidation: _lytjs_core.PluginDefinition<unknown>;
50
+
51
+ export { type FieldValidationConfig, type ValidationInstance, type ValidationMessages, type ValidationOptions, type ValidationPluginOptions, type ValidationResult, type ValidationRule, type ValidationRuleType, type ValidationSchema, type Validator, createValidationInstance, pluginValidation as default, defaultMessages };
package/dist/index.mjs ADDED
@@ -0,0 +1,248 @@
1
+ // src/index.ts
2
+ import { definePlugin } from "@lytjs/core";
3
+
4
+ // src/rules/required.ts
5
+ var validateRequired = (value) => {
6
+ if (value == null) return false;
7
+ if (typeof value === "string") return value.trim() !== "";
8
+ if (Array.isArray(value)) return value.length > 0;
9
+ if (typeof value === "object" && value !== null) {
10
+ return Object.keys(value).length > 0;
11
+ }
12
+ return true;
13
+ };
14
+
15
+ // src/rules/email.ts
16
+ var EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
17
+ var validateEmail = (value) => {
18
+ if (value == null || value === "") return true;
19
+ return EMAIL_REGEX.test(String(value));
20
+ };
21
+
22
+ // src/rules/phone.ts
23
+ var PHONE_REGEX = /^1[3-9]\d{9}$/;
24
+ var validatePhone = (value) => {
25
+ if (value == null || value === "") return true;
26
+ return PHONE_REGEX.test(String(value));
27
+ };
28
+
29
+ // src/rules/number.ts
30
+ var validateNumber = (value) => {
31
+ if (value == null || value === "") return true;
32
+ return !isNaN(Number(value)) && isFinite(Number(value));
33
+ };
34
+
35
+ // src/rules/range.ts
36
+ var validateMin = (value, ruleValue) => {
37
+ if (value == null || value === "") return true;
38
+ const num = Number(value);
39
+ const min = Number(ruleValue);
40
+ if (isNaN(num) || isNaN(min)) return true;
41
+ return num >= min;
42
+ };
43
+ var validateMax = (value, ruleValue) => {
44
+ if (value == null || value === "") return true;
45
+ const num = Number(value);
46
+ const max = Number(ruleValue);
47
+ if (isNaN(num) || isNaN(max)) return true;
48
+ return num <= max;
49
+ };
50
+
51
+ // src/rules/string.ts
52
+ var validateMinLength = (value, ruleValue) => {
53
+ if (value == null || value === "") return true;
54
+ const str = String(value);
55
+ const min = Number(ruleValue);
56
+ if (isNaN(min)) return true;
57
+ return str.length >= min;
58
+ };
59
+ var validateMaxLength = (value, ruleValue) => {
60
+ if (value == null || value === "") return true;
61
+ const str = String(value);
62
+ const max = Number(ruleValue);
63
+ if (isNaN(max)) return true;
64
+ return str.length <= max;
65
+ };
66
+ var validateLength = (value, ruleValue) => {
67
+ if (value == null || value === "") return true;
68
+ const str = String(value);
69
+ const len = Number(ruleValue);
70
+ if (isNaN(len)) return true;
71
+ return str.length === len;
72
+ };
73
+
74
+ // src/rules/pattern.ts
75
+ var validatePattern = (value, ruleValue) => {
76
+ if (value == null || value === "") return true;
77
+ const regex = typeof ruleValue === "string" ? new RegExp(ruleValue) : ruleValue;
78
+ if (!(regex instanceof RegExp)) return true;
79
+ return regex.test(String(value));
80
+ };
81
+
82
+ // src/rules/url.ts
83
+ var URL_REGEX = /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/i;
84
+ var validateUrl = (value) => {
85
+ if (value == null || value === "") return true;
86
+ return URL_REGEX.test(String(value));
87
+ };
88
+
89
+ // src/rules/uuid.ts
90
+ var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
91
+ var validateUuid = (value) => {
92
+ if (value == null || value === "") return true;
93
+ return UUID_REGEX.test(String(value));
94
+ };
95
+
96
+ // src/rules/date.ts
97
+ var validateDate = (value) => {
98
+ if (value == null || value === "") return true;
99
+ const date = new Date(value);
100
+ return !isNaN(date.getTime());
101
+ };
102
+
103
+ // src/rules/messages.ts
104
+ var defaultMessages = {
105
+ required: "\u6B64\u5B57\u6BB5\u4E3A\u5FC5\u586B\u9879",
106
+ email: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740",
107
+ phone: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u624B\u673A\u53F7\u7801",
108
+ number: "\u8BF7\u8F93\u5165\u6570\u5B57",
109
+ min: (value, label) => `${label || "\u503C"}\u4E0D\u80FD\u5C0F\u4E8E ${value}`,
110
+ max: (value, label) => `${label || "\u503C"}\u4E0D\u80FD\u5927\u4E8E ${value}`,
111
+ minLength: (value, label) => `${label || "\u957F\u5EA6"}\u4E0D\u80FD\u5C0F\u4E8E ${value}`,
112
+ maxLength: (value, label) => `${label || "\u957F\u5EA6"}\u4E0D\u80FD\u5927\u4E8E ${value}`,
113
+ length: (value, label) => `${label || "\u957F\u5EA6"}\u5FC5\u987B\u7B49\u4E8E ${value}`,
114
+ pattern: "\u683C\u5F0F\u4E0D\u6B63\u786E",
115
+ url: "\u8BF7\u8F93\u5165\u6709\u6548\u7684URL",
116
+ uuid: "\u8BF7\u8F93\u5165\u6709\u6548\u7684UUID",
117
+ date: "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u65E5\u671F",
118
+ custom: "\u6821\u9A8C\u5931\u8D25"
119
+ };
120
+
121
+ // src/index.ts
122
+ var builtInValidators = {
123
+ required: validateRequired,
124
+ email: validateEmail,
125
+ phone: validatePhone,
126
+ number: validateNumber,
127
+ min: validateMin,
128
+ max: validateMax,
129
+ minLength: validateMinLength,
130
+ maxLength: validateMaxLength,
131
+ length: validateLength,
132
+ pattern: validatePattern,
133
+ url: validateUrl,
134
+ uuid: validateUuid,
135
+ date: validateDate,
136
+ custom: () => true
137
+ };
138
+ function getMessage(messages, type, ruleValue, label) {
139
+ const msg = messages[type];
140
+ if (typeof msg === "function") {
141
+ return msg(ruleValue, label);
142
+ }
143
+ return msg || "\u6821\u9A8C\u5931\u8D25";
144
+ }
145
+ async function validateRule(rule, value, allValues, validators, messages, label) {
146
+ let isValid;
147
+ if (rule.type === "custom" && rule.validator) {
148
+ isValid = await rule.validator(value, allValues);
149
+ } else {
150
+ const validator = validators[rule.type];
151
+ if (!validator) return null;
152
+ isValid = await validator(value, rule.value, allValues);
153
+ }
154
+ if (!isValid) {
155
+ return rule.message || getMessage(messages, rule.type, rule.value, label);
156
+ }
157
+ return null;
158
+ }
159
+ async function validateFieldInternal(value, config, allValues, validators, messages, stopOnFirstError = false) {
160
+ const errors = [];
161
+ for (const rule of config.rules) {
162
+ const error = await validateRule(rule, value, allValues, validators, messages, config.label);
163
+ if (error) {
164
+ errors.push(error);
165
+ if (stopOnFirstError) break;
166
+ }
167
+ }
168
+ return {
169
+ valid: errors.length === 0,
170
+ errors
171
+ };
172
+ }
173
+ function createValidationInstance(options) {
174
+ const { messages = {}, stopOnFirstError = false } = options || {};
175
+ const customMessages = { ...defaultMessages, ...messages };
176
+ const customValidators = {};
177
+ const instance = {
178
+ validateField: async (field, value, rules, allValues) => {
179
+ return await validateFieldInternal(
180
+ value,
181
+ { rules },
182
+ allValues || {},
183
+ { ...builtInValidators, ...customValidators },
184
+ customMessages,
185
+ stopOnFirstError
186
+ );
187
+ },
188
+ validate: async (schema, values) => {
189
+ const allErrors = [];
190
+ for (const [field, config] of Object.entries(schema)) {
191
+ const result = await validateFieldInternal(
192
+ values[field],
193
+ config,
194
+ values,
195
+ { ...builtInValidators, ...customValidators },
196
+ customMessages,
197
+ stopOnFirstError
198
+ );
199
+ if (!result.valid) {
200
+ allErrors.push(...result.errors);
201
+ }
202
+ }
203
+ return {
204
+ valid: allErrors.length === 0,
205
+ errors: allErrors
206
+ };
207
+ },
208
+ setMessages: (newMessages) => {
209
+ Object.assign(customMessages, newMessages);
210
+ },
211
+ addRule: (type, validator, defaultMessage) => {
212
+ customValidators[type] = validator;
213
+ if (defaultMessage) {
214
+ customMessages[type] = defaultMessage;
215
+ }
216
+ }
217
+ };
218
+ return instance;
219
+ }
220
+ var pluginValidation = definePlugin({
221
+ name: "validation",
222
+ version: "6.0.0",
223
+ description: "LytJS official validation plugin for type-safe form validation",
224
+ author: "LytJS Team",
225
+ keywords: ["lytjs", "validation", "form", "validator"],
226
+ schema: {
227
+ type: "object",
228
+ object: {
229
+ properties: {
230
+ messages: { type: "object", default: {} },
231
+ validateOnChange: { type: "boolean", default: false },
232
+ validateOnBlur: { type: "boolean", default: false },
233
+ stopOnFirstError: { type: "boolean", default: false }
234
+ }
235
+ }
236
+ },
237
+ install(app, options) {
238
+ const validation = createValidationInstance(options);
239
+ app.config.globalProperties.$validation = validation;
240
+ app.provide("lyt-validation", validation);
241
+ }
242
+ });
243
+ var index_default = pluginValidation;
244
+ export {
245
+ createValidationInstance,
246
+ index_default as default,
247
+ defaultMessages
248
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@lytjs/plugin-validation",
3
+ "version": "6.5.0",
4
+ "description": "LytJS official validation plugin for type-safe form validation",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.cjs"
14
+ },
15
+ "./package.json": "./package.json"
16
+ },
17
+ "files": [
18
+ "dist"
19
+ ],
20
+ "sideEffects": false,
21
+ "scripts": {
22
+ "build": "tsup",
23
+ "dev": "tsup --watch",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "test:coverage": "vitest run --coverage",
27
+ "type-check": "tsc --noEmit",
28
+ "lint": "eslint \"src/**/*.ts\"",
29
+ "clean": "rm -rf dist"
30
+ },
31
+ "dependencies": {
32
+ "@lytjs/core": "^6.0.0",
33
+ "@lytjs/reactivity": "^6.0.0",
34
+ "@lytjs/plugin-form": "^6.0.0"
35
+ },
36
+ "devDependencies": {
37
+ "tsup": "^8.0.0",
38
+ "typescript": "^5.4.0",
39
+ "vitest": "^3.0.0"
40
+ },
41
+ "license": "MIT",
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "https://gitee.com/lytjs/lytjs.git",
45
+ "directory": "packages/plugins/packages/plugin-validation"
46
+ },
47
+ "keywords": [
48
+ "lytjs",
49
+ "validation",
50
+ "form",
51
+ "validator"
52
+ ]
53
+ }