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/i18n.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 多语言配置指南
|
|
2
2
|
|
|
3
|
-
**版本**:
|
|
4
|
-
**最后更新**: 2026-
|
|
3
|
+
**版本**: v2.0.0-beta.1
|
|
4
|
+
**最后更新**: 2026-04-30
|
|
5
5
|
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -21,10 +21,22 @@
|
|
|
21
21
|
|
|
22
22
|
schema-dsl 支持完整的多语言功能,允许你自定义字段标签和错误消息的翻译。
|
|
23
23
|
|
|
24
|
-
**
|
|
24
|
+
> **Node.js 要求**:`>=18.0.0`
|
|
25
|
+
|
|
26
|
+
**目录加载(Node >=18)默认支持的语言文件格式**:
|
|
27
|
+
- `.js`(CommonJS 语言包)
|
|
28
|
+
- `.cjs`
|
|
29
|
+
- `.json`
|
|
30
|
+
- `.jsonc`
|
|
31
|
+
- `.json5`
|
|
32
|
+
|
|
33
|
+
> **推荐**:如果你的应用是 `type: module` / ESM 项目,优先使用 `.cjs`、`.json`、`.jsonc`、`.json5`;`.js` 更适合 CommonJS 语言包文件。
|
|
34
|
+
|
|
35
|
+
**v2 当前能力**:
|
|
25
36
|
- ✅ 支持参数化语言切换(无需修改全局状态)
|
|
26
37
|
- ✅ 支持自定义错误消息(三种格式)
|
|
27
38
|
- ✅ TypeScript 类型定义完整
|
|
39
|
+
- ✅ 目录递归加载 `.js/.cjs/.json/.jsonc/.json5`
|
|
28
40
|
|
|
29
41
|
---
|
|
30
42
|
|
|
@@ -84,7 +96,7 @@ const path = require('path');
|
|
|
84
96
|
|
|
85
97
|
// ✅ 方式 1: 从目录加载(推荐)
|
|
86
98
|
dsl.config({
|
|
87
|
-
i18n: path.join(__dirname, 'i18n/dsl') //
|
|
99
|
+
i18n: path.join(__dirname, 'i18n/dsl') // Node >=18:支持 .js/.cjs/.json/.jsonc/.json5
|
|
88
100
|
});
|
|
89
101
|
|
|
90
102
|
// ✅ 方式 2: 直接传入对象
|
|
@@ -98,20 +110,20 @@ dsl.config({
|
|
|
98
110
|
|
|
99
111
|
### 目录结构
|
|
100
112
|
|
|
101
|
-
```
|
|
113
|
+
```text
|
|
102
114
|
project/
|
|
103
115
|
├── i18n/
|
|
104
116
|
│ └── dsl/ # schema-dsl 语言包目录
|
|
105
|
-
│ ├── zh-CN.
|
|
106
|
-
│ ├── en-US.
|
|
107
|
-
│ └── ja-JP.
|
|
117
|
+
│ ├── zh-CN.cjs # CommonJS / ESM 项目都稳定
|
|
118
|
+
│ ├── en-US.jsonc # 带注释/末尾逗号
|
|
119
|
+
│ └── ja-JP.json5 # JSON5 风格(可选)
|
|
108
120
|
```
|
|
109
121
|
|
|
110
122
|
---
|
|
111
123
|
|
|
112
124
|
## 📝 语言包格式
|
|
113
125
|
|
|
114
|
-
### 完整示例 (`i18n/dsl/zh-CN.
|
|
126
|
+
### 完整示例 (`i18n/dsl/zh-CN.cjs`)
|
|
115
127
|
|
|
116
128
|
```javascript
|
|
117
129
|
module.exports = {
|
|
@@ -190,7 +202,7 @@ dsl.config({
|
|
|
190
202
|
}
|
|
191
203
|
});
|
|
192
204
|
|
|
193
|
-
// 默认语言(
|
|
205
|
+
// 默认语言(en-US)
|
|
194
206
|
validate(schema, data);
|
|
195
207
|
|
|
196
208
|
// 切换到英文
|
|
@@ -203,8 +215,8 @@ validate(schema, data, { locale: 'en-US' });
|
|
|
203
215
|
|
|
204
216
|
### 通用错误
|
|
205
217
|
|
|
206
|
-
| 错误键 | 说明 |
|
|
207
|
-
|
|
218
|
+
| 错误键 | 说明 | 中文消息示例 |
|
|
219
|
+
|--------|------|--------------|
|
|
208
220
|
| `required` | 必填字段缺失 | {{#label}}是必填项 |
|
|
209
221
|
| `enum` | 枚举值不在范围 | {{#label}}必须是以下值之一: {{#allowed}} |
|
|
210
222
|
| `pattern` | 格式不正确 | {{#label}}格式不正确 |
|
|
@@ -260,7 +272,7 @@ validate(schema, data, { locale: 'en-US' });
|
|
|
260
272
|
| `pattern.idCard` | 身份证格式错误 |
|
|
261
273
|
| `pattern.objectId` | ObjectId格式错误 |
|
|
262
274
|
|
|
263
|
-
完整列表请参考: `
|
|
275
|
+
完整列表请参考: `src/locales/zh-CN.ts`
|
|
264
276
|
|
|
265
277
|
---
|
|
266
278
|
|
|
@@ -312,25 +324,25 @@ const schema = dsl({
|
|
|
312
324
|
|
|
313
325
|
#### 1. 目录结构
|
|
314
326
|
|
|
315
|
-
```
|
|
327
|
+
```text
|
|
316
328
|
project/
|
|
317
329
|
├── i18n/
|
|
318
330
|
│ └── dsl/
|
|
319
|
-
│ ├── zh-CN.
|
|
320
|
-
│ ├── en-US.
|
|
321
|
-
│ ├── ja-JP.
|
|
322
|
-
│ └── index.
|
|
331
|
+
│ ├── zh-CN.cjs # 中文
|
|
332
|
+
│ ├── en-US.jsonc # 英文
|
|
333
|
+
│ ├── ja-JP.json5 # 日语
|
|
334
|
+
│ └── index.cjs # 导出工具
|
|
323
335
|
```
|
|
324
336
|
|
|
325
|
-
#### 2. 导出工具 (`i18n/dsl/index.
|
|
337
|
+
#### 2. 导出工具 (`i18n/dsl/index.cjs`)
|
|
326
338
|
|
|
327
339
|
```javascript
|
|
328
340
|
const path = require('path');
|
|
329
341
|
|
|
330
342
|
module.exports = {
|
|
331
|
-
'zh-CN': require('./zh-CN'),
|
|
332
|
-
'en-US': require('./en-US'),
|
|
333
|
-
'ja-JP': require('./ja-JP')
|
|
343
|
+
'zh-CN': require('./zh-CN.cjs'),
|
|
344
|
+
'en-US': require('./en-US.jsonc'),
|
|
345
|
+
'ja-JP': require('./ja-JP.json5')
|
|
334
346
|
};
|
|
335
347
|
|
|
336
348
|
// 使用
|
|
@@ -342,14 +354,14 @@ dsl.config({
|
|
|
342
354
|
#### 3. 消息复用
|
|
343
355
|
|
|
344
356
|
```javascript
|
|
345
|
-
// i18n/dsl/common.
|
|
357
|
+
// i18n/dsl/common.cjs
|
|
346
358
|
module.exports = {
|
|
347
359
|
required: '{{#label}}是必填项',
|
|
348
360
|
minLength: '{{#label}}长度不能少于{{#limit}}个字符'
|
|
349
361
|
};
|
|
350
362
|
|
|
351
|
-
// i18n/dsl/zh-CN.
|
|
352
|
-
const common = require('./common');
|
|
363
|
+
// i18n/dsl/zh-CN.cjs
|
|
364
|
+
const common = require('./common.cjs');
|
|
353
365
|
|
|
354
366
|
module.exports = {
|
|
355
367
|
...common,
|
|
@@ -414,16 +426,16 @@ console.log(result.errors[0].message);
|
|
|
414
426
|
**检查清单**:
|
|
415
427
|
- ✅ 配置键是否正确(`i18n` 而非 `locales`)
|
|
416
428
|
- ✅ 目录路径是否正确
|
|
417
|
-
- ✅ 文件名是否为语言代码(如 `zh-CN.
|
|
418
|
-
- ✅
|
|
429
|
+
- ✅ 文件名是否为语言代码(如 `zh-CN.cjs` / `zh-CN.jsonc`)
|
|
430
|
+
- ✅ `.js` 文件是否为 CommonJS(`module.exports`),若是 ESM 项目优先使用 `.cjs` / `.json*`
|
|
419
431
|
|
|
420
432
|
### 2. 错误消息仍然是英文?
|
|
421
433
|
|
|
422
|
-
**原因**:
|
|
434
|
+
**原因**: 当前请求没有显式传入 `locale`,或全局默认语言尚未切到你期望的语言
|
|
423
435
|
|
|
424
436
|
```javascript
|
|
425
437
|
// 方法 1: 全局设置
|
|
426
|
-
const Locale = require('schema-dsl
|
|
438
|
+
const { Locale } = require('schema-dsl');
|
|
427
439
|
Locale.setLocale('zh-CN');
|
|
428
440
|
|
|
429
441
|
// 方法 2: 验证时指定
|
|
@@ -446,12 +458,19 @@ dsl('string!')
|
|
|
446
458
|
|
|
447
459
|
## 📚 相关文档
|
|
448
460
|
|
|
449
|
-
- [快速开始](
|
|
461
|
+
- [快速开始](https://github.com/vextjs/schema-dsl/blob/main/README.md)
|
|
450
462
|
- [API 参考](./api.md)
|
|
451
|
-
- [完整示例](
|
|
463
|
+
- [完整示例](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/i18n.ts)
|
|
464
|
+
|
|
465
|
+
---
|
|
466
|
+
|
|
467
|
+
## 对应示例文件
|
|
468
|
+
|
|
469
|
+
**示例入口**: [i18n.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/i18n.ts)
|
|
470
|
+
**说明**: 覆盖内置 locale 切换、字段级消息优先级,以及自定义 locale 的最小工作路径。
|
|
452
471
|
|
|
453
472
|
---
|
|
454
473
|
|
|
455
|
-
**文档生成时间**:
|
|
474
|
+
**文档生成时间**: 2026-05-08
|
|
456
475
|
**版本**: v1.0.1
|
|
457
476
|
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
---
|
|
2
|
+
pageType: home
|
|
3
|
+
|
|
4
|
+
hero:
|
|
5
|
+
name: schema-dsl
|
|
6
|
+
text: 简洁强大的 JSON Schema 验证库
|
|
7
|
+
tagline: DSL 语法 · 链式 API · 多格式导出 · 完整 TypeScript 支持
|
|
8
|
+
actions:
|
|
9
|
+
- theme: brand
|
|
10
|
+
text: 快速上手
|
|
11
|
+
link: /quick-start
|
|
12
|
+
- theme: alt
|
|
13
|
+
text: 文档索引
|
|
14
|
+
link: /doc-index
|
|
15
|
+
- theme: alt
|
|
16
|
+
text: GitHub
|
|
17
|
+
link: https://github.com/vextjs/schema-dsl
|
|
18
|
+
|
|
19
|
+
features:
|
|
20
|
+
- title: DSL 语法
|
|
21
|
+
details: 用紧凑 DSL 字符串或对象定义验证规则,覆盖常见字段、约束与组合场景。
|
|
22
|
+
- title: 高性能验证
|
|
23
|
+
details: 基于 AJV 8 封装,支持同步、异步和批量验证,并提供缓存管理能力。
|
|
24
|
+
- title: 多格式导出
|
|
25
|
+
details: 同一份 schema 可导出到 MongoDB、MySQL、PostgreSQL 和 Markdown 文档。
|
|
26
|
+
- title: 国际化
|
|
27
|
+
details: 内置多语言错误消息与动态 locale 切换,适合服务端和前端共享校验规则。
|
|
28
|
+
- title: 插件系统
|
|
29
|
+
details: 支持扩展关键字、格式和类型注册,兼容 v1 常见插件用法。
|
|
30
|
+
- title: TypeScript 友好
|
|
31
|
+
details: 提供完整类型声明,便于在现代 Node.js 与 TypeScript 项目中直接集成。
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 入口导航
|
|
35
|
+
|
|
36
|
+
- 在线文档: https://schema-dsl.github.io
|
|
37
|
+
- 本地文档索引: [doc-index.md](./doc-index.md)
|
|
38
|
+
- 快速开始: [quick-start.md](./quick-start.md)
|
|
39
|
+
- 功能索引: [FEATURE-INDEX.md](./FEATURE-INDEX.md)
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 对应示例文件
|
|
44
|
+
|
|
45
|
+
**示例入口**: [home.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/home.ts)
|
|
46
|
+
**说明**: 覆盖首页展示的 DSL 定义、便捷验证与编译复用路径,可作为 Batch 1 的总入口样板。
|
|
47
|
+
|
|
48
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# JSON Schema 基础
|
|
2
|
+
|
|
3
|
+
schema-dsl 生成的是 JSON Schema Draft 7 风格的对象,并额外保留少量内部字段供验证器使用。
|
|
4
|
+
|
|
5
|
+
常见字段:
|
|
6
|
+
|
|
7
|
+
- `type`
|
|
8
|
+
- `properties`
|
|
9
|
+
- `required`
|
|
10
|
+
- `minLength` / `maxLength`
|
|
11
|
+
- `minimum` / `maximum`
|
|
12
|
+
- `format`
|
|
13
|
+
- `enum`
|
|
14
|
+
- `items`
|
|
15
|
+
|
|
16
|
+
对外输出纯净 JSON Schema 时,请使用 `toJsonSchema()`。
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
const emailField = dsl('email!').label('邮箱');
|
|
20
|
+
|
|
21
|
+
emailField.toSchema();
|
|
22
|
+
// 含 _label / _customMessages 等内部字段
|
|
23
|
+
|
|
24
|
+
emailField.toJsonSchema();
|
|
25
|
+
// 纯净 JSON Schema,适合导出到外部系统
|
|
26
|
+
|
|
27
|
+
const objectSchema = dsl({
|
|
28
|
+
email: emailField,
|
|
29
|
+
age: dsl('number:18-100')
|
|
30
|
+
});
|
|
31
|
+
// dsl({ ... }) 入口直接返回 Draft 7 风格对象
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## 对应示例文件
|
|
37
|
+
|
|
38
|
+
**示例入口**: [json-schema-basics.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/json-schema-basics.ts)
|
|
39
|
+
**说明**: 直接对比 `toSchema()` 与 `toJsonSchema()` 的输出差异,并展示对象入口返回的 JSON Schema 结构。
|
|
40
|
+
|
|
@@ -79,19 +79,21 @@ email: 'email!'
|
|
|
79
79
|
</div>
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
**在导出 / 文档工具中**:
|
|
83
83
|
|
|
84
84
|
```json
|
|
85
85
|
{
|
|
86
86
|
"email": {
|
|
87
87
|
"type": "string",
|
|
88
88
|
"format": "email",
|
|
89
|
-
"
|
|
89
|
+
"_label": "邮箱地址", // label 在 schema-dsl 内部以 _label 保存
|
|
90
90
|
"description": "用于登录和接收系统通知" // 来自 description
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
```
|
|
94
94
|
|
|
95
|
+
`SchemaUtils.toMarkdown()`、导出器或你自己的表单渲染层,通常会再把 `_label` 映射成展示标题。
|
|
96
|
+
|
|
95
97
|
---
|
|
96
98
|
|
|
97
99
|
## 💡 最佳实践
|
|
@@ -257,6 +259,13 @@ apiKey: 'string:32!'
|
|
|
257
259
|
|
|
258
260
|
---
|
|
259
261
|
|
|
260
|
-
**记住**: label
|
|
262
|
+
**记住**: label用于错误消息和展示标题来源,description用于帮助说明!
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## 对应示例文件
|
|
267
|
+
|
|
268
|
+
**示例入口**: [label-vs-description.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/label-vs-description.ts)
|
|
269
|
+
**说明**: 直接展示 `_label` / `description` 在 schema 中的实际落点,以及验证错误如何消费 `label`。
|
|
261
270
|
|
|
262
271
|
|
|
@@ -86,7 +86,7 @@ console.log(markdown);
|
|
|
86
86
|
- `schema` (Object) - JSON Schema 对象
|
|
87
87
|
- `options` (Object) - 导出选项
|
|
88
88
|
- `title` (String) - 文档标题,默认: `'Schema 文档'`
|
|
89
|
-
- `locale` (String) - 语言代码,默认: `'
|
|
89
|
+
- `locale` (String) - 语言代码,默认: `'en-US'`
|
|
90
90
|
- 支持: `'zh-CN'` (中文), `'en-US'` (英文), `'ja-JP'` (日文)
|
|
91
91
|
- `includeExample` (Boolean) - 是否包含示例数据,默认: `true`
|
|
92
92
|
- `includeDescription` (Boolean) - 是否包含描述,默认: `true`
|
|
@@ -306,13 +306,15 @@ const mongoSchema = exporters.MongoDBExporter.export(schema, {
|
|
|
306
306
|
});
|
|
307
307
|
|
|
308
308
|
// 导出为 MySQL DDL
|
|
309
|
-
const mysqlDDL = exporters.MySQLExporter.export(
|
|
310
|
-
|
|
311
|
-
|
|
309
|
+
const mysqlDDL = exporters.MySQLExporter.export('users', schema);
|
|
310
|
+
|
|
311
|
+
// 导出为 PostgreSQL DDL
|
|
312
|
+
const pgDDL = exporters.PostgreSQLExporter.export('users', schema);
|
|
312
313
|
|
|
313
314
|
console.log('Markdown 文档:\n', markdown);
|
|
314
315
|
console.log('\nMongoDB Schema:\n', mongoSchema);
|
|
315
316
|
console.log('\nMySQL DDL:\n', mysqlDDL);
|
|
317
|
+
console.log('\nPostgreSQL DDL:\n', pgDDL);
|
|
316
318
|
```
|
|
317
319
|
|
|
318
320
|
---
|
|
@@ -392,6 +394,13 @@ console.log('✅ 文档已生成');
|
|
|
392
394
|
|
|
393
395
|
---
|
|
394
396
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
+
## 对应示例文件
|
|
398
|
+
|
|
399
|
+
**示例入口**: [markdown-exporter.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/markdown-exporter.ts)
|
|
400
|
+
**说明**: 覆盖 `MarkdownExporter.export()` 的中英文文档生成和标题/字段落点检查,适合作为文档导出最小样板。
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
**文档更新日期**: 2026-04-30
|
|
405
|
+
**版本**: v2.0.0-beta.1
|
|
397
406
|
|
package/docs/mongodb-exporter.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MongoDB 导出器文档
|
|
2
2
|
|
|
3
|
-
> **模块**: `
|
|
3
|
+
> **模块**: `src/exporters/MongoDBExporter.ts`
|
|
4
4
|
|
|
5
5
|
> **用途**: 将 JSON Schema 转换为 MongoDB 验证 Schema
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
## 概述
|
|
21
21
|
|
|
22
|
-
`MongoDBExporter` 将
|
|
22
|
+
`MongoDBExporter` 将 schema-dsl 生成的 JSON Schema 转换为 MongoDB 的 `$jsonSchema` 验证格式,可直接用于创建集合时的文档验证。
|
|
23
23
|
|
|
24
24
|
### 核心功能
|
|
25
25
|
|
|
@@ -104,7 +104,7 @@ const mongoSchema = exporter.export(jsonSchema);
|
|
|
104
104
|
```
|
|
105
105
|
|
|
106
106
|
**参数**:
|
|
107
|
-
- `jsonSchema` (Object):
|
|
107
|
+
- `jsonSchema` (Object): schema-dsl 生成的 JSON Schema 对象
|
|
108
108
|
|
|
109
109
|
**返回值**:
|
|
110
110
|
- `Object`: 包含 `$jsonSchema` 的 MongoDB 验证对象
|
|
@@ -272,7 +272,7 @@ async function createValidatedCollection() {
|
|
|
272
272
|
|
|
273
273
|
## 导出限制
|
|
274
274
|
|
|
275
|
-
⚠️ **重要提示**: 并非所有
|
|
275
|
+
⚠️ **重要提示**: 并非所有 schema-dsl 特性都能导出到数据库 Schema。
|
|
276
276
|
|
|
277
277
|
**不支持导出的特性**:
|
|
278
278
|
- ❌ 条件验证逻辑(`dsl.match()`, `dsl.if()`)
|
|
@@ -292,4 +292,11 @@ async function createValidatedCollection() {
|
|
|
292
292
|
- [TypeConverter](type-converter.md)
|
|
293
293
|
- [**导出限制说明**](export-limitations.md) ⚠️
|
|
294
294
|
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## 对应示例文件
|
|
298
|
+
|
|
299
|
+
**示例入口**: [mongodb-exporter.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/mongodb-exporter.ts)
|
|
300
|
+
**说明**: 覆盖 `$jsonSchema` 导出、`generateCreateCommand()` 和 `generateCommand()`,对应文档中的验证集合创建场景。
|
|
301
|
+
|
|
295
302
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# 多语言支持
|
|
2
|
+
|
|
3
|
+
多语言能力由 `Locale`、`dsl.config({ i18n })` 和验证选项 `locale` 提供。
|
|
4
|
+
|
|
5
|
+
常见入口:
|
|
6
|
+
|
|
7
|
+
- 运行时切换默认语言:`Locale.setLocale('en-US')`
|
|
8
|
+
- 运行时补充语言包:`Locale.addLocale('en-US', messages)`
|
|
9
|
+
- 从目录扫描语言包:`dsl.config({ i18n: '/path/to/locales' })`
|
|
10
|
+
- 单次验证覆盖语言:`validator.validate(schema, data, { locale: 'en-US' })`
|
|
11
|
+
|
|
12
|
+
当前 i18n 目录扫描支持这些语言包文件:`.js`、`.cjs`、`.json`、`.jsonc`、`.json5`。
|
|
13
|
+
|
|
14
|
+
更多内容请见:
|
|
15
|
+
|
|
16
|
+
- [i18n.md](./i18n.md)
|
|
17
|
+
- [i18n-user-guide.md](./i18n-user-guide.md)
|
|
18
|
+
- [dynamic-locale.md](./dynamic-locale.md)
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 对应示例文件
|
|
23
|
+
|
|
24
|
+
**示例入口**: [multi-language.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/multi-language.ts)
|
|
25
|
+
**说明**: 展示默认语言、按次覆盖 `locale` 以及可用语言列表的最小运行时示例。
|
|
26
|
+
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
## 📖 快速导航
|
|
6
6
|
|
|
7
7
|
- **单一类型验证**(本文档)
|
|
8
|
-
- **[联合类型验证](./union-
|
|
8
|
+
- **[联合类型验证](./union-types.md)** - 使用 `types:` 语法做真正的跨类型联合验证
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -216,46 +216,32 @@ const schema = dsl({
|
|
|
216
216
|
|
|
217
217
|
## 🚀 扩展新类型
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
当前版本优先通过公开运行时 API 扩展类型,而不是要求业务方修改内部 `DslAdapter` / `ErrorCodes` 源码。
|
|
220
220
|
|
|
221
|
-
###
|
|
221
|
+
### 推荐入口
|
|
222
222
|
|
|
223
223
|
```javascript
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
_parseType(dslString) {
|
|
227
|
-
// 添加新类型
|
|
228
|
-
if (dslString === 'phone') {
|
|
229
|
-
return {
|
|
230
|
-
type: 'string',
|
|
231
|
-
pattern: '^1[3-9]\\d{9}$',
|
|
232
|
-
minLength: 11,
|
|
233
|
-
maxLength: 11
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// 现有类型...
|
|
238
|
-
}
|
|
239
|
-
```
|
|
224
|
+
const { DslBuilder, TypeRegistry } = require('schema-dsl');
|
|
240
225
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
// lib/core/ErrorCodes.js
|
|
226
|
+
TypeRegistry.register('evenNumber', {
|
|
227
|
+
baseSchema: { type: 'number', multipleOf: 2 }
|
|
228
|
+
});
|
|
245
229
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
230
|
+
DslBuilder.registerType('phone-cn-lite', {
|
|
231
|
+
type: 'string',
|
|
232
|
+
pattern: '^1[3-9]\\d{9}$',
|
|
233
|
+
minLength: 11,
|
|
234
|
+
maxLength: 11
|
|
235
|
+
});
|
|
251
236
|
```
|
|
252
237
|
|
|
253
|
-
###
|
|
238
|
+
### 使用方式
|
|
254
239
|
|
|
255
240
|
```javascript
|
|
256
|
-
|
|
257
|
-
phone: 'phone!'
|
|
258
|
-
|
|
241
|
+
const schema = dsl({
|
|
242
|
+
phone: 'phone-cn-lite!',
|
|
243
|
+
luckyNumber: 'evenNumber'
|
|
244
|
+
});
|
|
259
245
|
```
|
|
260
246
|
|
|
261
247
|
---
|
|
@@ -317,7 +303,7 @@ amount: 'number:0-10000'
|
|
|
317
303
|
|
|
318
304
|
## 💡 总结
|
|
319
305
|
|
|
320
|
-
|
|
306
|
+
schema-dsl 的多类型支持采用**类型无关 Builder + 方法智能适配**设计:
|
|
321
307
|
|
|
322
308
|
1. **统一入口**: 所有类型都通过DslBuilder
|
|
323
309
|
2. **类型感知**: 方法内部检查类型兼容性
|
|
@@ -326,4 +312,11 @@ SchemaI-DSL的多类型支持采用**类型无关Builder + 方法智能适配**
|
|
|
326
312
|
|
|
327
313
|
**设计哲学**: 让最常见的场景(字符串验证)最简洁,其他类型保持DSL的简洁性。
|
|
328
314
|
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## 对应示例文件
|
|
318
|
+
|
|
319
|
+
**示例入口**: [multi-type-support.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/multi-type-support.ts)
|
|
320
|
+
**说明**: 用一个对象同时覆盖字符串、数字、布尔、日期、数组和枚举字段的推荐写法,以及对应的成功 / 失败验证路径。
|
|
321
|
+
|
|
329
322
|
|
package/docs/mysql-exporter.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MySQL 导出器文档
|
|
2
2
|
|
|
3
|
-
> **模块**: `
|
|
3
|
+
> **模块**: `src/exporters/MySQLExporter.ts`
|
|
4
4
|
|
|
5
5
|
> **用途**: 将 JSON Schema 转换为 MySQL CREATE TABLE 语句
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
## 概述
|
|
21
21
|
|
|
22
|
-
`MySQLExporter` 将
|
|
22
|
+
`MySQLExporter` 将 schema-dsl 生成的 JSON Schema 转换为 MySQL 的 DDL 语句,包括 `CREATE TABLE` 和索引创建语句。
|
|
23
23
|
|
|
24
24
|
### 核心功能
|
|
25
25
|
|
|
@@ -271,3 +271,10 @@ CREATE INDEX `idx_users_status` ON `users` (`status`);
|
|
|
271
271
|
- [TypeConverter](type-converter.md)
|
|
272
272
|
- [**导出限制说明**](export-limitations.md) ⚠️
|
|
273
273
|
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
## 对应示例文件
|
|
277
|
+
|
|
278
|
+
**示例入口**: [mysql-exporter.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/mysql-exporter.ts)
|
|
279
|
+
**说明**: 覆盖 `export()` 生成 DDL、主键检测,以及 `generateIndex()` 生成普通/唯一索引。
|
|
280
|
+
|
package/docs/number-operators.md
CHANGED
|
@@ -430,13 +430,20 @@ dsl({ level: 'number:=5' }) // 等于
|
|
|
430
430
|
|
|
431
431
|
## 📚 相关文档
|
|
432
432
|
|
|
433
|
-
- [DSL 语法速查](
|
|
434
|
-
- [完整示例](
|
|
435
|
-
- [测试用例](../test/unit/number-operators.test.
|
|
436
|
-
- [CHANGELOG](
|
|
433
|
+
- [DSL 语法速查](./dsl-syntax.md)
|
|
434
|
+
- [完整示例](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/number-operators.ts)
|
|
435
|
+
- [测试用例](../test/unit/number-operators.test.ts)
|
|
436
|
+
- [CHANGELOG](https://github.com/vextjs/schema-dsl/blob/main/CHANGELOG.md)
|
|
437
|
+
|
|
438
|
+
---
|
|
439
|
+
|
|
440
|
+
## 对应示例文件
|
|
441
|
+
|
|
442
|
+
**示例入口**: [number-operators.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/number-operators.ts)
|
|
443
|
+
**说明**: 覆盖 `>=`、`<`、`<=`、`=` 和整数比较运算符的成功/失败路径,便于直接观察边界行为。
|
|
437
444
|
|
|
438
445
|
---
|
|
439
446
|
|
|
440
447
|
**版本**: v1.1.2+
|
|
441
|
-
**更新时间**: 2026-
|
|
448
|
+
**更新时间**: 2026-05-08
|
|
442
449
|
|
|
@@ -13,9 +13,9 @@ schema-dsl 现在支持使用 `?` 显式标记可选字段,提供更清晰的
|
|
|
13
13
|
|
|
14
14
|
| 标记 | 含义 | 示例 | 说明 |
|
|
15
15
|
|------|------|------|------|
|
|
16
|
-
| `!` | 必填 | `string!` |
|
|
17
|
-
| `?` | 可选 | `string?` |
|
|
18
|
-
| 无标记 | 可选(默认) | `string` |
|
|
16
|
+
| `!` | 必填 | `string!` | 字段必须存在 |
|
|
17
|
+
| `?` | 可选 | `string?` | 字段可省略(显式表达) |
|
|
18
|
+
| 无标记 | 可选(默认) | `string` | 字段可省略(默认行为) |
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
@@ -207,7 +207,7 @@ const schema3 = dsl({
|
|
|
207
207
|
- ✅ **email?** - 支持
|
|
208
208
|
- ✅ **number:18-?** - 支持
|
|
209
209
|
- ✅ **array<string>?** - 支持
|
|
210
|
-
- ✅
|
|
210
|
+
- ✅ **相关单元测试已覆盖**
|
|
211
211
|
|
|
212
212
|
### 测试代码
|
|
213
213
|
|
|
@@ -236,28 +236,26 @@ console.log(validate(schema3, { username: 'test' }).valid); // true
|
|
|
236
236
|
|
|
237
237
|
## 🔧 实现细节
|
|
238
238
|
|
|
239
|
-
### DslBuilder
|
|
239
|
+
### DslParser / DslBuilder 标记处理
|
|
240
240
|
|
|
241
241
|
```javascript
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
dslWithoutMarker = processedDsl.slice(0, -1);
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
// ...
|
|
242
|
+
// DslParser.parseString()
|
|
243
|
+
if (s.endsWith('!')) {
|
|
244
|
+
required = true;
|
|
245
|
+
s = s.slice(0, -1);
|
|
246
|
+
} else if (s.endsWith('?')) {
|
|
247
|
+
s = s.slice(0, -1);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// DslBuilder constructor(兼容链式入口)
|
|
251
|
+
this._required = s.endsWith('!');
|
|
252
|
+
this._optional = s.endsWith('?') && !this._required;
|
|
253
|
+
if (this._required || this._optional) s = s.slice(0, -1);
|
|
258
254
|
}
|
|
259
255
|
```
|
|
260
256
|
|
|
257
|
+
当前版本会在 `DslParser.parseString()` 中统一剥离末尾 `!` / `?`,同时 `DslBuilder` 构造函数保留相同的兼容处理,因此字符串 DSL 和链式 Builder 两条入口都能识别可选标记。
|
|
258
|
+
|
|
261
259
|
---
|
|
262
260
|
|
|
263
261
|
## 📝 最佳实践
|
|
@@ -311,11 +309,18 @@ const schema = dsl({
|
|
|
311
309
|
## 📚 相关文档
|
|
312
310
|
|
|
313
311
|
- [DSL 语法完整指南](./dsl-syntax.md)
|
|
314
|
-
- [
|
|
315
|
-
- [
|
|
312
|
+
- [类型参考](./type-reference.md)
|
|
313
|
+
- [跨类型联合验证](./union-types.md)
|
|
316
314
|
|
|
317
315
|
---
|
|
318
316
|
|
|
319
317
|
**最后更新**: 2026-01-13
|
|
320
318
|
**作者**: schema-dsl Team
|
|
321
319
|
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## 对应示例文件
|
|
323
|
+
|
|
324
|
+
**示例入口**: [optional-marker-guide.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/optional-marker-guide.ts)
|
|
325
|
+
**说明**: 覆盖 `!` / `?` 的基础字段、对象字段和默认可选枚举场景,直接展示成功 / 失败路径。
|
|
326
|
+
|