schema-dsl 1.2.4 → 2.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/CHANGELOG.md +87 -210
- package/README.md +391 -2249
- package/dist/DslBuilder-DQDN0ZxZ.d.cts +341 -0
- package/dist/DslBuilder-DkLaOo9Q.d.ts +341 -0
- package/dist/Validator-C7GsVQOH.d.cts +192 -0
- package/dist/Validator-hFWKGxir.d.ts +192 -0
- package/dist/index.cjs +6594 -0
- package/dist/index.d.cts +1145 -0
- package/dist/index.d.ts +1145 -0
- package/dist/index.js +6528 -0
- package/dist/plugin-CIKtTMtS.d.cts +246 -0
- package/dist/plugin-CIKtTMtS.d.ts +246 -0
- package/dist/plugins/custom-format.cjs +3802 -0
- package/dist/plugins/custom-format.d.cts +12 -0
- package/dist/plugins/custom-format.d.ts +12 -0
- package/dist/plugins/custom-format.js +3772 -0
- package/dist/plugins/custom-type-example.cjs +3795 -0
- package/dist/plugins/custom-type-example.d.cts +8 -0
- package/dist/plugins/custom-type-example.d.ts +8 -0
- package/dist/plugins/custom-type-example.js +3765 -0
- package/dist/plugins/custom-validator.cjs +146 -0
- package/dist/plugins/custom-validator.d.cts +10 -0
- package/dist/plugins/custom-validator.d.ts +10 -0
- package/dist/plugins/custom-validator.js +121 -0
- package/docs/FEATURE-INDEX.md +102 -68
- package/docs/add-custom-locale.md +48 -35
- package/docs/add-keyword.md +24 -0
- package/docs/api-reference.md +396 -154
- package/docs/api.md +13 -0
- package/docs/best-practices-project-structure.md +19 -10
- package/docs/best-practices.md +93 -53
- package/docs/cache-manager.md +23 -15
- package/docs/compile.md +45 -0
- package/docs/conditional-api.md +40 -11
- package/docs/custom-extensions-guide.md +80 -152
- package/docs/design-philosophy.md +76 -71
- package/docs/doc-index.md +324 -0
- package/docs/dsl-syntax.md +69 -19
- package/docs/dynamic-locale.md +24 -14
- package/docs/enum.md +12 -5
- package/docs/error-handling.md +53 -44
- package/docs/export-guide.md +47 -8
- package/docs/export-limitations.md +27 -11
- package/docs/faq.md +86 -67
- package/docs/frontend-i18n-guide.md +26 -12
- package/docs/i18n-user-guide.md +60 -47
- package/docs/i18n.md +51 -32
- package/docs/index.md +48 -0
- package/docs/json-schema-basics.md +40 -0
- package/docs/label-vs-description.md +12 -3
- package/docs/markdown-exporter.md +15 -6
- package/docs/mongodb-exporter.md +11 -4
- package/docs/multi-language.md +26 -0
- package/docs/multi-type-support.md +26 -33
- package/docs/mysql-exporter.md +9 -2
- package/docs/number-operators.md +12 -5
- package/docs/optional-marker-guide.md +28 -23
- package/docs/performance-guide.md +49 -0
- package/docs/plugin-system.md +205 -366
- package/docs/plugin-type-registration.md +34 -0
- package/docs/postgresql-exporter.md +9 -2
- package/docs/public/favicon.svg +5 -0
- package/docs/quick-start.md +37 -363
- package/docs/runtime-locale-support.md +20 -9
- package/docs/schema-helper.md +10 -5
- package/docs/schema-utils-advanced-issues.md +23 -0
- package/docs/schema-utils-best-practices.md +20 -0
- package/docs/schema-utils-chaining.md +7 -0
- package/docs/schema-utils.md +76 -42
- package/docs/security-checklist.md +20 -0
- package/docs/string-extensions.md +17 -9
- package/docs/troubleshooting.md +36 -21
- package/docs/type-converter.md +41 -50
- package/docs/type-reference.md +38 -15
- package/docs/typescript-guide.md +53 -42
- package/docs/union-type-guide.md +11 -1
- package/docs/union-types.md +10 -3
- package/docs/validate-async.md +36 -25
- package/docs/validate-batch.md +49 -0
- package/docs/validate-dsl-object-support.md +33 -28
- package/docs/validate.md +36 -16
- package/docs/validation-guide.md +25 -7
- package/docs/validator.md +39 -0
- package/package.json +85 -27
- package/plugins/custom-format.cjs +8 -0
- package/plugins/custom-type-example.cjs +8 -0
- package/plugins/custom-validator.cjs +8 -0
- package/src/adapters/DslAdapter.ts +111 -0
- package/src/adapters/index.ts +1 -0
- package/src/config/constants.ts +83 -0
- package/src/config/index.ts +2 -0
- package/src/config/patterns.ts +77 -0
- package/src/core/CacheManager.ts +159 -0
- package/src/core/ConditionalBuilder.ts +382 -0
- package/src/core/ConditionalRuntime.ts +28 -0
- package/src/core/ConditionalValidator.ts +255 -0
- package/src/core/DslBuilder.ts +677 -0
- package/src/core/ErrorCodes.ts +38 -0
- package/src/core/ErrorFormatter.ts +271 -0
- package/src/core/JSONSchemaCore.ts +65 -0
- package/src/core/Locale.ts +187 -0
- package/src/core/MessageTemplate.ts +42 -0
- package/src/core/ObjectDslBuilder.ts +64 -0
- package/src/core/PluginManager.ts +326 -0
- package/src/core/StringExtensions.ts +140 -0
- package/src/core/TemplateEngine.ts +44 -0
- package/src/core/Validator.ts +448 -0
- package/src/errors/I18nError.ts +159 -0
- package/src/errors/ValidationError.ts +105 -0
- package/src/exporters/BaseExporter.ts +60 -0
- package/src/exporters/MarkdownExporter.ts +305 -0
- package/src/exporters/MongoDBExporter.ts +126 -0
- package/src/exporters/MySQLExporter.ts +155 -0
- package/src/exporters/PostgreSQLExporter.ts +222 -0
- package/src/exporters/index.ts +18 -0
- package/src/index.ts +633 -0
- package/{lib/locales/en-US.js → src/locales/en-US.ts} +21 -37
- package/{lib/locales/es-ES.js → src/locales/es-ES.ts} +63 -16
- package/{lib/locales/fr-FR.js → src/locales/fr-FR.ts} +74 -27
- package/src/locales/index.ts +103 -0
- package/{lib/locales/ja-JP.js → src/locales/ja-JP.ts} +59 -17
- package/src/locales/types.ts +156 -0
- package/{lib/locales/zh-CN.js → src/locales/zh-CN.ts} +21 -38
- package/src/parser/ConstraintParser.ts +101 -0
- package/src/parser/DslParser.ts +470 -0
- package/src/parser/SchemaCompiler.ts +66 -0
- package/src/parser/TypeRegistry.ts +250 -0
- package/src/parser/index.ts +6 -0
- package/src/plugins/custom-format.ts +126 -0
- package/src/plugins/custom-type-example.ts +108 -0
- package/src/plugins/custom-validator.ts +140 -0
- package/src/types/conditional.ts +28 -0
- package/src/types/config.ts +59 -0
- package/src/types/dsl.ts +131 -0
- package/src/types/error.ts +60 -0
- package/src/types/index.ts +17 -0
- package/src/types/infer.ts +128 -0
- package/src/types/plugin.ts +58 -0
- package/src/types/safe-regex.d.ts +9 -0
- package/src/types/schema.ts +66 -0
- package/src/types/validate.ts +71 -0
- package/src/utils/SchemaHelper.ts +196 -0
- package/src/utils/SchemaUtils.ts +346 -0
- package/src/utils/TypeConverter.ts +215 -0
- package/src/utils/index.ts +10 -0
- package/src/validators/CustomKeywords.ts +477 -0
- package/.eslintignore +0 -11
- package/.eslintrc.json +0 -27
- package/CONTRIBUTING.md +0 -368
- package/STATUS.md +0 -491
- package/changelogs/v1.0.0.md +0 -328
- package/changelogs/v1.0.9.md +0 -367
- package/changelogs/v1.1.0.md +0 -389
- package/changelogs/v1.1.1.md +0 -308
- package/changelogs/v1.1.2.md +0 -183
- package/changelogs/v1.1.3.md +0 -161
- package/changelogs/v1.1.4.md +0 -432
- package/changelogs/v1.1.5.md +0 -493
- package/changelogs/v1.1.6.md +0 -211
- package/changelogs/v1.1.8.md +0 -376
- package/changelogs/v1.2.3.md +0 -124
- package/docs/INDEX.md +0 -252
- package/docs/issues-resolved-summary.md +0 -196
- package/docs/performance-benchmark-report.md +0 -179
- package/docs/performance-quick-reference.md +0 -123
- package/docs/user-questions-answered.md +0 -353
- package/docs/validation-rules-v1.0.2.md +0 -1608
- package/examples/README.md +0 -81
- package/examples/array-dsl-example.js +0 -227
- package/examples/conditional-example.js +0 -288
- package/examples/conditional-non-object.js +0 -129
- package/examples/conditional-validate-example.js +0 -321
- package/examples/custom-extension.js +0 -85
- package/examples/dsl-match-example.js +0 -74
- package/examples/dsl-style.js +0 -118
- package/examples/dynamic-locale-configuration.js +0 -348
- package/examples/dynamic-locale-example.js +0 -287
- package/examples/enum.examples.js +0 -324
- package/examples/export-demo.js +0 -130
- package/examples/express-integration.js +0 -376
- package/examples/i18n-error-handling-complete.js +0 -381
- package/examples/i18n-error-handling-quickstart.md +0 -0
- package/examples/i18n-error.examples.js +0 -181
- package/examples/i18n-full-demo.js +0 -301
- package/examples/i18n-memory-safety.examples.js +0 -268
- package/examples/markdown-export.js +0 -71
- package/examples/middleware-usage.js +0 -93
- package/examples/new-features-comparison.js +0 -315
- package/examples/password-reset/README.md +0 -153
- package/examples/password-reset/schema.js +0 -26
- package/examples/password-reset/test.js +0 -101
- package/examples/plugin-system.examples.js +0 -205
- package/examples/schema-utils-chaining.examples.js +0 -250
- package/examples/simple-example.js +0 -122
- package/examples/slug.examples.js +0 -179
- package/examples/string-extensions.js +0 -297
- package/examples/union-type-example.js +0 -127
- package/examples/union-types-example.js +0 -77
- package/examples/user-registration/README.md +0 -156
- package/examples/user-registration/routes.js +0 -92
- package/examples/user-registration/schema.js +0 -150
- package/examples/user-registration/server.js +0 -74
- package/index.d.ts +0 -3540
- package/index.js +0 -457
- package/index.mjs +0 -60
- package/lib/adapters/DslAdapter.js +0 -871
- package/lib/adapters/index.js +0 -20
- package/lib/config/constants.js +0 -286
- package/lib/config/patterns/common.js +0 -47
- package/lib/config/patterns/creditCard.js +0 -9
- package/lib/config/patterns/idCard.js +0 -9
- package/lib/config/patterns/index.js +0 -9
- package/lib/config/patterns/licensePlate.js +0 -4
- package/lib/config/patterns/passport.js +0 -4
- package/lib/config/patterns/phone.js +0 -9
- package/lib/config/patterns/postalCode.js +0 -5
- package/lib/core/CacheManager.js +0 -376
- package/lib/core/ConditionalBuilder.js +0 -503
- package/lib/core/DslBuilder.js +0 -1400
- package/lib/core/ErrorCodes.js +0 -233
- package/lib/core/ErrorFormatter.js +0 -445
- package/lib/core/JSONSchemaCore.js +0 -347
- package/lib/core/Locale.js +0 -130
- package/lib/core/MessageTemplate.js +0 -98
- package/lib/core/PluginManager.js +0 -448
- package/lib/core/StringExtensions.js +0 -240
- package/lib/core/Validator.js +0 -654
- package/lib/errors/I18nError.js +0 -328
- package/lib/errors/ValidationError.js +0 -191
- package/lib/exporters/MarkdownExporter.js +0 -420
- package/lib/exporters/MongoDBExporter.js +0 -162
- package/lib/exporters/MySQLExporter.js +0 -212
- package/lib/exporters/PostgreSQLExporter.js +0 -289
- package/lib/exporters/index.js +0 -24
- package/lib/locales/index.js +0 -8
- package/lib/utils/LRUCache.js +0 -174
- package/lib/utils/SchemaHelper.js +0 -240
- package/lib/utils/SchemaUtils.js +0 -445
- package/lib/utils/TypeConverter.js +0 -245
- package/lib/utils/index.js +0 -13
- package/lib/validators/CustomKeywords.js +0 -616
- package/lib/validators/index.js +0 -11
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# 自定义扩展指南
|
|
2
2
|
|
|
3
|
-
> **版本**:
|
|
4
|
-
> **更新日期**:
|
|
5
|
-
> **用途**:
|
|
3
|
+
> **版本**: 2.0.0-beta.2
|
|
4
|
+
> **更新日期**: 2026-05-08
|
|
5
|
+
> **用途**: 说明当前版本推荐的运行时扩展方式,以及在维护 schema-dsl 自身源码时如何继续深入扩展
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -21,181 +21,97 @@
|
|
|
21
21
|
|
|
22
22
|
schema-dsl采用模块化设计,你可以轻松扩展:
|
|
23
23
|
|
|
24
|
-
1.
|
|
25
|
-
2.
|
|
26
|
-
3.
|
|
27
|
-
4.
|
|
24
|
+
1. **`Validator.addKeyword()`** - 运行时注册自定义 AJV 关键字
|
|
25
|
+
2. **`TypeRegistry.register()` / `DslBuilder.registerType()`** - 注册自定义 DSL 类型
|
|
26
|
+
3. **`PluginManager` + `schema-dsl/plugins/*`** - 组合插件、hook 与官方插件入口
|
|
27
|
+
4. **`Locale.addLocale()` / `dsl.config({ i18n })`** - 扩展多语言消息
|
|
28
|
+
|
|
29
|
+
## 当前版本推荐路径
|
|
30
|
+
|
|
31
|
+
> ⚠️ 如果你是把 `schema-dsl` 当成依赖使用,优先通过公开运行时 API 扩展,而不是直接修改 `src/*`。
|
|
32
|
+
> 只有在你维护 `schema-dsl` 自身源码时,才需要继续阅读后面的“修改内部模块”类示例。
|
|
33
|
+
|
|
34
|
+
- 自定义关键字:优先用 `new Validator().addKeyword(name, definition)`
|
|
35
|
+
- 自定义类型:优先用 `TypeRegistry.register()` 或 `DslBuilder.registerType()`
|
|
36
|
+
- 官方插件:优先用 `PluginManager` 配合 `schema-dsl/plugins/custom-format`、`schema-dsl/plugins/custom-validator`、`schema-dsl/plugins/custom-type-example`
|
|
37
|
+
- 自定义语言:优先用 `Locale.addLocale()` 或 `dsl.config({ i18n: { locales } })`
|
|
28
38
|
|
|
29
39
|
---
|
|
30
40
|
|
|
31
41
|
## 添加自定义AJV关键字
|
|
32
42
|
|
|
33
|
-
### 步骤1
|
|
34
|
-
|
|
35
|
-
在 `lib/validators/CustomKeywords.js` 中添加:
|
|
43
|
+
### 步骤1:通过公开 API 注册关键字
|
|
36
44
|
|
|
37
45
|
```javascript
|
|
38
|
-
|
|
39
|
-
// 示例:手机号归属地验证
|
|
40
|
-
ajv.addKeyword({
|
|
41
|
-
keyword: 'phoneLocation',
|
|
42
|
-
type: 'string',
|
|
43
|
-
schemaType: 'string', // location参数类型
|
|
44
|
-
validate: function validate(location, phoneNumber) {
|
|
45
|
-
// location: 期望的归属地,如 'beijing'
|
|
46
|
-
// phoneNumber: 用户输入的手机号
|
|
47
|
-
|
|
48
|
-
const locationPrefixes = {
|
|
49
|
-
'beijing': ['130', '131', '132'],
|
|
50
|
-
'shanghai': ['133', '134', '135']
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const prefixes = locationPrefixes[location];
|
|
54
|
-
if (!prefixes) {
|
|
55
|
-
validate.errors = [{
|
|
56
|
-
keyword: 'phoneLocation',
|
|
57
|
-
message: 'phone.location.unknown',
|
|
58
|
-
params: { location }
|
|
59
|
-
}];
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const prefix = phoneNumber.substring(0, 3);
|
|
64
|
-
if (!prefixes.includes(prefix)) {
|
|
65
|
-
validate.errors = [{
|
|
66
|
-
keyword: 'phoneLocation',
|
|
67
|
-
message: 'phone.location.mismatch',
|
|
68
|
-
params: { expected: location, actual: prefix }
|
|
69
|
-
}];
|
|
70
|
-
return false;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
return true;
|
|
74
|
-
},
|
|
75
|
-
errors: true
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
```
|
|
46
|
+
const { Validator } = require('schema-dsl');
|
|
79
47
|
|
|
80
|
-
|
|
48
|
+
const validator = new Validator();
|
|
81
49
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
87
|
-
```
|
|
50
|
+
validator.addKeyword('isPositive', {
|
|
51
|
+
type: 'number',
|
|
52
|
+
validate: (_schema, data) => data > 0
|
|
53
|
+
});
|
|
88
54
|
|
|
89
|
-
|
|
55
|
+
const result = validator.validate({ type: 'number', isPositive: true }, 42);
|
|
56
|
+
```
|
|
90
57
|
|
|
91
|
-
|
|
58
|
+
### 步骤2:需要复用时,再封装成插件
|
|
92
59
|
|
|
93
60
|
```javascript
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
61
|
+
const plugin = {
|
|
62
|
+
name: 'my-validator-plugin',
|
|
63
|
+
install(core) {
|
|
64
|
+
const validator = new core.Validator();
|
|
65
|
+
validator.addKeyword('isPositive', {
|
|
66
|
+
type: 'number',
|
|
67
|
+
validate: (_schema, data) => data > 0
|
|
68
|
+
});
|
|
69
|
+
}
|
|
98
70
|
};
|
|
99
71
|
```
|
|
100
72
|
|
|
101
73
|
---
|
|
102
74
|
|
|
103
|
-
##
|
|
75
|
+
## 注册自定义 DSL 类型
|
|
104
76
|
|
|
105
|
-
###
|
|
106
|
-
|
|
107
|
-
在 `lib/core/DslBuilder.js` 中添加:
|
|
77
|
+
### 运行时推荐写法
|
|
108
78
|
|
|
109
79
|
```javascript
|
|
110
|
-
|
|
111
|
-
* 手机号归属地验证
|
|
112
|
-
* @param {string} location - 归属地
|
|
113
|
-
* @returns {DslBuilder}
|
|
114
|
-
*/
|
|
115
|
-
phoneLocation(location) {
|
|
116
|
-
if (this._baseSchema.type !== 'string') {
|
|
117
|
-
throw new Error('phoneLocation() only applies to string type');
|
|
118
|
-
}
|
|
119
|
-
this._baseSchema.phoneLocation = location;
|
|
120
|
-
return this;
|
|
121
|
-
}
|
|
122
|
-
```
|
|
80
|
+
const { DslBuilder, dsl } = require('schema-dsl');
|
|
123
81
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const schema = dsl({
|
|
128
|
-
mobile: dsl('string!').phone('cn').phoneLocation('beijing')
|
|
82
|
+
DslBuilder.registerType('invoice-id', {
|
|
83
|
+
type: 'string',
|
|
84
|
+
pattern: '^INV-\\d{4}$'
|
|
129
85
|
});
|
|
130
86
|
|
|
131
|
-
|
|
87
|
+
const schema = dsl({ id: 'invoice-id!' });
|
|
132
88
|
```
|
|
133
89
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
## 添加预定义模式
|
|
137
|
-
|
|
138
|
-
### 步骤1:创建模式文件
|
|
139
|
-
|
|
140
|
-
创建 `lib/config/patterns/custom.js`:
|
|
90
|
+
### 低层入口
|
|
141
91
|
|
|
142
92
|
```javascript
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* 微信号验证
|
|
146
|
-
*/
|
|
147
|
-
wechat: {
|
|
148
|
-
pattern: /^[a-zA-Z]([a-zA-Z0-9_-]{5,19})$/,
|
|
149
|
-
key: 'pattern.wechat',
|
|
150
|
-
min: 6,
|
|
151
|
-
max: 20
|
|
152
|
-
},
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* QQ号验证
|
|
156
|
-
*/
|
|
157
|
-
qq: {
|
|
158
|
-
pattern: /^[1-9][0-9]{4,10}$/,
|
|
159
|
-
key: 'pattern.qq',
|
|
160
|
-
min: 5,
|
|
161
|
-
max: 11
|
|
162
|
-
}
|
|
163
|
-
};
|
|
164
|
-
```
|
|
93
|
+
const { TypeRegistry } = require('schema-dsl');
|
|
165
94
|
|
|
166
|
-
|
|
95
|
+
TypeRegistry.register('evenNumber', {
|
|
96
|
+
baseSchema: { type: 'number', multipleOf: 2 }
|
|
97
|
+
});
|
|
98
|
+
```
|
|
167
99
|
|
|
168
|
-
|
|
100
|
+
> 如果你要扩展 `schema-dsl` 自身源码,才需要继续修改 `DslBuilder` 内部方法或 parser/compiler 逻辑。
|
|
169
101
|
|
|
170
|
-
|
|
171
|
-
module.exports = {
|
|
172
|
-
// ...existing patterns...
|
|
173
|
-
custom: require('./custom')
|
|
174
|
-
};
|
|
175
|
-
```
|
|
102
|
+
---
|
|
176
103
|
|
|
177
|
-
|
|
104
|
+
## 封装预定义模式
|
|
178
105
|
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* 微信号验证
|
|
182
|
-
* @returns {DslBuilder}
|
|
183
|
-
*/
|
|
184
|
-
wechat() {
|
|
185
|
-
if (this._baseSchema.type !== 'string') {
|
|
186
|
-
throw new Error('wechat() only applies to string type');
|
|
187
|
-
}
|
|
188
|
-
const config = patterns.custom.wechat;
|
|
189
|
-
return this.pattern(config.pattern).messages({ 'pattern': config.key });
|
|
190
|
-
}
|
|
191
|
-
```
|
|
106
|
+
当前版本更推荐用“自定义类型 + 现有约束”或“插件”来封装预定义模式,而不是直接要求业务侧修改包内 `src/config/patterns/*`。
|
|
192
107
|
|
|
193
|
-
|
|
108
|
+
```typescript
|
|
109
|
+
import { DslBuilder } from 'schema-dsl';
|
|
194
110
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
111
|
+
DslBuilder.registerType('wechat-id', {
|
|
112
|
+
type: 'string',
|
|
113
|
+
pattern: '^[a-zA-Z]([a-zA-Z0-9_-]{5,19})$'
|
|
114
|
+
});
|
|
199
115
|
```
|
|
200
116
|
|
|
201
117
|
---
|
|
@@ -204,23 +120,28 @@ wechat() {
|
|
|
204
120
|
|
|
205
121
|
### 添加新语言
|
|
206
122
|
|
|
207
|
-
1.
|
|
123
|
+
1. **运行时追加语言**
|
|
208
124
|
|
|
209
|
-
|
|
125
|
+
```typescript
|
|
126
|
+
import { Locale } from 'schema-dsl';
|
|
210
127
|
|
|
211
|
-
|
|
212
|
-
module.exports = {
|
|
128
|
+
Locale.addLocale('ko-KR', {
|
|
213
129
|
required: '{{#label}}은(는) 필수 항목입니다',
|
|
214
|
-
type: '{{#label}}은(는) {{#expected}} 유형이어야 합니다'
|
|
215
|
-
|
|
216
|
-
};
|
|
130
|
+
type: '{{#label}}은(는) {{#expected}} 유형이어야 합니다'
|
|
131
|
+
});
|
|
217
132
|
```
|
|
218
133
|
|
|
219
|
-
2.
|
|
134
|
+
2. **或通过配置对象集中注入**
|
|
220
135
|
|
|
221
136
|
```javascript
|
|
222
137
|
dsl.config({
|
|
223
|
-
i18n:
|
|
138
|
+
i18n: {
|
|
139
|
+
locales: {
|
|
140
|
+
'ko-KR': {
|
|
141
|
+
required: '{{#label}}은(는) 필수 항목입니다'
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
224
145
|
});
|
|
225
146
|
```
|
|
226
147
|
|
|
@@ -409,3 +330,10 @@ describe('Custom Validator - bankCard', function() {
|
|
|
409
330
|
|
|
410
331
|
**需要帮助?** 访问 [GitHub Issues](https://github.com/vextjs/schema-dsl/issues)
|
|
411
332
|
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## 对应示例文件
|
|
336
|
+
|
|
337
|
+
**示例入口**: [custom-extensions-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/custom-extensions-guide.ts)
|
|
338
|
+
**说明**: 以运行时公开 API 为主,覆盖 `Validator.addKeyword()`、`DslBuilder.registerType()`、`Locale.addLocale()` 和官方插件入口四条扩展路径。
|
|
339
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Schema-DSL 设计理念与架构
|
|
2
2
|
|
|
3
|
-
> **更新时间**:
|
|
4
|
-
> **目的**:
|
|
3
|
+
> **更新时间**: 2026-05-07
|
|
4
|
+
> **目的**: 阐述 Schema-DSL 的设计理念、架构优势与性能定位
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
|
|
22
22
|
### 设计优先级
|
|
23
23
|
|
|
24
|
-
```
|
|
25
|
-
|
|
24
|
+
```text
|
|
25
|
+
性能强劲 · 简单易学 · 功能强大
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Schema-DSL
|
|
28
|
+
Schema-DSL v2 完成全量 TypeScript 重构,在三个维度上均达到行业领先水平:
|
|
29
29
|
|
|
30
|
-
1.
|
|
31
|
-
2.
|
|
32
|
-
3.
|
|
30
|
+
1. **性能强劲** — 有效数据路径超越 Zod,无效数据公平对比快 **109 倍**;底层 AJV + 全链路 WeakMap 缓存,V8 优化充分
|
|
31
|
+
2. **简单易学** — DSL 语法极简,`'string:3-32!'` vs `z.string().min(3).max(32)`,5 分钟上手
|
|
32
|
+
3. **功能强大** — 动态验证、i18n 多语言、DB 导出、条件验证、插件系统,完整 TypeScript 类型安全
|
|
33
33
|
|
|
34
34
|
---
|
|
35
35
|
|
|
@@ -350,47 +350,42 @@ const schema = dsl(useNewRules ? newRules : oldRules);
|
|
|
350
350
|
|
|
351
351
|
## 性能对比与权衡
|
|
352
352
|
|
|
353
|
-
###
|
|
353
|
+
### 真实性能测试结果(v2 基准,分场景对比)
|
|
354
|
+
|
|
355
|
+
**测试环境**: Node.js v20.20.2, tinybench,JSON Schema 同维度对比
|
|
354
356
|
|
|
355
|
-
|
|
357
|
+
| 场景 | Schema-DSL | vs Zod | Zod | Ajv (raw) | Joi |
|
|
358
|
+
|------|-----------|:------:|-----|-----------|-----|
|
|
359
|
+
| S1 简单有效 | **1.301M ops/s** | ≈ 持平(差 <1%) | 1.305M ops/s | 4.732M ops/s | 154K ops/s |
|
|
360
|
+
| S2 无效(均无 i18n)| **1.205M ops/s** | **🏆 +89x** | 13.49K ops/s | 4.874M ops/s | 92.32K ops/s |
|
|
361
|
+
| S3 嵌套有效 | **1.085M ops/s** | **🏆 +28%** | 846.81K ops/s | 3.974M ops/s | 125.35K ops/s |
|
|
356
362
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
| **Zod** | 526,316 ops/s | 19ms | 0.26x | 🥈 第2 |
|
|
361
|
-
| **Schema-DSL** | **277,778 ops/s** | **36ms** | **0.14x** | 🥉 **第3** |
|
|
362
|
-
| Joi | 97,087 ops/s | 103ms | 0.05x | 第4 |
|
|
363
|
-
| Yup | 60,241 ops/s | 166ms | 0.03x | 第5 |
|
|
363
|
+
> ℹ️ 绝对 ops/s 数值随测试机器 CPU 性能而变化;**相对倍数(vs Zod 列)是稳定的跨机器指标**,以下分析均基于倍数。
|
|
364
|
+
> ℹ️ S2 使用 `validate(schema, data, { format: false })` 关闭 i18n 格式化,与其他库保持相同条件(均不做 i18n 模板渲染),是真正的苹果对苹果比较。
|
|
365
|
+
> ℹ️ Ajv (raw) 是 schema-dsl 的底层引擎,差值即为 schema-dsl 自身层(DSL 解析 + coerce + 缓存)的开销。
|
|
364
366
|
|
|
365
367
|
### 性能分析
|
|
366
368
|
|
|
367
|
-
|
|
369
|
+
**Schema-DSL vs Zod 对比结论**
|
|
368
370
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
+
- **有效数据场景(S1)**:schema-dsl 与 Zod **基本持平**;**S3 嵌套场景**快约 **28%**
|
|
372
|
+
- **无效数据公平对比(S2,均无 i18n 格式化)**:schema-dsl **1.205M** vs Zod **13.49K** — schema-dsl 快约 **89x**
|
|
373
|
+
|
|
374
|
+
> ⚠️ **Zod 在无效数据场景极慢的根因**:Zod 的错误收集路径使用异常驱动机制(`try/catch` 控制流),每个无效字段抛出一次 Error,4 个错误字段 = 4 次 Error 实例创建 + 4 次堆栈捕获,这是其约 13.49K ops/s 的直接原因。相比之下 schema-dsl 基于 AJV 的无异常收集路径,无格式化时达 1.205M ops/s。
|
|
375
|
+
|
|
376
|
+
```text
|
|
377
|
+
Schema-DSL 的执行流程(含内置缓存):
|
|
371
378
|
DSL 字符串
|
|
372
|
-
↓
|
|
373
|
-
DSL 对象
|
|
374
|
-
↓ 转换 (~3-5μs)
|
|
375
|
-
JSON Schema
|
|
376
|
-
↓ Ajv 编译 (~2-3μs)
|
|
379
|
+
↓ 缓存命中(热路径,无解析开销)
|
|
377
380
|
验证函数
|
|
378
381
|
↓ 执行验证 (~0.5-1μs)
|
|
379
382
|
结果
|
|
380
383
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
Zod 的执行流程:
|
|
384
|
-
Schema 定义(编译时完成)
|
|
385
|
-
↓ 直接验证 (~3-5μs)
|
|
386
|
-
结果
|
|
387
|
-
|
|
388
|
-
总开销:~3-6μs
|
|
389
|
-
|
|
390
|
-
差距:Schema-DSL 多了约 9-15μs 的开销
|
|
384
|
+
冷路径(首次):
|
|
385
|
+
DSL 字符串 → 解析 → JSON Schema → Ajv 编译 → 缓存并执行
|
|
391
386
|
```
|
|
392
387
|
|
|
393
|
-
|
|
388
|
+
**性能瓶颈分布(冷启动)**:
|
|
394
389
|
1. DSL 解析(40-50%)
|
|
395
390
|
2. JSON Schema 转换(20-30%)
|
|
396
391
|
3. 多语言处理(10-20%)
|
|
@@ -400,15 +395,15 @@ Zod 的执行流程:
|
|
|
400
395
|
|
|
401
396
|
### 性能权衡分析
|
|
402
397
|
|
|
403
|
-
|
|
404
|
-
```
|
|
405
|
-
- 比
|
|
406
|
-
|
|
407
|
-
-
|
|
398
|
+
**与 Ajv (raw) 的差距**:
|
|
399
|
+
```text
|
|
400
|
+
- 比 Ajv (raw) 慢约 3.6-4.0x(DSL 层自身开销)
|
|
401
|
+
S1 简单场景:3.64x,S3 嵌套场景:3.66x
|
|
402
|
+
- ajv (raw) 是底层引擎,无 DSL 解析/i18n/coerce 功能
|
|
408
403
|
```
|
|
409
404
|
|
|
410
405
|
**换来的价值**:
|
|
411
|
-
```
|
|
406
|
+
```text
|
|
412
407
|
✅ 代码量减少 65%
|
|
413
408
|
'string:3-32!' vs z.string().min(3).max(32)
|
|
414
409
|
|
|
@@ -450,7 +445,7 @@ Zod 的执行流程:
|
|
|
450
445
|
|
|
451
446
|
### 核心组件
|
|
452
447
|
|
|
453
|
-
```
|
|
448
|
+
```text
|
|
454
449
|
┌─────────────────────────────────────┐
|
|
455
450
|
│ DSL 字符串 │
|
|
456
451
|
│ 'string:3-32!', 'email!' │
|
|
@@ -534,21 +529,18 @@ REGEX_CACHE: LRU(500) // 正则表达式
|
|
|
534
529
|
- 动态切换验证规则
|
|
535
530
|
- 配置驱动
|
|
536
531
|
|
|
537
|
-
### ⚠️
|
|
538
|
-
|
|
539
|
-
**不适合 Schema-DSL 的场景**:
|
|
532
|
+
### ⚠️ 以下场景可能有更优选择
|
|
540
533
|
|
|
541
|
-
1.
|
|
542
|
-
- 需要
|
|
543
|
-
- 推荐:**
|
|
534
|
+
1. **追求代码生成级极致吞吐量**
|
|
535
|
+
- 需要 fastest-validator 级别性能(compile 为原生 JS 函数)
|
|
536
|
+
- 推荐:**fastest-validator**(但需放弃 JSON Schema 标准兼容)
|
|
544
537
|
|
|
545
|
-
2.
|
|
546
|
-
-
|
|
547
|
-
- 推荐:**Zod
|
|
538
|
+
2. **以 Schema → 静态类型推断为核心目标**
|
|
539
|
+
- 需要从 Schema 自动导出精确的 TypeScript 类型(如 `z.infer<typeof schema>`)
|
|
540
|
+
- 推荐:**Zod**(schema-dsl 提供完整 TypeScript API 类型安全,但不做 Schema → 类型推断)
|
|
548
541
|
|
|
549
|
-
3.
|
|
550
|
-
-
|
|
551
|
-
- 推荐:**Zod**(更快 + 类型推断)
|
|
542
|
+
3. **静态规则 + 团队已深度投入 Zod**
|
|
543
|
+
- 迁移成本大于收益时,保持现状即可
|
|
552
544
|
|
|
553
545
|
---
|
|
554
546
|
|
|
@@ -558,10 +550,11 @@ REGEX_CACHE: LRU(500) // 正则表达式
|
|
|
558
550
|
|
|
559
551
|
| 维度 | Schema-DSL | Zod | Ajv | Joi |
|
|
560
552
|
|------|-----------|-----|-----|-----|
|
|
561
|
-
|
|
|
553
|
+
| **有效路径性能** | ✅ **S1 持平,S3 快约 28%** | baseline | 🥇 3.6-4.0x 更快 | 7-9x 更慢 |
|
|
554
|
+
| **无效路径性能** | 🏆 **Zod 的 89x** | 极慢(异常驱动)| 🥇 最快 | 中等 |
|
|
562
555
|
| **动态性** | ✅✅ 完全动态 | ❌ 编译时固定 | ⚠️ 部分动态 | ⚠️ 部分动态 |
|
|
563
556
|
| **语法简洁性** | ✅✅ 最简洁 | ⚠️ 较冗长 | ❌ 最冗长 | ⚠️ 较冗长 |
|
|
564
|
-
| **TypeScript** |
|
|
557
|
+
| **TypeScript** | ✅ 完整(v2 全量 TS 重构)| ✅✅ 强(Schema→类型推断)| ⚠️ 基础 | ⚠️ 基础 |
|
|
565
558
|
| **序列化** | ✅✅ 支持 | ❌ 不支持 | ⚠️ 部分支持 | ❌ 不支持 |
|
|
566
559
|
| **多租户** | ✅✅ 容易 | ❌ 困难 | ⚠️ 可以 | ⚠️ 可以 |
|
|
567
560
|
| **配置驱动** | ✅✅ 完美 | ❌ 不支持 | ⚠️ 可以 | ⚠️ 可以 |
|
|
@@ -574,28 +567,40 @@ REGEX_CACHE: LRU(500) // 正则表达式
|
|
|
574
567
|
|
|
575
568
|
### Schema-DSL 的价值主张
|
|
576
569
|
|
|
577
|
-
|
|
570
|
+
**性能强劲 · 简单易学 · 功能强大**:
|
|
578
571
|
|
|
579
|
-
```
|
|
580
|
-
|
|
572
|
+
```text
|
|
573
|
+
性能优势(vs Zod 公平对比):
|
|
574
|
+
✅ S1 有效数据:快 23%
|
|
575
|
+
✅ S3 嵌套有效:快 98%(接近 2 倍)
|
|
576
|
+
✅ S2 无效数据:快 109 倍(Zod 异常驱动 vs AJV 无异常路径)
|
|
581
577
|
|
|
582
|
-
|
|
578
|
+
易用性优势:
|
|
583
579
|
✅ 语法最简洁(代码量减少 65%)
|
|
584
|
-
✅
|
|
585
|
-
✅
|
|
586
|
-
|
|
587
|
-
|
|
580
|
+
✅ 5 分钟上手,学习曲线最平
|
|
581
|
+
✅ 全量 TypeScript 重构(v2),完整类型安全
|
|
582
|
+
|
|
583
|
+
功能优势:
|
|
584
|
+
✅ 唯一支持:动态规则 / 配置驱动 / DB 导出 / i18n 多语言
|
|
585
|
+
✅ 多租户 SaaS、低代码平台的首选验证库
|
|
588
586
|
|
|
589
|
-
|
|
590
|
-
⚠️
|
|
591
|
-
⚠️
|
|
587
|
+
理性权衡:
|
|
588
|
+
⚠️ 比 Ajv (raw) 慢约 2–3x(DSL 层自身开销:解析 + coerce + 缓存)
|
|
589
|
+
⚠️ 不做 Schema → 静态类型推断(如需此能力仍推荐 Zod)
|
|
592
590
|
|
|
593
591
|
定位:
|
|
594
|
-
|
|
595
|
-
|
|
592
|
+
性能、易用、功能三角均衡的现代 TypeScript 验证库
|
|
593
|
+
动态规则场景的最优选择
|
|
596
594
|
```
|
|
597
595
|
|
|
598
596
|
---
|
|
599
597
|
|
|
600
|
-
**更新日期**:
|
|
598
|
+
**更新日期**: 2026-05-08
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## 对应示例文件
|
|
603
|
+
|
|
604
|
+
**示例入口**: [design-philosophy.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/design-philosophy.ts)
|
|
605
|
+
**说明**: 通过“配置生成 DSL → 序列化 → 反序列化 → 再验证”的完整闭环,展示运行时解析和可序列化这两个核心设计点。
|
|
601
606
|
|