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
package/index.d.ts
DELETED
|
@@ -1,3540 +0,0 @@
|
|
|
1
|
-
// Type definitions for schema-dsl v1.1.8
|
|
2
|
-
// Project: https://github.com/vextjs/schema-dsl
|
|
3
|
-
// Definitions by: schema-dsl Team
|
|
4
|
-
|
|
5
|
-
// ========== 核心类型 ==========
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* 错误消息配置(字符串或对象)
|
|
9
|
-
*
|
|
10
|
-
* @description v1.1.5 新增:支持对象格式配置错误代码和消息
|
|
11
|
-
*
|
|
12
|
-
* @example 字符串格式(向后兼容)
|
|
13
|
-
* ```typescript
|
|
14
|
-
* const messages = {
|
|
15
|
-
* 'user.notFound': '用户不存在'
|
|
16
|
-
* };
|
|
17
|
-
* ```
|
|
18
|
-
*
|
|
19
|
-
* @example 对象格式(v1.1.5 新增)
|
|
20
|
-
* ```typescript
|
|
21
|
-
* const messages = {
|
|
22
|
-
* 'account.notFound': {
|
|
23
|
-
* code: 'ACCOUNT_NOT_FOUND',
|
|
24
|
-
* message: '账户不存在'
|
|
25
|
-
* }
|
|
26
|
-
* };
|
|
27
|
-
* ```
|
|
28
|
-
*
|
|
29
|
-
* @since v1.1.5
|
|
30
|
-
*/
|
|
31
|
-
export type ErrorMessageConfig =
|
|
32
|
-
| string // 向后兼容:'账户不存在'
|
|
33
|
-
| { // 新格式:{ code, message }
|
|
34
|
-
/** 错误代码(可选,默认使用 key) */
|
|
35
|
-
code?: string;
|
|
36
|
-
/** 错误消息(必需) */
|
|
37
|
-
message: string;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* 语言包定义
|
|
42
|
-
*
|
|
43
|
-
* @description 语言包对象,key 为错误代码,value 为错误消息配置
|
|
44
|
-
*
|
|
45
|
-
* @example
|
|
46
|
-
* ```typescript
|
|
47
|
-
* const zhCN: LocaleMessages = {
|
|
48
|
-
* 'user.notFound': '用户不存在',
|
|
49
|
-
* 'account.notFound': {
|
|
50
|
-
* code: 'ACCOUNT_NOT_FOUND',
|
|
51
|
-
* message: '账户不存在'
|
|
52
|
-
* }
|
|
53
|
-
* };
|
|
54
|
-
* ```
|
|
55
|
-
*
|
|
56
|
-
* @since v1.1.5
|
|
57
|
-
*/
|
|
58
|
-
export interface LocaleMessages {
|
|
59
|
-
[key: string]: ErrorMessageConfig;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* JSON Schema 对象
|
|
64
|
-
*
|
|
65
|
-
* @description JSON Schema draft-07 规范的类型定义
|
|
66
|
-
* @see https://json-schema.org/draft-07/schema
|
|
67
|
-
*
|
|
68
|
-
* @example
|
|
69
|
-
* ```typescript
|
|
70
|
-
* const schema: JSONSchema = {
|
|
71
|
-
* type: 'object',
|
|
72
|
-
* properties: {
|
|
73
|
-
* username: { type: 'string', minLength: 3, maxLength: 32 },
|
|
74
|
-
* email: { type: 'string', format: 'email' }
|
|
75
|
-
* },
|
|
76
|
-
* required: ['username', 'email']
|
|
77
|
-
* };
|
|
78
|
-
* ```
|
|
79
|
-
*/
|
|
80
|
-
export interface JSONSchema {
|
|
81
|
-
/** 数据类型 */
|
|
82
|
-
type?: string | string[];
|
|
83
|
-
/** 对象属性定义 */
|
|
84
|
-
properties?: Record<string, JSONSchema>;
|
|
85
|
-
/** 必填字段列表 */
|
|
86
|
-
required?: string[];
|
|
87
|
-
/** 字符串/数组最小长度 */
|
|
88
|
-
minLength?: number;
|
|
89
|
-
/** 字符串/数组最大长度 */
|
|
90
|
-
maxLength?: number;
|
|
91
|
-
/** 数字最小值 */
|
|
92
|
-
minimum?: number;
|
|
93
|
-
/** 数字最大值 */
|
|
94
|
-
maximum?: number;
|
|
95
|
-
/** 正则表达式验证 */
|
|
96
|
-
pattern?: string;
|
|
97
|
-
/** 格式验证(email, url, date等) */
|
|
98
|
-
format?: string;
|
|
99
|
-
/** 枚举值 */
|
|
100
|
-
enum?: any[];
|
|
101
|
-
/** 数组项定义 */
|
|
102
|
-
items?: JSONSchema;
|
|
103
|
-
/** 字段标题 */
|
|
104
|
-
title?: string;
|
|
105
|
-
/** 字段描述 */
|
|
106
|
-
description?: string;
|
|
107
|
-
/** 默认值 */
|
|
108
|
-
default?: any;
|
|
109
|
-
/** 示例值 */
|
|
110
|
-
examples?: any[];
|
|
111
|
-
/** 扩展字段 */
|
|
112
|
-
[key: string]: any;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* 验证结果
|
|
117
|
-
*
|
|
118
|
-
* @description validate()方法的返回值类型
|
|
119
|
-
*
|
|
120
|
-
* @example
|
|
121
|
-
* ```typescript
|
|
122
|
-
* const result: ValidationResult = schema.validate({ username: 'test' });
|
|
123
|
-
*
|
|
124
|
-
* if (result.valid) {
|
|
125
|
-
* console.log('验证通过', result.data);
|
|
126
|
-
* } else {
|
|
127
|
-
* console.log('验证失败', result.errors);
|
|
128
|
-
* }
|
|
129
|
-
* ```
|
|
130
|
-
*/
|
|
131
|
-
export interface ValidationResult<T = any> {
|
|
132
|
-
/** 是否验证通过 */
|
|
133
|
-
valid: boolean;
|
|
134
|
-
/** 验证错误列表(仅在valid=false时存在) */
|
|
135
|
-
errors?: ValidationError[];
|
|
136
|
-
/** 验证后的数据(仅在valid=true时存在) */
|
|
137
|
-
data?: T;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* 验证错误
|
|
142
|
-
*
|
|
143
|
-
* @description 详细的验证错误信息
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* ```typescript
|
|
147
|
-
* const error: ValidationError = {
|
|
148
|
-
* message: '用户名至少需要 3 个字符',
|
|
149
|
-
* path: 'username',
|
|
150
|
-
* keyword: 'minLength',
|
|
151
|
-
* params: { limit: 3 }
|
|
152
|
-
* };
|
|
153
|
-
* ```
|
|
154
|
-
*/
|
|
155
|
-
export interface ValidationError {
|
|
156
|
-
/** 错误消息 */
|
|
157
|
-
message: string;
|
|
158
|
-
/** 错误字段路径(使用点号分隔) */
|
|
159
|
-
path: string;
|
|
160
|
-
/** 验证关键字(min, max, email等) */
|
|
161
|
-
keyword: string;
|
|
162
|
-
/** 验证参数 */
|
|
163
|
-
params?: Record<string, any>;
|
|
164
|
-
/** 错误字段(别名,同path) */
|
|
165
|
-
field?: string;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* 验证选项
|
|
170
|
-
*
|
|
171
|
-
* @description validate() 和 Validator.validate() 的配置选项
|
|
172
|
-
*
|
|
173
|
-
* @example
|
|
174
|
-
* ```typescript
|
|
175
|
-
* const options: ValidateOptions = {
|
|
176
|
-
* format: true,
|
|
177
|
-
* locale: 'zh-CN',
|
|
178
|
-
* messages: {
|
|
179
|
-
* min: '至少需要 {{#limit}} 个字符'
|
|
180
|
-
* }
|
|
181
|
-
* };
|
|
182
|
-
*
|
|
183
|
-
* const result = validate(schema, data, options);
|
|
184
|
-
* ```
|
|
185
|
-
*/
|
|
186
|
-
export interface ValidateOptions {
|
|
187
|
-
/** 是否格式化错误(默认true) */
|
|
188
|
-
format?: boolean;
|
|
189
|
-
/** 动态指定语言(如 'zh-CN', 'en-US', 'ja-JP', 'es-ES', 'fr-FR') */
|
|
190
|
-
locale?: string;
|
|
191
|
-
/** 自定义错误消息 */
|
|
192
|
-
messages?: ErrorMessages;
|
|
193
|
-
/** 扩展选项 */
|
|
194
|
-
[key: string]: any;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/**
|
|
198
|
-
* 错误消息对象
|
|
199
|
-
*
|
|
200
|
-
* @description 自定义错误消息的配置对象
|
|
201
|
-
*
|
|
202
|
-
* @example
|
|
203
|
-
* ```typescript
|
|
204
|
-
* const messages: ErrorMessages = {
|
|
205
|
-
* min: '至少需要 {{#limit}} 个字符',
|
|
206
|
-
* max: '最多 {{#limit}} 个字符',
|
|
207
|
-
* email: '邮箱格式不正确',
|
|
208
|
-
* required: '这是必填项'
|
|
209
|
-
* };
|
|
210
|
-
*
|
|
211
|
-
* const schema = dsl({ email: 'email!' }, { messages });
|
|
212
|
-
* ```
|
|
213
|
-
*/
|
|
214
|
-
export interface ErrorMessages {
|
|
215
|
-
/** 最小长度/最小值错误 (v1.0.3+: 推荐使用min代替minLength) */
|
|
216
|
-
min?: string;
|
|
217
|
-
/** 最大长度/最大值错误 (v1.0.3+: 推荐使用max代替maxLength) */
|
|
218
|
-
max?: string;
|
|
219
|
-
/** 最小长度错误 (向后兼容,推荐使用min) */
|
|
220
|
-
minLength?: string;
|
|
221
|
-
/** 最大长度错误 (向后兼容,推荐使用max) */
|
|
222
|
-
maxLength?: string;
|
|
223
|
-
/** 最小值错误 (向后兼容,推荐使用min) */
|
|
224
|
-
minimum?: string;
|
|
225
|
-
/** 最大值错误 (向后兼容,推荐使用max) */
|
|
226
|
-
maximum?: string;
|
|
227
|
-
/** 数组最小长度错误 (向后兼容,推荐使用min) */
|
|
228
|
-
minItems?: string;
|
|
229
|
-
/** 数组最大长度错误 (向后兼容,推荐使用max) */
|
|
230
|
-
maxItems?: string;
|
|
231
|
-
/** 正则表达式验证错误 */
|
|
232
|
-
pattern?: string;
|
|
233
|
-
/** 格式验证错误 */
|
|
234
|
-
format?: string;
|
|
235
|
-
/** 枚举值验证错误 */
|
|
236
|
-
enum?: string;
|
|
237
|
-
/** 邮箱格式错误 */
|
|
238
|
-
email?: string;
|
|
239
|
-
/** URL格式错误 */
|
|
240
|
-
url?: string;
|
|
241
|
-
/** 必填项错误 */
|
|
242
|
-
required?: string;
|
|
243
|
-
/** 类型错误 */
|
|
244
|
-
type?: string;
|
|
245
|
-
/** 自定义错误消息 */
|
|
246
|
-
[key: string]: string | undefined;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// ========== DslBuilder 类 ==========
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* DSL Builder 类
|
|
253
|
-
*
|
|
254
|
-
* @description 提供链式API来构建Schema定义
|
|
255
|
-
*
|
|
256
|
-
* @example
|
|
257
|
-
* ```typescript
|
|
258
|
-
* // 基础用法
|
|
259
|
-
* const builder = new DslBuilder('email!');
|
|
260
|
-
* builder.pattern(/custom/).label('邮箱地址');
|
|
261
|
-
*
|
|
262
|
-
* // 链式调用
|
|
263
|
-
* const schema = new DslBuilder('string:3-32!')
|
|
264
|
-
* .pattern(/^[a-zA-Z0-9_]+$/, '只能包含字母、数字和下划线')
|
|
265
|
-
* .label('用户名')
|
|
266
|
-
* .messages({
|
|
267
|
-
* min: '至少3个字符',
|
|
268
|
-
* max: '最多32个字符'
|
|
269
|
-
* })
|
|
270
|
-
* .toSchema();
|
|
271
|
-
* ```
|
|
272
|
-
*/
|
|
273
|
-
export class DslBuilder {
|
|
274
|
-
/**
|
|
275
|
-
* 注册自定义类型(供插件使用)
|
|
276
|
-
* @param name - 类型名称
|
|
277
|
-
* @param schema - JSON Schema对象或生成函数
|
|
278
|
-
* @static
|
|
279
|
-
* @since v1.1.0
|
|
280
|
-
*
|
|
281
|
-
* @example
|
|
282
|
-
* ```typescript
|
|
283
|
-
* // 注册自定义类型
|
|
284
|
-
* DslBuilder.registerType('phone-cn', {
|
|
285
|
-
* type: 'string',
|
|
286
|
-
* pattern: '^1[3-9]\\d{9}$',
|
|
287
|
-
* minLength: 11,
|
|
288
|
-
* maxLength: 11
|
|
289
|
-
* });
|
|
290
|
-
*
|
|
291
|
-
* // 在DSL中使用
|
|
292
|
-
* const schema = dsl({ phone: 'phone-cn!' });
|
|
293
|
-
* const schema2 = dsl({ contact: 'types:email|phone-cn' });
|
|
294
|
-
* ```
|
|
295
|
-
*/
|
|
296
|
-
static registerType(name: string, schema: JSONSchema | (() => JSONSchema)): void;
|
|
297
|
-
|
|
298
|
-
/**
|
|
299
|
-
* 检查类型是否已注册
|
|
300
|
-
* @param type - 类型名称
|
|
301
|
-
* @returns 是否已注册
|
|
302
|
-
* @static
|
|
303
|
-
* @since v1.1.0
|
|
304
|
-
*
|
|
305
|
-
* @example
|
|
306
|
-
* ```typescript
|
|
307
|
-
* DslBuilder.hasType('string'); // true (内置)
|
|
308
|
-
* DslBuilder.hasType('phone-cn'); // false (未注册)
|
|
309
|
-
* DslBuilder.registerType('phone-cn', { ... });
|
|
310
|
-
* DslBuilder.hasType('phone-cn'); // true (已注册)
|
|
311
|
-
* ```
|
|
312
|
-
*/
|
|
313
|
-
static hasType(type: string): boolean;
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* 获取所有已注册的自定义类型
|
|
317
|
-
* @returns 自定义类型名称数组
|
|
318
|
-
* @static
|
|
319
|
-
* @since v1.1.0
|
|
320
|
-
*
|
|
321
|
-
* @example
|
|
322
|
-
* ```typescript
|
|
323
|
-
* const types = DslBuilder.getCustomTypes();
|
|
324
|
-
* console.log(types); // ['phone-cn', 'order-id', ...]
|
|
325
|
-
* ```
|
|
326
|
-
*/
|
|
327
|
-
static getCustomTypes(): string[];
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* 清除所有自定义类型(主要用于测试)
|
|
331
|
-
* @static
|
|
332
|
-
* @since v1.1.0
|
|
333
|
-
*
|
|
334
|
-
* @example
|
|
335
|
-
* ```typescript
|
|
336
|
-
* DslBuilder.clearCustomTypes();
|
|
337
|
-
* ```
|
|
338
|
-
*/
|
|
339
|
-
static clearCustomTypes(): void;
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* 构造函数
|
|
343
|
-
* @param dslString - DSL字符串(如 'email!', 'string:3-32!', 'string?', 'types:string|number')
|
|
344
|
-
*
|
|
345
|
-
* @example 基础类型
|
|
346
|
-
* ```typescript
|
|
347
|
-
* const builder = new DslBuilder('email!'); // 必填邮箱
|
|
348
|
-
* const builder2 = new DslBuilder('string:3-32'); // 可选字符串(默认)
|
|
349
|
-
* const builder3 = new DslBuilder('string?'); // 显式可选字符串
|
|
350
|
-
* const builder4 = new DslBuilder('email?'); // 显式可选邮箱
|
|
351
|
-
* const builder5 = new DslBuilder('types:string|number'); // 联合类型
|
|
352
|
-
* ```
|
|
353
|
-
*
|
|
354
|
-
* @example 必填与可选标记
|
|
355
|
-
* ```typescript
|
|
356
|
-
* new DslBuilder('string!') // 必填字符串
|
|
357
|
-
* new DslBuilder('string') // 可选字符串(默认)
|
|
358
|
-
* new DslBuilder('string?') // 显式可选字符串
|
|
359
|
-
* new DslBuilder('email?') // 可选邮箱
|
|
360
|
-
* new DslBuilder('string:3-32?') // 可选字符串,长度3-32
|
|
361
|
-
* ```
|
|
362
|
-
*
|
|
363
|
-
* @example 数字类型比较运算符 (v1.1.2+)
|
|
364
|
-
* ```typescript
|
|
365
|
-
* // 范围约束(包括边界值)
|
|
366
|
-
* new DslBuilder('number:0-100') // 0 <= x <= 100
|
|
367
|
-
* new DslBuilder('number:18-') // x >= 18
|
|
368
|
-
* new DslBuilder('number:-100') // x <= 100
|
|
369
|
-
* new DslBuilder('number:100') // x <= 100
|
|
370
|
-
*
|
|
371
|
-
* // 比较运算符(v1.1.2新增)
|
|
372
|
-
* new DslBuilder('number:>0') // x > 0 (不包括0)
|
|
373
|
-
* new DslBuilder('number:>=18') // x >= 18
|
|
374
|
-
* new DslBuilder('number:<100') // x < 100 (不包括100)
|
|
375
|
-
* new DslBuilder('number:<=100') // x <= 100
|
|
376
|
-
* new DslBuilder('number:=100') // x = 100 (精确等于)
|
|
377
|
-
*
|
|
378
|
-
* // 支持小数和负数
|
|
379
|
-
* new DslBuilder('number:>0.5') // x > 0.5
|
|
380
|
-
* new DslBuilder('number:>-10') // x > -10
|
|
381
|
-
* new DslBuilder('number:<=99.99') // x <= 99.99
|
|
382
|
-
*
|
|
383
|
-
* // 配合必填标记
|
|
384
|
-
* new DslBuilder('number:>=18!') // 必填且 >= 18
|
|
385
|
-
* new DslBuilder('number:>0!') // 必填且 > 0
|
|
386
|
-
* new DslBuilder('number:>0?') // 可选且 > 0(当有值时)
|
|
387
|
-
* ```
|
|
388
|
-
*/
|
|
389
|
-
constructor(dslString: string);
|
|
390
|
-
|
|
391
|
-
/**
|
|
392
|
-
* 添加正则验证
|
|
393
|
-
* @param regex - 正则表达式或字符串
|
|
394
|
-
* @param message - 自定义错误消息
|
|
395
|
-
* @returns 当前实例(支持链式调用)
|
|
396
|
-
*
|
|
397
|
-
* @example
|
|
398
|
-
* ```typescript
|
|
399
|
-
* builder
|
|
400
|
-
* .pattern(/^[a-zA-Z]+$/)
|
|
401
|
-
* .pattern('^\\d{6}$', '必须是6位数字');
|
|
402
|
-
* ```
|
|
403
|
-
*/
|
|
404
|
-
pattern(regex: RegExp | string, message?: string): this;
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* 设置字段标签
|
|
408
|
-
* @param text - 字段的显示名称
|
|
409
|
-
* @returns 当前实例(支持链式调用)
|
|
410
|
-
*
|
|
411
|
-
* @example
|
|
412
|
-
* ```typescript
|
|
413
|
-
* builder.label('用户邮箱');
|
|
414
|
-
* ```
|
|
415
|
-
*/
|
|
416
|
-
label(text: string): this;
|
|
417
|
-
|
|
418
|
-
/**
|
|
419
|
-
* 自定义错误消息
|
|
420
|
-
* @param messages - 错误消息对象
|
|
421
|
-
* @returns 当前实例(支持链式调用)
|
|
422
|
-
*
|
|
423
|
-
* @example
|
|
424
|
-
* ```typescript
|
|
425
|
-
* builder.messages({
|
|
426
|
-
* min: '至少{{#limit}}个字符',
|
|
427
|
-
* required: '这是必填项'
|
|
428
|
-
* });
|
|
429
|
-
* ```
|
|
430
|
-
*/
|
|
431
|
-
messages(messages: ErrorMessages): this;
|
|
432
|
-
|
|
433
|
-
/**
|
|
434
|
-
* 设置描述
|
|
435
|
-
* @param text - 字段描述文本
|
|
436
|
-
* @returns 当前实例(支持链式调用)
|
|
437
|
-
*
|
|
438
|
-
* @example
|
|
439
|
-
* ```typescript
|
|
440
|
-
* builder.description('用户的注册邮箱');
|
|
441
|
-
* ```
|
|
442
|
-
*/
|
|
443
|
-
description(text: string): this;
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* 添加自定义验证器
|
|
447
|
-
* @param validator - 验证函数
|
|
448
|
-
* @returns 当前实例(支持链式调用)
|
|
449
|
-
*
|
|
450
|
-
* @example
|
|
451
|
-
* ```typescript
|
|
452
|
-
* builder.custom((value) => {
|
|
453
|
-
* return value.includes('@');
|
|
454
|
-
* });
|
|
455
|
-
*
|
|
456
|
-
* // 异步验证
|
|
457
|
-
* builder.custom(async (value) => {
|
|
458
|
-
* const exists = await checkEmailExists(value);
|
|
459
|
-
* return !exists;
|
|
460
|
-
* });
|
|
461
|
-
*
|
|
462
|
-
* // 返回错误对象
|
|
463
|
-
* builder.custom((value) => {
|
|
464
|
-
* if (!value.includes('@')) {
|
|
465
|
-
* return { error: 'EMAIL_INVALID', message: '邮箱格式不正确' };
|
|
466
|
-
* }
|
|
467
|
-
* return true;
|
|
468
|
-
* });
|
|
469
|
-
* ```
|
|
470
|
-
*/
|
|
471
|
-
custom(validator: (value: any) => boolean | Promise<boolean> | { error: string; message: string }): this;
|
|
472
|
-
|
|
473
|
-
/**
|
|
474
|
-
* 条件验证
|
|
475
|
-
* @param refField - 参考字段
|
|
476
|
-
* @param options - 条件选项
|
|
477
|
-
* @returns 当前实例(支持链式调用)
|
|
478
|
-
*
|
|
479
|
-
* @example
|
|
480
|
-
* ```typescript
|
|
481
|
-
* // 当userType=admin时,email必填
|
|
482
|
-
* dsl({
|
|
483
|
-
* userType: 'string',
|
|
484
|
-
* email: 'email'.when('userType', {
|
|
485
|
-
* is: 'admin',
|
|
486
|
-
* then: dsl('email!'),
|
|
487
|
-
* otherwise: dsl('email')
|
|
488
|
-
* })
|
|
489
|
-
* });
|
|
490
|
-
* ```
|
|
491
|
-
*/
|
|
492
|
-
// ⚠️ DEPRECATED: .when() method removed - use dsl.if() instead
|
|
493
|
-
// when(refField: string, options: { is: any; then: DslBuilder | JSONSchema; otherwise?: DslBuilder | JSONSchema; }): this;
|
|
494
|
-
|
|
495
|
-
/**
|
|
496
|
-
* 设置默认值
|
|
497
|
-
* @param value - 默认值
|
|
498
|
-
* @returns 当前实例(支持链式调用)
|
|
499
|
-
*
|
|
500
|
-
* @example
|
|
501
|
-
* ```typescript
|
|
502
|
-
* builder.default('user@example.com');
|
|
503
|
-
* ```
|
|
504
|
-
*/
|
|
505
|
-
default(value: any): this;
|
|
506
|
-
|
|
507
|
-
/**
|
|
508
|
-
* 转为JSON Schema
|
|
509
|
-
* @returns JSON Schema对象
|
|
510
|
-
*
|
|
511
|
-
* @example
|
|
512
|
-
* ```typescript
|
|
513
|
-
* const schema = builder.toSchema();
|
|
514
|
-
* console.log(schema);
|
|
515
|
-
* // { type: 'string', format: 'email', ... }
|
|
516
|
-
* ```
|
|
517
|
-
*/
|
|
518
|
-
toSchema(): JSONSchema;
|
|
519
|
-
|
|
520
|
-
/**
|
|
521
|
-
* 验证数据
|
|
522
|
-
* @param data - 要验证的数据
|
|
523
|
-
* @param context - 验证上下文(可选)
|
|
524
|
-
* @returns 验证结果的Promise
|
|
525
|
-
*
|
|
526
|
-
* @example
|
|
527
|
-
* ```typescript
|
|
528
|
-
* const result = await builder.validate({ email: 'test@example.com' });
|
|
529
|
-
* if (result.valid) {
|
|
530
|
-
* console.log('验证通过');
|
|
531
|
-
* }
|
|
532
|
-
* ```
|
|
533
|
-
*/
|
|
534
|
-
validate(data: any, context?: any): Promise<ValidationResult>;
|
|
535
|
-
|
|
536
|
-
/**
|
|
537
|
-
* 用户名验证(自动设置合理约束)
|
|
538
|
-
* @param preset - 预设配置
|
|
539
|
-
* @returns 当前实例(支持链式调用)
|
|
540
|
-
*
|
|
541
|
-
* @example
|
|
542
|
-
* ```typescript
|
|
543
|
-
* // 预设方式
|
|
544
|
-
* builder.username('short'); // 3-16字符
|
|
545
|
-
* builder.username('medium'); // 3-32字符
|
|
546
|
-
* builder.username('long'); // 3-64字符
|
|
547
|
-
*
|
|
548
|
-
* // 范围字符串
|
|
549
|
-
* builder.username('5-20');
|
|
550
|
-
*
|
|
551
|
-
* // 详细配置
|
|
552
|
-
* builder.username({
|
|
553
|
-
* minLength: 5,
|
|
554
|
-
* maxLength: 20,
|
|
555
|
-
* allowUnderscore: true,
|
|
556
|
-
* allowNumber: true
|
|
557
|
-
* });
|
|
558
|
-
* ```
|
|
559
|
-
*/
|
|
560
|
-
username(preset?: 'short' | 'medium' | 'long' | string | { minLength?: number; maxLength?: number; allowUnderscore?: boolean; allowNumber?: boolean }): this;
|
|
561
|
-
|
|
562
|
-
/**
|
|
563
|
-
* 密码强度验证
|
|
564
|
-
* @param strength - 密码强度等级
|
|
565
|
-
* @returns 当前实例(支持链式调用)
|
|
566
|
-
*
|
|
567
|
-
* @example
|
|
568
|
-
* ```typescript
|
|
569
|
-
* builder.password('weak'); // 6+ 字符
|
|
570
|
-
* builder.password('medium'); // 8+ 字符,包含大小写
|
|
571
|
-
* builder.password('strong'); // 10+ 字符,包含大小写+数字
|
|
572
|
-
* builder.password('veryStrong'); // 12+ 字符,包含大小写+数字+特殊字符
|
|
573
|
-
* ```
|
|
574
|
-
*/
|
|
575
|
-
password(strength?: 'weak' | 'medium' | 'strong' | 'veryStrong'): this;
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* 手机号验证
|
|
579
|
-
* @param country - 国家代码
|
|
580
|
-
* @returns 当前实例(支持链式调用)
|
|
581
|
-
*
|
|
582
|
-
* @example
|
|
583
|
-
* ```typescript
|
|
584
|
-
* builder.phone('cn'); // 中国大陆 (11位)
|
|
585
|
-
* builder.phone('us'); // 美国
|
|
586
|
-
* builder.phone('hk'); // 香港
|
|
587
|
-
* builder.phone('tw'); // 台湾
|
|
588
|
-
* builder.phone('international'); // 国际号码
|
|
589
|
-
* ```
|
|
590
|
-
*/
|
|
591
|
-
phone(country?: 'cn' | 'us' | 'uk' | 'hk' | 'tw' | 'international'): this;
|
|
592
|
-
|
|
593
|
-
/**
|
|
594
|
-
* 设置格式
|
|
595
|
-
* @param format - 格式名称 (email, url, uuid, date, date-time, time, ipv4, ipv6等)
|
|
596
|
-
* @returns 当前实例(支持链式调用)
|
|
597
|
-
*
|
|
598
|
-
* @example
|
|
599
|
-
* ```typescript
|
|
600
|
-
* builder.format('email');
|
|
601
|
-
* builder.format('uuid');
|
|
602
|
-
* builder.format('date-time');
|
|
603
|
-
* ```
|
|
604
|
-
*/
|
|
605
|
-
format(format: string): this;
|
|
606
|
-
|
|
607
|
-
/**
|
|
608
|
-
* 手机号别名(phoneNumber是phone的别名)
|
|
609
|
-
* @param country - 国家代码
|
|
610
|
-
* @returns 当前实例(支持链式调用)
|
|
611
|
-
*
|
|
612
|
-
* @example
|
|
613
|
-
* ```typescript
|
|
614
|
-
* builder.phoneNumber('cn'); // 等同于 phone('cn')
|
|
615
|
-
* ```
|
|
616
|
-
*/
|
|
617
|
-
phoneNumber(country?: 'cn' | 'us' | 'uk' | 'hk' | 'tw' | 'international'): this;
|
|
618
|
-
|
|
619
|
-
/**
|
|
620
|
-
* 身份证验证
|
|
621
|
-
* @param country - 国家代码(目前仅支持 'cn')
|
|
622
|
-
* @returns 当前实例(支持链式调用)
|
|
623
|
-
*
|
|
624
|
-
* @example
|
|
625
|
-
* ```typescript
|
|
626
|
-
* builder.idCard('cn'); // 中国身份证18位
|
|
627
|
-
* ```
|
|
628
|
-
*/
|
|
629
|
-
idCard(country?: 'cn'): this;
|
|
630
|
-
|
|
631
|
-
/**
|
|
632
|
-
* URL Slug 验证(只能包含小写字母、数字和连字符)
|
|
633
|
-
* @returns 当前实例(支持链式调用)
|
|
634
|
-
*
|
|
635
|
-
* @example
|
|
636
|
-
* ```typescript
|
|
637
|
-
* builder.slug(); // my-blog-post, hello-world-123
|
|
638
|
-
* ```
|
|
639
|
-
*/
|
|
640
|
-
slug(): this;
|
|
641
|
-
|
|
642
|
-
/**
|
|
643
|
-
* 信用卡验证
|
|
644
|
-
* @param type - 卡类型
|
|
645
|
-
* @returns 当前实例(支持链式调用)
|
|
646
|
-
*
|
|
647
|
-
* @example
|
|
648
|
-
* ```typescript
|
|
649
|
-
* builder.creditCard('visa');
|
|
650
|
-
* builder.creditCard('mastercard');
|
|
651
|
-
* builder.creditCard('amex');
|
|
652
|
-
* ```
|
|
653
|
-
*/
|
|
654
|
-
creditCard(type?: 'visa' | 'mastercard' | 'amex' | 'discover' | 'jcb' | 'unionpay'): this;
|
|
655
|
-
|
|
656
|
-
/**
|
|
657
|
-
* 车牌号验证
|
|
658
|
-
* @param country - 国家代码
|
|
659
|
-
* @returns 当前实例(支持链式调用)
|
|
660
|
-
*
|
|
661
|
-
* @example
|
|
662
|
-
* ```typescript
|
|
663
|
-
* builder.licensePlate('cn'); // 中国车牌号
|
|
664
|
-
* ```
|
|
665
|
-
*/
|
|
666
|
-
licensePlate(country?: 'cn' | 'us' | 'uk'): this;
|
|
667
|
-
|
|
668
|
-
/**
|
|
669
|
-
* 邮政编码验证
|
|
670
|
-
* @param country - 国家代码
|
|
671
|
-
* @returns 当前实例(支持链式调用)
|
|
672
|
-
*
|
|
673
|
-
* @example
|
|
674
|
-
* ```typescript
|
|
675
|
-
* builder.postalCode('cn'); // 中国邮政编码6位
|
|
676
|
-
* builder.postalCode('us'); // 美国邮政编码
|
|
677
|
-
* ```
|
|
678
|
-
*/
|
|
679
|
-
postalCode(country?: 'cn' | 'us' | 'uk'): this;
|
|
680
|
-
|
|
681
|
-
/**
|
|
682
|
-
* 护照号码验证
|
|
683
|
-
* @param country - 国家代码
|
|
684
|
-
* @returns 当前实例(支持链式调用)
|
|
685
|
-
*
|
|
686
|
-
* @example
|
|
687
|
-
* ```typescript
|
|
688
|
-
* builder.passport('cn'); // 中国护照号
|
|
689
|
-
* ```
|
|
690
|
-
*/
|
|
691
|
-
passport(country?: 'cn' | 'us' | 'uk'): this;
|
|
692
|
-
|
|
693
|
-
/**
|
|
694
|
-
* String 最小长度(使用AJV原生minLength)
|
|
695
|
-
* @param n - 最小长度
|
|
696
|
-
* @returns 当前实例(支持链式调用)
|
|
697
|
-
*
|
|
698
|
-
* @example
|
|
699
|
-
* ```typescript
|
|
700
|
-
* dsl('string!').min(3); // 最少3个字符
|
|
701
|
-
* ```
|
|
702
|
-
*/
|
|
703
|
-
min(n: number): this;
|
|
704
|
-
|
|
705
|
-
/**
|
|
706
|
-
* String 最大长度(使用AJV原生maxLength)
|
|
707
|
-
* @param n - 最大长度
|
|
708
|
-
* @returns 当前实例(支持链式调用)
|
|
709
|
-
*
|
|
710
|
-
* @example
|
|
711
|
-
* ```typescript
|
|
712
|
-
* dsl('string!').max(32); // 最多32个字符
|
|
713
|
-
* ```
|
|
714
|
-
*/
|
|
715
|
-
max(n: number): this;
|
|
716
|
-
|
|
717
|
-
/**
|
|
718
|
-
* String 精确长度
|
|
719
|
-
* @param n - 精确长度
|
|
720
|
-
* @returns 当前实例(支持链式调用)
|
|
721
|
-
*
|
|
722
|
-
* @example
|
|
723
|
-
* ```typescript
|
|
724
|
-
* dsl('string!').length(11); // 必须是11个字符
|
|
725
|
-
* ```
|
|
726
|
-
*/
|
|
727
|
-
length(n: number): this;
|
|
728
|
-
|
|
729
|
-
/**
|
|
730
|
-
* String 只能包含字母和数字
|
|
731
|
-
* @returns 当前实例(支持链式调用)
|
|
732
|
-
*
|
|
733
|
-
* @example
|
|
734
|
-
* ```typescript
|
|
735
|
-
* dsl('string!').alphanum(); // 只能是字母和数字
|
|
736
|
-
* ```
|
|
737
|
-
*/
|
|
738
|
-
alphanum(): this;
|
|
739
|
-
|
|
740
|
-
/**
|
|
741
|
-
* String 不能包含前后空格
|
|
742
|
-
* @returns 当前实例(支持链式调用)
|
|
743
|
-
*
|
|
744
|
-
* @example
|
|
745
|
-
* ```typescript
|
|
746
|
-
* dsl('string!').trim(); // 不能有前后空格
|
|
747
|
-
* ```
|
|
748
|
-
*/
|
|
749
|
-
trim(): this;
|
|
750
|
-
|
|
751
|
-
/**
|
|
752
|
-
* String 必须是小写
|
|
753
|
-
* @returns 当前实例(支持链式调用)
|
|
754
|
-
*
|
|
755
|
-
* @example
|
|
756
|
-
* ```typescript
|
|
757
|
-
* dsl('string!').lowercase(); // 必须全小写
|
|
758
|
-
* ```
|
|
759
|
-
*/
|
|
760
|
-
lowercase(): this;
|
|
761
|
-
|
|
762
|
-
/**
|
|
763
|
-
* String 必须是大写
|
|
764
|
-
* @returns 当前实例(支持链式调用)
|
|
765
|
-
*
|
|
766
|
-
* @example
|
|
767
|
-
* ```typescript
|
|
768
|
-
* dsl('string!').uppercase(); // 必须全大写
|
|
769
|
-
* ```
|
|
770
|
-
*/
|
|
771
|
-
uppercase(): this;
|
|
772
|
-
|
|
773
|
-
/**
|
|
774
|
-
* Number 小数位数限制
|
|
775
|
-
* @param n - 最大小数位数
|
|
776
|
-
* @returns 当前实例(支持链式调用)
|
|
777
|
-
*
|
|
778
|
-
* @example
|
|
779
|
-
* ```typescript
|
|
780
|
-
* dsl('number!').precision(2); // 最多2位小数
|
|
781
|
-
* ```
|
|
782
|
-
*/
|
|
783
|
-
precision(n: number): this;
|
|
784
|
-
|
|
785
|
-
/**
|
|
786
|
-
* Number 倍数验证(使用AJV原生multipleOf)
|
|
787
|
-
* @param n - 必须是此数的倍数
|
|
788
|
-
* @returns 当前实例(支持链式调用)
|
|
789
|
-
*
|
|
790
|
-
* @example
|
|
791
|
-
* ```typescript
|
|
792
|
-
* dsl('number!').multiple(5); // 必须是5的倍数
|
|
793
|
-
* ```
|
|
794
|
-
*/
|
|
795
|
-
multiple(n: number): this;
|
|
796
|
-
|
|
797
|
-
/**
|
|
798
|
-
* Number 端口号验证(1-65535)
|
|
799
|
-
* @returns 当前实例(支持链式调用)
|
|
800
|
-
*
|
|
801
|
-
* @example
|
|
802
|
-
* ```typescript
|
|
803
|
-
* dsl('integer!').port(); // 必须是有效端口号
|
|
804
|
-
* ```
|
|
805
|
-
*/
|
|
806
|
-
port(): this;
|
|
807
|
-
|
|
808
|
-
/**
|
|
809
|
-
* Object 要求所有属性都必须存在
|
|
810
|
-
* @returns 当前实例(支持链式调用)
|
|
811
|
-
*
|
|
812
|
-
* @example
|
|
813
|
-
* ```typescript
|
|
814
|
-
* dsl({ name: 'string', age: 'number' }).requireAll();
|
|
815
|
-
* ```
|
|
816
|
-
*/
|
|
817
|
-
requireAll(): this;
|
|
818
|
-
|
|
819
|
-
/**
|
|
820
|
-
* Object 严格模式,不允许额外属性
|
|
821
|
-
* @returns 当前实例(支持链式调用)
|
|
822
|
-
*
|
|
823
|
-
* @example
|
|
824
|
-
* ```typescript
|
|
825
|
-
* dsl({ name: 'string!' }).strict();
|
|
826
|
-
* ```
|
|
827
|
-
*/
|
|
828
|
-
strict(): this;
|
|
829
|
-
|
|
830
|
-
/**
|
|
831
|
-
* Array 不允许稀疏数组
|
|
832
|
-
* @returns 当前实例(支持链式调用)
|
|
833
|
-
*
|
|
834
|
-
* @example
|
|
835
|
-
* ```typescript
|
|
836
|
-
* dsl('array<string>').noSparse();
|
|
837
|
-
* ```
|
|
838
|
-
*/
|
|
839
|
-
noSparse(): this;
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* Array 必须包含指定元素
|
|
843
|
-
* @param items - 必须包含的元素数组
|
|
844
|
-
* @returns 当前实例(支持链式调用)
|
|
845
|
-
*
|
|
846
|
-
* @example
|
|
847
|
-
* ```typescript
|
|
848
|
-
* dsl('array<string>').includesRequired(['admin', 'user']);
|
|
849
|
-
* ```
|
|
850
|
-
*/
|
|
851
|
-
includesRequired(items: any[]): this;
|
|
852
|
-
|
|
853
|
-
/**
|
|
854
|
-
* Date 自定义日期格式验证
|
|
855
|
-
* @param fmt - 日期格式(YYYY-MM-DD, YYYY/MM/DD, DD-MM-YYYY, DD/MM/YYYY, ISO8601)
|
|
856
|
-
* @returns 当前实例(支持链式调用)
|
|
857
|
-
*
|
|
858
|
-
* @example
|
|
859
|
-
* ```typescript
|
|
860
|
-
* dsl('string!').dateFormat('YYYY-MM-DD');
|
|
861
|
-
* ```
|
|
862
|
-
*/
|
|
863
|
-
dateFormat(fmt: 'YYYY-MM-DD' | 'YYYY/MM/DD' | 'DD-MM-YYYY' | 'DD/MM/YYYY' | 'ISO8601' | string): this;
|
|
864
|
-
|
|
865
|
-
/**
|
|
866
|
-
* Date 必须晚于指定日期
|
|
867
|
-
* @param date - 比较日期
|
|
868
|
-
* @returns 当前实例(支持链式调用)
|
|
869
|
-
*
|
|
870
|
-
* @example
|
|
871
|
-
* ```typescript
|
|
872
|
-
* dsl('date!').after('2024-01-01');
|
|
873
|
-
* ```
|
|
874
|
-
*/
|
|
875
|
-
after(date: string): this;
|
|
876
|
-
|
|
877
|
-
/**
|
|
878
|
-
* Date 必须早于指定日期
|
|
879
|
-
* @param date - 比较日期
|
|
880
|
-
* @returns 当前实例(支持链式调用)
|
|
881
|
-
*
|
|
882
|
-
* @example
|
|
883
|
-
* ```typescript
|
|
884
|
-
* dsl('date!').before('2025-12-31');
|
|
885
|
-
* ```
|
|
886
|
-
*/
|
|
887
|
-
before(date: string): this;
|
|
888
|
-
|
|
889
|
-
/**
|
|
890
|
-
* Pattern 域名验证
|
|
891
|
-
* @returns 当前实例(支持链式调用)
|
|
892
|
-
*
|
|
893
|
-
* @example
|
|
894
|
-
* ```typescript
|
|
895
|
-
* dsl('string!').domain(); // example.com, sub.example.com
|
|
896
|
-
* ```
|
|
897
|
-
*/
|
|
898
|
-
domain(): this;
|
|
899
|
-
|
|
900
|
-
/**
|
|
901
|
-
* Pattern IP地址验证(IPv4或IPv6)
|
|
902
|
-
* @returns 当前实例(支持链式调用)
|
|
903
|
-
*
|
|
904
|
-
* @example
|
|
905
|
-
* ```typescript
|
|
906
|
-
* dsl('string!').ip(); // 192.168.1.1 或 2001:0db8:85a3::8a2e:0370:7334
|
|
907
|
-
* ```
|
|
908
|
-
*/
|
|
909
|
-
ip(): this;
|
|
910
|
-
|
|
911
|
-
/**
|
|
912
|
-
* Pattern Base64编码验证
|
|
913
|
-
* @returns 当前实例(支持链式调用)
|
|
914
|
-
*
|
|
915
|
-
* @example
|
|
916
|
-
* ```typescript
|
|
917
|
-
* dsl('string!').base64();
|
|
918
|
-
* ```
|
|
919
|
-
*/
|
|
920
|
-
base64(): this;
|
|
921
|
-
|
|
922
|
-
/**
|
|
923
|
-
* Pattern JWT令牌验证
|
|
924
|
-
* @returns 当前实例(支持链式调用)
|
|
925
|
-
*
|
|
926
|
-
* @example
|
|
927
|
-
* ```typescript
|
|
928
|
-
* dsl('string!').jwt();
|
|
929
|
-
* ```
|
|
930
|
-
*/
|
|
931
|
-
jwt(): this;
|
|
932
|
-
|
|
933
|
-
/**
|
|
934
|
-
* Pattern JSON字符串验证
|
|
935
|
-
* @returns 当前实例(支持链式调用)
|
|
936
|
-
*
|
|
937
|
-
* @example
|
|
938
|
-
* ```typescript
|
|
939
|
-
* dsl('string!').json();
|
|
940
|
-
* ```
|
|
941
|
-
*/
|
|
942
|
-
json(): this;
|
|
943
|
-
|
|
944
|
-
/**
|
|
945
|
-
* 日期大于验证
|
|
946
|
-
* @param date - 对比日期
|
|
947
|
-
* @returns 当前实例(支持链式调用)
|
|
948
|
-
*
|
|
949
|
-
* @example
|
|
950
|
-
* ```typescript
|
|
951
|
-
* dsl('string!').dateGreater('2025-01-01');
|
|
952
|
-
* ```
|
|
953
|
-
*/
|
|
954
|
-
dateGreater(date: string): this;
|
|
955
|
-
|
|
956
|
-
/**
|
|
957
|
-
* 日期小于验证
|
|
958
|
-
* @param date - 对比日期
|
|
959
|
-
* @returns 当前实例(支持链式调用)
|
|
960
|
-
*
|
|
961
|
-
* @example
|
|
962
|
-
* ```typescript
|
|
963
|
-
* dsl('string!').dateLess('2025-12-31');
|
|
964
|
-
* ```
|
|
965
|
-
*/
|
|
966
|
-
dateLess(date: string): this;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
// ========== String 扩展 ==========
|
|
970
|
-
|
|
971
|
-
/**
|
|
972
|
-
* String 扩展全局接口
|
|
973
|
-
*
|
|
974
|
-
* ⚠️ TypeScript 用户注意事项
|
|
975
|
-
*
|
|
976
|
-
* 由于 TypeScript 对全局扩展的类型推导限制,在 .ts 文件中使用链式调用时,
|
|
977
|
-
* 推荐使用 dsl() 函数包裹字符串以获得完整的类型提示:
|
|
978
|
-
*
|
|
979
|
-
* @example
|
|
980
|
-
* ```typescript
|
|
981
|
-
* // ❌ 不推荐:可能缺少类型提示
|
|
982
|
-
* const schema = dsl({
|
|
983
|
-
* email: 'email!'.label('邮箱') // TypeScript 可能无法推导
|
|
984
|
-
* });
|
|
985
|
-
*
|
|
986
|
-
* // ✅ 推荐:使用 dsl() 包裹获得完整类型推导
|
|
987
|
-
* const schema = dsl({
|
|
988
|
-
* email: dsl('email!').label('邮箱').pattern(/custom/)
|
|
989
|
-
* });
|
|
990
|
-
*
|
|
991
|
-
* // ✅ 也可以:先定义再使用
|
|
992
|
-
* const emailField = dsl('email!').label('邮箱');
|
|
993
|
-
* const schema = dsl({ email: emailField });
|
|
994
|
-
*
|
|
995
|
-
* // 📝 JavaScript 用户不受影响,可以直接使用
|
|
996
|
-
* const schema = dsl({
|
|
997
|
-
* email: 'email!'.label('邮箱') // JavaScript 中完全正常
|
|
998
|
-
* });
|
|
999
|
-
* ```
|
|
1000
|
-
*/
|
|
1001
|
-
|
|
1002
|
-
// ========== String 扩展说明 ==========
|
|
1003
|
-
|
|
1004
|
-
/**
|
|
1005
|
-
* ⚠️ String 原型扩展的 TypeScript 限制
|
|
1006
|
-
*
|
|
1007
|
-
* 本库在运行时扩展了 String.prototype,允许在 JavaScript 中直接链式调用:
|
|
1008
|
-
* ```javascript
|
|
1009
|
-
* const schema = dsl({ email: 'email!'.label('邮箱') }); // ✅ JavaScript 中完全正常
|
|
1010
|
-
* ```
|
|
1011
|
-
*
|
|
1012
|
-
* 但在 TypeScript 中,为了**避免污染全局 String 类型**(会导致原生方法如 trim() 的类型推断错误),
|
|
1013
|
-
* 我们**不提供**全局 String 接口扩展。
|
|
1014
|
-
*
|
|
1015
|
-
* TypeScript 用户请使用以下方式:
|
|
1016
|
-
*
|
|
1017
|
-
* @example
|
|
1018
|
-
* ```typescript
|
|
1019
|
-
* import { dsl } from 'schema-dsl';
|
|
1020
|
-
*
|
|
1021
|
-
* // ✅ 推荐:使用 dsl() 函数获得完整类型提示
|
|
1022
|
-
* const schema = dsl({
|
|
1023
|
-
* email: dsl('email!').label('邮箱').pattern(/custom/)
|
|
1024
|
-
* });
|
|
1025
|
-
*
|
|
1026
|
-
* // ✅ 或者先定义再使用
|
|
1027
|
-
* const emailField = dsl('email!').label('邮箱');
|
|
1028
|
-
* const schema = dsl({ email: emailField });
|
|
1029
|
-
*
|
|
1030
|
-
* // ❌ 避免:在 TypeScript 中直接对字符串字面量链式调用
|
|
1031
|
-
* // 这在运行时可以工作,但 TypeScript 无法提供类型提示
|
|
1032
|
-
* const schema = dsl({
|
|
1033
|
-
* email: 'email!'.label('邮箱') // TypeScript: 类型错误
|
|
1034
|
-
* });
|
|
1035
|
-
* ```
|
|
1036
|
-
*
|
|
1037
|
-
* 📝 说明:
|
|
1038
|
-
* - JavaScript 用户不受影响,可以直接使用字符串链式调用
|
|
1039
|
-
* - TypeScript 用户应使用 dsl() 函数包裹字符串以获得类型提示
|
|
1040
|
-
* - 移除全局 String 扩展是为了防止污染原生 String 方法的类型定义
|
|
1041
|
-
*/
|
|
1042
|
-
|
|
1043
|
-
// ========== dsl() 函数 ==========
|
|
1044
|
-
|
|
1045
|
-
/**
|
|
1046
|
-
* DSL 定义对象
|
|
1047
|
-
*
|
|
1048
|
-
* @description 支持多种类型的Schema定义
|
|
1049
|
-
*/
|
|
1050
|
-
export type DslDefinition = string | DslBuilder | {
|
|
1051
|
-
[key: string]: DslDefinition;
|
|
1052
|
-
};
|
|
1053
|
-
|
|
1054
|
-
/**
|
|
1055
|
-
* SchemaIO 配置选项
|
|
1056
|
-
*
|
|
1057
|
-
* @description 用于配置验证器和错误消息的选项
|
|
1058
|
-
*
|
|
1059
|
-
* @example
|
|
1060
|
-
* ```typescript
|
|
1061
|
-
* const options: SchemaIOOptions = {
|
|
1062
|
-
* allErrors: true,
|
|
1063
|
-
* messages: {
|
|
1064
|
-
* min: '至少需要 {{#limit}} 个字符',
|
|
1065
|
-
* required: '这是必填项'
|
|
1066
|
-
* },
|
|
1067
|
-
* locale: 'zh-CN'
|
|
1068
|
-
* };
|
|
1069
|
-
* ```
|
|
1070
|
-
*/
|
|
1071
|
-
export interface SchemaIOOptions {
|
|
1072
|
-
/** 是否返回所有错误(默认false,只返回第一个) */
|
|
1073
|
-
allErrors?: boolean;
|
|
1074
|
-
/** 是否启用详细模式 */
|
|
1075
|
-
verbose?: boolean;
|
|
1076
|
-
/** 自定义错误消息 */
|
|
1077
|
-
messages?: ErrorMessages;
|
|
1078
|
-
/** 语言代码 */
|
|
1079
|
-
locale?: string;
|
|
1080
|
-
/** 是否启用缓存 */
|
|
1081
|
-
cache?: boolean;
|
|
1082
|
-
/** 缓存大小限制 */
|
|
1083
|
-
cacheSize?: number;
|
|
1084
|
-
/** 扩展选项 */
|
|
1085
|
-
[key: string]: any;
|
|
1086
|
-
}
|
|
1087
|
-
|
|
1088
|
-
/**
|
|
1089
|
-
* dsl() 函数(主入口)
|
|
1090
|
-
*
|
|
1091
|
-
* @description schema-dsl 的核心函数,用于创建 Schema 定义
|
|
1092
|
-
*
|
|
1093
|
-
* @example
|
|
1094
|
-
* ```typescript
|
|
1095
|
-
* // 1. 字符串:返回 DslBuilder(用于进一步配置)
|
|
1096
|
-
* const builder = dsl('email!');
|
|
1097
|
-
* builder.label('邮箱地址').messages({ required: '必填' });
|
|
1098
|
-
*
|
|
1099
|
-
* // 2. 对象:返回 JSON Schema(用于验证)
|
|
1100
|
-
* const schema = dsl({
|
|
1101
|
-
* username: 'string:3-32!',
|
|
1102
|
-
* email: 'email!',
|
|
1103
|
-
* age: 'number:18-100'
|
|
1104
|
-
* });
|
|
1105
|
-
*
|
|
1106
|
-
* // 3. 带选项的对象
|
|
1107
|
-
* const schema = dsl({
|
|
1108
|
-
* username: 'string:3-32!'
|
|
1109
|
-
* }, {
|
|
1110
|
-
* allErrors: true,
|
|
1111
|
-
* messages: {
|
|
1112
|
-
* min: '至少需要 {{#limit}} 个字符'
|
|
1113
|
-
* }
|
|
1114
|
-
* });
|
|
1115
|
-
*
|
|
1116
|
-
* // 4. 验证数据(使用顶层 validate 函数)
|
|
1117
|
-
* const result = validate(schema, { username: 'test' });
|
|
1118
|
-
* ```
|
|
1119
|
-
*/
|
|
1120
|
-
export function dsl(definition: string): DslBuilder;
|
|
1121
|
-
export function dsl(definition: Record<string, DslDefinition>, options?: SchemaIOOptions): JSONSchema;
|
|
1122
|
-
export function dsl(definition: string | Record<string, DslDefinition>, options?: SchemaIOOptions): DslBuilder | JSONSchema;
|
|
1123
|
-
|
|
1124
|
-
/**
|
|
1125
|
-
* 全局配置
|
|
1126
|
-
*
|
|
1127
|
-
* @description dsl命名空间的全局配置和工具方法
|
|
1128
|
-
*
|
|
1129
|
-
* @remark 注意:dsl() 返回的是 JSONSchema 对象,不是类实例
|
|
1130
|
-
* 验证时使用顶层函数 validate(schema, data) 而非 schema.validate(data)
|
|
1131
|
-
*/
|
|
1132
|
-
export namespace dsl {
|
|
1133
|
-
/**
|
|
1134
|
-
* 条件规则 (if / _if)
|
|
1135
|
-
*
|
|
1136
|
-
* @description 支持两种方式的条件判断
|
|
1137
|
-
*
|
|
1138
|
-
* 注意:在 TypeScript 中因为 if 是保留字,请使用 dsl['if'] 或 dsl._if
|
|
1139
|
-
*
|
|
1140
|
-
* @example TypeScript 用法
|
|
1141
|
-
* ```typescript
|
|
1142
|
-
* // 使用字符串索引访问
|
|
1143
|
-
* const schema = dsl({
|
|
1144
|
-
* isVip: 'boolean',
|
|
1145
|
-
* discount: dsl['if']('isVip', 'number:10-50!', 'number:0-10')
|
|
1146
|
-
* });
|
|
1147
|
-
*
|
|
1148
|
-
* // 或使用 _if 别名
|
|
1149
|
-
* const schema2 = dsl({
|
|
1150
|
-
* email: dsl._if(d => d.age >= 18).then('email!').else('email')
|
|
1151
|
-
* });
|
|
1152
|
-
* ```
|
|
1153
|
-
*
|
|
1154
|
-
* @example JavaScript 用法
|
|
1155
|
-
* ```javascript
|
|
1156
|
-
* // JavaScript 中可以直接使用 dsl.if
|
|
1157
|
-
* const schema = dsl({
|
|
1158
|
-
* discount: dsl.if('isVip', 'number:10-50!', 'number:0-10')
|
|
1159
|
-
* });
|
|
1160
|
-
* ```
|
|
1161
|
-
*/
|
|
1162
|
-
export { _if as if };
|
|
1163
|
-
export function _if(condition: (data: any) => boolean): ConditionalBuilder;
|
|
1164
|
-
export function _if(conditionField: string, thenSchema: string | DslBuilder | JSONSchema, elseSchema?: string | DslBuilder | JSONSchema): any;
|
|
1165
|
-
|
|
1166
|
-
/**
|
|
1167
|
-
* 全局配置
|
|
1168
|
-
*
|
|
1169
|
-
* @description 配置全局的验证规则和语言包
|
|
1170
|
-
*
|
|
1171
|
-
* @example
|
|
1172
|
-
* ```typescript
|
|
1173
|
-
* // 方式 1: 使用 i18n 配置(推荐,v1.0.4+)
|
|
1174
|
-
* dsl.config({
|
|
1175
|
-
* i18n: {
|
|
1176
|
-
* locales: {
|
|
1177
|
-
* 'zh-CN': { required: '必填' },
|
|
1178
|
-
* 'en-US': { required: 'Required' }
|
|
1179
|
-
* }
|
|
1180
|
-
* }
|
|
1181
|
-
* });
|
|
1182
|
-
*
|
|
1183
|
-
* // 方式 2: 使用 locales 配置(向后兼容)
|
|
1184
|
-
* dsl.config({
|
|
1185
|
-
* locales: {
|
|
1186
|
-
* 'zh-CN': { required: '必填' }
|
|
1187
|
-
* }
|
|
1188
|
-
* });
|
|
1189
|
-
*
|
|
1190
|
-
* // 自定义手机号规则
|
|
1191
|
-
* dsl.config({
|
|
1192
|
-
* patterns: {
|
|
1193
|
-
* phone: {
|
|
1194
|
-
* cn: {
|
|
1195
|
-
* pattern: /^1[3-9]\d{9}$/,
|
|
1196
|
-
* min: 11,
|
|
1197
|
-
* max: 11,
|
|
1198
|
-
* key: 'phone.cn'
|
|
1199
|
-
* }
|
|
1200
|
-
* }
|
|
1201
|
-
* }
|
|
1202
|
-
* });
|
|
1203
|
-
* ```
|
|
1204
|
-
*/
|
|
1205
|
-
export function config(options: {
|
|
1206
|
-
/** i18n 配置(推荐,v1.0.4+) */
|
|
1207
|
-
i18n?: I18nConfig;
|
|
1208
|
-
/** 缓存配置 */
|
|
1209
|
-
cache?: CacheConfig;
|
|
1210
|
-
/** 自定义验证规则 */
|
|
1211
|
-
patterns?: {
|
|
1212
|
-
/** 手机号规则 */
|
|
1213
|
-
phone?: Record<string, { pattern: RegExp; min?: number; max?: number; key?: string }>;
|
|
1214
|
-
/** 身份证号规则 */
|
|
1215
|
-
idCard?: Record<string, { pattern: RegExp; min?: number; max?: number; key?: string }>;
|
|
1216
|
-
/** 信用卡号规则 */
|
|
1217
|
-
creditCard?: Record<string, { pattern: RegExp; msg?: string }>;
|
|
1218
|
-
};
|
|
1219
|
-
/** 手机号规则(兼容旧版) */
|
|
1220
|
-
phone?: Record<string, { pattern: RegExp; min?: number; max?: number; key?: string }>;
|
|
1221
|
-
/** 语言包配置(兼容旧版,推荐使用 i18n.locales) */
|
|
1222
|
-
locales?: Record<string, ErrorMessages> | string;
|
|
1223
|
-
}): void;
|
|
1224
|
-
|
|
1225
|
-
/**
|
|
1226
|
-
* 匹配规则
|
|
1227
|
-
*
|
|
1228
|
-
* @description 根据值匹配不同的Schema定义
|
|
1229
|
-
*
|
|
1230
|
-
* @example
|
|
1231
|
-
* ```typescript
|
|
1232
|
-
* const schema = dsl({
|
|
1233
|
-
* userType: 'string',
|
|
1234
|
-
* profile: dsl.match('userType', {
|
|
1235
|
-
* 'admin': { role: 'string!', permissions: 'array' },
|
|
1236
|
-
* 'user': { bio: 'string' }
|
|
1237
|
-
* })
|
|
1238
|
-
* });
|
|
1239
|
-
* ```
|
|
1240
|
-
*/
|
|
1241
|
-
export function match(value: any, cases: Record<string, any>): any;
|
|
1242
|
-
|
|
1243
|
-
/**
|
|
1244
|
-
* 条件规则 (if)
|
|
1245
|
-
*
|
|
1246
|
-
* @description 根据条件字段的值选择不同的Schema
|
|
1247
|
-
*
|
|
1248
|
-
* JavaScript 中使用: `dsl.if(condition, thenSchema, elseSchema)`
|
|
1249
|
-
* TypeScript 中使用: `dsl['if'](condition, thenSchema, elseSchema)` 或 `dsl._if(...)`
|
|
1250
|
-
*
|
|
1251
|
-
* @param condition - 条件字段名
|
|
1252
|
-
* @param thenSchema - 条件为 true 时的 Schema
|
|
1253
|
-
* @param elseSchema - 条件为 false 时的 Schema(可选)
|
|
1254
|
-
*
|
|
1255
|
-
* @example
|
|
1256
|
-
* ```typescript
|
|
1257
|
-
* // TypeScript 中因为 if 是保留字,需要用字符串索引或 _if
|
|
1258
|
-
* const schema = dsl({
|
|
1259
|
-
* isVip: 'boolean',
|
|
1260
|
-
* discount: dsl['if']('isVip', 'number:10-50!', 'number:0-10')
|
|
1261
|
-
* });
|
|
1262
|
-
*
|
|
1263
|
-
* // 或者使用 _if 别名
|
|
1264
|
-
* const schema2 = dsl({
|
|
1265
|
-
* age: 'number',
|
|
1266
|
-
* license: dsl._if('age', 'boolean!', 'boolean')
|
|
1267
|
-
* });
|
|
1268
|
-
* ```
|
|
1269
|
-
*
|
|
1270
|
-
* @example
|
|
1271
|
-
* ```javascript
|
|
1272
|
-
* // JavaScript 中可以直接使用 dsl.if
|
|
1273
|
-
* const schema = dsl({
|
|
1274
|
-
* isVip: 'boolean',
|
|
1275
|
-
* discount: dsl.if('isVip', 'number:10-50!', 'number:0-10')
|
|
1276
|
-
* });
|
|
1277
|
-
* ```
|
|
1278
|
-
*/
|
|
1279
|
-
|
|
1280
|
-
/**
|
|
1281
|
-
* 便捷验证方法(dsl命名空间别名)
|
|
1282
|
-
*
|
|
1283
|
-
* @description 与顶层 validate 函数相同
|
|
1284
|
-
*/
|
|
1285
|
-
export const validate: typeof import('./index').validate;
|
|
1286
|
-
|
|
1287
|
-
/**
|
|
1288
|
-
* 异步验证方法(dsl命名空间别名)
|
|
1289
|
-
*
|
|
1290
|
-
* @description 与顶层 validateAsync 函数相同
|
|
1291
|
-
*/
|
|
1292
|
-
export const validateAsync: typeof import('./index').validateAsync;
|
|
1293
|
-
|
|
1294
|
-
/**
|
|
1295
|
-
* 多语言错误快捷方法 (v1.1.1+)
|
|
1296
|
-
*
|
|
1297
|
-
* @description 统一的多语言错误抛出机制
|
|
1298
|
-
*
|
|
1299
|
-
* @example
|
|
1300
|
-
* ```typescript
|
|
1301
|
-
* import { dsl } from 'schema-dsl';
|
|
1302
|
-
*
|
|
1303
|
-
* // 创建错误
|
|
1304
|
-
* const error = dsl.error.create('account.notFound');
|
|
1305
|
-
*
|
|
1306
|
-
* // 直接抛出
|
|
1307
|
-
* dsl.error.throw('account.notFound');
|
|
1308
|
-
*
|
|
1309
|
-
* // 断言风格
|
|
1310
|
-
* dsl.error.assert(account, 'account.notFound');
|
|
1311
|
-
* dsl.error.assert(
|
|
1312
|
-
* account.balance >= 100,
|
|
1313
|
-
* 'account.insufficientBalance',
|
|
1314
|
-
* { balance: account.balance, required: 100 }
|
|
1315
|
-
* );
|
|
1316
|
-
* ```
|
|
1317
|
-
*/
|
|
1318
|
-
export const error: {
|
|
1319
|
-
/**
|
|
1320
|
-
* 创建多语言错误(不抛出)
|
|
1321
|
-
*
|
|
1322
|
-
* @param code - 错误代码(多语言 key)
|
|
1323
|
-
* @param paramsOrLocale - 错误参数对象 或 语言代码(智能识别,v1.1.8+)
|
|
1324
|
-
* @param statusCode - HTTP 状态码
|
|
1325
|
-
* @param locale - 语言环境(仅当第2个参数是对象时有效)
|
|
1326
|
-
* @returns 错误实例
|
|
1327
|
-
*
|
|
1328
|
-
* @example 简化语法(v1.1.8+)
|
|
1329
|
-
* ```typescript
|
|
1330
|
-
* // 直接传语言参数
|
|
1331
|
-
* const error = dsl.error.create('account.notFound', 'zh-CN');
|
|
1332
|
-
* const error = dsl.error.create('account.notFound', 'en-US', 404);
|
|
1333
|
-
* ```
|
|
1334
|
-
*
|
|
1335
|
-
* @example 标准语法(完全兼容)
|
|
1336
|
-
* ```typescript
|
|
1337
|
-
* // 带参数对象
|
|
1338
|
-
* const error = dsl.error.create('account.notFound', { id: '123' }, 404, 'zh-CN');
|
|
1339
|
-
*
|
|
1340
|
-
* // 空参数对象
|
|
1341
|
-
* const error = dsl.error.create('account.notFound', {}, 404, 'zh-CN');
|
|
1342
|
-
* ```
|
|
1343
|
-
*
|
|
1344
|
-
* @example 全局语言
|
|
1345
|
-
* ```typescript
|
|
1346
|
-
* Locale.setLocale('zh-CN');
|
|
1347
|
-
* const error = dsl.error.create('account.notFound');
|
|
1348
|
-
* ```
|
|
1349
|
-
*
|
|
1350
|
-
* @example 运行时指定语言
|
|
1351
|
-
* ```typescript
|
|
1352
|
-
* const error1 = dsl.error.create('account.notFound', {}, 404, 'zh-CN');
|
|
1353
|
-
* const error2 = dsl.error.create('account.notFound', {}, 404, 'en-US');
|
|
1354
|
-
* ```
|
|
1355
|
-
*/
|
|
1356
|
-
create(
|
|
1357
|
-
code: string,
|
|
1358
|
-
paramsOrLocale?: Record<string, any> | string,
|
|
1359
|
-
statusCode?: number,
|
|
1360
|
-
locale?: string
|
|
1361
|
-
): I18nError;
|
|
1362
|
-
|
|
1363
|
-
/**
|
|
1364
|
-
* 抛出多语言错误
|
|
1365
|
-
*
|
|
1366
|
-
* @param code - 错误代码(多语言 key)
|
|
1367
|
-
* @param paramsOrLocale - 错误参数对象 或 语言代码(智能识别,v1.1.8+)
|
|
1368
|
-
* @param statusCode - HTTP 状态码
|
|
1369
|
-
* @param locale - 语言环境(仅当第2个参数是对象时有效)
|
|
1370
|
-
* @throws I18nError
|
|
1371
|
-
*
|
|
1372
|
-
* @example 简化语法(v1.1.8+)
|
|
1373
|
-
* ```typescript
|
|
1374
|
-
* // 直接传语言参数
|
|
1375
|
-
* dsl.error.throw('account.notFound', 'zh-CN');
|
|
1376
|
-
* dsl.error.throw('account.notFound', 'en-US', 404);
|
|
1377
|
-
* ```
|
|
1378
|
-
*
|
|
1379
|
-
* @example 标准语法(完全兼容)
|
|
1380
|
-
* ```typescript
|
|
1381
|
-
* // 带参数对象
|
|
1382
|
-
* dsl.error.throw('account.notFound', { id: '123' }, 404, 'zh-CN');
|
|
1383
|
-
*
|
|
1384
|
-
* // 空参数对象
|
|
1385
|
-
* dsl.error.throw('account.notFound', {}, 404, 'zh-CN');
|
|
1386
|
-
* ```
|
|
1387
|
-
*
|
|
1388
|
-
* @example 全局语言
|
|
1389
|
-
* ```typescript
|
|
1390
|
-
* Locale.setLocale('zh-CN');
|
|
1391
|
-
* dsl.error.throw('account.notFound');
|
|
1392
|
-
* ```
|
|
1393
|
-
*
|
|
1394
|
-
* @example 运行时指定语言
|
|
1395
|
-
* ```typescript
|
|
1396
|
-
* dsl.error.throw('account.notFound', {}, 404, 'en-US');
|
|
1397
|
-
* ```
|
|
1398
|
-
*/
|
|
1399
|
-
throw(
|
|
1400
|
-
code: string,
|
|
1401
|
-
paramsOrLocale?: Record<string, any> | string,
|
|
1402
|
-
statusCode?: number,
|
|
1403
|
-
locale?: string
|
|
1404
|
-
): never;
|
|
1405
|
-
|
|
1406
|
-
/**
|
|
1407
|
-
* 断言方法 - 条件不满足时抛错
|
|
1408
|
-
*
|
|
1409
|
-
* @param condition - 条件表达式
|
|
1410
|
-
* @param code - 错误代码(多语言 key)
|
|
1411
|
-
* @param paramsOrLocale - 错误参数对象 或 语言代码(智能识别,v1.1.8+)
|
|
1412
|
-
* @param statusCode - HTTP 状态码
|
|
1413
|
-
* @param locale - 语言环境(仅当第3个参数是对象时有效)
|
|
1414
|
-
* @throws I18nError 条件为 false 时抛出
|
|
1415
|
-
*
|
|
1416
|
-
* @example 简化语法(v1.1.8+)
|
|
1417
|
-
* ```typescript
|
|
1418
|
-
* // 直接传语言参数
|
|
1419
|
-
* dsl.error.assert(account, 'account.notFound', 'zh-CN');
|
|
1420
|
-
* dsl.error.assert(account, 'account.notFound', 'en-US', 404);
|
|
1421
|
-
* ```
|
|
1422
|
-
*
|
|
1423
|
-
* @example 标准语法(完全兼容)
|
|
1424
|
-
* ```typescript
|
|
1425
|
-
* // 带参数对象
|
|
1426
|
-
* dsl.error.assert(account, 'account.notFound', { id: '123' }, 404, 'zh-CN');
|
|
1427
|
-
*
|
|
1428
|
-
* // 空参数对象
|
|
1429
|
-
* dsl.error.assert(account, 'account.notFound', {}, 404, 'zh-CN');
|
|
1430
|
-
* ```
|
|
1431
|
-
*
|
|
1432
|
-
* @example 全局语言
|
|
1433
|
-
* ```typescript
|
|
1434
|
-
* Locale.setLocale('zh-CN');
|
|
1435
|
-
* dsl.error.assert(account, 'account.notFound');
|
|
1436
|
-
* ```
|
|
1437
|
-
*
|
|
1438
|
-
* @example 运行时指定语言
|
|
1439
|
-
* ```typescript
|
|
1440
|
-
* dsl.error.assert(account, 'account.notFound', {}, 404, 'en-US');
|
|
1441
|
-
* ```
|
|
1442
|
-
*/
|
|
1443
|
-
assert(
|
|
1444
|
-
condition: any,
|
|
1445
|
-
code: string,
|
|
1446
|
-
paramsOrLocale?: Record<string, any> | string,
|
|
1447
|
-
statusCode?: number,
|
|
1448
|
-
locale?: string
|
|
1449
|
-
): asserts condition;
|
|
1450
|
-
};
|
|
1451
|
-
}
|
|
1452
|
-
|
|
1453
|
-
// ========== Validator 类 ==========
|
|
1454
|
-
|
|
1455
|
-
/**
|
|
1456
|
-
* Validator 选项
|
|
1457
|
-
*
|
|
1458
|
-
* @description 验证器的配置选项
|
|
1459
|
-
*/
|
|
1460
|
-
export interface ValidatorOptions {
|
|
1461
|
-
/** 是否返回所有错误 */
|
|
1462
|
-
allErrors?: boolean;
|
|
1463
|
-
/** 是否启用详细模式 */
|
|
1464
|
-
verbose?: boolean;
|
|
1465
|
-
/** 自定义格式验证 */
|
|
1466
|
-
formats?: Record<string, RegExp | ((value: any) => boolean)>;
|
|
1467
|
-
/** 严格模式 */
|
|
1468
|
-
strict?: boolean;
|
|
1469
|
-
/** 扩展选项 */
|
|
1470
|
-
[key: string]: any;
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
/**
|
|
1474
|
-
* 验证器类
|
|
1475
|
-
*
|
|
1476
|
-
* @description 基于ajv的JSON Schema验证器
|
|
1477
|
-
*
|
|
1478
|
-
* @example
|
|
1479
|
-
* ```typescript
|
|
1480
|
-
* // 创建验证器
|
|
1481
|
-
* const validator = new Validator({ allErrors: true });
|
|
1482
|
-
*
|
|
1483
|
-
* // 验证数据
|
|
1484
|
-
* const schema = dsl({ email: 'email!' }).toJsonSchema();
|
|
1485
|
-
* const result = validator.validate(schema, { email: 'test@example.com' });
|
|
1486
|
-
*
|
|
1487
|
-
* if (result.valid) {
|
|
1488
|
-
* console.log('验证通过');
|
|
1489
|
-
* } else {
|
|
1490
|
-
* console.log('错误:', result.errors);
|
|
1491
|
-
* }
|
|
1492
|
-
*
|
|
1493
|
-
* // 获取底层ajv实例
|
|
1494
|
-
* const ajv = validator.getAjv();
|
|
1495
|
-
* ```
|
|
1496
|
-
*/
|
|
1497
|
-
export class Validator {
|
|
1498
|
-
/**
|
|
1499
|
-
* 构造函数
|
|
1500
|
-
* @param options - 验证器选项
|
|
1501
|
-
*/
|
|
1502
|
-
constructor(options?: ValidatorOptions);
|
|
1503
|
-
|
|
1504
|
-
/**
|
|
1505
|
-
* 验证数据
|
|
1506
|
-
* @param schema - JSON Schema对象
|
|
1507
|
-
* @param data - 要验证的数据
|
|
1508
|
-
* @param options - 验证选项
|
|
1509
|
-
* @returns 验证结果
|
|
1510
|
-
*
|
|
1511
|
-
* @example
|
|
1512
|
-
* ```typescript
|
|
1513
|
-
* const validator = new Validator();
|
|
1514
|
-
*
|
|
1515
|
-
* // 使用默认语言
|
|
1516
|
-
* const result1 = validator.validate(schema, data);
|
|
1517
|
-
*
|
|
1518
|
-
* // 动态指定语言
|
|
1519
|
-
* const result2 = validator.validate(schema, data, { locale: 'zh-CN' });
|
|
1520
|
-
*
|
|
1521
|
-
* // 自定义错误消息
|
|
1522
|
-
* const result3 = validator.validate(schema, data, {
|
|
1523
|
-
* locale: 'zh-CN',
|
|
1524
|
-
* messages: { min: '至少{{#limit}}个字符' }
|
|
1525
|
-
* });
|
|
1526
|
-
* ```
|
|
1527
|
-
*/
|
|
1528
|
-
validate<T = any>(schema: JSONSchema, data: any, options?: ValidateOptions): ValidationResult<T>;
|
|
1529
|
-
|
|
1530
|
-
/**
|
|
1531
|
-
* 获取底层ajv实例
|
|
1532
|
-
* @returns ajv实例
|
|
1533
|
-
*/
|
|
1534
|
-
getAjv(): any;
|
|
1535
|
-
|
|
1536
|
-
/**
|
|
1537
|
-
* 添加自定义格式
|
|
1538
|
-
* @param name - 格式名称
|
|
1539
|
-
* @param validator - 验证函数或正则表达式
|
|
1540
|
-
*
|
|
1541
|
-
* @example
|
|
1542
|
-
* ```typescript
|
|
1543
|
-
* validator.addFormat('phone-cn', /^1[3-9]\d{9}$/);
|
|
1544
|
-
* validator.addFormat('custom', (value) => {
|
|
1545
|
-
* return value.startsWith('prefix-');
|
|
1546
|
-
* });
|
|
1547
|
-
* ```
|
|
1548
|
-
*/
|
|
1549
|
-
addFormat(name: string, validator: RegExp | ((value: any) => boolean)): void;
|
|
1550
|
-
|
|
1551
|
-
/**
|
|
1552
|
-
* 添加自定义关键字
|
|
1553
|
-
* @param keyword - 关键字名称
|
|
1554
|
-
* @param definition - 关键字定义
|
|
1555
|
-
*/
|
|
1556
|
-
addKeyword(keyword: string, definition: any): void;
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
/**
|
|
1560
|
-
* 便捷验证方法(同步)
|
|
1561
|
-
*
|
|
1562
|
-
* @description 使用默认的单例Validator,无需new
|
|
1563
|
-
*
|
|
1564
|
-
* @example
|
|
1565
|
-
* ```typescript
|
|
1566
|
-
* import { dsl, validate } from 'schema-dsl';
|
|
1567
|
-
*
|
|
1568
|
-
* const schema = dsl({ email: 'email!' });
|
|
1569
|
-
*
|
|
1570
|
-
* // 基本验证
|
|
1571
|
-
* const result1 = validate(schema, { email: 'test@example.com' });
|
|
1572
|
-
*
|
|
1573
|
-
* // 指定语言
|
|
1574
|
-
* const result2 = validate(schema, { email: 'invalid' }, { locale: 'zh-CN' });
|
|
1575
|
-
*
|
|
1576
|
-
* if (result2.valid) {
|
|
1577
|
-
* console.log('验证通过');
|
|
1578
|
-
* } else {
|
|
1579
|
-
* console.log('错误:', result2.errors); // 中文错误消息
|
|
1580
|
-
* }
|
|
1581
|
-
* ```
|
|
1582
|
-
*/
|
|
1583
|
-
|
|
1584
|
-
/**
|
|
1585
|
-
* 全局配置函数
|
|
1586
|
-
*
|
|
1587
|
-
* @description dsl.config 的顶层导出别名,两种方式等效
|
|
1588
|
-
*
|
|
1589
|
-
* @example
|
|
1590
|
-
* ```typescript
|
|
1591
|
-
* import { config } from 'schema-dsl';
|
|
1592
|
-
* // 或
|
|
1593
|
-
* import { dsl } from 'schema-dsl';
|
|
1594
|
-
*
|
|
1595
|
-
* // 两种方式都可以
|
|
1596
|
-
* config({ i18n: './locales' });
|
|
1597
|
-
* dsl.config({ i18n: './locales' });
|
|
1598
|
-
* ```
|
|
1599
|
-
*/
|
|
1600
|
-
export const config: typeof dsl.config;
|
|
1601
|
-
|
|
1602
|
-
export function validate<T = any>(
|
|
1603
|
-
schema: JSONSchema | DslBuilder,
|
|
1604
|
-
data: any,
|
|
1605
|
-
options?: ValidateOptions
|
|
1606
|
-
): ValidationResult<T>;
|
|
1607
|
-
|
|
1608
|
-
/**
|
|
1609
|
-
* 便捷异步验证方法(推荐)
|
|
1610
|
-
*
|
|
1611
|
-
* @description
|
|
1612
|
-
* - 异步验证数据,验证失败时抛出 ValidationError
|
|
1613
|
-
* - 推荐在异步场景下使用此方法
|
|
1614
|
-
* - 验证成功返回验证后的数据,失败抛出异常
|
|
1615
|
-
*
|
|
1616
|
-
* @param schema - JSON Schema对象或DslBuilder实例
|
|
1617
|
-
* @param data - 要验证的数据
|
|
1618
|
-
* @param options - 验证选项(可选)
|
|
1619
|
-
* @returns 验证成功返回数据的Promise
|
|
1620
|
-
* @throws {ValidationError} 验证失败时抛出
|
|
1621
|
-
*
|
|
1622
|
-
* @example
|
|
1623
|
-
* ```typescript
|
|
1624
|
-
* import { dsl, validateAsync, ValidationError } from 'schema-dsl';
|
|
1625
|
-
*
|
|
1626
|
-
* const schema = dsl({
|
|
1627
|
-
* email: dsl('email!').label('邮箱'),
|
|
1628
|
-
* username: dsl('string:3-32!').label('用户名')
|
|
1629
|
-
* });
|
|
1630
|
-
*
|
|
1631
|
-
* try {
|
|
1632
|
-
* const validData = await validateAsync(schema, {
|
|
1633
|
-
* email: 'test@example.com',
|
|
1634
|
-
* username: 'testuser'
|
|
1635
|
-
* });
|
|
1636
|
-
* console.log('验证通过:', validData);
|
|
1637
|
-
* } catch (error) {
|
|
1638
|
-
* if (error instanceof ValidationError) {
|
|
1639
|
-
* console.log('验证失败:', error.errors);
|
|
1640
|
-
* error.errors.forEach(err => {
|
|
1641
|
-
* console.log(`${err.path}: ${err.message}`);
|
|
1642
|
-
* });
|
|
1643
|
-
* }
|
|
1644
|
-
* }
|
|
1645
|
-
* ```
|
|
1646
|
-
*/
|
|
1647
|
-
export function validateAsync<T = any>(
|
|
1648
|
-
schema: JSONSchema | DslBuilder,
|
|
1649
|
-
data: any,
|
|
1650
|
-
options?: ValidatorOptions
|
|
1651
|
-
): Promise<T>;
|
|
1652
|
-
|
|
1653
|
-
/**
|
|
1654
|
-
* 验证错误类
|
|
1655
|
-
*
|
|
1656
|
-
* @description 当 validateAsync 验证失败时抛出此错误
|
|
1657
|
-
*
|
|
1658
|
-
* @example
|
|
1659
|
-
* ```typescript
|
|
1660
|
-
* import { ValidationError, validateAsync, dsl } from 'schema-dsl';
|
|
1661
|
-
*
|
|
1662
|
-
* const schema = dsl({
|
|
1663
|
-
* email: dsl('email!').label('邮箱'),
|
|
1664
|
-
* age: dsl('number:18-100').label('年龄')
|
|
1665
|
-
* });
|
|
1666
|
-
*
|
|
1667
|
-
* try {
|
|
1668
|
-
* await validateAsync(schema, { email: 'invalid' });
|
|
1669
|
-
* } catch (error) {
|
|
1670
|
-
* if (error instanceof ValidationError) {
|
|
1671
|
-
* // 获取所有错误
|
|
1672
|
-
* console.log('错误列表:', error.errors);
|
|
1673
|
-
*
|
|
1674
|
-
* // 获取错误数量
|
|
1675
|
-
* console.log('错误数量:', error.errors.length);
|
|
1676
|
-
*
|
|
1677
|
-
* // 遍历处理每个字段错误
|
|
1678
|
-
* error.errors.forEach(err => {
|
|
1679
|
-
* console.log(`字段 ${err.path}: ${err.message}`);
|
|
1680
|
-
* });
|
|
1681
|
-
*
|
|
1682
|
-
* // 转为 JSON 格式
|
|
1683
|
-
* const json = error.toJSON();
|
|
1684
|
-
* console.log('JSON格式:', json);
|
|
1685
|
-
* }
|
|
1686
|
-
* }
|
|
1687
|
-
* ```
|
|
1688
|
-
*/
|
|
1689
|
-
export class ValidationError extends Error {
|
|
1690
|
-
/** 错误名称(固定为 'ValidationError') */
|
|
1691
|
-
readonly name: 'ValidationError';
|
|
1692
|
-
|
|
1693
|
-
/** 错误消息 */
|
|
1694
|
-
message: string;
|
|
1695
|
-
|
|
1696
|
-
/** 验证错误列表 */
|
|
1697
|
-
errors: ValidationError[];
|
|
1698
|
-
|
|
1699
|
-
/**
|
|
1700
|
-
* 构造函数
|
|
1701
|
-
* @param errors - 验证错误数组
|
|
1702
|
-
* @param message - 错误消息(可选)
|
|
1703
|
-
*/
|
|
1704
|
-
constructor(errors: ValidationError[], message?: string);
|
|
1705
|
-
|
|
1706
|
-
/**
|
|
1707
|
-
* 转为 JSON 格式
|
|
1708
|
-
* @returns JSON 对象
|
|
1709
|
-
*/
|
|
1710
|
-
toJSON(): {
|
|
1711
|
-
name: string;
|
|
1712
|
-
message: string;
|
|
1713
|
-
errors: Array<{
|
|
1714
|
-
field: string;
|
|
1715
|
-
message: string;
|
|
1716
|
-
keyword: string;
|
|
1717
|
-
params?: Record<string, any>;
|
|
1718
|
-
}>;
|
|
1719
|
-
};
|
|
1720
|
-
|
|
1721
|
-
/**
|
|
1722
|
-
* 获取指定字段的错误
|
|
1723
|
-
* @param field - 字段路径
|
|
1724
|
-
* @returns 错误对象或 null
|
|
1725
|
-
*/
|
|
1726
|
-
getFieldError(field: string): ValidationError | null;
|
|
1727
|
-
|
|
1728
|
-
/**
|
|
1729
|
-
* 获取所有字段的错误映射
|
|
1730
|
-
* @returns 字段错误映射对象
|
|
1731
|
-
*/
|
|
1732
|
-
getFieldErrors(): Record<string, ValidationError>;
|
|
1733
|
-
|
|
1734
|
-
/**
|
|
1735
|
-
* 检查指定字段是否有错误
|
|
1736
|
-
* @param field - 字段路径
|
|
1737
|
-
* @returns 是否有错误
|
|
1738
|
-
*/
|
|
1739
|
-
hasFieldError(field: string): boolean;
|
|
1740
|
-
|
|
1741
|
-
/**
|
|
1742
|
-
* 获取错误总数
|
|
1743
|
-
* @returns 错误数量
|
|
1744
|
-
*/
|
|
1745
|
-
getErrorCount(): number;
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
/**
|
|
1749
|
-
* I18nError - 多语言错误类
|
|
1750
|
-
*
|
|
1751
|
-
* @version 1.1.1
|
|
1752
|
-
*
|
|
1753
|
-
* @description 统一的多语言错误抛出机制,支持:
|
|
1754
|
-
* - 多语言 key 自动翻译
|
|
1755
|
-
* - 参数插值(如 {{#balance}}, {{#required}})
|
|
1756
|
-
* - 自定义错误代码
|
|
1757
|
-
* - Express/Koa 集成
|
|
1758
|
-
*
|
|
1759
|
-
* @example 基础用法
|
|
1760
|
-
* ```typescript
|
|
1761
|
-
* import { I18nError } from 'schema-dsl';
|
|
1762
|
-
*
|
|
1763
|
-
* // 直接抛出
|
|
1764
|
-
* throw I18nError.create('account.notFound');
|
|
1765
|
-
* // 中文: "账户不存在"
|
|
1766
|
-
* // 英文: "Account not found"
|
|
1767
|
-
* ```
|
|
1768
|
-
*
|
|
1769
|
-
* @example 带参数
|
|
1770
|
-
* ```typescript
|
|
1771
|
-
* I18nError.throw('account.insufficientBalance', {
|
|
1772
|
-
* balance: 50,
|
|
1773
|
-
* required: 100
|
|
1774
|
-
* });
|
|
1775
|
-
* // 输出: "余额不足,当前余额50,需要100"
|
|
1776
|
-
* ```
|
|
1777
|
-
*
|
|
1778
|
-
* @example 断言风格
|
|
1779
|
-
* ```typescript
|
|
1780
|
-
* function getAccount(id: string) {
|
|
1781
|
-
* const account = db.findAccount(id);
|
|
1782
|
-
* I18nError.assert(account, 'account.notFound');
|
|
1783
|
-
* I18nError.assert(
|
|
1784
|
-
* account.balance >= 100,
|
|
1785
|
-
* 'account.insufficientBalance',
|
|
1786
|
-
* { balance: account.balance, required: 100 }
|
|
1787
|
-
* );
|
|
1788
|
-
* return account;
|
|
1789
|
-
* }
|
|
1790
|
-
* ```
|
|
1791
|
-
*
|
|
1792
|
-
* @example Express 集成
|
|
1793
|
-
* ```typescript
|
|
1794
|
-
* app.use((error, req, res, next) => {
|
|
1795
|
-
* if (error instanceof I18nError) {
|
|
1796
|
-
* return res.status(error.statusCode).json(error.toJSON());
|
|
1797
|
-
* }
|
|
1798
|
-
* next(error);
|
|
1799
|
-
* });
|
|
1800
|
-
* ```
|
|
1801
|
-
*/
|
|
1802
|
-
export class I18nError extends Error {
|
|
1803
|
-
/** 错误名称(固定为 'I18nError') */
|
|
1804
|
-
readonly name: 'I18nError';
|
|
1805
|
-
|
|
1806
|
-
/** 错误消息(已翻译) */
|
|
1807
|
-
message: string;
|
|
1808
|
-
|
|
1809
|
-
/** 原始 key(v1.1.5 新增) */
|
|
1810
|
-
originalKey: string;
|
|
1811
|
-
|
|
1812
|
-
/** 错误代码(从对象提取或使用 key) */
|
|
1813
|
-
code: string;
|
|
1814
|
-
|
|
1815
|
-
/** 错误参数(用于插值) */
|
|
1816
|
-
params: Record<string, any>;
|
|
1817
|
-
|
|
1818
|
-
/** HTTP 状态码 */
|
|
1819
|
-
statusCode: number;
|
|
1820
|
-
|
|
1821
|
-
/** 使用的语言环境 */
|
|
1822
|
-
locale: string;
|
|
1823
|
-
|
|
1824
|
-
/**
|
|
1825
|
-
* 构造函数
|
|
1826
|
-
* @param code - 错误代码(多语言 key)
|
|
1827
|
-
* @param params - 错误参数(用于插值)
|
|
1828
|
-
* @param statusCode - HTTP 状态码(默认 400)
|
|
1829
|
-
* @param locale - 语言环境(默认使用当前语言)
|
|
1830
|
-
*/
|
|
1831
|
-
constructor(
|
|
1832
|
-
code: string,
|
|
1833
|
-
params?: Record<string, any>,
|
|
1834
|
-
statusCode?: number,
|
|
1835
|
-
locale?: string
|
|
1836
|
-
);
|
|
1837
|
-
|
|
1838
|
-
/**
|
|
1839
|
-
* 静态工厂方法 - 创建错误(不抛出)
|
|
1840
|
-
*
|
|
1841
|
-
* @param code - 错误代码
|
|
1842
|
-
* @param paramsOrLocale - 错误参数对象 或 语言代码(智能识别,v1.1.8+)
|
|
1843
|
-
* @param statusCode - HTTP 状态码
|
|
1844
|
-
* @param locale - 语言环境(仅当第2个参数是对象时有效)
|
|
1845
|
-
* @returns 错误实例
|
|
1846
|
-
*
|
|
1847
|
-
* @example 简化语法(v1.1.8+)
|
|
1848
|
-
* ```typescript
|
|
1849
|
-
* // 直接传语言参数
|
|
1850
|
-
* const error = I18nError.create('account.notFound', 'zh-CN');
|
|
1851
|
-
* const error = I18nError.create('account.notFound', 'en-US', 404);
|
|
1852
|
-
* ```
|
|
1853
|
-
*
|
|
1854
|
-
* @example 标准语法(完全兼容)
|
|
1855
|
-
* ```typescript
|
|
1856
|
-
* // 带参数对象
|
|
1857
|
-
* const error = I18nError.create('account.notFound', { id: '123' }, 404, 'zh-CN');
|
|
1858
|
-
* ```
|
|
1859
|
-
*
|
|
1860
|
-
* @example 全局语言
|
|
1861
|
-
* ```typescript
|
|
1862
|
-
* Locale.setLocale('zh-CN');
|
|
1863
|
-
* const error = I18nError.create('account.notFound');
|
|
1864
|
-
* // message: "账户不存在"
|
|
1865
|
-
* ```
|
|
1866
|
-
*
|
|
1867
|
-
* @example 运行时指定语言
|
|
1868
|
-
* ```typescript
|
|
1869
|
-
* const error1 = I18nError.create('account.notFound', {}, 404, 'zh-CN');
|
|
1870
|
-
* // message: "账户不存在"
|
|
1871
|
-
*
|
|
1872
|
-
* const error2 = I18nError.create('account.notFound', {}, 404, 'en-US');
|
|
1873
|
-
* // message: "Account not found"
|
|
1874
|
-
* ```
|
|
1875
|
-
*/
|
|
1876
|
-
static create(
|
|
1877
|
-
code: string,
|
|
1878
|
-
paramsOrLocale?: Record<string, any> | string,
|
|
1879
|
-
statusCode?: number,
|
|
1880
|
-
locale?: string
|
|
1881
|
-
): I18nError;
|
|
1882
|
-
|
|
1883
|
-
/**
|
|
1884
|
-
* 静态工厂方法 - 直接抛出错误
|
|
1885
|
-
*
|
|
1886
|
-
* @param code - 错误代码
|
|
1887
|
-
* @param paramsOrLocale - 错误参数对象 或 语言代码(智能识别,v1.1.8+)
|
|
1888
|
-
* @param statusCode - HTTP 状态码
|
|
1889
|
-
* @param locale - 语言环境(仅当第2个参数是对象时有效)
|
|
1890
|
-
* @throws I18nError
|
|
1891
|
-
*
|
|
1892
|
-
* @example 简化语法(v1.1.8+)
|
|
1893
|
-
* ```typescript
|
|
1894
|
-
* // 直接传语言参数
|
|
1895
|
-
* I18nError.throw('account.notFound', 'zh-CN');
|
|
1896
|
-
* I18nError.throw('account.notFound', 'en-US', 404);
|
|
1897
|
-
* ```
|
|
1898
|
-
*
|
|
1899
|
-
* @example 标准语法(完全兼容)
|
|
1900
|
-
* ```typescript
|
|
1901
|
-
* // 带参数对象
|
|
1902
|
-
* I18nError.throw('account.notFound', { id: '123' }, 404, 'zh-CN');
|
|
1903
|
-
* ```
|
|
1904
|
-
*
|
|
1905
|
-
* @example 全局语言
|
|
1906
|
-
* ```typescript
|
|
1907
|
-
* Locale.setLocale('zh-CN');
|
|
1908
|
-
* I18nError.throw('account.notFound');
|
|
1909
|
-
* ```
|
|
1910
|
-
*
|
|
1911
|
-
* @example 运行时指定语言
|
|
1912
|
-
* ```typescript
|
|
1913
|
-
* I18nError.throw('account.notFound', {}, 404, 'en-US');
|
|
1914
|
-
* ```
|
|
1915
|
-
*/
|
|
1916
|
-
static throw(
|
|
1917
|
-
code: string,
|
|
1918
|
-
paramsOrLocale?: Record<string, any> | string,
|
|
1919
|
-
statusCode?: number,
|
|
1920
|
-
locale?: string
|
|
1921
|
-
): never;
|
|
1922
|
-
|
|
1923
|
-
/**
|
|
1924
|
-
* 断言方法 - 条件不满足时抛错
|
|
1925
|
-
*
|
|
1926
|
-
* @param condition - 条件表达式
|
|
1927
|
-
* @param code - 错误代码
|
|
1928
|
-
* @param paramsOrLocale - 错误参数对象 或 语言代码(智能识别,v1.1.8+)
|
|
1929
|
-
* @param statusCode - HTTP 状态码
|
|
1930
|
-
* @param locale - 语言环境(仅当第3个参数是对象时有效)
|
|
1931
|
-
* @throws I18nError 条件为 false 时抛出
|
|
1932
|
-
*
|
|
1933
|
-
* @example 简化语法(v1.1.8+)
|
|
1934
|
-
* ```typescript
|
|
1935
|
-
* // 直接传语言参数
|
|
1936
|
-
* I18nError.assert(account, 'account.notFound', 'zh-CN');
|
|
1937
|
-
* I18nError.assert(account, 'account.notFound', 'en-US', 404);
|
|
1938
|
-
* ```
|
|
1939
|
-
*
|
|
1940
|
-
* @example 标准语法(完全兼容)
|
|
1941
|
-
* ```typescript
|
|
1942
|
-
* // 带参数对象
|
|
1943
|
-
* I18nError.assert(account, 'account.notFound', { id: '123' }, 404, 'zh-CN');
|
|
1944
|
-
* ```
|
|
1945
|
-
*
|
|
1946
|
-
* @example 全局语言
|
|
1947
|
-
* ```typescript
|
|
1948
|
-
* Locale.setLocale('zh-CN');
|
|
1949
|
-
* I18nError.assert(account, 'account.notFound');
|
|
1950
|
-
* ```
|
|
1951
|
-
*
|
|
1952
|
-
* @example 运行时指定语言
|
|
1953
|
-
* ```typescript
|
|
1954
|
-
* I18nError.assert(account, 'account.notFound', {}, 404, 'en-US');
|
|
1955
|
-
* ```
|
|
1956
|
-
*/
|
|
1957
|
-
static assert(
|
|
1958
|
-
condition: any,
|
|
1959
|
-
code: string,
|
|
1960
|
-
paramsOrLocale?: Record<string, any> | string,
|
|
1961
|
-
statusCode?: number,
|
|
1962
|
-
locale?: string
|
|
1963
|
-
): asserts condition;
|
|
1964
|
-
|
|
1965
|
-
/**
|
|
1966
|
-
* 检查错误是否为指定代码
|
|
1967
|
-
* @param code - 错误代码
|
|
1968
|
-
* @returns 是否匹配
|
|
1969
|
-
*/
|
|
1970
|
-
is(code: string): boolean;
|
|
1971
|
-
|
|
1972
|
-
/**
|
|
1973
|
-
* 转为 JSON 格式(用于 API 响应)
|
|
1974
|
-
* @returns JSON 对象
|
|
1975
|
-
*
|
|
1976
|
-
* @example
|
|
1977
|
-
* ```typescript
|
|
1978
|
-
* const json = error.toJSON();
|
|
1979
|
-
* // {
|
|
1980
|
-
* // error: 'I18nError',
|
|
1981
|
-
* // originalKey: 'account.notFound', // v1.1.5 新增
|
|
1982
|
-
* // code: 'ACCOUNT_NOT_FOUND',
|
|
1983
|
-
* // message: '账户不存在',
|
|
1984
|
-
* // params: {},
|
|
1985
|
-
* // statusCode: 400,
|
|
1986
|
-
* // locale: 'zh-CN'
|
|
1987
|
-
* // }
|
|
1988
|
-
* ```
|
|
1989
|
-
*
|
|
1990
|
-
* @since v1.1.5 - 新增 originalKey 字段
|
|
1991
|
-
*/
|
|
1992
|
-
toJSON(): {
|
|
1993
|
-
error: string;
|
|
1994
|
-
originalKey: string; // v1.1.5 新增
|
|
1995
|
-
code: string;
|
|
1996
|
-
message: string;
|
|
1997
|
-
params: Record<string, any>;
|
|
1998
|
-
statusCode: number;
|
|
1999
|
-
locale: string;
|
|
2000
|
-
};
|
|
2001
|
-
|
|
2002
|
-
/**
|
|
2003
|
-
* 转为字符串
|
|
2004
|
-
* @returns 格式化的错误信息
|
|
2005
|
-
*/
|
|
2006
|
-
toString(): string;
|
|
2007
|
-
}
|
|
2008
|
-
|
|
2009
|
-
/**
|
|
2010
|
-
* 获取默认Validator实例(单例)
|
|
2011
|
-
*
|
|
2012
|
-
* @description 获取全局共享的Validator实例
|
|
2013
|
-
*
|
|
2014
|
-
* @example
|
|
2015
|
-
* ```typescript
|
|
2016
|
-
* import { getDefaultValidator } from 'schema-dsl';
|
|
2017
|
-
*
|
|
2018
|
-
* const validator = getDefaultValidator();
|
|
2019
|
-
* validator.addFormat('custom', /pattern/);
|
|
2020
|
-
* ```
|
|
2021
|
-
*/
|
|
2022
|
-
export function getDefaultValidator(): Validator;
|
|
2023
|
-
|
|
2024
|
-
// ========== 导出器 ==========
|
|
2025
|
-
|
|
2026
|
-
/**
|
|
2027
|
-
* MongoDB 导出器选项
|
|
2028
|
-
*/
|
|
2029
|
-
export interface MongoDBExporterOptions {
|
|
2030
|
-
/** 严格模式(默认false) */
|
|
2031
|
-
strict?: boolean;
|
|
2032
|
-
/** 时间戳字段 */
|
|
2033
|
-
timestamps?: boolean;
|
|
2034
|
-
/** 集合名称 */
|
|
2035
|
-
collectionName?: string;
|
|
2036
|
-
}
|
|
2037
|
-
|
|
2038
|
-
/**
|
|
2039
|
-
* MongoDB 导出器
|
|
2040
|
-
*
|
|
2041
|
-
* @description 将JSON Schema导出为MongoDB验证规则
|
|
2042
|
-
*
|
|
2043
|
-
* @example
|
|
2044
|
-
* ```typescript
|
|
2045
|
-
* const exporter = new MongoDBExporter({ strict: true });
|
|
2046
|
-
* const mongoSchema = exporter.export(jsonSchema);
|
|
2047
|
-
*
|
|
2048
|
-
* // 生成MongoDB命令
|
|
2049
|
-
* const command = exporter.generateCommand('users', jsonSchema);
|
|
2050
|
-
* console.log(command);
|
|
2051
|
-
* // db.createCollection("users", { validator: { $jsonSchema: {...} } })
|
|
2052
|
-
* ```
|
|
2053
|
-
*/
|
|
2054
|
-
export class MongoDBExporter {
|
|
2055
|
-
/**
|
|
2056
|
-
* 构造函数
|
|
2057
|
-
* @param options - 导出选项
|
|
2058
|
-
*/
|
|
2059
|
-
constructor(options?: MongoDBExporterOptions);
|
|
2060
|
-
|
|
2061
|
-
/**
|
|
2062
|
-
* 导出为MongoDB Schema
|
|
2063
|
-
* @param schema - JSON Schema对象
|
|
2064
|
-
* @returns MongoDB验证规则
|
|
2065
|
-
*/
|
|
2066
|
-
export(schema: JSONSchema): any;
|
|
2067
|
-
|
|
2068
|
-
/**
|
|
2069
|
-
* 生成MongoDB创建集合命令
|
|
2070
|
-
* @param collectionName - 集合名称
|
|
2071
|
-
* @param schema - JSON Schema对象
|
|
2072
|
-
* @returns MongoDB命令字符串
|
|
2073
|
-
*/
|
|
2074
|
-
generateCommand(collectionName: string, schema: JSONSchema): string;
|
|
2075
|
-
}
|
|
2076
|
-
|
|
2077
|
-
/**
|
|
2078
|
-
* MySQL 导出器选项
|
|
2079
|
-
*/
|
|
2080
|
-
export interface MySQLExporterOptions {
|
|
2081
|
-
/** 表名 */
|
|
2082
|
-
tableName?: string;
|
|
2083
|
-
/** 存储引擎(默认InnoDB) */
|
|
2084
|
-
engine?: string;
|
|
2085
|
-
/** 字符集(默认utf8mb4) */
|
|
2086
|
-
charset?: string;
|
|
2087
|
-
/** 排序规则 */
|
|
2088
|
-
collation?: string;
|
|
2089
|
-
}
|
|
2090
|
-
|
|
2091
|
-
/**
|
|
2092
|
-
* MySQL 导出器
|
|
2093
|
-
*
|
|
2094
|
-
* @description 将JSON Schema导出为MySQL CREATE TABLE语句
|
|
2095
|
-
*
|
|
2096
|
-
* @example
|
|
2097
|
-
* ```typescript
|
|
2098
|
-
* const exporter = new MySQLExporter();
|
|
2099
|
-
* const sql = exporter.export(jsonSchema, {
|
|
2100
|
-
* tableName: 'users',
|
|
2101
|
-
* engine: 'InnoDB',
|
|
2102
|
-
* charset: 'utf8mb4'
|
|
2103
|
-
* });
|
|
2104
|
-
*
|
|
2105
|
-
* console.log(sql);
|
|
2106
|
-
* // CREATE TABLE `users` (
|
|
2107
|
-
* // `id` INT PRIMARY KEY AUTO_INCREMENT,
|
|
2108
|
-
* // `username` VARCHAR(32) NOT NULL,
|
|
2109
|
-
* // ...
|
|
2110
|
-
* // ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
2111
|
-
* ```
|
|
2112
|
-
*/
|
|
2113
|
-
export class MySQLExporter {
|
|
2114
|
-
/**
|
|
2115
|
-
* 构造函数
|
|
2116
|
-
*/
|
|
2117
|
-
constructor();
|
|
2118
|
-
|
|
2119
|
-
/**
|
|
2120
|
-
* 导出为MySQL CREATE TABLE语句
|
|
2121
|
-
* @param schema - JSON Schema对象
|
|
2122
|
-
* @param options - 导出选项
|
|
2123
|
-
* @returns SQL语句
|
|
2124
|
-
*/
|
|
2125
|
-
export(schema: JSONSchema, options?: MySQLExporterOptions): string;
|
|
2126
|
-
}
|
|
2127
|
-
|
|
2128
|
-
/**
|
|
2129
|
-
* PostgreSQL 导出器选项
|
|
2130
|
-
*/
|
|
2131
|
-
export interface PostgreSQLExporterOptions {
|
|
2132
|
-
/** 表名 */
|
|
2133
|
-
tableName?: string;
|
|
2134
|
-
/** 模式名 */
|
|
2135
|
-
schemaName?: string;
|
|
2136
|
-
}
|
|
2137
|
-
|
|
2138
|
-
/**
|
|
2139
|
-
* PostgreSQL 导出器
|
|
2140
|
-
*
|
|
2141
|
-
* @description 将JSON Schema导出为PostgreSQL CREATE TABLE语句
|
|
2142
|
-
*
|
|
2143
|
-
* @example
|
|
2144
|
-
* ```typescript
|
|
2145
|
-
* const exporter = new PostgreSQLExporter();
|
|
2146
|
-
* const sql = exporter.export(jsonSchema, {
|
|
2147
|
-
* tableName: 'users',
|
|
2148
|
-
* schemaName: 'public'
|
|
2149
|
-
* });
|
|
2150
|
-
*
|
|
2151
|
-
* console.log(sql);
|
|
2152
|
-
* // CREATE TABLE public.users (
|
|
2153
|
-
* // id SERIAL PRIMARY KEY,
|
|
2154
|
-
* // username VARCHAR(32) NOT NULL,
|
|
2155
|
-
* // ...
|
|
2156
|
-
* // );
|
|
2157
|
-
* ```
|
|
2158
|
-
*/
|
|
2159
|
-
export class PostgreSQLExporter {
|
|
2160
|
-
/**
|
|
2161
|
-
* 构造函数
|
|
2162
|
-
*/
|
|
2163
|
-
constructor();
|
|
2164
|
-
|
|
2165
|
-
/**
|
|
2166
|
-
* 导出为PostgreSQL CREATE TABLE语句
|
|
2167
|
-
* @param schema - JSON Schema对象
|
|
2168
|
-
* @param options - 导出选项
|
|
2169
|
-
* @returns SQL语句
|
|
2170
|
-
*/
|
|
2171
|
-
export(schema: JSONSchema, options?: PostgreSQLExporterOptions): string;
|
|
2172
|
-
}
|
|
2173
|
-
|
|
2174
|
-
/**
|
|
2175
|
-
* 导出器命名空间
|
|
2176
|
-
*
|
|
2177
|
-
* @description 统一的导出器访问入口
|
|
2178
|
-
*
|
|
2179
|
-
* @example
|
|
2180
|
-
* ```typescript
|
|
2181
|
-
* import { exporters } from 'schema-dsl';
|
|
2182
|
-
*
|
|
2183
|
-
* const mongoExporter = new exporters.MongoDBExporter();
|
|
2184
|
-
* const mysqlExporter = new exporters.MySQLExporter();
|
|
2185
|
-
* const pgExporter = new exporters.PostgreSQLExporter();
|
|
2186
|
-
* ```
|
|
2187
|
-
*/
|
|
2188
|
-
|
|
2189
|
-
// ========== 工具函数 ==========
|
|
2190
|
-
|
|
2191
|
-
/**
|
|
2192
|
-
* 类型转换工具
|
|
2193
|
-
*
|
|
2194
|
-
* @description 提供多种Schema类型之间的转换
|
|
2195
|
-
*
|
|
2196
|
-
* @example
|
|
2197
|
-
* ```typescript
|
|
2198
|
-
* // 转换为JSON Schema类型
|
|
2199
|
-
* const jsonType = TypeConverter.toJSONSchemaType('email');
|
|
2200
|
-
* // { type: 'string', format: 'email' }
|
|
2201
|
-
*
|
|
2202
|
-
* // 转换为MongoDB类型
|
|
2203
|
-
* const mongoType = TypeConverter.toMongoDBType('string');
|
|
2204
|
-
* // 'String'
|
|
2205
|
-
*
|
|
2206
|
-
* // 转换为MySQL类型
|
|
2207
|
-
* const mysqlType = TypeConverter.toMySQLType('string', { maxLength: 255 });
|
|
2208
|
-
* // 'VARCHAR(255)'
|
|
2209
|
-
* ```
|
|
2210
|
-
*/
|
|
2211
|
-
export class TypeConverter {
|
|
2212
|
-
/**
|
|
2213
|
-
* 转换为JSON Schema类型
|
|
2214
|
-
* @param simpleType - 简单类型名称
|
|
2215
|
-
* @returns JSON Schema类型对象
|
|
2216
|
-
*/
|
|
2217
|
-
static toJSONSchemaType(simpleType: string): JSONSchema;
|
|
2218
|
-
|
|
2219
|
-
/**
|
|
2220
|
-
* 转换为MongoDB类型
|
|
2221
|
-
* @param jsonSchemaType - JSON Schema类型
|
|
2222
|
-
* @returns MongoDB类型字符串
|
|
2223
|
-
*/
|
|
2224
|
-
static toMongoDBType(jsonSchemaType: string): string;
|
|
2225
|
-
|
|
2226
|
-
/**
|
|
2227
|
-
* 转换为MySQL类型
|
|
2228
|
-
* @param jsonSchemaType - JSON Schema类型
|
|
2229
|
-
* @param constraints - 约束条件
|
|
2230
|
-
* @returns MySQL类型字符串
|
|
2231
|
-
*/
|
|
2232
|
-
static toMySQLType(jsonSchemaType: string, constraints?: Record<string, any>): string;
|
|
2233
|
-
|
|
2234
|
-
/**
|
|
2235
|
-
* 转换为PostgreSQL类型
|
|
2236
|
-
* @param jsonSchemaType - JSON Schema类型
|
|
2237
|
-
* @param constraints - 约束条件
|
|
2238
|
-
* @returns PostgreSQL类型字符串
|
|
2239
|
-
*/
|
|
2240
|
-
static toPostgreSQLType(jsonSchemaType: string, constraints?: Record<string, any>): string;
|
|
2241
|
-
|
|
2242
|
-
/**
|
|
2243
|
-
* 规范化属性名
|
|
2244
|
-
* @param name - 原属性名
|
|
2245
|
-
* @param style - 命名风格
|
|
2246
|
-
* @returns 规范化后的属性名
|
|
2247
|
-
*
|
|
2248
|
-
* @example
|
|
2249
|
-
* ```typescript
|
|
2250
|
-
* TypeConverter.normalizePropertyName('userName', 'snake_case');
|
|
2251
|
-
* // 'user_name'
|
|
2252
|
-
*
|
|
2253
|
-
* TypeConverter.normalizePropertyName('user_name', 'camelCase');
|
|
2254
|
-
* // 'userName'
|
|
2255
|
-
* ```
|
|
2256
|
-
*/
|
|
2257
|
-
static normalizePropertyName(name: string, style?: 'snake_case' | 'camelCase'): string;
|
|
2258
|
-
|
|
2259
|
-
/**
|
|
2260
|
-
* 将format转换为正则表达式
|
|
2261
|
-
* @param format - 格式名称
|
|
2262
|
-
* @returns 正则表达式字符串或null
|
|
2263
|
-
*/
|
|
2264
|
-
static formatToRegex(format: string): string | null;
|
|
2265
|
-
|
|
2266
|
-
/**
|
|
2267
|
-
* 合并Schema
|
|
2268
|
-
* @param base - 基础Schema
|
|
2269
|
-
* @param override - 覆盖Schema
|
|
2270
|
-
* @returns 合并后的Schema
|
|
2271
|
-
*/
|
|
2272
|
-
static mergeSchemas(base: JSONSchema, override: JSONSchema): JSONSchema;
|
|
2273
|
-
|
|
2274
|
-
/**
|
|
2275
|
-
* 提取约束条件
|
|
2276
|
-
* @param schema - JSON Schema对象
|
|
2277
|
-
* @returns 约束条件对象
|
|
2278
|
-
*/
|
|
2279
|
-
static extractConstraints(schema: JSONSchema): Record<string, any>;
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
/**
|
|
2283
|
-
* Schema 辅助工具
|
|
2284
|
-
*
|
|
2285
|
-
* @description 提供Schema的基础操作方法
|
|
2286
|
-
*
|
|
2287
|
-
* @example
|
|
2288
|
-
* ```typescript
|
|
2289
|
-
* // 合并Schema
|
|
2290
|
-
* const merged = SchemaHelper.merge(schema1, schema2);
|
|
2291
|
-
*
|
|
2292
|
-
* // 克隆Schema
|
|
2293
|
-
* const cloned = SchemaHelper.clone(schema);
|
|
2294
|
-
* ```
|
|
2295
|
-
*/
|
|
2296
|
-
export class SchemaHelper {
|
|
2297
|
-
/**
|
|
2298
|
-
* 合并多个Schema
|
|
2299
|
-
* @param schema1 - 第一个Schema
|
|
2300
|
-
* @param schema2 - 第二个Schema
|
|
2301
|
-
* @returns 合并后的Schema
|
|
2302
|
-
*/
|
|
2303
|
-
static merge(schema1: JSONSchema, schema2: JSONSchema): JSONSchema;
|
|
2304
|
-
|
|
2305
|
-
/**
|
|
2306
|
-
* 克隆Schema
|
|
2307
|
-
* @param schema - 要克隆的Schema
|
|
2308
|
-
* @returns 克隆的Schema副本
|
|
2309
|
-
*/
|
|
2310
|
-
static clone(schema: JSONSchema): JSONSchema;
|
|
2311
|
-
}
|
|
2312
|
-
|
|
2313
|
-
/**
|
|
2314
|
-
* Schema 工具类 (v2.0.1+)
|
|
2315
|
-
*
|
|
2316
|
-
* @description 提供高级Schema操作和工具方法
|
|
2317
|
-
*
|
|
2318
|
-
* @example
|
|
2319
|
-
* ```typescript
|
|
2320
|
-
* // 创建可复用的Schema片段
|
|
2321
|
-
* const addressFragment = SchemaUtils.reusable(() => ({
|
|
2322
|
-
* city: 'string!',
|
|
2323
|
-
* street: 'string!',
|
|
2324
|
-
* zip: 'string'
|
|
2325
|
-
* }));
|
|
2326
|
-
*
|
|
2327
|
-
* // 创建Schema库
|
|
2328
|
-
* const library = SchemaUtils.createLibrary({
|
|
2329
|
-
* user: () => ({ username: 'string!', email: 'email!' }),
|
|
2330
|
-
* address: addressFragment
|
|
2331
|
-
* });
|
|
2332
|
-
*
|
|
2333
|
-
* // 使用Schema库
|
|
2334
|
-
* const schema = dsl({
|
|
2335
|
-
* user: library.user(),
|
|
2336
|
-
* address: library.address()
|
|
2337
|
-
* });
|
|
2338
|
-
* ```
|
|
2339
|
-
*/
|
|
2340
|
-
export class SchemaUtils {
|
|
2341
|
-
/**
|
|
2342
|
-
* 创建可复用的Schema片段
|
|
2343
|
-
* @param factory - Schema工厂函数
|
|
2344
|
-
* @returns 可复用的工厂函数
|
|
2345
|
-
*/
|
|
2346
|
-
static reusable<T>(factory: () => T): () => T;
|
|
2347
|
-
|
|
2348
|
-
/**
|
|
2349
|
-
* 创建Schema片段库
|
|
2350
|
-
* @param fragments - Schema片段对象
|
|
2351
|
-
* @returns Schema库对象
|
|
2352
|
-
*/
|
|
2353
|
-
static createLibrary<T extends Record<string, () => any>>(fragments: T): T;
|
|
2354
|
-
|
|
2355
|
-
/**
|
|
2356
|
-
* 合并多个Schema
|
|
2357
|
-
* @param schemas - 要合并的Schema数组
|
|
2358
|
-
* @returns 合并后的Schema
|
|
2359
|
-
*/
|
|
2360
|
-
static merge(...schemas: JSONSchema[]): JSONSchema;
|
|
2361
|
-
|
|
2362
|
-
/**
|
|
2363
|
-
* 扩展Schema
|
|
2364
|
-
* @param baseSchema - 基础Schema
|
|
2365
|
-
* @param extensions - 扩展字段
|
|
2366
|
-
* @returns 扩展后的Schema
|
|
2367
|
-
*/
|
|
2368
|
-
static extend(baseSchema: JSONSchema, extensions: Record<string, any>): JSONSchema;
|
|
2369
|
-
|
|
2370
|
-
/**
|
|
2371
|
-
* 挑选Schema的部分字段
|
|
2372
|
-
* @param schema - 原Schema
|
|
2373
|
-
* @param fields - 要挑选的字段列表
|
|
2374
|
-
* @returns 新Schema
|
|
2375
|
-
*
|
|
2376
|
-
* @example
|
|
2377
|
-
* ```typescript
|
|
2378
|
-
* const userSchema = dsl({
|
|
2379
|
-
* username: 'string!',
|
|
2380
|
-
* email: 'email!',
|
|
2381
|
-
* password: 'string!',
|
|
2382
|
-
* age: 'number'
|
|
2383
|
-
* });
|
|
2384
|
-
*
|
|
2385
|
-
* const loginSchema = SchemaUtils.pick(userSchema, ['username', 'password']);
|
|
2386
|
-
* ```
|
|
2387
|
-
*/
|
|
2388
|
-
static pick(schema: JSONSchema, fields: string[]): JSONSchema;
|
|
2389
|
-
|
|
2390
|
-
/**
|
|
2391
|
-
* 排除Schema的部分字段
|
|
2392
|
-
* @param schema - 原Schema
|
|
2393
|
-
* @param fields - 要排除的字段列表
|
|
2394
|
-
* @returns 新Schema
|
|
2395
|
-
*
|
|
2396
|
-
* @example
|
|
2397
|
-
* ```typescript
|
|
2398
|
-
* const publicUserSchema = SchemaUtils.omit(userSchema, ['password']);
|
|
2399
|
-
* ```
|
|
2400
|
-
*/
|
|
2401
|
-
static omit(schema: JSONSchema, fields: string[]): JSONSchema;
|
|
2402
|
-
|
|
2403
|
-
/**
|
|
2404
|
-
* 创建带性能监控的Validator
|
|
2405
|
-
* @param validator - 原Validator实例
|
|
2406
|
-
* @returns 包装后的Validator
|
|
2407
|
-
*/
|
|
2408
|
-
static withPerformance(validator: Validator): Validator;
|
|
2409
|
-
|
|
2410
|
-
/**
|
|
2411
|
-
* 批量验证
|
|
2412
|
-
* @param schema - JSON Schema对象
|
|
2413
|
-
* @param dataArray - 数据数组
|
|
2414
|
-
* @param validator - Validator实例
|
|
2415
|
-
* @returns 批量验证结果
|
|
2416
|
-
*
|
|
2417
|
-
* @example
|
|
2418
|
-
* ```typescript
|
|
2419
|
-
* const results = SchemaUtils.validateBatch(
|
|
2420
|
-
* schema,
|
|
2421
|
-
* [data1, data2, data3],
|
|
2422
|
-
* validator
|
|
2423
|
-
* );
|
|
2424
|
-
*
|
|
2425
|
-
* console.log(results.summary);
|
|
2426
|
-
* // {
|
|
2427
|
-
* // total: 3,
|
|
2428
|
-
* // valid: 2,
|
|
2429
|
-
* // invalid: 1,
|
|
2430
|
-
* // duration: 15,
|
|
2431
|
-
* // averageTime: 5
|
|
2432
|
-
* // }
|
|
2433
|
-
* ```
|
|
2434
|
-
*/
|
|
2435
|
-
static validateBatch(schema: JSONSchema, dataArray: any[], validator: Validator): {
|
|
2436
|
-
results: Array<{ index: number; valid: boolean; errors: any; data: any }>;
|
|
2437
|
-
summary: { total: number; valid: number; invalid: number; duration: number; averageTime: number };
|
|
2438
|
-
};
|
|
2439
|
-
|
|
2440
|
-
/**
|
|
2441
|
-
* 检查嵌套深度
|
|
2442
|
-
* @param schema - JSON Schema对象
|
|
2443
|
-
* @param maxDepth - 最大深度(默认10)
|
|
2444
|
-
* @returns 检查结果
|
|
2445
|
-
*/
|
|
2446
|
-
static validateNestingDepth(schema: JSONSchema, maxDepth?: number): {
|
|
2447
|
-
valid: boolean;
|
|
2448
|
-
depth: number;
|
|
2449
|
-
path: string;
|
|
2450
|
-
message: string;
|
|
2451
|
-
};
|
|
2452
|
-
|
|
2453
|
-
/**
|
|
2454
|
-
* 导出为Markdown文档
|
|
2455
|
-
* @param schema - JSON Schema对象
|
|
2456
|
-
* @param options - 导出选项
|
|
2457
|
-
* @returns Markdown字符串
|
|
2458
|
-
*/
|
|
2459
|
-
static toMarkdown(schema: JSONSchema, options?: { title?: string; locale?: string }): string;
|
|
2460
|
-
|
|
2461
|
-
/**
|
|
2462
|
-
* 导出为HTML文档
|
|
2463
|
-
* @param schema - JSON Schema对象
|
|
2464
|
-
* @param options - 导出选项
|
|
2465
|
-
* @returns HTML字符串
|
|
2466
|
-
*/
|
|
2467
|
-
static toHTML(schema: JSONSchema, options?: { title?: string }): string;
|
|
2468
|
-
|
|
2469
|
-
/**
|
|
2470
|
-
* 克隆Schema
|
|
2471
|
-
* @param schema - 要克隆的Schema
|
|
2472
|
-
* @returns Schema副本
|
|
2473
|
-
*/
|
|
2474
|
-
static clone(schema: JSONSchema): JSONSchema;
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
// ========== 错误代码 ==========
|
|
2478
|
-
|
|
2479
|
-
/**
|
|
2480
|
-
* 错误代码常量
|
|
2481
|
-
*
|
|
2482
|
-
* @description 预定义的错误代码和消息
|
|
2483
|
-
*
|
|
2484
|
-
* @example
|
|
2485
|
-
* ```typescript
|
|
2486
|
-
* import { ErrorCodes } from 'schema-dsl';
|
|
2487
|
-
*
|
|
2488
|
-
* console.log(ErrorCodes.min);
|
|
2489
|
-
* // { code: 'MIN_LENGTH', message: 'Must be at least {{#limit}} characters', zhCN: '至少需要 {{#limit}} 个字符' }
|
|
2490
|
-
*
|
|
2491
|
-
* console.log(ErrorCodes.email);
|
|
2492
|
-
* // { code: 'INVALID_EMAIL', message: 'Invalid email format', zhCN: '邮箱格式不正确' }
|
|
2493
|
-
* ```
|
|
2494
|
-
*/
|
|
2495
|
-
export const ErrorCodes: {
|
|
2496
|
-
/** 最小长度/最小值错误 */
|
|
2497
|
-
min: { code: string; message: string; zhCN: string };
|
|
2498
|
-
/** 最大长度/最大值错误 */
|
|
2499
|
-
max: { code: string; message: string; zhCN: string };
|
|
2500
|
-
/** 邮箱格式错误 */
|
|
2501
|
-
email: { code: string; message: string; zhCN: string };
|
|
2502
|
-
/** URL格式错误 */
|
|
2503
|
-
url: { code: string; message: string; zhCN: string };
|
|
2504
|
-
/** 正则表达式验证错误 */
|
|
2505
|
-
pattern: { code: string; message: string; zhCN: string };
|
|
2506
|
-
/** 必填项错误 */
|
|
2507
|
-
required: { code: string; message: string; zhCN: string };
|
|
2508
|
-
/** 类型错误 */
|
|
2509
|
-
type: { code: string; message: string; zhCN: string };
|
|
2510
|
-
/** 枚举值错误 */
|
|
2511
|
-
enum: { code: string; message: string; zhCN: string };
|
|
2512
|
-
};
|
|
2513
|
-
|
|
2514
|
-
// ========== 多语言 ==========
|
|
2515
|
-
|
|
2516
|
-
/**
|
|
2517
|
-
* 多语言支持
|
|
2518
|
-
*
|
|
2519
|
-
* @description 提供国际化支持的工具类
|
|
2520
|
-
*
|
|
2521
|
-
* @example
|
|
2522
|
-
* ```typescript
|
|
2523
|
-
* import { Locale } from 'schema-dsl';
|
|
2524
|
-
*
|
|
2525
|
-
* // 设置语言
|
|
2526
|
-
* Locale.setLocale('zh-CN');
|
|
2527
|
-
*
|
|
2528
|
-
* // 获取当前语言
|
|
2529
|
-
* console.log(Locale.getLocale()); // 'zh-CN'
|
|
2530
|
-
*
|
|
2531
|
-
* // 添加自定义语言包
|
|
2532
|
-
* Locale.addLocale('ja-JP', {
|
|
2533
|
-
* required: '必須項目です',
|
|
2534
|
-
* min: '{{#limit}}文字以上必要です'
|
|
2535
|
-
* });
|
|
2536
|
-
*
|
|
2537
|
-
* // 获取可用语言列表
|
|
2538
|
-
* console.log(Locale.getAvailableLocales()); // ['zh-CN', 'en-US', 'ja-JP', ...]
|
|
2539
|
-
* ```
|
|
2540
|
-
*/
|
|
2541
|
-
export class Locale {
|
|
2542
|
-
/**
|
|
2543
|
-
* 设置当前语言
|
|
2544
|
-
* @param lang - 语言代码
|
|
2545
|
-
*/
|
|
2546
|
-
static setLocale(lang: 'en-US' | 'zh-CN' | 'ja-JP' | 'fr-FR' | 'es-ES' | string): void;
|
|
2547
|
-
|
|
2548
|
-
/**
|
|
2549
|
-
* 获取当前语言
|
|
2550
|
-
* @returns 语言代码
|
|
2551
|
-
*/
|
|
2552
|
-
static getLocale(): string;
|
|
2553
|
-
|
|
2554
|
-
/**
|
|
2555
|
-
* 添加语言包
|
|
2556
|
-
* @param locale - 语言代码
|
|
2557
|
-
* @param messages - 错误消息
|
|
2558
|
-
*/
|
|
2559
|
-
static addLocale(locale: string, messages: ErrorMessages): void;
|
|
2560
|
-
|
|
2561
|
-
/**
|
|
2562
|
-
* 设置当前语言包的消息
|
|
2563
|
-
* @param messages - 错误消息
|
|
2564
|
-
*/
|
|
2565
|
-
static setMessages(messages: ErrorMessages): void;
|
|
2566
|
-
|
|
2567
|
-
/**
|
|
2568
|
-
* 获取错误消息
|
|
2569
|
-
* @param type - 错误类型
|
|
2570
|
-
* @param customMessages - 自定义消息(可选)
|
|
2571
|
-
* @returns 错误消息字符串
|
|
2572
|
-
*/
|
|
2573
|
-
static getMessage(type: string, customMessages?: ErrorMessages): string;
|
|
2574
|
-
|
|
2575
|
-
/**
|
|
2576
|
-
* 获取可用的语言列表
|
|
2577
|
-
* @returns 语言代码数组
|
|
2578
|
-
*/
|
|
2579
|
-
static getAvailableLocales(): string[];
|
|
2580
|
-
}
|
|
2581
|
-
|
|
2582
|
-
// ========== JSONSchemaCore 类 ==========
|
|
2583
|
-
|
|
2584
|
-
/**
|
|
2585
|
-
* JSON Schema 核心类
|
|
2586
|
-
*
|
|
2587
|
-
* @description 对 JSON Schema 进行封装,提供验证和操作方法
|
|
2588
|
-
*
|
|
2589
|
-
* @example
|
|
2590
|
-
* ```typescript
|
|
2591
|
-
* const core = new JSONSchemaCore({
|
|
2592
|
-
* type: 'string',
|
|
2593
|
-
* minLength: 3,
|
|
2594
|
-
* maxLength: 32
|
|
2595
|
-
* });
|
|
2596
|
-
*
|
|
2597
|
-
* const result = core.validate('test');
|
|
2598
|
-
* console.log(result.valid); // true
|
|
2599
|
-
* ```
|
|
2600
|
-
*/
|
|
2601
|
-
export class JSONSchemaCore {
|
|
2602
|
-
/**
|
|
2603
|
-
* 构造函数
|
|
2604
|
-
* @param schema - JSON Schema 对象
|
|
2605
|
-
*/
|
|
2606
|
-
constructor(schema: JSONSchema);
|
|
2607
|
-
|
|
2608
|
-
/**
|
|
2609
|
-
* 验证数据
|
|
2610
|
-
* @param data - 要验证的数据
|
|
2611
|
-
* @returns 验证结果
|
|
2612
|
-
*/
|
|
2613
|
-
validate<T = any>(data: any): ValidationResult<T>;
|
|
2614
|
-
|
|
2615
|
-
/**
|
|
2616
|
-
* 获取 JSON Schema 对象
|
|
2617
|
-
* @returns JSON Schema
|
|
2618
|
-
*/
|
|
2619
|
-
toJsonSchema(): JSONSchema;
|
|
2620
|
-
}
|
|
2621
|
-
|
|
2622
|
-
// ========== ErrorFormatter 类 ==========
|
|
2623
|
-
|
|
2624
|
-
/**
|
|
2625
|
-
* 错误格式化器
|
|
2626
|
-
*
|
|
2627
|
-
* @description 格式化 ajv 验证错误为友好的错误消息
|
|
2628
|
-
*
|
|
2629
|
-
* @example
|
|
2630
|
-
* ```typescript
|
|
2631
|
-
* const formatter = new ErrorFormatter();
|
|
2632
|
-
* const errors = formatter.format(ajvErrors, { locale: 'zh-CN' });
|
|
2633
|
-
* ```
|
|
2634
|
-
*/
|
|
2635
|
-
export class ErrorFormatter {
|
|
2636
|
-
/**
|
|
2637
|
-
* 格式化错误
|
|
2638
|
-
* @param errors - ajv 错误数组
|
|
2639
|
-
* @param options - 格式化选项
|
|
2640
|
-
* @returns 格式化后的错误数组
|
|
2641
|
-
*/
|
|
2642
|
-
format(errors: any[], options?: { locale?: string }): ValidationError[];
|
|
2643
|
-
}
|
|
2644
|
-
|
|
2645
|
-
// ========== MessageTemplate 类 ==========
|
|
2646
|
-
|
|
2647
|
-
/**
|
|
2648
|
-
* 消息模板类
|
|
2649
|
-
*
|
|
2650
|
-
* @description 处理错误消息模板和变量替换
|
|
2651
|
-
*
|
|
2652
|
-
* @example
|
|
2653
|
-
* ```typescript
|
|
2654
|
-
* const template = new MessageTemplate('至少需要{{#limit}}个字符');
|
|
2655
|
-
* const message = template.render({ limit: 3 });
|
|
2656
|
-
* console.log(message); // "至少需要3个字符"
|
|
2657
|
-
* ```
|
|
2658
|
-
*/
|
|
2659
|
-
export class MessageTemplate {
|
|
2660
|
-
/**
|
|
2661
|
-
* 构造函数
|
|
2662
|
-
* @param template - 消息模板字符串
|
|
2663
|
-
*/
|
|
2664
|
-
constructor(template: string);
|
|
2665
|
-
|
|
2666
|
-
/**
|
|
2667
|
-
* 渲染模板
|
|
2668
|
-
* @param variables - 模板变量
|
|
2669
|
-
* @returns 渲染后的消息
|
|
2670
|
-
*/
|
|
2671
|
-
render(variables: Record<string, any>): string;
|
|
2672
|
-
|
|
2673
|
-
/**
|
|
2674
|
-
* 静态渲染方法
|
|
2675
|
-
* @param template - 消息模板
|
|
2676
|
-
* @param variables - 模板变量
|
|
2677
|
-
* @returns 渲染后的消息
|
|
2678
|
-
*/
|
|
2679
|
-
static render(template: string, variables: Record<string, any>): string;
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
// ========== CacheManager 类 ==========
|
|
2683
|
-
|
|
2684
|
-
/**
|
|
2685
|
-
* 缓存管理器选项
|
|
2686
|
-
*/
|
|
2687
|
-
export interface CacheManagerOptions {
|
|
2688
|
-
/** 最大缓存条目数 */
|
|
2689
|
-
maxSize?: number;
|
|
2690
|
-
/** 缓存过期时间(毫秒) */
|
|
2691
|
-
ttl?: number;
|
|
2692
|
-
}
|
|
2693
|
-
|
|
2694
|
-
/**
|
|
2695
|
-
* 缓存管理器
|
|
2696
|
-
*
|
|
2697
|
-
* @description LRU 缓存管理器,用于缓存编译后的 Schema
|
|
2698
|
-
*
|
|
2699
|
-
* @example
|
|
2700
|
-
* ```typescript
|
|
2701
|
-
* const cache = new CacheManager({ maxSize: 1000, ttl: 60000 });
|
|
2702
|
-
*
|
|
2703
|
-
* // 设置缓存
|
|
2704
|
-
* cache.set('key', value);
|
|
2705
|
-
*
|
|
2706
|
-
* // 获取缓存
|
|
2707
|
-
* const value = cache.get('key');
|
|
2708
|
-
*
|
|
2709
|
-
* // 清空缓存
|
|
2710
|
-
* cache.clear();
|
|
2711
|
-
* ```
|
|
2712
|
-
*/
|
|
2713
|
-
export class CacheManager {
|
|
2714
|
-
/**
|
|
2715
|
-
* 构造函数
|
|
2716
|
-
* @param options - 缓存选项
|
|
2717
|
-
*/
|
|
2718
|
-
constructor(options?: CacheManagerOptions);
|
|
2719
|
-
|
|
2720
|
-
/**
|
|
2721
|
-
* 缓存选项
|
|
2722
|
-
*/
|
|
2723
|
-
options: CacheManagerOptions;
|
|
2724
|
-
|
|
2725
|
-
/**
|
|
2726
|
-
* 设置缓存
|
|
2727
|
-
* @param key - 缓存键
|
|
2728
|
-
* @param value - 缓存值
|
|
2729
|
-
*/
|
|
2730
|
-
set(key: string, value: any): void;
|
|
2731
|
-
|
|
2732
|
-
/**
|
|
2733
|
-
* 获取缓存
|
|
2734
|
-
* @param key - 缓存键
|
|
2735
|
-
* @returns 缓存值或 undefined
|
|
2736
|
-
*/
|
|
2737
|
-
get(key: string): any | undefined;
|
|
2738
|
-
|
|
2739
|
-
/**
|
|
2740
|
-
* 检查缓存是否存在
|
|
2741
|
-
* @param key - 缓存键
|
|
2742
|
-
* @returns 是否存在
|
|
2743
|
-
*/
|
|
2744
|
-
has(key: string): boolean;
|
|
2745
|
-
|
|
2746
|
-
/**
|
|
2747
|
-
* 删除缓存
|
|
2748
|
-
* @param key - 缓存键
|
|
2749
|
-
*/
|
|
2750
|
-
delete(key: string): void;
|
|
2751
|
-
|
|
2752
|
-
/**
|
|
2753
|
-
* 清空所有缓存
|
|
2754
|
-
*/
|
|
2755
|
-
clear(): void;
|
|
2756
|
-
|
|
2757
|
-
/**
|
|
2758
|
-
* 获取缓存统计信息
|
|
2759
|
-
* @returns 统计信息对象
|
|
2760
|
-
*/
|
|
2761
|
-
getStats(): {
|
|
2762
|
-
size: number;
|
|
2763
|
-
hits: number;
|
|
2764
|
-
misses: number;
|
|
2765
|
-
evictions: number;
|
|
2766
|
-
};
|
|
2767
|
-
}
|
|
2768
|
-
|
|
2769
|
-
// ========== PluginManager 类 ==========
|
|
2770
|
-
|
|
2771
|
-
/**
|
|
2772
|
-
* 插件接口
|
|
2773
|
-
*/
|
|
2774
|
-
export interface Plugin {
|
|
2775
|
-
/** 插件名称 */
|
|
2776
|
-
name: string;
|
|
2777
|
-
/** 插件版本 */
|
|
2778
|
-
version: string;
|
|
2779
|
-
/** 插件描述 */
|
|
2780
|
-
description?: string;
|
|
2781
|
-
/** 安装方法 */
|
|
2782
|
-
install(core: any, options?: any, context?: any): void;
|
|
2783
|
-
/** 卸载方法(可选) */
|
|
2784
|
-
uninstall?(core: any, context?: any): void;
|
|
2785
|
-
}
|
|
2786
|
-
|
|
2787
|
-
/**
|
|
2788
|
-
* 插件管理器
|
|
2789
|
-
*
|
|
2790
|
-
* @description 管理验证库的插件系统
|
|
2791
|
-
*
|
|
2792
|
-
* @example
|
|
2793
|
-
* ```typescript
|
|
2794
|
-
* const pluginManager = new PluginManager();
|
|
2795
|
-
*
|
|
2796
|
-
* // 注册插件
|
|
2797
|
-
* pluginManager.register({
|
|
2798
|
-
* name: 'my-plugin',
|
|
2799
|
-
* version: '1.0.0',
|
|
2800
|
-
* install(core) {
|
|
2801
|
-
* // 安装逻辑
|
|
2802
|
-
* }
|
|
2803
|
-
* });
|
|
2804
|
-
*
|
|
2805
|
-
* // 安装插件
|
|
2806
|
-
* pluginManager.install(schemaCore);
|
|
2807
|
-
*
|
|
2808
|
-
* // 获取插件
|
|
2809
|
-
* const plugin = pluginManager.get('my-plugin');
|
|
2810
|
-
* ```
|
|
2811
|
-
*/
|
|
2812
|
-
export class PluginManager {
|
|
2813
|
-
/**
|
|
2814
|
-
* 构造函数
|
|
2815
|
-
*/
|
|
2816
|
-
constructor();
|
|
2817
|
-
|
|
2818
|
-
/**
|
|
2819
|
-
* 注册插件
|
|
2820
|
-
* @param plugin - 插件对象
|
|
2821
|
-
*/
|
|
2822
|
-
register(plugin: Plugin): void;
|
|
2823
|
-
|
|
2824
|
-
/**
|
|
2825
|
-
* 安装所有插件
|
|
2826
|
-
* @param core - 核心对象
|
|
2827
|
-
* @param options - 安装选项
|
|
2828
|
-
*/
|
|
2829
|
-
install(core: any, options?: any): void;
|
|
2830
|
-
|
|
2831
|
-
/**
|
|
2832
|
-
* 获取插件
|
|
2833
|
-
* @param name - 插件名称
|
|
2834
|
-
* @returns 插件对象或 undefined
|
|
2835
|
-
*/
|
|
2836
|
-
get(name: string): Plugin | undefined;
|
|
2837
|
-
|
|
2838
|
-
/**
|
|
2839
|
-
* 卸载插件
|
|
2840
|
-
* @param name - 插件名称
|
|
2841
|
-
*/
|
|
2842
|
-
uninstall(name: string): void;
|
|
2843
|
-
|
|
2844
|
-
/**
|
|
2845
|
-
* 列出所有插件
|
|
2846
|
-
* @returns 插件名称数组
|
|
2847
|
-
*/
|
|
2848
|
-
list(): string[];
|
|
2849
|
-
|
|
2850
|
-
/**
|
|
2851
|
-
* 清空所有插件
|
|
2852
|
-
*/
|
|
2853
|
-
clear(): void;
|
|
2854
|
-
}
|
|
2855
|
-
|
|
2856
|
-
// ========== MarkdownExporter 类 ==========
|
|
2857
|
-
|
|
2858
|
-
/**
|
|
2859
|
-
* Markdown 导出器选项
|
|
2860
|
-
*/
|
|
2861
|
-
export interface MarkdownExporterOptions {
|
|
2862
|
-
/** 文档标题 */
|
|
2863
|
-
title?: string;
|
|
2864
|
-
/** 语言(zh-CN, en-US等) */
|
|
2865
|
-
locale?: string;
|
|
2866
|
-
/** 是否包含示例数据 */
|
|
2867
|
-
includeExamples?: boolean;
|
|
2868
|
-
}
|
|
2869
|
-
|
|
2870
|
-
/**
|
|
2871
|
-
* Markdown 导出器
|
|
2872
|
-
*
|
|
2873
|
-
* @description 将 JSON Schema 导出为 Markdown 文档
|
|
2874
|
-
*
|
|
2875
|
-
* @example
|
|
2876
|
-
* ```typescript
|
|
2877
|
-
* const exporter = new MarkdownExporter();
|
|
2878
|
-
* const markdown = exporter.export(schema, {
|
|
2879
|
-
* title: '用户注册 API',
|
|
2880
|
-
* locale: 'zh-CN',
|
|
2881
|
-
* includeExamples: true
|
|
2882
|
-
* });
|
|
2883
|
-
*
|
|
2884
|
-
* console.log(markdown);
|
|
2885
|
-
* // # 用户注册 API
|
|
2886
|
-
* //
|
|
2887
|
-
* // ## 字段列表
|
|
2888
|
-
* // | 字段名 | 类型 | 必填 | 约束 | 说明 |
|
|
2889
|
-
* // |--------|------|------|------|------|
|
|
2890
|
-
* // | username | 字符串 | ✅ | 长度: 3-32 | - |
|
|
2891
|
-
* ```
|
|
2892
|
-
*/
|
|
2893
|
-
export class MarkdownExporter {
|
|
2894
|
-
/**
|
|
2895
|
-
* 构造函数
|
|
2896
|
-
*/
|
|
2897
|
-
constructor();
|
|
2898
|
-
|
|
2899
|
-
/**
|
|
2900
|
-
* 导出为 Markdown
|
|
2901
|
-
* @param schema - JSON Schema 对象
|
|
2902
|
-
* @param options - 导出选项
|
|
2903
|
-
* @returns Markdown 字符串
|
|
2904
|
-
*/
|
|
2905
|
-
export(schema: JSONSchema, options?: MarkdownExporterOptions): string;
|
|
2906
|
-
|
|
2907
|
-
/**
|
|
2908
|
-
* 静态导出方法
|
|
2909
|
-
* @param schema - JSON Schema 对象
|
|
2910
|
-
* @param options - 导出选项
|
|
2911
|
-
* @returns Markdown 字符串
|
|
2912
|
-
*/
|
|
2913
|
-
static export(schema: JSONSchema, options?: MarkdownExporterOptions): string;
|
|
2914
|
-
}
|
|
2915
|
-
|
|
2916
|
-
// ========== CustomKeywords 类 ==========
|
|
2917
|
-
|
|
2918
|
-
/**
|
|
2919
|
-
* 自定义关键字
|
|
2920
|
-
*
|
|
2921
|
-
* @description 扩展 ajv 的自定义验证关键字
|
|
2922
|
-
*
|
|
2923
|
-
* @example
|
|
2924
|
-
* ```typescript
|
|
2925
|
-
* // 添加自定义关键字通常通过 Validator 的 addKeyword 方法
|
|
2926
|
-
* const validator = new Validator();
|
|
2927
|
-
* const ajv = validator.getAjv();
|
|
2928
|
-
|
|
2929
|
-
// ========== dsl.config 选项 ==========
|
|
2930
|
-
|
|
2931
|
-
/**
|
|
2932
|
-
* i18n 配置选项
|
|
2933
|
-
*
|
|
2934
|
-
* @description 支持三种配置方式(v1.2.3 新增 localesPath 对象形态 + 递归子目录)
|
|
2935
|
-
*
|
|
2936
|
-
* @example
|
|
2937
|
-
* ```typescript
|
|
2938
|
-
* // 方式1: 直接传字符串路径(支持递归子目录,v1.2.3+)
|
|
2939
|
-
* dsl.config({
|
|
2940
|
-
* i18n: './locales'
|
|
2941
|
-
* });
|
|
2942
|
-
*
|
|
2943
|
-
* // 方式2: 传入语言包对象
|
|
2944
|
-
* dsl.config({
|
|
2945
|
-
* i18n: {
|
|
2946
|
-
* 'zh-CN': { required: '必填' },
|
|
2947
|
-
* 'en-US': { required: 'Required' }
|
|
2948
|
-
* }
|
|
2949
|
-
* });
|
|
2950
|
-
*
|
|
2951
|
-
* // 方式3: 含 localesPath 的对象(v1.2.3 修复真正生效)
|
|
2952
|
-
* dsl.config({
|
|
2953
|
-
* i18n: { localesPath: './i18n/labels' }
|
|
2954
|
-
* });
|
|
2955
|
-
* ```
|
|
2956
|
-
*/
|
|
2957
|
-
export type I18nConfig =
|
|
2958
|
-
| string
|
|
2959
|
-
| Record<string, ErrorMessages>
|
|
2960
|
-
| { localesPath: string };
|
|
2961
|
-
|
|
2962
|
-
/**
|
|
2963
|
-
* 缓存配置选项(v2.3.0+)
|
|
2964
|
-
*/
|
|
2965
|
-
export interface CacheConfig {
|
|
2966
|
-
/** 最大缓存条目数 */
|
|
2967
|
-
maxSize?: number;
|
|
2968
|
-
/** 缓存过期时间(毫秒) */
|
|
2969
|
-
ttl?: number;
|
|
2970
|
-
}
|
|
2971
|
-
|
|
2972
|
-
/**
|
|
2973
|
-
* dsl.config() 配置选项(v2.3.0+)
|
|
2974
|
-
*
|
|
2975
|
-
* @description 全局配置选项,包括多语言和缓存设置
|
|
2976
|
-
*
|
|
2977
|
-
* @example
|
|
2978
|
-
* ```typescript
|
|
2979
|
-
* // 配置多语言
|
|
2980
|
-
* dsl.config({
|
|
2981
|
-
* i18n: {
|
|
2982
|
-
* locales: {
|
|
2983
|
-
* 'zh-CN': { 'username': '用户名' },
|
|
2984
|
-
* 'en-US': { 'username': 'Username' }
|
|
2985
|
-
* }
|
|
2986
|
-
* }
|
|
2987
|
-
* });
|
|
2988
|
-
*
|
|
2989
|
-
* // 配置缓存
|
|
2990
|
-
* dsl.config({
|
|
2991
|
-
* cache: {
|
|
2992
|
-
* maxSize: 5000,
|
|
2993
|
-
* ttl: 60000
|
|
2994
|
-
* }
|
|
2995
|
-
* });
|
|
2996
|
-
*
|
|
2997
|
-
* // 同时配置多个选项
|
|
2998
|
-
* dsl.config({
|
|
2999
|
-
* i18n: { locales: {...} },
|
|
3000
|
-
* cache: { maxSize: 5000 },
|
|
3001
|
-
* patterns: {
|
|
3002
|
-
* phone: { cn: /^1[3-9]\d{9}$/ }
|
|
3003
|
-
* }
|
|
3004
|
-
* });
|
|
3005
|
-
* ```
|
|
3006
|
-
*/
|
|
3007
|
-
export interface DslConfigOptions {
|
|
3008
|
-
/** i18n 配置 */
|
|
3009
|
-
i18n?: I18nConfig;
|
|
3010
|
-
/** 缓存配置 */
|
|
3011
|
-
cache?: CacheConfig;
|
|
3012
|
-
/** 自定义验证规则扩展 */
|
|
3013
|
-
patterns?: {
|
|
3014
|
-
/** 手机号验证规则 */
|
|
3015
|
-
phone?: Record<string, RegExp>;
|
|
3016
|
-
/** 身份证验证规则 */
|
|
3017
|
-
idCard?: Record<string, RegExp>;
|
|
3018
|
-
/** 信用卡验证规则 */
|
|
3019
|
-
creditCard?: Record<string, RegExp>;
|
|
3020
|
-
};
|
|
3021
|
-
/** 向后兼容:手机号验证规则(推荐使用 patterns.phone) */
|
|
3022
|
-
phone?: Record<string, RegExp>;
|
|
3023
|
-
}
|
|
3024
|
-
|
|
3025
|
-
// ========== exporters 对象 ==========
|
|
3026
|
-
|
|
3027
|
-
/**
|
|
3028
|
-
* 导出器集合
|
|
3029
|
-
*
|
|
3030
|
-
* @description 包含所有导出器的对象
|
|
3031
|
-
*
|
|
3032
|
-
* @example
|
|
3033
|
-
* ```typescript
|
|
3034
|
-
* import { exporters } from 'schema-dsl';
|
|
3035
|
-
*
|
|
3036
|
-
* // 使用 MongoDB 导出器
|
|
3037
|
-
* const mongoSchema = exporters.MongoDBExporter.export(schema);
|
|
3038
|
-
*
|
|
3039
|
-
* // 使用 MySQL 导出器
|
|
3040
|
-
* const mysqlDDL = new exporters.MySQLExporter().export(schema, { tableName: 'users' });
|
|
3041
|
-
* ```
|
|
3042
|
-
*/
|
|
3043
|
-
export const exporters: {
|
|
3044
|
-
MongoDBExporter: typeof MongoDBExporter;
|
|
3045
|
-
MySQLExporter: typeof MySQLExporter;
|
|
3046
|
-
PostgreSQLExporter: typeof PostgreSQLExporter;
|
|
3047
|
-
MarkdownExporter: typeof MarkdownExporter;
|
|
3048
|
-
};
|
|
3049
|
-
|
|
3050
|
-
// ========== String 扩展控制 ==========
|
|
3051
|
-
|
|
3052
|
-
/**
|
|
3053
|
-
* 安装 String 扩展
|
|
3054
|
-
*
|
|
3055
|
-
* @description 将DSL方法添加到String.prototype,使字符串支持链式调用
|
|
3056
|
-
*
|
|
3057
|
-
* @example
|
|
3058
|
-
* ```typescript
|
|
3059
|
-
* import { installStringExtensions } from 'schema-dsl';
|
|
3060
|
-
*
|
|
3061
|
-
* // 安装扩展
|
|
3062
|
-
* installStringExtensions();
|
|
3063
|
-
*
|
|
3064
|
-
* // 现在可以在字符串上使用DSL方法
|
|
3065
|
-
* const schema = dsl({
|
|
3066
|
-
* email: 'email!'.label('邮箱地址').messages({ required: '必填' })
|
|
3067
|
-
* });
|
|
3068
|
-
* ```
|
|
3069
|
-
*/
|
|
3070
|
-
export function installStringExtensions(): void;
|
|
3071
|
-
|
|
3072
|
-
/**
|
|
3073
|
-
* 卸载 String 扩展
|
|
3074
|
-
*
|
|
3075
|
-
* @description 从String.prototype移除DSL方法
|
|
3076
|
-
*
|
|
3077
|
-
* @example
|
|
3078
|
-
* ```typescript
|
|
3079
|
-
* import { uninstallStringExtensions } from 'schema-dsl';
|
|
3080
|
-
*
|
|
3081
|
-
* // 卸载扩展
|
|
3082
|
-
* uninstallStringExtensions();
|
|
3083
|
-
*
|
|
3084
|
-
* // 字符串不再支持DSL方法
|
|
3085
|
-
* ```
|
|
3086
|
-
*/
|
|
3087
|
-
export function uninstallStringExtensions(): void;
|
|
3088
|
-
|
|
3089
|
-
// ========== 默认导出 ==========
|
|
3090
|
-
|
|
3091
|
-
// ========== 验证器扩展 ==========
|
|
3092
|
-
|
|
3093
|
-
/**
|
|
3094
|
-
* 自定义关键字
|
|
3095
|
-
*
|
|
3096
|
-
* @description 扩展ajv的自定义验证关键字
|
|
3097
|
-
*
|
|
3098
|
-
* @example
|
|
3099
|
-
* ```typescript
|
|
3100
|
-
* import { CustomKeywords, Validator } from 'schema-dsl';
|
|
3101
|
-
*
|
|
3102
|
-
* const validator = new Validator();
|
|
3103
|
-
* const ajv = validator.getAjv();
|
|
3104
|
-
* CustomKeywords.registerAll(ajv);
|
|
3105
|
-
* ```
|
|
3106
|
-
*/
|
|
3107
|
-
export const CustomKeywords: {
|
|
3108
|
-
/**
|
|
3109
|
-
* 注册所有自定义关键字到ajv实例
|
|
3110
|
-
* @param ajv - ajv实例
|
|
3111
|
-
*/
|
|
3112
|
-
registerAll(ajv: any): void;
|
|
3113
|
-
|
|
3114
|
-
/**
|
|
3115
|
-
* 注册元数据关键字
|
|
3116
|
-
* @param ajv - ajv实例
|
|
3117
|
-
*/
|
|
3118
|
-
registerMetadataKeywords(ajv: any): void;
|
|
3119
|
-
|
|
3120
|
-
/**
|
|
3121
|
-
* 注册字符串验证器
|
|
3122
|
-
* @param ajv - ajv实例
|
|
3123
|
-
*/
|
|
3124
|
-
registerStringValidators(ajv: any): void;
|
|
3125
|
-
|
|
3126
|
-
/**
|
|
3127
|
-
* 注册数字验证器
|
|
3128
|
-
* @param ajv - ajv实例
|
|
3129
|
-
*/
|
|
3130
|
-
registerNumberValidators(ajv: any): void;
|
|
3131
|
-
|
|
3132
|
-
/**
|
|
3133
|
-
* 注册对象验证器
|
|
3134
|
-
* @param ajv - ajv实例
|
|
3135
|
-
*/
|
|
3136
|
-
registerObjectValidators(ajv: any): void;
|
|
3137
|
-
|
|
3138
|
-
/**
|
|
3139
|
-
* 注册数组验证器
|
|
3140
|
-
* @param ajv - ajv实例
|
|
3141
|
-
*/
|
|
3142
|
-
registerArrayValidators(ajv: any): void;
|
|
3143
|
-
|
|
3144
|
-
/**
|
|
3145
|
-
* 注册日期验证器
|
|
3146
|
-
* @param ajv - ajv实例
|
|
3147
|
-
*/
|
|
3148
|
-
registerDateValidators(ajv: any): void;
|
|
3149
|
-
};
|
|
3150
|
-
|
|
3151
|
-
// ========== 常量 ==========
|
|
3152
|
-
|
|
3153
|
-
/**
|
|
3154
|
-
* SchemaIO 配置常量
|
|
3155
|
-
*
|
|
3156
|
-
* @description 所有魔法数字和配置项的统一定义
|
|
3157
|
-
*
|
|
3158
|
-
* @example
|
|
3159
|
-
* ```typescript
|
|
3160
|
-
* import { CONSTANTS } from 'schema-dsl';
|
|
3161
|
-
*
|
|
3162
|
-
* console.log(CONSTANTS.VALIDATION.MAX_RECURSION_DEPTH); // 100
|
|
3163
|
-
* console.log(CONSTANTS.CACHE.SCHEMA_CACHE.MAX_SIZE); // 5000
|
|
3164
|
-
* ```
|
|
3165
|
-
*/
|
|
3166
|
-
export const CONSTANTS: {
|
|
3167
|
-
/** 验证配置 */
|
|
3168
|
-
VALIDATION: {
|
|
3169
|
-
/** 递归深度限制 */
|
|
3170
|
-
MAX_RECURSION_DEPTH: number;
|
|
3171
|
-
/** 数组大小限制 */
|
|
3172
|
-
MAX_ARRAY_SIZE: number;
|
|
3173
|
-
/** 字符串长度限制 */
|
|
3174
|
-
MAX_STRING_LENGTH: number;
|
|
3175
|
-
/** 对象属性数量限制 */
|
|
3176
|
-
MAX_OBJECT_KEYS: number;
|
|
3177
|
-
/** 验证超时时间(ms) */
|
|
3178
|
-
DEFAULT_TIMEOUT: number;
|
|
3179
|
-
/** 正则表达式超时(ms) */
|
|
3180
|
-
REGEX_TIMEOUT: number;
|
|
3181
|
-
/** 自定义验证函数超时(ms) */
|
|
3182
|
-
CUSTOM_VALIDATOR_TIMEOUT: number;
|
|
3183
|
-
/** 默认选项 */
|
|
3184
|
-
DEFAULT_OPTIONS: {
|
|
3185
|
-
abortEarly: boolean;
|
|
3186
|
-
stripUnknown: boolean;
|
|
3187
|
-
convert: boolean;
|
|
3188
|
-
presence: string;
|
|
3189
|
-
allowUnknown: boolean;
|
|
3190
|
-
skipFunctions: boolean;
|
|
3191
|
-
};
|
|
3192
|
-
};
|
|
3193
|
-
/** 缓存配置 */
|
|
3194
|
-
CACHE: {
|
|
3195
|
-
/** 缓存开关 */
|
|
3196
|
-
ENABLED: boolean;
|
|
3197
|
-
/** Schema编译缓存 */
|
|
3198
|
-
SCHEMA_CACHE: {
|
|
3199
|
-
/** 最大缓存条目 */
|
|
3200
|
-
MAX_SIZE: number;
|
|
3201
|
-
/** 缓存过期时间(ms) */
|
|
3202
|
-
TTL: number;
|
|
3203
|
-
};
|
|
3204
|
-
};
|
|
3205
|
-
/** 格式配置 */
|
|
3206
|
-
FORMAT: Record<string, any>;
|
|
3207
|
-
/** 类型配置 */
|
|
3208
|
-
TYPES: Record<string, any>;
|
|
3209
|
-
/** 错误配置 */
|
|
3210
|
-
ERRORS: Record<string, any>;
|
|
3211
|
-
};
|
|
3212
|
-
|
|
3213
|
-
/**
|
|
3214
|
-
* 版本信息
|
|
3215
|
-
*
|
|
3216
|
-
* @description 当前schema-dsl版本号
|
|
3217
|
-
*
|
|
3218
|
-
* @example
|
|
3219
|
-
* ```typescript
|
|
3220
|
-
* import { VERSION } from 'schema-dsl';
|
|
3221
|
-
*
|
|
3222
|
-
* console.log(`schema-dsl version: ${VERSION}`); // schema-dsl version: 1.0.4
|
|
3223
|
-
* ```
|
|
3224
|
-
*/
|
|
3225
|
-
export const VERSION: string;
|
|
3226
|
-
|
|
3227
|
-
/**
|
|
3228
|
-
* 链式条件构建器
|
|
3229
|
-
*
|
|
3230
|
-
* @description 提供流畅的条件判断 API,类似 JavaScript if-else 语句
|
|
3231
|
-
*
|
|
3232
|
-
* @example
|
|
3233
|
-
* ```typescript
|
|
3234
|
-
* import { dsl } from 'schema-dsl';
|
|
3235
|
-
*
|
|
3236
|
-
* // 简单条件 + 错误消息
|
|
3237
|
-
* const schema = dsl({
|
|
3238
|
-
* email: dsl.if((data) => data.age >= 18)
|
|
3239
|
-
* .message('未成年用户不能注册')
|
|
3240
|
-
* });
|
|
3241
|
-
*
|
|
3242
|
-
* // 多条件 and
|
|
3243
|
-
* const schema2 = dsl({
|
|
3244
|
-
* email: dsl.if((data) => data.age >= 18)
|
|
3245
|
-
* .and((data) => data.userType === 'admin')
|
|
3246
|
-
* .then('email!')
|
|
3247
|
-
* });
|
|
3248
|
-
*
|
|
3249
|
-
* // 多条件 or
|
|
3250
|
-
* const schema3 = dsl({
|
|
3251
|
-
* status: dsl.if((data) => data.age < 18)
|
|
3252
|
-
* .or((data) => data.isBlocked)
|
|
3253
|
-
* .message('不允许注册')
|
|
3254
|
-
* });
|
|
3255
|
-
* ```
|
|
3256
|
-
*
|
|
3257
|
-
* @internal
|
|
3258
|
-
* 注意:此类不应直接导入使用。请通过 dsl.if() 或 dsl['if']() 返回实例。
|
|
3259
|
-
*
|
|
3260
|
-
* @example 正确用法
|
|
3261
|
-
* ```typescript
|
|
3262
|
-
* import { dsl } from 'schema-dsl';
|
|
3263
|
-
*
|
|
3264
|
-
* // TypeScript
|
|
3265
|
-
* const builder = dsl['if'](d => d.age > 18);
|
|
3266
|
-
* // 或
|
|
3267
|
-
* const builder2 = dsl._if(d => d.age > 18);
|
|
3268
|
-
*
|
|
3269
|
-
* // JavaScript
|
|
3270
|
-
* const builder3 = dsl.if(d => d.age > 18);
|
|
3271
|
-
* ```
|
|
3272
|
-
*/
|
|
3273
|
-
export class ConditionalBuilder {
|
|
3274
|
-
/**
|
|
3275
|
-
* 开始条件判断
|
|
3276
|
-
* @param condition - 条件函数,接收完整数据对象
|
|
3277
|
-
* @returns 当前实例(支持链式调用)
|
|
3278
|
-
*/
|
|
3279
|
-
if(condition: (data: any) => boolean): this;
|
|
3280
|
-
|
|
3281
|
-
/**
|
|
3282
|
-
* 添加 AND 条件(与前一个条件组合)
|
|
3283
|
-
*
|
|
3284
|
-
* @version 1.1.1 支持为每个 .and() 条件设置独立的错误消息
|
|
3285
|
-
*
|
|
3286
|
-
* @param condition - 条件函数
|
|
3287
|
-
* @returns 当前实例(支持链式调用)
|
|
3288
|
-
*
|
|
3289
|
-
* @example 基础用法(传统 AND 逻辑)
|
|
3290
|
-
* ```typescript
|
|
3291
|
-
* // 所有条件都为 true 才失败
|
|
3292
|
-
* dsl.if(d => d.age >= 18)
|
|
3293
|
-
* .and(d => d.userType === 'admin')
|
|
3294
|
-
* .then('email!')
|
|
3295
|
-
* ```
|
|
3296
|
-
*
|
|
3297
|
-
* @example v1.1.0+ 独立消息(推荐)
|
|
3298
|
-
* ```typescript
|
|
3299
|
-
* // 每个条件都有自己的错误消息
|
|
3300
|
-
* dsl.if(d => !d)
|
|
3301
|
-
* .message('ACCOUNT_NOT_FOUND')
|
|
3302
|
-
* .and(d => d.balance < 100)
|
|
3303
|
-
* .message('INSUFFICIENT_BALANCE')
|
|
3304
|
-
* .assert(account);
|
|
3305
|
-
*
|
|
3306
|
-
* // 工作原理:链式检查模式
|
|
3307
|
-
* // - 第一个条件失败 → 返回 'ACCOUNT_NOT_FOUND'
|
|
3308
|
-
* // - 第二个条件失败 → 返回 'INSUFFICIENT_BALANCE'
|
|
3309
|
-
* // - 所有条件通过 → 验证成功
|
|
3310
|
-
* ```
|
|
3311
|
-
*
|
|
3312
|
-
* @example 多个 .and() 条件
|
|
3313
|
-
* ```typescript
|
|
3314
|
-
* dsl.if(d => !d)
|
|
3315
|
-
* .message('NOT_FOUND')
|
|
3316
|
-
* .and(d => d.status !== 'active')
|
|
3317
|
-
* .message('INACTIVE')
|
|
3318
|
-
* .and(d => d.balance < 100)
|
|
3319
|
-
* .message('INSUFFICIENT')
|
|
3320
|
-
* .assert(account);
|
|
3321
|
-
* // 依次检查,第一个失败的返回其消息
|
|
3322
|
-
* ```
|
|
3323
|
-
*/
|
|
3324
|
-
and(condition: (data: any) => boolean): this;
|
|
3325
|
-
|
|
3326
|
-
/**
|
|
3327
|
-
* 添加 OR 条件(与前一个条件组合)
|
|
3328
|
-
*
|
|
3329
|
-
* @version 1.1.1 支持为 .or() 条件设置独立的错误消息
|
|
3330
|
-
*
|
|
3331
|
-
* @param condition - 条件函数
|
|
3332
|
-
* @returns 当前实例(支持链式调用)
|
|
3333
|
-
*
|
|
3334
|
-
* @example 基础用法
|
|
3335
|
-
* ```typescript
|
|
3336
|
-
* // 任一条件为 true 就失败
|
|
3337
|
-
* dsl.if((data) => data.age < 18)
|
|
3338
|
-
* .or((data) => data.isBlocked)
|
|
3339
|
-
* .message('不允许注册')
|
|
3340
|
-
* ```
|
|
3341
|
-
*
|
|
3342
|
-
* @example v1.1.0+ 独立消息
|
|
3343
|
-
* ```typescript
|
|
3344
|
-
* dsl.if(d => d.age < 18)
|
|
3345
|
-
* .message('未成年用户不能注册')
|
|
3346
|
-
* .or(d => d.isBlocked)
|
|
3347
|
-
* .message('账户已被封禁')
|
|
3348
|
-
* .assert(data);
|
|
3349
|
-
* // 哪个条件为 true 就返回哪个消息
|
|
3350
|
-
* ```
|
|
3351
|
-
*/
|
|
3352
|
-
or(condition: (data: any) => boolean): this;
|
|
3353
|
-
|
|
3354
|
-
/**
|
|
3355
|
-
* 添加 else-if 分支
|
|
3356
|
-
* @param condition - 条件函数
|
|
3357
|
-
* @returns 当前实例(支持链式调用)
|
|
3358
|
-
*/
|
|
3359
|
-
elseIf(condition: (data: any) => boolean): this;
|
|
3360
|
-
|
|
3361
|
-
/**
|
|
3362
|
-
* 设置错误消息(支持多语言 key)
|
|
3363
|
-
*
|
|
3364
|
-
* @version 1.1.1 支持为 .and() 和 .or() 条件设置独立消息
|
|
3365
|
-
*
|
|
3366
|
-
* 条件为 true 时自动抛出此错误
|
|
3367
|
-
*
|
|
3368
|
-
* @param msg - 错误消息或多语言 key
|
|
3369
|
-
* @returns 当前实例(支持链式调用)
|
|
3370
|
-
*
|
|
3371
|
-
* @example 基础用法
|
|
3372
|
-
* ```typescript
|
|
3373
|
-
* // 如果是未成年人(条件为true),抛出错误
|
|
3374
|
-
* dsl.if((data) => data.age < 18)
|
|
3375
|
-
* .message('未成年用户不能注册')
|
|
3376
|
-
* ```
|
|
3377
|
-
*
|
|
3378
|
-
* @example v1.1.0+ 为 .and() 设置独立消息
|
|
3379
|
-
* ```typescript
|
|
3380
|
-
* dsl.if((data) => !data)
|
|
3381
|
-
* .message('账户不存在')
|
|
3382
|
-
* .and((data) => data.balance < 100)
|
|
3383
|
-
* .message('余额不足')
|
|
3384
|
-
* .assert(account);
|
|
3385
|
-
* // 每个条件都有自己的错误消息
|
|
3386
|
-
* ```
|
|
3387
|
-
*
|
|
3388
|
-
* @example 链式检查模式说明
|
|
3389
|
-
* ```typescript
|
|
3390
|
-
* // 启用条件:
|
|
3391
|
-
* // 1. 使用 .message() 模式(不是 .then()/.else())
|
|
3392
|
-
* // 2. root 条件有 .message()
|
|
3393
|
-
* // 3. 有 .and() 条件
|
|
3394
|
-
* // 4. 没有 .or() 条件
|
|
3395
|
-
*
|
|
3396
|
-
* // ✅ 启用链式检查
|
|
3397
|
-
* dsl.if(d => !d).message('A').and(d => d < 100).message('B')
|
|
3398
|
-
*
|
|
3399
|
-
* // ❌ 不启用(有 .or())
|
|
3400
|
-
* dsl.if(d => !d).message('A').and(d => d < 100).or(d => d > 200).message('B')
|
|
3401
|
-
* ```
|
|
3402
|
-
*/
|
|
3403
|
-
message(msg: string): this;
|
|
3404
|
-
|
|
3405
|
-
/**
|
|
3406
|
-
* 设置满足条件时的 Schema
|
|
3407
|
-
* @param schema - DSL 字符串或 Schema 对象
|
|
3408
|
-
* @returns 当前实例(支持链式调用)
|
|
3409
|
-
*/
|
|
3410
|
-
then(schema: string | DslBuilder | JSONSchema): this;
|
|
3411
|
-
|
|
3412
|
-
/**
|
|
3413
|
-
* 设置默认 Schema(所有条件都不满足时)
|
|
3414
|
-
* 可选:不写 else 就是不验证
|
|
3415
|
-
* @param schema - DSL 字符串、Schema 对象或 null
|
|
3416
|
-
* @returns 当前实例(支持链式调用)
|
|
3417
|
-
*/
|
|
3418
|
-
else(schema: string | DslBuilder | JSONSchema | null): this;
|
|
3419
|
-
|
|
3420
|
-
/**
|
|
3421
|
-
* 快捷验证方法 - 返回完整验证结果
|
|
3422
|
-
* @param data - 待验证的数据(任意类型)
|
|
3423
|
-
* @param options - 验证选项(可选)
|
|
3424
|
-
* @returns 验证结果 { valid, errors, data }
|
|
3425
|
-
*
|
|
3426
|
-
* @example
|
|
3427
|
-
* ```typescript
|
|
3428
|
-
* // 一行代码验证
|
|
3429
|
-
* const result = dsl.if(d => d.age < 18)
|
|
3430
|
-
* .message('未成年')
|
|
3431
|
-
* .validate({ age: 16 });
|
|
3432
|
-
*
|
|
3433
|
-
* // 复用验证器
|
|
3434
|
-
* const validator = dsl.if(d => d.age < 18).message('未成年');
|
|
3435
|
-
* const r1 = validator.validate({ age: 16 });
|
|
3436
|
-
* const r2 = validator.validate({ age: 20 });
|
|
3437
|
-
* ```
|
|
3438
|
-
*/
|
|
3439
|
-
validate<T = any>(data: T, options?: ValidateOptions): ValidationResult<T>;
|
|
3440
|
-
|
|
3441
|
-
/**
|
|
3442
|
-
* 异步验证方法 - 失败自动抛出异常
|
|
3443
|
-
* @param data - 待验证的数据
|
|
3444
|
-
* @param options - 验证选项(可选)
|
|
3445
|
-
* @returns 验证通过返回数据,失败抛出异常
|
|
3446
|
-
* @throws ValidationError 验证失败抛出异常
|
|
3447
|
-
*
|
|
3448
|
-
* @example
|
|
3449
|
-
* ```typescript
|
|
3450
|
-
* // 异步验证,失败自动抛错
|
|
3451
|
-
* try {
|
|
3452
|
-
* const data = await dsl.if(d => d.age < 18)
|
|
3453
|
-
* .message('未成年')
|
|
3454
|
-
* .validateAsync({ age: 16 });
|
|
3455
|
-
* } catch (error) {
|
|
3456
|
-
* console.log(error.message);
|
|
3457
|
-
* }
|
|
3458
|
-
*
|
|
3459
|
-
* // Express 中间件
|
|
3460
|
-
* app.post('/register', async (req, res, next) => {
|
|
3461
|
-
* try {
|
|
3462
|
-
* await dsl.if(d => d.age < 18)
|
|
3463
|
-
* .message('未成年用户不能注册')
|
|
3464
|
-
* .validateAsync(req.body);
|
|
3465
|
-
* // 验证通过,继续处理...
|
|
3466
|
-
* } catch (error) {
|
|
3467
|
-
* next(error);
|
|
3468
|
-
* }
|
|
3469
|
-
* });
|
|
3470
|
-
* ```
|
|
3471
|
-
*/
|
|
3472
|
-
validateAsync<T = any>(data: T, options?: ValidateOptions): Promise<T>;
|
|
3473
|
-
|
|
3474
|
-
/**
|
|
3475
|
-
* 断言方法 - 同步验证,失败直接抛错
|
|
3476
|
-
* @param data - 待验证的数据
|
|
3477
|
-
* @param options - 验证选项(可选)
|
|
3478
|
-
* @returns 验证通过返回数据
|
|
3479
|
-
* @throws Error 验证失败抛出错误
|
|
3480
|
-
*
|
|
3481
|
-
* @example
|
|
3482
|
-
* ```typescript
|
|
3483
|
-
* // 断言验证,失败直接抛错
|
|
3484
|
-
* try {
|
|
3485
|
-
* dsl.if(d => d.age < 18)
|
|
3486
|
-
* .message('未成年')
|
|
3487
|
-
* .assert({ age: 16 });
|
|
3488
|
-
* } catch (error) {
|
|
3489
|
-
* console.log(error.message);
|
|
3490
|
-
* }
|
|
3491
|
-
*
|
|
3492
|
-
* // 函数中快速断言
|
|
3493
|
-
* function registerUser(userData: any) {
|
|
3494
|
-
* dsl.if(d => d.age < 18)
|
|
3495
|
-
* .message('未成年用户不能注册')
|
|
3496
|
-
* .assert(userData);
|
|
3497
|
-
*
|
|
3498
|
-
* // 验证通过,继续处理...
|
|
3499
|
-
* return createUser(userData);
|
|
3500
|
-
* }
|
|
3501
|
-
* ```
|
|
3502
|
-
*/
|
|
3503
|
-
assert<T = any>(data: T, options?: ValidateOptions): T;
|
|
3504
|
-
|
|
3505
|
-
/**
|
|
3506
|
-
* 快捷检查方法 - 只返回 boolean
|
|
3507
|
-
* @param data - 待验证的数据
|
|
3508
|
-
* @returns 验证是否通过
|
|
3509
|
-
*
|
|
3510
|
-
* @example
|
|
3511
|
-
* ```typescript
|
|
3512
|
-
* // 快速判断
|
|
3513
|
-
* const isValid = dsl.if(d => d.age < 18)
|
|
3514
|
-
* .message('未成年')
|
|
3515
|
-
* .check({ age: 16 });
|
|
3516
|
-
* // => false
|
|
3517
|
-
*
|
|
3518
|
-
* // 断言场景
|
|
3519
|
-
* if (!validator.check(userData)) {
|
|
3520
|
-
* console.log('验证失败');
|
|
3521
|
-
* }
|
|
3522
|
-
* ```
|
|
3523
|
-
*/
|
|
3524
|
-
check(data: any): boolean;
|
|
3525
|
-
}
|
|
3526
|
-
|
|
3527
|
-
/**
|
|
3528
|
-
* 默认导出(dsl函数)
|
|
3529
|
-
*
|
|
3530
|
-
* @example
|
|
3531
|
-
* ```typescript
|
|
3532
|
-
* import schema-dsl from 'schema-dsl';
|
|
3533
|
-
*
|
|
3534
|
-
* const schema = schema-dsl({
|
|
3535
|
-
* username: 'string:3-32!',
|
|
3536
|
-
* email: 'email!'
|
|
3537
|
-
* });
|
|
3538
|
-
* ```
|
|
3539
|
-
*/
|
|
3540
|
-
export default dsl;
|