schema-dsl 1.0.0 → 1.0.4
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/CHANGELOG.md +263 -529
- package/README.md +814 -896
- package/STATUS.md +135 -2
- package/docs/INDEX.md +1 -2
- package/docs/api-reference.md +1 -292
- package/docs/custom-extensions-guide.md +411 -0
- package/docs/enum.md +475 -0
- package/docs/i18n.md +394 -0
- package/docs/performance-benchmark-report.md +179 -0
- package/docs/plugin-system.md +8 -8
- package/docs/typescript-guide.md +554 -0
- package/docs/validate-async.md +1 -1
- package/docs/validation-rules-v1.0.2.md +1601 -0
- package/examples/README.md +81 -0
- package/examples/enum.examples.js +324 -0
- package/examples/express-integration.js +54 -54
- package/examples/i18n-full-demo.js +15 -24
- package/examples/schema-utils-chaining.examples.js +2 -2
- package/examples/slug.examples.js +179 -0
- package/index.d.ts +246 -17
- package/index.js +30 -34
- package/lib/config/constants.js +1 -1
- package/lib/config/patterns/common.js +47 -0
- package/lib/config/patterns/index.js +2 -1
- package/lib/core/DslBuilder.js +500 -8
- package/lib/core/StringExtensions.js +31 -0
- package/lib/core/Validator.js +42 -15
- package/lib/errors/ValidationError.js +3 -3
- package/lib/locales/en-US.js +79 -19
- package/lib/locales/es-ES.js +60 -19
- package/lib/locales/fr-FR.js +84 -43
- package/lib/locales/ja-JP.js +83 -42
- package/lib/locales/zh-CN.js +32 -0
- package/lib/validators/CustomKeywords.js +405 -0
- package/package.json +1 -1
- package/.github/CODE_OF_CONDUCT.md +0 -45
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -57
- package/.github/ISSUE_TEMPLATE/config.yml +0 -11
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -45
- package/.github/ISSUE_TEMPLATE/question.md +0 -31
- package/.github/PULL_REQUEST_TEMPLATE.md +0 -70
- package/.github/SECURITY.md +0 -184
- package/.github/workflows/ci.yml +0 -33
- package/plugins/custom-format.js +0 -101
- package/plugins/custom-validator.js +0 -200
|
@@ -14,26 +14,24 @@ console.log('========== 多语言配置完整示例 ==========\n');
|
|
|
14
14
|
// ========================================
|
|
15
15
|
// 步骤 1:应用启动时配置
|
|
16
16
|
// ========================================
|
|
17
|
-
console.log('【步骤 1
|
|
17
|
+
console.log('【步骤 1】配置用户语言包\n');
|
|
18
18
|
|
|
19
19
|
dsl.config({
|
|
20
|
-
//
|
|
20
|
+
// 方式 A:从目录加载(推荐用于大型项目)
|
|
21
|
+
// i18n: path.join(__dirname, 'i18n/dsl')
|
|
22
|
+
|
|
23
|
+
// 方式 B:直接传入对象(推荐用于小型项目)
|
|
21
24
|
i18n: {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
'
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
'age': '年龄',
|
|
33
|
-
'phone': '手机号',
|
|
34
|
-
|
|
35
|
-
// 嵌套字段
|
|
36
|
-
'address.city': '城市',
|
|
25
|
+
'zh-CN': {
|
|
26
|
+
// 字段标签
|
|
27
|
+
'username': '用户名',
|
|
28
|
+
'email': '邮箱地址',
|
|
29
|
+
'password': '密码',
|
|
30
|
+
'age': '年龄',
|
|
31
|
+
'phone': '手机号',
|
|
32
|
+
|
|
33
|
+
// 嵌套字段
|
|
34
|
+
'address.city': '城市',
|
|
37
35
|
'address.street': '街道',
|
|
38
36
|
|
|
39
37
|
// 自定义错误消息
|
|
@@ -79,13 +77,6 @@ dsl.config({
|
|
|
79
77
|
'custom.passwordWeak': 'パスワードが弱すぎます'
|
|
80
78
|
}
|
|
81
79
|
}
|
|
82
|
-
},
|
|
83
|
-
|
|
84
|
-
// 缓存配置(可选,大型项目推荐)
|
|
85
|
-
cache: {
|
|
86
|
-
maxSize: 10000, // 大型项目:1万个 Schema
|
|
87
|
-
ttl: 7200000 // 2 小时
|
|
88
|
-
}
|
|
89
80
|
});
|
|
90
81
|
|
|
91
82
|
console.log('✅ 配置完成\n');
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* SchemaUtils 核心方法示例
|
|
3
3
|
*
|
|
4
|
-
* 展示
|
|
4
|
+
* 展示 v1.0.3 简化后的核心 4 个方法:
|
|
5
5
|
* 1. omit() - 排除字段
|
|
6
6
|
* 2. pick() - 保留字段
|
|
7
7
|
* 3. partial() - 部分验证
|
|
8
8
|
* 4. extend() - 扩展字段
|
|
9
9
|
*
|
|
10
|
-
* @version
|
|
10
|
+
* @version 1.0.3 (简化版)
|
|
11
11
|
* @date 2025-12-29
|
|
12
12
|
*/
|
|
13
13
|
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* slug 类型验证示例
|
|
3
|
+
*
|
|
4
|
+
* slug 用于 URL 友好的字符串,只能包含:
|
|
5
|
+
* - 小写字母 (a-z)
|
|
6
|
+
* - 数字 (0-9)
|
|
7
|
+
* - 连字符 (-)
|
|
8
|
+
*
|
|
9
|
+
* 格式规则:
|
|
10
|
+
* - 必须以字母或数字开头
|
|
11
|
+
* - 必须以字母或数字结尾
|
|
12
|
+
* - 中间可以有连字符,但不能连续
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const { dsl, validate } = require('../index');
|
|
16
|
+
|
|
17
|
+
// ========== 示例 1: 基础用法 ==========
|
|
18
|
+
console.log('\n========== 示例 1: 基础 slug 验证 ==========');
|
|
19
|
+
|
|
20
|
+
const schema1 = dsl({
|
|
21
|
+
slug: 'slug!'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
console.log('✅ 有效的 slug:');
|
|
25
|
+
console.log(' my-blog-post:', validate(schema1, { slug: 'my-blog-post' }).valid);
|
|
26
|
+
console.log(' hello-world:', validate(schema1, { slug: 'hello-world' }).valid);
|
|
27
|
+
console.log(' post-123:', validate(schema1, { slug: 'post-123' }).valid);
|
|
28
|
+
console.log(' article:', validate(schema1, { slug: 'article' }).valid);
|
|
29
|
+
|
|
30
|
+
console.log('\n❌ 无效的 slug:');
|
|
31
|
+
console.log(' My-Blog-Post:', validate(schema1, { slug: 'My-Blog-Post' }).valid); // 大写
|
|
32
|
+
console.log(' hello_world:', validate(schema1, { slug: 'hello_world' }).valid); // 下划线
|
|
33
|
+
console.log(' -hello:', validate(schema1, { slug: '-hello' }).valid); // 开头连字符
|
|
34
|
+
console.log(' hello-:', validate(schema1, { slug: 'hello-' }).valid); // 结尾连字符
|
|
35
|
+
console.log(' hello--world:', validate(schema1, { slug: 'hello--world' }).valid); // 连续连字符
|
|
36
|
+
console.log(' hello world:', validate(schema1, { slug: 'hello world' }).valid); // 空格
|
|
37
|
+
|
|
38
|
+
// ========== 示例 2: DSL 字符串语法 ==========
|
|
39
|
+
console.log('\n========== 示例 2: DSL 字符串语法 ==========');
|
|
40
|
+
|
|
41
|
+
const schema2 = dsl({
|
|
42
|
+
articleSlug: 'slug:3-100!' // slug + 长度限制
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
console.log('✅ 3-100字符的 slug:');
|
|
46
|
+
console.log(' abc:', validate(schema2, { articleSlug: 'abc' }).valid);
|
|
47
|
+
console.log(' my-long-article-title-with-many-words:',
|
|
48
|
+
validate(schema2, { articleSlug: 'my-long-article-title-with-many-words' }).valid);
|
|
49
|
+
|
|
50
|
+
console.log('\n❌ 长度不符:');
|
|
51
|
+
console.log(' ab:', validate(schema2, { articleSlug: 'ab' }).valid); // 太短
|
|
52
|
+
|
|
53
|
+
// ========== 示例 3: 链式调用 ==========
|
|
54
|
+
console.log('\n========== 示例 3: 链式调用 ==========');
|
|
55
|
+
|
|
56
|
+
const schema3 = dsl({
|
|
57
|
+
pageSlug: 'string!'.slug().label('页面别名')
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
console.log('✅ 链式调用验证:');
|
|
61
|
+
console.log(' about-us:', validate(schema3, { pageSlug: 'about-us' }).valid);
|
|
62
|
+
console.log(' contact:', validate(schema3, { pageSlug: 'contact' }).valid);
|
|
63
|
+
|
|
64
|
+
// ========== 示例 4: 实际应用场景 ==========
|
|
65
|
+
console.log('\n========== 示例 4: 博客文章Schema ==========');
|
|
66
|
+
|
|
67
|
+
const blogPostSchema = dsl({
|
|
68
|
+
title: 'string:1-200!',
|
|
69
|
+
slug: 'slug:3-100!',
|
|
70
|
+
author: 'string!',
|
|
71
|
+
content: 'string:10-!',
|
|
72
|
+
tags: 'array<slug>', // slug 数组
|
|
73
|
+
publishedAt: 'datetime'
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const validPost = {
|
|
77
|
+
title: 'Getting Started with Node.js',
|
|
78
|
+
slug: 'getting-started-with-nodejs',
|
|
79
|
+
author: 'John Doe',
|
|
80
|
+
content: 'This is a comprehensive guide to Node.js...',
|
|
81
|
+
tags: ['nodejs', 'javascript', 'backend'],
|
|
82
|
+
publishedAt: '2025-12-31T10:00:00Z'
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
console.log('✅ 有效的博客文章:', validate(blogPostSchema, validPost).valid);
|
|
86
|
+
|
|
87
|
+
const invalidPost = {
|
|
88
|
+
title: 'Invalid Post',
|
|
89
|
+
slug: 'Invalid Slug!', // ❌ 包含大写和特殊字符
|
|
90
|
+
author: 'Jane',
|
|
91
|
+
content: 'Short content',
|
|
92
|
+
tags: ['Node.js', 'JavaScript'], // ❌ 标签包含大写
|
|
93
|
+
publishedAt: '2025-12-31T10:00:00Z'
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const result = validate(blogPostSchema, invalidPost);
|
|
97
|
+
console.log('\n❌ 无效的博客文章:', result.valid);
|
|
98
|
+
if (!result.valid) {
|
|
99
|
+
console.log('错误信息:');
|
|
100
|
+
result.errors.forEach(err => {
|
|
101
|
+
console.log(` - ${err.path}: ${err.message}`);
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ========== 示例 5: URL 生成应用 ==========
|
|
106
|
+
console.log('\n========== 示例 5: 自动生成 slug ==========');
|
|
107
|
+
|
|
108
|
+
function generateSlug(title) {
|
|
109
|
+
return title
|
|
110
|
+
.toLowerCase()
|
|
111
|
+
.replace(/[^a-z0-9]+/g, '-') // 替换非字母数字为连字符
|
|
112
|
+
.replace(/^-+|-+$/g, ''); // 移除首尾连字符
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const titles = [
|
|
116
|
+
'Hello World!',
|
|
117
|
+
'Getting Started with Node.js',
|
|
118
|
+
'Top 10 JavaScript Tips & Tricks',
|
|
119
|
+
'2025年的技术趋势'
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
const urlSchema = dsl({ slug: 'slug!' });
|
|
123
|
+
|
|
124
|
+
console.log('标题 → slug 转换:');
|
|
125
|
+
titles.forEach(title => {
|
|
126
|
+
const slug = generateSlug(title);
|
|
127
|
+
const isValid = validate(urlSchema, { slug }).valid;
|
|
128
|
+
console.log(` "${title}"`);
|
|
129
|
+
console.log(` → "${slug}" ${isValid ? '✅' : '❌'}`);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ========== 示例 6: 多语言支持 ==========
|
|
133
|
+
console.log('\n========== 示例 6: 多语言错误消息 ==========');
|
|
134
|
+
|
|
135
|
+
const { Validator } = require('../index');
|
|
136
|
+
const validator = new Validator();
|
|
137
|
+
|
|
138
|
+
const schema6 = dsl({ slug: 'slug!' });
|
|
139
|
+
|
|
140
|
+
// 中文
|
|
141
|
+
const resultCN = validator.validate(schema6, { slug: 'Invalid Slug!' }, { locale: 'zh-CN' });
|
|
142
|
+
console.log('中文错误:', resultCN.errors[0]?.message);
|
|
143
|
+
|
|
144
|
+
// 英文
|
|
145
|
+
const resultEN = validator.validate(schema6, { slug: 'Invalid Slug!' }, { locale: 'en-US' });
|
|
146
|
+
console.log('英文错误:', resultEN.errors[0]?.message);
|
|
147
|
+
|
|
148
|
+
// 西班牙语
|
|
149
|
+
const resultES = validator.validate(schema6, { slug: 'Invalid Slug!' }, { locale: 'es-ES' });
|
|
150
|
+
console.log('西班牙语错误:', resultES.errors[0]?.message);
|
|
151
|
+
|
|
152
|
+
// ========== 示例 7: 常见错误 ==========
|
|
153
|
+
console.log('\n========== 示例 7: 常见 slug 错误 ==========');
|
|
154
|
+
|
|
155
|
+
const testCases = [
|
|
156
|
+
{ slug: 'valid-slug-123', expected: true, reason: '✅ 正确格式' },
|
|
157
|
+
{ slug: 'Valid-Slug', expected: false, reason: '❌ 包含大写字母' },
|
|
158
|
+
{ slug: 'hello_world', expected: false, reason: '❌ 包含下划线' },
|
|
159
|
+
{ slug: 'hello world', expected: false, reason: '❌ 包含空格' },
|
|
160
|
+
{ slug: '-hello', expected: false, reason: '❌ 以连字符开头' },
|
|
161
|
+
{ slug: 'hello-', expected: false, reason: '❌ 以连字符结尾' },
|
|
162
|
+
{ slug: 'hello--world', expected: false, reason: '❌ 连续连字符' },
|
|
163
|
+
{ slug: 'hello.world', expected: false, reason: '❌ 包含点号' },
|
|
164
|
+
{ slug: '123-456', expected: true, reason: '✅ 纯数字+连字符' },
|
|
165
|
+
{ slug: 'a', expected: true, reason: '✅ 单个字母' },
|
|
166
|
+
{ slug: '1', expected: true, reason: '✅ 单个数字' }
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
const testSchema = dsl({ slug: 'slug!' });
|
|
170
|
+
|
|
171
|
+
console.log('测试用例:');
|
|
172
|
+
testCases.forEach(({ slug, expected, reason }) => {
|
|
173
|
+
const result = validate(testSchema, { slug });
|
|
174
|
+
const passed = result.valid === expected;
|
|
175
|
+
console.log(` ${passed ? '✅' : '❌'} "${slug}" - ${reason}`);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
console.log('\n========== 所有示例完成 ==========');
|
|
179
|
+
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// Type definitions for
|
|
2
|
-
// Project: https://github.com/
|
|
3
|
-
// Definitions by:
|
|
1
|
+
// Type definitions for schema-dsl v1.0.3
|
|
2
|
+
// Project: https://github.com/vextjs/schema-dsl
|
|
3
|
+
// Definitions by: schema-dsl Team
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
declare module 'schema-dsl' {
|
|
@@ -130,9 +130,9 @@ declare module 'schema-dsl' {
|
|
|
130
130
|
* ```
|
|
131
131
|
*/
|
|
132
132
|
export interface ErrorMessages {
|
|
133
|
-
/** 最小长度/最小值错误 (
|
|
133
|
+
/** 最小长度/最小值错误 (v1.0.3+: 推荐使用min代替minLength) */
|
|
134
134
|
min?: string;
|
|
135
|
-
/** 最大长度/最大值错误 (
|
|
135
|
+
/** 最大长度/最大值错误 (v1.0.3+: 推荐使用max代替maxLength) */
|
|
136
136
|
max?: string;
|
|
137
137
|
/** 最小长度错误 (向后兼容,推荐使用min) */
|
|
138
138
|
minLength?: string;
|
|
@@ -411,30 +411,100 @@ declare module 'schema-dsl' {
|
|
|
411
411
|
|
|
412
412
|
/**
|
|
413
413
|
* String 扩展全局接口
|
|
414
|
-
*
|
|
414
|
+
*
|
|
415
|
+
* ⚠️ TypeScript 用户注意事项
|
|
416
|
+
*
|
|
417
|
+
* 由于 TypeScript 对全局扩展的类型推导限制,在 .ts 文件中使用链式调用时,
|
|
418
|
+
* 推荐使用 dsl() 函数包裹字符串以获得完整的类型提示:
|
|
415
419
|
*
|
|
416
420
|
* @example
|
|
417
421
|
* ```typescript
|
|
422
|
+
* // ❌ 不推荐:可能缺少类型提示
|
|
423
|
+
* const schema = dsl({
|
|
424
|
+
* email: 'email!'.label('邮箱') // TypeScript 可能无法推导
|
|
425
|
+
* });
|
|
426
|
+
*
|
|
427
|
+
* // ✅ 推荐:使用 dsl() 包裹获得完整类型推导
|
|
418
428
|
* const schema = dsl({
|
|
419
|
-
* email: 'email!'
|
|
429
|
+
* email: dsl('email!').label('邮箱').pattern(/custom/)
|
|
430
|
+
* });
|
|
431
|
+
*
|
|
432
|
+
* // ✅ 也可以:先定义再使用
|
|
433
|
+
* const emailField = dsl('email!').label('邮箱');
|
|
434
|
+
* const schema = dsl({ email: emailField });
|
|
435
|
+
*
|
|
436
|
+
* // 📝 JavaScript 用户不受影响,可以直接使用
|
|
437
|
+
* const schema = dsl({
|
|
438
|
+
* email: 'email!'.label('邮箱') // JavaScript 中完全正常
|
|
420
439
|
* });
|
|
421
440
|
* ```
|
|
422
441
|
*/
|
|
423
442
|
global {
|
|
424
443
|
interface String {
|
|
444
|
+
/**
|
|
445
|
+
* 添加正则验证
|
|
446
|
+
* @deprecated TypeScript 用户请使用 dsl(string).pattern()
|
|
447
|
+
*/
|
|
425
448
|
pattern(regex: RegExp | string, message?: string): DslBuilder;
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* 设置字段标签
|
|
452
|
+
* @deprecated TypeScript 用户请使用 dsl(string).label()
|
|
453
|
+
*/
|
|
426
454
|
label(text: string): DslBuilder;
|
|
455
|
+
|
|
456
|
+
/**
|
|
457
|
+
* 自定义错误消息
|
|
458
|
+
* @deprecated TypeScript 用户请使用 dsl(string).messages()
|
|
459
|
+
*/
|
|
427
460
|
messages(messages: ErrorMessages): DslBuilder;
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* 设置描述
|
|
464
|
+
* @deprecated TypeScript 用户请使用 dsl(string).description()
|
|
465
|
+
*/
|
|
428
466
|
description(text: string): DslBuilder;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* 自定义验证器
|
|
470
|
+
* @deprecated TypeScript 用户请使用 dsl(string).custom()
|
|
471
|
+
*/
|
|
429
472
|
custom(validator: (value: any) => boolean | Promise<boolean> | { error: string; message: string }): DslBuilder;
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* 条件验证
|
|
476
|
+
* @deprecated TypeScript 用户请使用 dsl(string).when()
|
|
477
|
+
*/
|
|
430
478
|
when(refField: string, options: { is: any; then: DslBuilder | JSONSchema; otherwise?: DslBuilder | JSONSchema }): DslBuilder;
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* 设置默认值
|
|
482
|
+
* @deprecated TypeScript 用户请使用 dsl(string).default()
|
|
483
|
+
*/
|
|
431
484
|
default(value: any): DslBuilder;
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* 转为 JSON Schema
|
|
488
|
+
* @deprecated TypeScript 用户请使用 dsl(string).toSchema()
|
|
489
|
+
*/
|
|
432
490
|
toSchema(): JSONSchema;
|
|
433
|
-
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* 用户名验证
|
|
494
|
+
* @deprecated TypeScript 用户请使用 dsl(string).username()
|
|
495
|
+
*/
|
|
434
496
|
username(preset?: 'short' | 'medium' | 'long' | string): DslBuilder;
|
|
435
|
-
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* 密码强度验证
|
|
500
|
+
* @deprecated TypeScript 用户请使用 dsl(string).password()
|
|
501
|
+
*/
|
|
436
502
|
password(strength?: 'weak' | 'medium' | 'strong' | 'veryStrong'): DslBuilder;
|
|
437
|
-
|
|
503
|
+
|
|
504
|
+
/**
|
|
505
|
+
* 手机号验证
|
|
506
|
+
* @deprecated TypeScript 用户请使用 dsl(string).phone()
|
|
507
|
+
*/
|
|
438
508
|
phone(country?: 'cn' | 'us' | 'uk' | 'hk' | 'tw' | 'international'): DslBuilder;
|
|
439
509
|
}
|
|
440
510
|
}
|
|
@@ -623,8 +693,25 @@ declare module 'schema-dsl' {
|
|
|
623
693
|
*
|
|
624
694
|
* @example
|
|
625
695
|
* ```typescript
|
|
696
|
+
* // 方式 1: 使用 i18n 配置(推荐,v1.0.4+)
|
|
697
|
+
* dsl.config({
|
|
698
|
+
* i18n: {
|
|
699
|
+
* locales: {
|
|
700
|
+
* 'zh-CN': { required: '必填' },
|
|
701
|
+
* 'en-US': { required: 'Required' }
|
|
702
|
+
* }
|
|
703
|
+
* }
|
|
704
|
+
* });
|
|
705
|
+
*
|
|
706
|
+
* // 方式 2: 使用 locales 配置(向后兼容)
|
|
707
|
+
* dsl.config({
|
|
708
|
+
* locales: {
|
|
709
|
+
* 'zh-CN': { required: '必填' }
|
|
710
|
+
* }
|
|
711
|
+
* });
|
|
712
|
+
*
|
|
713
|
+
* // 自定义手机号规则
|
|
626
714
|
* dsl.config({
|
|
627
|
-
* // 自定义手机号规则
|
|
628
715
|
* patterns: {
|
|
629
716
|
* phone: {
|
|
630
717
|
* cn: {
|
|
@@ -634,13 +721,15 @@ declare module 'schema-dsl' {
|
|
|
634
721
|
* key: 'phone.cn'
|
|
635
722
|
* }
|
|
636
723
|
* }
|
|
637
|
-
* }
|
|
638
|
-
* // 设置语言包
|
|
639
|
-
* locales: 'zh-CN'
|
|
724
|
+
* }
|
|
640
725
|
* });
|
|
641
726
|
* ```
|
|
642
727
|
*/
|
|
643
728
|
export function config(options: {
|
|
729
|
+
/** i18n 配置(推荐,v1.0.4+) */
|
|
730
|
+
i18n?: I18nConfig;
|
|
731
|
+
/** 缓存配置 */
|
|
732
|
+
cache?: CacheConfig;
|
|
644
733
|
/** 自定义验证规则 */
|
|
645
734
|
patterns?: {
|
|
646
735
|
/** 手机号规则 */
|
|
@@ -652,7 +741,7 @@ declare module 'schema-dsl' {
|
|
|
652
741
|
};
|
|
653
742
|
/** 手机号规则(兼容旧版) */
|
|
654
743
|
phone?: Record<string, { pattern: RegExp; min?: number; max?: number; key?: string }>;
|
|
655
|
-
/**
|
|
744
|
+
/** 语言包配置(兼容旧版,推荐使用 i18n.locales) */
|
|
656
745
|
locales?: Record<string, ErrorMessages> | string;
|
|
657
746
|
}): void;
|
|
658
747
|
|
|
@@ -826,8 +915,8 @@ declare module 'schema-dsl' {
|
|
|
826
915
|
}
|
|
827
916
|
|
|
828
917
|
/**
|
|
829
|
-
*
|
|
830
|
-
*
|
|
918
|
+
* 便捷验证方法(同步)
|
|
919
|
+
*
|
|
831
920
|
* @description 使用默认的单例Validator,无需new
|
|
832
921
|
*
|
|
833
922
|
* @example
|
|
@@ -844,6 +933,146 @@ declare module 'schema-dsl' {
|
|
|
844
933
|
*/
|
|
845
934
|
export function validate<T = any>(schema: JSONSchema | SchemaIO, data: any): ValidationResult<T>;
|
|
846
935
|
|
|
936
|
+
/**
|
|
937
|
+
* 便捷异步验证方法(推荐)
|
|
938
|
+
*
|
|
939
|
+
* @description
|
|
940
|
+
* - 异步验证数据,验证失败时抛出 ValidationError
|
|
941
|
+
* - 推荐在异步场景下使用此方法
|
|
942
|
+
* - 验证成功返回验证后的数据,失败抛出异常
|
|
943
|
+
*
|
|
944
|
+
* @param schema - JSON Schema对象或SchemaIO实例
|
|
945
|
+
* @param data - 要验证的数据
|
|
946
|
+
* @param options - 验证选项(可选)
|
|
947
|
+
* @returns 验证成功返回数据的Promise
|
|
948
|
+
* @throws {ValidationError} 验证失败时抛出
|
|
949
|
+
*
|
|
950
|
+
* @example
|
|
951
|
+
* ```typescript
|
|
952
|
+
* import { dsl, validateAsync, ValidationError } from 'schema-dsl';
|
|
953
|
+
*
|
|
954
|
+
* const schema = dsl({
|
|
955
|
+
* email: dsl('email!').label('邮箱'),
|
|
956
|
+
* username: dsl('string:3-32!').label('用户名')
|
|
957
|
+
* });
|
|
958
|
+
*
|
|
959
|
+
* try {
|
|
960
|
+
* const validData = await validateAsync(schema, {
|
|
961
|
+
* email: 'test@example.com',
|
|
962
|
+
* username: 'testuser'
|
|
963
|
+
* });
|
|
964
|
+
* console.log('验证通过:', validData);
|
|
965
|
+
* } catch (error) {
|
|
966
|
+
* if (error instanceof ValidationError) {
|
|
967
|
+
* console.log('验证失败:', error.errors);
|
|
968
|
+
* error.errors.forEach(err => {
|
|
969
|
+
* console.log(`${err.path}: ${err.message}`);
|
|
970
|
+
* });
|
|
971
|
+
* }
|
|
972
|
+
* }
|
|
973
|
+
* ```
|
|
974
|
+
*/
|
|
975
|
+
export function validateAsync<T = any>(
|
|
976
|
+
schema: JSONSchema | SchemaIO,
|
|
977
|
+
data: any,
|
|
978
|
+
options?: ValidatorOptions
|
|
979
|
+
): Promise<T>;
|
|
980
|
+
|
|
981
|
+
/**
|
|
982
|
+
* 验证错误类
|
|
983
|
+
*
|
|
984
|
+
* @description 当 validateAsync 验证失败时抛出此错误
|
|
985
|
+
*
|
|
986
|
+
* @example
|
|
987
|
+
* ```typescript
|
|
988
|
+
* import { ValidationError, validateAsync, dsl } from 'schema-dsl';
|
|
989
|
+
*
|
|
990
|
+
* const schema = dsl({
|
|
991
|
+
* email: dsl('email!').label('邮箱'),
|
|
992
|
+
* age: dsl('number:18-100').label('年龄')
|
|
993
|
+
* });
|
|
994
|
+
*
|
|
995
|
+
* try {
|
|
996
|
+
* await validateAsync(schema, { email: 'invalid' });
|
|
997
|
+
* } catch (error) {
|
|
998
|
+
* if (error instanceof ValidationError) {
|
|
999
|
+
* // 获取所有错误
|
|
1000
|
+
* console.log('错误列表:', error.errors);
|
|
1001
|
+
*
|
|
1002
|
+
* // 获取错误数量
|
|
1003
|
+
* console.log('错误数量:', error.errors.length);
|
|
1004
|
+
*
|
|
1005
|
+
* // 遍历处理每个字段错误
|
|
1006
|
+
* error.errors.forEach(err => {
|
|
1007
|
+
* console.log(`字段 ${err.path}: ${err.message}`);
|
|
1008
|
+
* });
|
|
1009
|
+
*
|
|
1010
|
+
* // 转为 JSON 格式
|
|
1011
|
+
* const json = error.toJSON();
|
|
1012
|
+
* console.log('JSON格式:', json);
|
|
1013
|
+
* }
|
|
1014
|
+
* }
|
|
1015
|
+
* ```
|
|
1016
|
+
*/
|
|
1017
|
+
export class ValidationError extends Error {
|
|
1018
|
+
/** 错误名称(固定为 'ValidationError') */
|
|
1019
|
+
readonly name: 'ValidationError';
|
|
1020
|
+
|
|
1021
|
+
/** 错误消息 */
|
|
1022
|
+
message: string;
|
|
1023
|
+
|
|
1024
|
+
/** 验证错误列表 */
|
|
1025
|
+
errors: ValidationError[];
|
|
1026
|
+
|
|
1027
|
+
/**
|
|
1028
|
+
* 构造函数
|
|
1029
|
+
* @param errors - 验证错误数组
|
|
1030
|
+
* @param message - 错误消息(可选)
|
|
1031
|
+
*/
|
|
1032
|
+
constructor(errors: ValidationError[], message?: string);
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* 转为 JSON 格式
|
|
1036
|
+
* @returns JSON 对象
|
|
1037
|
+
*/
|
|
1038
|
+
toJSON(): {
|
|
1039
|
+
name: string;
|
|
1040
|
+
message: string;
|
|
1041
|
+
errors: Array<{
|
|
1042
|
+
field: string;
|
|
1043
|
+
message: string;
|
|
1044
|
+
keyword: string;
|
|
1045
|
+
params?: Record<string, any>;
|
|
1046
|
+
}>;
|
|
1047
|
+
};
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* 获取指定字段的错误
|
|
1051
|
+
* @param field - 字段路径
|
|
1052
|
+
* @returns 错误对象或 null
|
|
1053
|
+
*/
|
|
1054
|
+
getFieldError(field: string): ValidationError | null;
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* 获取所有字段的错误映射
|
|
1058
|
+
* @returns 字段错误映射对象
|
|
1059
|
+
*/
|
|
1060
|
+
getFieldErrors(): Record<string, ValidationError>;
|
|
1061
|
+
|
|
1062
|
+
/**
|
|
1063
|
+
* 检查指定字段是否有错误
|
|
1064
|
+
* @param field - 字段路径
|
|
1065
|
+
* @returns 是否有错误
|
|
1066
|
+
*/
|
|
1067
|
+
hasFieldError(field: string): boolean;
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* 获取错误总数
|
|
1071
|
+
* @returns 错误数量
|
|
1072
|
+
*/
|
|
1073
|
+
getErrorCount(): number;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
847
1076
|
/**
|
|
848
1077
|
* 获取默认Validator实例(单例)
|
|
849
1078
|
*
|