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/docs/type-converter.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# TypeConverter 类型转换工具
|
|
2
2
|
|
|
3
|
-
> **模块**: `
|
|
3
|
+
> **模块**: `src/utils/TypeConverter.ts`
|
|
4
4
|
|
|
5
5
|
> **用途**: 提供 JSON Schema 与各种数据库类型之间的转换
|
|
6
6
|
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
## 快速开始
|
|
35
35
|
|
|
36
36
|
```javascript
|
|
37
|
-
const { TypeConverter } = require('schema-dsl
|
|
37
|
+
const { TypeConverter } = require('schema-dsl');
|
|
38
38
|
|
|
39
39
|
// JSON Schema 类型转 MongoDB 类型
|
|
40
40
|
const mongoType = TypeConverter.toMongoDBType('integer');
|
|
@@ -45,40 +45,29 @@ const mysqlType = TypeConverter.toMySQLType('string', { maxLength: 100 });
|
|
|
45
45
|
console.log(mysqlType); // 'VARCHAR(100)'
|
|
46
46
|
|
|
47
47
|
// JSON Schema 类型转 PostgreSQL 类型
|
|
48
|
-
const pgType = TypeConverter.toPostgreSQLType('string', { format: '
|
|
49
|
-
console.log(pgType); // '
|
|
48
|
+
const pgType = TypeConverter.toPostgreSQLType('string', { format: 'date-time' });
|
|
49
|
+
console.log(pgType); // 'TIMESTAMP'
|
|
50
50
|
```
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
54
|
## API 参考
|
|
55
55
|
|
|
56
|
-
### `toJSONSchemaType(
|
|
56
|
+
### `toJSONSchemaType(nativeType)`
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
将类型标识转换为 JSON Schema 的 `type` 字符串。
|
|
59
59
|
|
|
60
60
|
```javascript
|
|
61
61
|
TypeConverter.toJSONSchemaType('string');
|
|
62
|
-
//
|
|
62
|
+
// 'string'
|
|
63
63
|
|
|
64
|
-
TypeConverter.toJSONSchemaType('
|
|
65
|
-
//
|
|
64
|
+
TypeConverter.toJSONSchemaType('integer');
|
|
65
|
+
// 'integer'
|
|
66
66
|
|
|
67
|
-
TypeConverter.toJSONSchemaType('
|
|
68
|
-
//
|
|
67
|
+
TypeConverter.toJSONSchemaType('email');
|
|
68
|
+
// 'string'
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
-
**支持的别名**:
|
|
72
|
-
|
|
73
|
-
| 完整类型 | 别名 |
|
|
74
|
-
|---------|------|
|
|
75
|
-
| `string` | `str`, `s` |
|
|
76
|
-
| `number` | `num`, `n` |
|
|
77
|
-
| `integer` | `int`, `i` |
|
|
78
|
-
| `boolean` | `bool`, `b` |
|
|
79
|
-
| `object` | `obj`, `o` |
|
|
80
|
-
| `array` | `arr`, `a` |
|
|
81
|
-
|
|
82
71
|
---
|
|
83
72
|
|
|
84
73
|
### `toMongoDBType(jsonSchemaType)`
|
|
@@ -94,7 +83,7 @@ TypeConverter.toMongoDBType('boolean'); // 'bool'
|
|
|
94
83
|
|
|
95
84
|
---
|
|
96
85
|
|
|
97
|
-
### `toMySQLType(jsonSchemaType,
|
|
86
|
+
### `toMySQLType(jsonSchemaType, schemaFragment)`
|
|
98
87
|
|
|
99
88
|
JSON Schema 类型转 MySQL 数据类型。
|
|
100
89
|
|
|
@@ -115,21 +104,21 @@ TypeConverter.toMySQLType('string', { maxLength: 500 });
|
|
|
115
104
|
TypeConverter.toMySQLType('string', { format: 'email' });
|
|
116
105
|
// 'VARCHAR(255)'
|
|
117
106
|
|
|
118
|
-
//
|
|
119
|
-
TypeConverter.toMySQLType('integer', { maximum: 100 });
|
|
107
|
+
// 整数范围(minimum + maximum 命中 TINYINT 分支)
|
|
108
|
+
TypeConverter.toMySQLType('integer', { minimum: 0, maximum: 100 });
|
|
120
109
|
// 'TINYINT'
|
|
121
110
|
```
|
|
122
111
|
|
|
123
112
|
---
|
|
124
113
|
|
|
125
|
-
### `toPostgreSQLType(jsonSchemaType,
|
|
114
|
+
### `toPostgreSQLType(jsonSchemaType, schemaFragment)`
|
|
126
115
|
|
|
127
116
|
JSON Schema 类型转 PostgreSQL 数据类型。
|
|
128
117
|
|
|
129
118
|
```javascript
|
|
130
119
|
// UUID 格式
|
|
131
120
|
TypeConverter.toPostgreSQLType('string', { format: 'uuid' });
|
|
132
|
-
// 'UUID
|
|
121
|
+
// 'VARCHAR(255)' // 当前实现不会因 format=uuid 自动切换到 UUID 列类型
|
|
133
122
|
|
|
134
123
|
// 日期时间
|
|
135
124
|
TypeConverter.toPostgreSQLType('string', { format: 'date-time' });
|
|
@@ -142,34 +131,35 @@ TypeConverter.toPostgreSQLType('object');
|
|
|
142
131
|
|
|
143
132
|
---
|
|
144
133
|
|
|
145
|
-
### `normalizePropertyName(name
|
|
134
|
+
### `normalizePropertyName(name)`
|
|
146
135
|
|
|
147
|
-
|
|
136
|
+
规范化属性名:去除首尾空白、将非法字符替换为下划线,并压缩连续下划线。
|
|
148
137
|
|
|
149
138
|
```javascript
|
|
150
|
-
|
|
151
|
-
TypeConverter.normalizePropertyName('userName', 'snake_case');
|
|
139
|
+
TypeConverter.normalizePropertyName('user name');
|
|
152
140
|
// 'user_name'
|
|
153
141
|
|
|
154
|
-
TypeConverter.normalizePropertyName('
|
|
155
|
-
// '
|
|
142
|
+
TypeConverter.normalizePropertyName('123created-at');
|
|
143
|
+
// '123created_at'
|
|
156
144
|
```
|
|
157
145
|
|
|
158
146
|
---
|
|
159
147
|
|
|
160
148
|
### `formatToRegex(format)`
|
|
161
149
|
|
|
162
|
-
|
|
150
|
+
获取格式对应的 `RegExp` 实例;未知格式返回 `null`。
|
|
163
151
|
|
|
164
152
|
```javascript
|
|
165
|
-
TypeConverter.formatToRegex('email');
|
|
166
|
-
|
|
153
|
+
const emailRegex = TypeConverter.formatToRegex('email');
|
|
154
|
+
emailRegex?.test('user@example.com');
|
|
155
|
+
// true
|
|
167
156
|
|
|
168
|
-
TypeConverter.formatToRegex('uuid');
|
|
169
|
-
|
|
157
|
+
const uuidRegex = TypeConverter.formatToRegex('uuid');
|
|
158
|
+
uuidRegex?.test('123e4567-e89b-12d3-a456-426614174000');
|
|
159
|
+
// true
|
|
170
160
|
|
|
171
|
-
TypeConverter.formatToRegex('
|
|
172
|
-
//
|
|
161
|
+
TypeConverter.formatToRegex('unknown');
|
|
162
|
+
// null
|
|
173
163
|
```
|
|
174
164
|
|
|
175
165
|
---
|
|
@@ -217,12 +207,8 @@ const constraints = TypeConverter.extractConstraints(schema);
|
|
|
217
207
|
// {
|
|
218
208
|
// minLength: 3,
|
|
219
209
|
// maxLength: 32,
|
|
220
|
-
// minimum: undefined,
|
|
221
|
-
// maximum: undefined,
|
|
222
210
|
// pattern: '^[a-z]+$',
|
|
223
|
-
// format: 'email'
|
|
224
|
-
// enum: undefined,
|
|
225
|
-
// default: undefined
|
|
211
|
+
// format: 'email'
|
|
226
212
|
// }
|
|
227
213
|
```
|
|
228
214
|
|
|
@@ -251,7 +237,7 @@ const constraints = TypeConverter.extractConstraints(schema);
|
|
|
251
237
|
| `string` | `maxLength: 500` | `TEXT` |
|
|
252
238
|
| `string` | `format: email` | `VARCHAR(255)` |
|
|
253
239
|
| `string` | `format: date-time` | `DATETIME` |
|
|
254
|
-
| `integer` | `maximum:
|
|
240
|
+
| `integer` | `minimum: 0, maximum: 100` | `TINYINT` |
|
|
255
241
|
| `integer` | `maximum: 32767` | `SMALLINT` |
|
|
256
242
|
| `integer` | `maximum: 2147483647` | `INT` |
|
|
257
243
|
| `integer` | - | `BIGINT` |
|
|
@@ -267,11 +253,9 @@ const constraints = TypeConverter.extractConstraints(schema);
|
|
|
267
253
|
| `string` | - | `VARCHAR(255)` |
|
|
268
254
|
| `string` | `maxLength: 50` | `VARCHAR(50)` |
|
|
269
255
|
| `string` | `maxLength: 500` | `TEXT` |
|
|
270
|
-
| `string` | `format: uuid` | `
|
|
256
|
+
| `string` | `format: uuid` | `VARCHAR(255)` |
|
|
271
257
|
| `string` | `format: date` | `DATE` |
|
|
272
258
|
| `string` | `format: date-time` | `TIMESTAMP` |
|
|
273
|
-
| `integer` | `maximum: 32767` | `SMALLINT` |
|
|
274
|
-
| `integer` | `maximum: 2147483647` | `INTEGER` |
|
|
275
259
|
| `integer` | - | `BIGINT` |
|
|
276
260
|
| `number` | - | `DOUBLE PRECISION` |
|
|
277
261
|
| `boolean` | - | `BOOLEAN` |
|
|
@@ -285,7 +269,7 @@ const constraints = TypeConverter.extractConstraints(schema);
|
|
|
285
269
|
### 批量类型转换
|
|
286
270
|
|
|
287
271
|
```javascript
|
|
288
|
-
const { TypeConverter } = require('schema-dsl
|
|
272
|
+
const { TypeConverter } = require('schema-dsl');
|
|
289
273
|
|
|
290
274
|
const fields = ['string', 'number', 'integer', 'boolean', 'object', 'array'];
|
|
291
275
|
|
|
@@ -317,3 +301,10 @@ console.log(regex.test('invalid-email')); // false
|
|
|
317
301
|
- [MySQL 导出器](mysql-exporter.md)
|
|
318
302
|
- [PostgreSQL 导出器](postgresql-exporter.md)
|
|
319
303
|
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## 对应示例文件
|
|
307
|
+
|
|
308
|
+
**示例入口**: [type-converter.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/type-converter.ts)
|
|
309
|
+
**说明**: 覆盖类型映射、枚举到 MySQL `ENUM(...)`、PostgreSQL 实际 UUID 映射、属性名规范化、正则获取、Schema 合并和约束提取。
|
|
310
|
+
|
package/docs/type-reference.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
# schema-dsl
|
|
1
|
+
# schema-dsl 类型参考
|
|
2
2
|
|
|
3
|
-
> **更新时间**:
|
|
3
|
+
> **更新时间**: 2026-05-08
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
### 基本类型
|
|
10
10
|
|
|
11
|
-
| 类型 |
|
|
11
|
+
| 类型 | schema-dsl DSL | JSON Schema | 说明 |
|
|
12
12
|
|------|----------|-------------|------|
|
|
13
13
|
| 字符串 | `string` | `{ type: 'string' }` | 文本类型 |
|
|
14
14
|
| 数字 | `number` | `{ type: 'number' }` | 浮点数 |
|
|
@@ -23,14 +23,17 @@
|
|
|
23
23
|
|
|
24
24
|
### 格式类型(基于 string)
|
|
25
25
|
|
|
26
|
-
| 类型 |
|
|
26
|
+
| 类型 | schema-dsl DSL | JSON Schema format | 说明 |
|
|
27
27
|
|------|----------|-------------------|------|
|
|
28
28
|
| 邮箱 | `email` | `email` | 邮箱地址 |
|
|
29
29
|
| URL | `url` | `uri` | 网址 |
|
|
30
|
+
| URI | `uri` | `uri` | URI 字符串 |
|
|
30
31
|
| UUID | `uuid` | `uuid` | UUID格式 |
|
|
32
|
+
| IP(IPv4/IPv6) | `ip` | `anyOf(ipv4, ipv6)` | 双栈 IP |
|
|
31
33
|
| 日期 | `date` | `date` | YYYY-MM-DD |
|
|
32
34
|
| 日期时间 | `datetime` | `date-time` | ISO 8601 |
|
|
33
35
|
| 时间 | `time` | `time` | HH:mm:ss |
|
|
36
|
+
| 主机名 | `hostname` | `hostname` | 主机名 |
|
|
34
37
|
| IPv4 | `ipv4` | `ipv4` | IPv4地址 |
|
|
35
38
|
| IPv6 | `ipv6` | `ipv6` | IPv6地址 |
|
|
36
39
|
|
|
@@ -38,13 +41,22 @@
|
|
|
38
41
|
|
|
39
42
|
### 特殊类型
|
|
40
43
|
|
|
41
|
-
| 类型 |
|
|
44
|
+
| 类型 | schema-dsl DSL | JSON Schema | 说明 |
|
|
42
45
|
|------|----------|-------------|------|
|
|
43
46
|
| 二进制 | `binary` | `contentEncoding: base64` | Base64编码 |
|
|
44
47
|
| ObjectId | `objectId` | `pattern: ^[0-9a-fA-F]{24}$` | MongoDB ObjectId |
|
|
45
48
|
| HexColor | `hexColor` | `pattern: ^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$` | CSS 16进制颜色 |
|
|
46
49
|
| MAC地址 | `macAddress` | `pattern: ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$` | MAC地址 |
|
|
47
50
|
| Cron | `cron` | `pattern: ...` | Cron表达式 |
|
|
51
|
+
| Slug | `slug` | `pattern: ^[a-z0-9]+(?:-[a-z0-9]+)*$` | URL Slug |
|
|
52
|
+
| 中文姓名 | `chineseName` | `pattern: ^[\u4e00-\u9fa5]{2,10}$` | 中文姓名 |
|
|
53
|
+
| 中文文本 | `chinese` | `pattern: ^[\u4e00-\u9fa5]+$` | 纯中文文本 |
|
|
54
|
+
| 邮箱域扩展 | `emailDomain` | `format: email` | 邮箱 + 域名扩展校验 |
|
|
55
|
+
| 只含字母数字 | `alphanum` | `alphanum: true` | 自定义 AJV keyword |
|
|
56
|
+
| 小写字符串 | `lower` | `lowercase: true` | 自定义 AJV keyword |
|
|
57
|
+
| 大写字符串 | `upper` | `uppercase: true` | 自定义 AJV keyword |
|
|
58
|
+
| JSON字符串 | `json` | `jsonString: true` | 自定义 AJV keyword |
|
|
59
|
+
| 端口号 | `port` | `port: true` | 整数端口号 |
|
|
48
60
|
|
|
49
61
|
---
|
|
50
62
|
|
|
@@ -87,14 +99,14 @@ const schema8 = dsl({ data: 'any' });
|
|
|
87
99
|
|
|
88
100
|
---
|
|
89
101
|
|
|
90
|
-
###
|
|
102
|
+
### 参数化 DSL 类型
|
|
91
103
|
|
|
92
104
|
```javascript
|
|
93
|
-
//
|
|
105
|
+
// 手机号(默认 cn)
|
|
94
106
|
const schema1 = dsl({ mobile: 'phone:cn!' });
|
|
95
107
|
|
|
96
108
|
// 身份证
|
|
97
|
-
const schema2 = dsl({
|
|
109
|
+
const schema2 = dsl({ idCard: 'idCard:cn!' });
|
|
98
110
|
|
|
99
111
|
// 信用卡
|
|
100
112
|
const schema3 = dsl({ card: 'creditCard:visa!' });
|
|
@@ -106,7 +118,7 @@ const schema4 = dsl({ plate: 'licensePlate:cn!' });
|
|
|
106
118
|
const schema5 = dsl({ zip: 'postalCode:cn!' });
|
|
107
119
|
|
|
108
120
|
// 护照
|
|
109
|
-
const schema6 = dsl({
|
|
121
|
+
const schema6 = dsl({ passportNo: 'passport:cn!' });
|
|
110
122
|
```
|
|
111
123
|
|
|
112
124
|
---
|
|
@@ -156,7 +168,7 @@ const schema = dsl({
|
|
|
156
168
|
|
|
157
169
|
### 完整对照表
|
|
158
170
|
|
|
159
|
-
| joi |
|
|
171
|
+
| joi | schema-dsl DSL | 说明 |
|
|
160
172
|
|-----|--------------|------|
|
|
161
173
|
| `Joi.string()` | `'string'` | 字符串 |
|
|
162
174
|
| `Joi.string().email()` | `'email'` | 邮箱 |
|
|
@@ -190,23 +202,27 @@ const schema = dsl({
|
|
|
190
202
|
|
|
191
203
|
## ❓ 常见问题
|
|
192
204
|
|
|
193
|
-
### Q1:
|
|
205
|
+
### Q1: 为什么没有直接叫 `Joi.alternatives()` 的 API?
|
|
194
206
|
|
|
195
|
-
A:
|
|
207
|
+
A: schema-dsl 把这类需求拆成两类:
|
|
208
|
+
|
|
209
|
+
- 单字段跨类型联合: 使用 `types:` 语法
|
|
210
|
+
- 根据其他字段做条件分支: 使用 `dsl.match()`
|
|
196
211
|
|
|
197
212
|
```javascript
|
|
198
213
|
const schema = dsl({
|
|
214
|
+
value: 'types:string|number',
|
|
199
215
|
contactType: 'email|phone',
|
|
200
216
|
contact: dsl.match('contactType', {
|
|
201
217
|
email: 'email!',
|
|
202
|
-
phone: '
|
|
218
|
+
phone: 'phone:cn!'
|
|
203
219
|
})
|
|
204
220
|
});
|
|
205
221
|
```
|
|
206
222
|
|
|
207
223
|
### Q2: 为什么 `integer` 不是 `number().integer()`?
|
|
208
224
|
|
|
209
|
-
A:
|
|
225
|
+
A: schema-dsl 使用 JSON Schema 标准,`integer` 是独立类型。
|
|
210
226
|
|
|
211
227
|
### Q3: 不支持简写吗?
|
|
212
228
|
|
|
@@ -214,6 +230,13 @@ A: 不支持 `s`/`n`/`i`/`b` 等简写,统一使用完整类型名(`string`/
|
|
|
214
230
|
|
|
215
231
|
---
|
|
216
232
|
|
|
217
|
-
**最后更新**:
|
|
233
|
+
**最后更新**: 2026-05-08
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## 对应示例文件
|
|
238
|
+
|
|
239
|
+
**示例入口**: [type-reference.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/type-reference.ts)
|
|
240
|
+
**说明**: 用一份 schema 串起常用内置类型、参数化 DSL 类型和运行时错误路径,方便快速核对实际支持范围。
|
|
218
241
|
|
|
219
242
|
|
package/docs/typescript-guide.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# TypeScript 使用指南
|
|
2
2
|
|
|
3
|
-
> **版本**: schema-dsl
|
|
4
|
-
> **更新日期**: 2026-
|
|
3
|
+
> **版本**: schema-dsl v2.0.0-beta.2
|
|
4
|
+
> **更新日期**: 2026-05-08
|
|
5
5
|
> **重要**: v1.0.6 移除了全局 String 类型扩展以避免类型污染
|
|
6
6
|
|
|
7
7
|
---
|
|
@@ -106,7 +106,10 @@ const emailBuilder = dsl('email!');
|
|
|
106
106
|
emailBuilder.label('邮箱')
|
|
107
107
|
// ^? IDE 自动提示所有可用方法
|
|
108
108
|
.pattern(/^[a-z]+@[a-z]+\.[a-z]+$/)
|
|
109
|
-
.
|
|
109
|
+
.error({ required: '邮箱必填' });
|
|
110
|
+
|
|
111
|
+
> ℹ️ 当前类型声明优先覆盖稳定链式 API,例如 `label()`、`pattern()`、`error()`、`default()`。
|
|
112
|
+
> 某些运行时扩展方法依然可用,但如果类型声明未暴露,建议在 TypeScript 代码里优先改写为上述稳定组合。
|
|
110
113
|
```
|
|
111
114
|
|
|
112
115
|
---
|
|
@@ -128,12 +131,13 @@ emailBuilder.label('邮箱')
|
|
|
128
131
|
```typescript
|
|
129
132
|
const schema = dsl({
|
|
130
133
|
username: dsl('string:3-32!')
|
|
131
|
-
.pattern(/^[a-zA-Z0-9_]
|
|
134
|
+
.pattern(/^[a-zA-Z0-9_]+$/)
|
|
132
135
|
.label('用户名'),
|
|
136
|
+
.error({ pattern: '只能包含字母、数字和下划线' }),
|
|
133
137
|
|
|
134
138
|
email: dsl('email!')
|
|
135
139
|
.label('邮箱地址')
|
|
136
|
-
.
|
|
140
|
+
.error({ required: '邮箱必填' }),
|
|
137
141
|
|
|
138
142
|
age: dsl('number:18-100')
|
|
139
143
|
.label('年龄')
|
|
@@ -151,17 +155,21 @@ const schema = dsl({
|
|
|
151
155
|
// 定义可复用的字段
|
|
152
156
|
const emailField = dsl('email!')
|
|
153
157
|
.label('邮箱地址')
|
|
154
|
-
.
|
|
158
|
+
.error({ required: '邮箱必填' });
|
|
155
159
|
|
|
156
160
|
const usernameField = dsl('string:3-32!')
|
|
157
161
|
.pattern(/^[a-zA-Z0-9_]+$/)
|
|
158
|
-
.label('用户名')
|
|
162
|
+
.label('用户名')
|
|
163
|
+
.error({ pattern: '用户名只能包含字母、数字和下划线' });
|
|
159
164
|
|
|
160
165
|
// 组合使用
|
|
161
166
|
const registrationSchema = dsl({
|
|
162
167
|
email: emailField,
|
|
163
168
|
username: usernameField,
|
|
164
|
-
password: dsl('string:8-64!')
|
|
169
|
+
password: dsl('string:8-64!')
|
|
170
|
+
.pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
|
|
171
|
+
.label('密码')
|
|
172
|
+
.error({ pattern: '密码至少 8 位且必须包含字母和数字' })
|
|
165
173
|
});
|
|
166
174
|
|
|
167
175
|
const loginSchema = dsl({
|
|
@@ -203,20 +211,18 @@ import { dsl, validateAsync, ValidationError } from 'schema-dsl';
|
|
|
203
211
|
const registrationSchema = dsl({
|
|
204
212
|
profile: dsl({
|
|
205
213
|
username: dsl('string:3-32!')
|
|
206
|
-
.pattern(/^[a-zA-Z0-9_]
|
|
214
|
+
.pattern(/^[a-zA-Z0-9_]+$/)
|
|
207
215
|
.label('用户名')
|
|
208
|
-
.
|
|
209
|
-
min: '用户名至少3个字符',
|
|
210
|
-
max: '用户名最多32个字符'
|
|
211
|
-
}),
|
|
216
|
+
.error({ pattern: '只能包含字母、数字和下划线' }),
|
|
212
217
|
|
|
213
218
|
email: dsl('email!')
|
|
214
219
|
.label('邮箱地址')
|
|
215
|
-
.
|
|
220
|
+
.error({ required: '邮箱必填' }),
|
|
216
221
|
|
|
217
|
-
password: dsl('string!')
|
|
218
|
-
.
|
|
219
|
-
.label('密码')
|
|
222
|
+
password: dsl('string:8-64!')
|
|
223
|
+
.pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
|
|
224
|
+
.label('密码')
|
|
225
|
+
.error({ pattern: '密码至少 8 位且必须包含字母和数字' }),
|
|
220
226
|
|
|
221
227
|
age: dsl('number:18-100')
|
|
222
228
|
.label('年龄')
|
|
@@ -269,7 +275,7 @@ registerUser({
|
|
|
269
275
|
### 4.2 API 请求验证
|
|
270
276
|
|
|
271
277
|
```typescript
|
|
272
|
-
import { dsl, validateAsync } from 'schema-dsl';
|
|
278
|
+
import { ValidationError, dsl, validateAsync } from 'schema-dsl';
|
|
273
279
|
import express from 'express';
|
|
274
280
|
|
|
275
281
|
const app = express();
|
|
@@ -322,15 +328,17 @@ import { dsl } from 'schema-dsl';
|
|
|
322
328
|
const commonFields = {
|
|
323
329
|
email: dsl('email!')
|
|
324
330
|
.label('邮箱地址')
|
|
325
|
-
.
|
|
331
|
+
.error({ required: '邮箱必填' }),
|
|
326
332
|
|
|
327
333
|
username: dsl('string:3-32!')
|
|
328
334
|
.pattern(/^[a-zA-Z0-9_]+$/)
|
|
329
|
-
.label('用户名')
|
|
335
|
+
.label('用户名')
|
|
336
|
+
.error({ pattern: '用户名只能包含字母、数字和下划线' }),
|
|
330
337
|
|
|
331
|
-
password: dsl('string!')
|
|
332
|
-
.
|
|
338
|
+
password: dsl('string:8-64!')
|
|
339
|
+
.pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
|
|
333
340
|
.label('密码')
|
|
341
|
+
.error({ pattern: '密码至少 8 位且必须包含字母和数字' })
|
|
334
342
|
};
|
|
335
343
|
|
|
336
344
|
// 注册表单
|
|
@@ -456,23 +464,21 @@ try {
|
|
|
456
464
|
|
|
457
465
|
## 6. 进阶技巧
|
|
458
466
|
|
|
459
|
-
### 6.1
|
|
467
|
+
### 6.1 额外业务规则
|
|
460
468
|
|
|
461
469
|
```typescript
|
|
462
470
|
const schema = dsl({
|
|
463
|
-
username: dsl('string:3-32!')
|
|
464
|
-
.custom(async (value) => {
|
|
465
|
-
// 异步验证(检查用户名是否已存在)
|
|
466
|
-
const exists = await checkUsernameExists(value);
|
|
467
|
-
if (exists) {
|
|
468
|
-
return { error: 'USERNAME_EXISTS', message: '用户名已存在' };
|
|
469
|
-
}
|
|
470
|
-
return true;
|
|
471
|
-
})
|
|
472
|
-
.label('用户名')
|
|
471
|
+
username: dsl('string:3-32!').label('用户名')
|
|
473
472
|
});
|
|
473
|
+
|
|
474
|
+
const result = await validateAsync(schema, data);
|
|
475
|
+
if (result.username === 'admin') {
|
|
476
|
+
throw new Error('用户名已存在');
|
|
477
|
+
}
|
|
474
478
|
```
|
|
475
479
|
|
|
480
|
+
这种写法的好处是:结构校验仍由 schema-dsl 负责,业务唯一性、数据库查重等规则继续留在 TypeScript 业务层,避免把外部依赖塞进字段声明。
|
|
481
|
+
|
|
476
482
|
### 6.2 条件验证
|
|
477
483
|
|
|
478
484
|
```typescript
|
|
@@ -499,14 +505,14 @@ const baseUserSchema = dsl({
|
|
|
499
505
|
});
|
|
500
506
|
|
|
501
507
|
// 扩展为管理员 Schema
|
|
502
|
-
const adminSchema = SchemaUtils.extend(baseUserSchema
|
|
508
|
+
const adminSchema = SchemaUtils.extend(baseUserSchema, {
|
|
503
509
|
role: dsl('string!').default('admin').label('角色'),
|
|
504
510
|
permissions: dsl('array<string>').label('权限列表')
|
|
505
511
|
});
|
|
506
512
|
|
|
507
513
|
// 只选择部分字段
|
|
508
514
|
const publicUserSchema = SchemaUtils.pick(
|
|
509
|
-
baseUserSchema
|
|
515
|
+
baseUserSchema,
|
|
510
516
|
['username']
|
|
511
517
|
);
|
|
512
518
|
```
|
|
@@ -515,16 +521,14 @@ const publicUserSchema = SchemaUtils.pick(
|
|
|
515
521
|
|
|
516
522
|
## 7. 性能优化
|
|
517
523
|
|
|
518
|
-
### 7.1 Schema
|
|
524
|
+
### 7.1 复用 Schema 与默认缓存
|
|
519
525
|
|
|
520
526
|
```typescript
|
|
521
|
-
// 预编译 Schema(只编译一次)
|
|
522
527
|
const schema = dsl({
|
|
523
528
|
email: dsl('email!').label('邮箱')
|
|
524
529
|
});
|
|
525
|
-
schema.compile(); // 预编译
|
|
526
530
|
|
|
527
|
-
//
|
|
531
|
+
// 多次验证会复用默认 Validator 的编译缓存
|
|
528
532
|
await validateAsync(schema, data1);
|
|
529
533
|
await validateAsync(schema, data2);
|
|
530
534
|
await validateAsync(schema, data3);
|
|
@@ -554,7 +558,7 @@ dsl.config({
|
|
|
554
558
|
4. ✅ **复用常用字段定义**
|
|
555
559
|
5. ✅ **使用 `ValidationError` 类型守卫处理错误**
|
|
556
560
|
6. ✅ **为用户提供友好的错误消息**
|
|
557
|
-
7. ✅
|
|
561
|
+
7. ✅ **复用常用 Schema 对象,让默认缓存命中**
|
|
558
562
|
|
|
559
563
|
---
|
|
560
564
|
|
|
@@ -568,6 +572,13 @@ dsl.config({
|
|
|
568
572
|
|
|
569
573
|
---
|
|
570
574
|
|
|
571
|
-
|
|
572
|
-
|
|
575
|
+
## 对应示例文件
|
|
576
|
+
|
|
577
|
+
**示例入口**: [typescript-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/typescript-guide.ts)
|
|
578
|
+
**说明**: 展示 TypeScript 下推荐的 `dsl()` 包裹写法、`validate<T>()` / `validateAsync<T>()`、以及 `ValidationError` 的字段错误读取方式。
|
|
579
|
+
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
**更新日期**: 2026-05-08
|
|
583
|
+
**文档版本**: v2.0.0-beta.2
|
|
573
584
|
|
package/docs/union-type-guide.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> 使用 `.pattern()` 方法匹配多种格式
|
|
4
4
|
|
|
5
|
+
> ⚠️ 这篇文档描述的是“同一字符串字段匹配多种格式”的做法。
|
|
6
|
+
> 如果你需要真正的跨基础类型联合语义,例如 `string | number | null`,请优先使用 [types: 语法](./union-types.md)。
|
|
7
|
+
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
## 基本用法
|
|
@@ -137,11 +140,18 @@ testData.forEach((data, index) => {
|
|
|
137
140
|
```
|
|
138
141
|
|
|
139
142
|
**输出**:
|
|
140
|
-
```
|
|
143
|
+
```text
|
|
141
144
|
测试1: ✅
|
|
142
145
|
测试2: ✅
|
|
143
146
|
测试3: ❌
|
|
144
147
|
错误: 必须是邮箱或手机号
|
|
145
148
|
```
|
|
146
149
|
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 对应示例文件
|
|
153
|
+
|
|
154
|
+
**示例入口**: [union-type-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/union-type-guide.ts)
|
|
155
|
+
**说明**: 展示基于 `.pattern()` 的“单个字符串字段匹配多种格式”方案,以及对应的中英文错误消息。
|
|
156
|
+
|
|
147
157
|
|
package/docs/union-types.md
CHANGED
|
@@ -55,7 +55,7 @@ validate(schema, { value: 101 }); // ❌ 超范围
|
|
|
55
55
|
|
|
56
56
|
### 基本格式
|
|
57
57
|
|
|
58
|
-
```
|
|
58
|
+
```text
|
|
59
59
|
types:type1|type2|type3[!]
|
|
60
60
|
```
|
|
61
61
|
|
|
@@ -65,7 +65,7 @@ types:type1|type2|type3[!]
|
|
|
65
65
|
|
|
66
66
|
### 带约束格式
|
|
67
67
|
|
|
68
|
-
```
|
|
68
|
+
```text
|
|
69
69
|
types:type1:constraint1|type2:constraint2
|
|
70
70
|
```
|
|
71
71
|
|
|
@@ -267,7 +267,7 @@ DslBuilder.registerType('phone_cn', { ... });
|
|
|
267
267
|
|
|
268
268
|
- [插件系统](./plugin-system.md)
|
|
269
269
|
- [自定义类型注册](./plugin-type-registration.md)
|
|
270
|
-
- [
|
|
270
|
+
- [贡献指南](https://github.com/vextjs/schema-dsl/blob/main/CONTRIBUTING.md)
|
|
271
271
|
|
|
272
272
|
---
|
|
273
273
|
|
|
@@ -275,3 +275,10 @@ DslBuilder.registerType('phone_cn', { ... });
|
|
|
275
275
|
|
|
276
276
|
- **v1.1.0** - 首次发布跨类型联合验证功能
|
|
277
277
|
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## 对应示例文件
|
|
281
|
+
|
|
282
|
+
**示例入口**: [union-types.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/union-types.ts)
|
|
283
|
+
**说明**: 覆盖 `types:` 语法的 `oneOf` 生成、字符串/数字联合,以及内置类型与自定义类型混用路径。
|
|
284
|
+
|