schema-dsl 2.0.0 → 2.0.1
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 +130 -113
- package/LICENSE +21 -21
- package/README.md +628 -628
- package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
- package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
- package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
- package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
- package/dist/index.cjs +75 -29
- package/dist/index.d.cts +10 -4
- package/dist/index.d.ts +10 -4
- package/dist/index.js +75 -29
- package/dist/plugins/custom-format.cjs +33 -17
- package/dist/plugins/custom-format.d.cts +1 -1
- package/dist/plugins/custom-format.d.ts +1 -1
- package/dist/plugins/custom-format.js +33 -17
- package/dist/plugins/custom-type-example.cjs +33 -17
- package/dist/plugins/custom-type-example.d.cts +1 -1
- package/dist/plugins/custom-type-example.d.ts +1 -1
- package/dist/plugins/custom-type-example.js +33 -17
- package/dist/plugins/custom-validator.cjs +0 -2
- package/dist/plugins/custom-validator.d.cts +1 -1
- package/dist/plugins/custom-validator.d.ts +1 -1
- package/dist/plugins/custom-validator.js +0 -2
- package/docs/FEATURE-INDEX.md +553 -553
- package/docs/add-custom-locale.md +496 -496
- package/docs/add-keyword.md +24 -24
- package/docs/api-reference.md +1047 -1047
- package/docs/api.md +13 -13
- package/docs/best-practices-project-structure.md +417 -417
- package/docs/best-practices.md +712 -712
- package/docs/cache-manager.md +344 -344
- package/docs/compile.md +45 -45
- package/docs/conditional-api.md +1307 -1307
- package/docs/custom-extensions-guide.md +339 -339
- package/docs/design-philosophy.md +606 -606
- package/docs/doc-index.md +324 -324
- package/docs/dsl-syntax.md +714 -714
- package/docs/dynamic-locale.md +608 -608
- package/docs/enum.md +482 -482
- package/docs/error-handling.md +1975 -1975
- package/docs/export-guide.md +501 -501
- package/docs/export-limitations.md +567 -567
- package/docs/faq.md +596 -596
- package/docs/frontend-i18n-guide.md +307 -307
- package/docs/i18n-user-guide.md +487 -487
- package/docs/i18n.md +476 -476
- package/docs/index.md +48 -48
- package/docs/json-schema-basics.md +40 -40
- package/docs/label-vs-description.md +271 -271
- package/docs/markdown-exporter.md +406 -406
- package/docs/mongodb-exporter.md +302 -302
- package/docs/multi-language.md +26 -26
- package/docs/multi-type-support.md +322 -322
- package/docs/mysql-exporter.md +280 -280
- package/docs/number-operators.md +449 -449
- package/docs/optional-marker-guide.md +326 -326
- package/docs/performance-guide.md +49 -49
- package/docs/plugin-system.md +381 -381
- package/docs/plugin-type-registration.md +34 -34
- package/docs/postgresql-exporter.md +311 -311
- package/docs/public/favicon.svg +4 -4
- package/docs/quick-start.md +435 -435
- package/docs/runtime-locale-support.md +532 -532
- package/docs/schema-helper.md +345 -345
- package/docs/schema-utils-advanced-issues.md +23 -23
- package/docs/schema-utils-best-practices.md +20 -20
- package/docs/schema-utils-chaining.md +150 -150
- package/docs/schema-utils.md +524 -524
- package/docs/security-checklist.md +20 -20
- package/docs/string-extensions.md +488 -488
- package/docs/troubleshooting.md +486 -486
- package/docs/type-converter.md +310 -310
- package/docs/type-reference.md +242 -242
- package/docs/typescript-guide.md +584 -584
- package/docs/union-type-guide.md +157 -157
- package/docs/union-types.md +284 -284
- package/docs/validate-async.md +491 -491
- package/docs/validate-batch.md +49 -49
- package/docs/validate-dsl-object-support.md +578 -578
- package/docs/validate.md +506 -506
- package/docs/validation-guide.md +502 -502
- package/docs/validator.md +39 -39
- package/package.json +131 -131
- package/plugins/custom-format.cjs +8 -8
- package/plugins/custom-type-example.cjs +8 -8
- package/plugins/custom-validator.cjs +8 -8
- package/src/adapters/DslAdapter.ts +111 -111
- package/src/adapters/index.ts +1 -1
- package/src/config/constants.ts +83 -83
- package/src/config/index.ts +2 -2
- package/src/config/patterns.ts +77 -77
- package/src/core/CacheManager.ts +169 -159
- package/src/core/ConditionalBuilder.ts +382 -382
- package/src/core/ConditionalRuntime.ts +27 -27
- package/src/core/ConditionalValidator.ts +254 -254
- package/src/core/DslBuilder.ts +687 -677
- package/src/core/ErrorCodes.ts +38 -38
- package/src/core/ErrorFormatter.ts +271 -271
- package/src/core/JSONSchemaCore.ts +65 -65
- package/src/core/Locale.ts +187 -187
- package/src/core/MessageTemplate.ts +42 -42
- package/src/core/ObjectDslBuilder.ts +64 -64
- package/src/core/PluginManager.ts +326 -326
- package/src/core/StringExtensions.ts +140 -140
- package/src/core/TemplateEngine.ts +44 -44
- package/src/core/Validator.ts +448 -448
- package/src/errors/I18nError.ts +159 -159
- package/src/errors/ValidationError.ts +105 -105
- package/src/exporters/BaseExporter.ts +60 -60
- package/src/exporters/MarkdownExporter.ts +305 -305
- package/src/exporters/MongoDBExporter.ts +126 -126
- package/src/exporters/MySQLExporter.ts +156 -155
- package/src/exporters/PostgreSQLExporter.ts +222 -222
- package/src/exporters/index.ts +18 -18
- package/src/index.ts +651 -633
- package/src/locales/en-US.ts +160 -160
- package/src/locales/es-ES.ts +160 -160
- package/src/locales/fr-FR.ts +160 -160
- package/src/locales/index.ts +103 -103
- package/src/locales/ja-JP.ts +160 -160
- package/src/locales/types.ts +156 -156
- package/src/locales/zh-CN.ts +160 -160
- package/src/parser/ConstraintParser.ts +101 -101
- package/src/parser/DslParser.ts +470 -470
- package/src/parser/SchemaCompiler.ts +66 -66
- package/src/parser/TypeRegistry.ts +250 -250
- package/src/parser/index.ts +6 -6
- package/src/plugins/custom-format.ts +124 -126
- package/src/plugins/custom-type-example.ts +106 -108
- package/src/plugins/custom-validator.ts +138 -140
- package/src/types/conditional.ts +28 -28
- package/src/types/config.ts +59 -59
- package/src/types/dsl.ts +131 -131
- package/src/types/error.ts +60 -60
- package/src/types/index.ts +17 -17
- package/src/types/infer.ts +127 -127
- package/src/types/plugin.ts +58 -58
- package/src/types/safe-regex.d.ts +9 -9
- package/src/types/schema.ts +66 -66
- package/src/types/validate.ts +71 -71
- package/src/utils/SchemaHelper.ts +196 -196
- package/src/utils/SchemaUtils.ts +365 -346
- package/src/utils/TypeConverter.ts +215 -215
- package/src/utils/index.ts +10 -10
- package/src/validators/CustomKeywords.ts +477 -477
package/src/locales/zh-CN.ts
CHANGED
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
import type { LocaleMessages } from './types.js'
|
|
2
|
-
|
|
3
|
-
const zhCN: LocaleMessages = {
|
|
4
|
-
// Generic
|
|
5
|
-
required: '{{#label}}不能为空',
|
|
6
|
-
type: '{{#label}}应该是 {{#expected}} 类型',
|
|
7
|
-
min: '{{#label}}长度不能少于{{#limit}}个字符',
|
|
8
|
-
max: '{{#label}}长度不能超过{{#limit}}个字符',
|
|
9
|
-
length: '{{#label}}长度必须是{{#limit}}个字符',
|
|
10
|
-
pattern: '{{#label}}格式不正确',
|
|
11
|
-
enum: '{{#label}}必须是以下值之一: {{#allowed}}',
|
|
12
|
-
custom: '{{#label}}验证失败: {{#message}}',
|
|
13
|
-
circular: '{{#label}}检测到循环引用',
|
|
14
|
-
'max-depth': '超过最大递归深度 ({{#depth}}) at {{#label}}',
|
|
15
|
-
exception: '{{#label}}验证异常: {{#message}}',
|
|
16
|
-
|
|
17
|
-
// Conditional
|
|
18
|
-
'conditional.underAge': '未成年用户不能注册',
|
|
19
|
-
'conditional.blocked': '账号已被封禁',
|
|
20
|
-
'conditional.notAllowed': '不允许注册',
|
|
21
|
-
|
|
22
|
-
// I18nError — generic
|
|
23
|
-
'error.notFound': '找不到{{#resource}}',
|
|
24
|
-
'error.forbidden': '没有权限访问{{#resource}}',
|
|
25
|
-
'error.unauthorized': '未授权,请先登录',
|
|
26
|
-
'error.invalid': '{{#field}}无效',
|
|
27
|
-
'error.duplicate': '{{#resource}}已存在',
|
|
28
|
-
'error.conflict': '操作冲突: {{#reason}}',
|
|
29
|
-
|
|
30
|
-
// Account
|
|
31
|
-
'account.notFound': { code: 'ACCOUNT_NOT_FOUND', message: '账户不存在' },
|
|
32
|
-
'account.inactive': '账户未激活',
|
|
33
|
-
'account.banned': '账户已被封禁',
|
|
34
|
-
'account.insufficientBalance': {
|
|
35
|
-
code: 'INSUFFICIENT_BALANCE',
|
|
36
|
-
message: '余额不足,当前余额{{#balance}},需要{{#required}}',
|
|
37
|
-
},
|
|
38
|
-
'account.insufficientCredits': '积分不足,当前积分{{#credits}},需要{{#required}}',
|
|
39
|
-
|
|
40
|
-
// User
|
|
41
|
-
'user.notFound': '用户不存在',
|
|
42
|
-
'user.notVerified': '用户未验证',
|
|
43
|
-
'user.noPermission': '没有管理员权限',
|
|
44
|
-
|
|
45
|
-
// Order
|
|
46
|
-
'order.notPaid': { code: 'ORDER_NOT_PAID', message: '订单未支付' },
|
|
47
|
-
'order.paymentMissing': '缺少支付信息',
|
|
48
|
-
'order.addressMissing': '缺少收货地址',
|
|
49
|
-
|
|
50
|
-
// Format
|
|
51
|
-
'format.email': '{{#label}}必须是有效的邮箱地址',
|
|
52
|
-
'format.url': '{{#label}}必须是有效的URL地址',
|
|
53
|
-
'format.uuid': '{{#label}}必须是有效的UUID',
|
|
54
|
-
'format.date': '{{#label}}必须是有效的日期格式 (YYYY-MM-DD)',
|
|
55
|
-
'format.datetime': '{{#label}}必须是有效的日期时间格式 (ISO 8601)',
|
|
56
|
-
'format.time': '{{#label}}必须是有效的时间格式 (HH:mm:ss)',
|
|
57
|
-
'format.ipv4': '{{#label}}必须是有效的IPv4地址',
|
|
58
|
-
'format.ipv6': '{{#label}}必须是有效的IPv6地址',
|
|
59
|
-
'format.binary': '{{#label}}必须是有效的Base64编码',
|
|
60
|
-
|
|
61
|
-
// String
|
|
62
|
-
'string.hostname': '{{#label}}必须是有效的主机名',
|
|
63
|
-
'string.pattern': '{{#label}}格式不符合要求',
|
|
64
|
-
'string.enum': '{{#label}}必须是以下值之一: {{#valids}}',
|
|
65
|
-
'string.length': '{{#label}}长度必须是{{#limit}}个字符',
|
|
66
|
-
'string.alphanum': '{{#label}}只能包含字母和数字',
|
|
67
|
-
'string.trim': '{{#label}}不能包含前后空格',
|
|
68
|
-
'string.lowercase': '{{#label}}必须是小写',
|
|
69
|
-
'string.uppercase': '{{#label}}必须是大写',
|
|
70
|
-
|
|
71
|
-
// Number
|
|
72
|
-
'number.base': '{{#label}}必须是数字类型',
|
|
73
|
-
'number.min': '{{#label}}不能小于{{#limit}}',
|
|
74
|
-
'number.max': '{{#label}}不能大于{{#limit}}',
|
|
75
|
-
'number.integer': '{{#label}}必须是整数',
|
|
76
|
-
'number.positive': '{{#label}}必须是正数',
|
|
77
|
-
'number.negative': '{{#label}}必须是负数',
|
|
78
|
-
'number.precision': '{{#label}}小数位数不能超过{{#limit}}位',
|
|
79
|
-
'number.port': '{{#label}}必须是有效的端口号(1-65535)',
|
|
80
|
-
|
|
81
|
-
// Boolean
|
|
82
|
-
'boolean.base': '{{#label}}必须是布尔类型',
|
|
83
|
-
|
|
84
|
-
// Object
|
|
85
|
-
'object.base': '{{#label}}必须是对象类型',
|
|
86
|
-
'object.min': '{{#label}}至少需要{{#limit}}个属性',
|
|
87
|
-
'object.max': '{{#label}}最多只能有{{#limit}}个属性',
|
|
88
|
-
'object.unknown': '{{#label}}包含未知属性: {{#key}}',
|
|
89
|
-
'object.missing': '{{#label}}缺少必需属性',
|
|
90
|
-
'object.schema': '{{#label}}包含额外属性',
|
|
91
|
-
'additionalProperties': '{{#label}}不允许有额外属性: {{#key}}',
|
|
92
|
-
|
|
93
|
-
// Array
|
|
94
|
-
'array.base': '{{#label}}必须是数组类型',
|
|
95
|
-
'array.min': '{{#label}}至少需要{{#limit}}个元素',
|
|
96
|
-
'array.max': '{{#label}}最多只能有{{#limit}}个元素',
|
|
97
|
-
'array.length': '{{#label}}必须有{{#limit}}个元素',
|
|
98
|
-
'array.unique': '{{#label}}不能包含重复元素',
|
|
99
|
-
'array.sparse': '{{#label}}不能是稀疏数组',
|
|
100
|
-
'array.includesRequired': '{{#label}}必须包含指定元素',
|
|
101
|
-
|
|
102
|
-
// Date
|
|
103
|
-
'date.base': '{{#label}}必须是有效的日期',
|
|
104
|
-
'date.min': '{{#label}}不能早于{{#limit}}',
|
|
105
|
-
'date.max': '{{#label}}不能晚于{{#limit}}',
|
|
106
|
-
'date.format': '{{#label}}日期格式不正确',
|
|
107
|
-
'date.greater': '{{#label}}必须晚于{{#limit}}',
|
|
108
|
-
'date.less': '{{#label}}必须早于{{#limit}}',
|
|
109
|
-
|
|
110
|
-
// Any
|
|
111
|
-
'any.required': '{{#label}}是必填项',
|
|
112
|
-
'any.invalid': '{{#label}}包含无效值',
|
|
113
|
-
'any.only': '{{#label}}必须匹配{{#valids}}',
|
|
114
|
-
'any.unknown': '不允许字段{{#key}}',
|
|
115
|
-
|
|
116
|
-
// Patterns
|
|
117
|
-
'pattern.phone': '请输入有效的手机号',
|
|
118
|
-
'pattern.phone.international': '请输入有效的国际手机号',
|
|
119
|
-
'pattern.idCard': '请输入有效的身份证号码',
|
|
120
|
-
'pattern.creditCard': '无效的信用卡号',
|
|
121
|
-
'pattern.creditCard.visa': '无效的Visa卡号',
|
|
122
|
-
'pattern.creditCard.mastercard': '无效的万事达卡号',
|
|
123
|
-
'pattern.creditCard.amex': '无效的美国运通卡号',
|
|
124
|
-
'pattern.creditCard.discover': '无效的Discover卡号',
|
|
125
|
-
'pattern.creditCard.jcb': '无效的JCB卡号',
|
|
126
|
-
'pattern.creditCard.unionpay': '无效的银联卡号',
|
|
127
|
-
'pattern.licensePlate': '请输入有效的车牌号',
|
|
128
|
-
'pattern.postalCode': '请输入有效的邮政编码',
|
|
129
|
-
'pattern.passport': '请输入有效的护照号码',
|
|
130
|
-
'pattern.objectId': '无效的 ObjectId',
|
|
131
|
-
'pattern.hexColor': '无效的十六进制颜色值',
|
|
132
|
-
'pattern.macAddress': '无效的 MAC 地址',
|
|
133
|
-
'pattern.cron': '无效的 Cron 表达式',
|
|
134
|
-
'pattern.slug': 'URL别名只能包含小写字母、数字和连字符',
|
|
135
|
-
'pattern.domain': '{{#label}}必须是有效的域名',
|
|
136
|
-
'pattern.ip': '{{#label}}必须是有效的IP地址',
|
|
137
|
-
'pattern.base64': '{{#label}}必须是有效的Base64编码',
|
|
138
|
-
'pattern.jwt': '{{#label}}必须是有效的JWT令牌',
|
|
139
|
-
'pattern.json': '{{#label}}必须是有效的JSON字符串',
|
|
140
|
-
'pattern.username': '用户名必须以字母开头,只能包含字母、数字和下划线',
|
|
141
|
-
'pattern.password.weak': '密码至少6位',
|
|
142
|
-
'pattern.password.medium': '密码至少8位,需包含字母和数字',
|
|
143
|
-
'pattern.password.strong': '密码至少8位,需包含大小写字母和数字',
|
|
144
|
-
'pattern.password.veryStrong': '密码至少10位,需包含大小写字母、数字和特殊字符',
|
|
145
|
-
'pattern.emailOrPhone': '必须是邮箱或手机号',
|
|
146
|
-
'pattern.usernameOrEmail': '必须是用户名或邮箱',
|
|
147
|
-
'pattern.httpOrHttps': '必须是 http 或 https 开头的 URL',
|
|
148
|
-
|
|
149
|
-
// oneOf
|
|
150
|
-
oneOf: '{{#label}}必须匹配以下类型之一',
|
|
151
|
-
'oneOf.invalid': '{{#label}}的值不匹配任何允许的类型',
|
|
152
|
-
|
|
153
|
-
// Error fallback
|
|
154
|
-
UNKNOWN_ERROR: '未知的验证错误',
|
|
155
|
-
CUSTOM_VALIDATION_FAILED: '自定义验证失败',
|
|
156
|
-
ASYNC_VALIDATION_NOT_SUPPORTED: '同步验证不支持异步操作',
|
|
157
|
-
VALIDATE_MUST_BE_FUNCTION: 'validate 必须是一个函数',
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export default zhCN
|
|
1
|
+
import type { LocaleMessages } from './types.js'
|
|
2
|
+
|
|
3
|
+
const zhCN: LocaleMessages = {
|
|
4
|
+
// Generic
|
|
5
|
+
required: '{{#label}}不能为空',
|
|
6
|
+
type: '{{#label}}应该是 {{#expected}} 类型',
|
|
7
|
+
min: '{{#label}}长度不能少于{{#limit}}个字符',
|
|
8
|
+
max: '{{#label}}长度不能超过{{#limit}}个字符',
|
|
9
|
+
length: '{{#label}}长度必须是{{#limit}}个字符',
|
|
10
|
+
pattern: '{{#label}}格式不正确',
|
|
11
|
+
enum: '{{#label}}必须是以下值之一: {{#allowed}}',
|
|
12
|
+
custom: '{{#label}}验证失败: {{#message}}',
|
|
13
|
+
circular: '{{#label}}检测到循环引用',
|
|
14
|
+
'max-depth': '超过最大递归深度 ({{#depth}}) at {{#label}}',
|
|
15
|
+
exception: '{{#label}}验证异常: {{#message}}',
|
|
16
|
+
|
|
17
|
+
// Conditional
|
|
18
|
+
'conditional.underAge': '未成年用户不能注册',
|
|
19
|
+
'conditional.blocked': '账号已被封禁',
|
|
20
|
+
'conditional.notAllowed': '不允许注册',
|
|
21
|
+
|
|
22
|
+
// I18nError — generic
|
|
23
|
+
'error.notFound': '找不到{{#resource}}',
|
|
24
|
+
'error.forbidden': '没有权限访问{{#resource}}',
|
|
25
|
+
'error.unauthorized': '未授权,请先登录',
|
|
26
|
+
'error.invalid': '{{#field}}无效',
|
|
27
|
+
'error.duplicate': '{{#resource}}已存在',
|
|
28
|
+
'error.conflict': '操作冲突: {{#reason}}',
|
|
29
|
+
|
|
30
|
+
// Account
|
|
31
|
+
'account.notFound': { code: 'ACCOUNT_NOT_FOUND', message: '账户不存在' },
|
|
32
|
+
'account.inactive': '账户未激活',
|
|
33
|
+
'account.banned': '账户已被封禁',
|
|
34
|
+
'account.insufficientBalance': {
|
|
35
|
+
code: 'INSUFFICIENT_BALANCE',
|
|
36
|
+
message: '余额不足,当前余额{{#balance}},需要{{#required}}',
|
|
37
|
+
},
|
|
38
|
+
'account.insufficientCredits': '积分不足,当前积分{{#credits}},需要{{#required}}',
|
|
39
|
+
|
|
40
|
+
// User
|
|
41
|
+
'user.notFound': '用户不存在',
|
|
42
|
+
'user.notVerified': '用户未验证',
|
|
43
|
+
'user.noPermission': '没有管理员权限',
|
|
44
|
+
|
|
45
|
+
// Order
|
|
46
|
+
'order.notPaid': { code: 'ORDER_NOT_PAID', message: '订单未支付' },
|
|
47
|
+
'order.paymentMissing': '缺少支付信息',
|
|
48
|
+
'order.addressMissing': '缺少收货地址',
|
|
49
|
+
|
|
50
|
+
// Format
|
|
51
|
+
'format.email': '{{#label}}必须是有效的邮箱地址',
|
|
52
|
+
'format.url': '{{#label}}必须是有效的URL地址',
|
|
53
|
+
'format.uuid': '{{#label}}必须是有效的UUID',
|
|
54
|
+
'format.date': '{{#label}}必须是有效的日期格式 (YYYY-MM-DD)',
|
|
55
|
+
'format.datetime': '{{#label}}必须是有效的日期时间格式 (ISO 8601)',
|
|
56
|
+
'format.time': '{{#label}}必须是有效的时间格式 (HH:mm:ss)',
|
|
57
|
+
'format.ipv4': '{{#label}}必须是有效的IPv4地址',
|
|
58
|
+
'format.ipv6': '{{#label}}必须是有效的IPv6地址',
|
|
59
|
+
'format.binary': '{{#label}}必须是有效的Base64编码',
|
|
60
|
+
|
|
61
|
+
// String
|
|
62
|
+
'string.hostname': '{{#label}}必须是有效的主机名',
|
|
63
|
+
'string.pattern': '{{#label}}格式不符合要求',
|
|
64
|
+
'string.enum': '{{#label}}必须是以下值之一: {{#valids}}',
|
|
65
|
+
'string.length': '{{#label}}长度必须是{{#limit}}个字符',
|
|
66
|
+
'string.alphanum': '{{#label}}只能包含字母和数字',
|
|
67
|
+
'string.trim': '{{#label}}不能包含前后空格',
|
|
68
|
+
'string.lowercase': '{{#label}}必须是小写',
|
|
69
|
+
'string.uppercase': '{{#label}}必须是大写',
|
|
70
|
+
|
|
71
|
+
// Number
|
|
72
|
+
'number.base': '{{#label}}必须是数字类型',
|
|
73
|
+
'number.min': '{{#label}}不能小于{{#limit}}',
|
|
74
|
+
'number.max': '{{#label}}不能大于{{#limit}}',
|
|
75
|
+
'number.integer': '{{#label}}必须是整数',
|
|
76
|
+
'number.positive': '{{#label}}必须是正数',
|
|
77
|
+
'number.negative': '{{#label}}必须是负数',
|
|
78
|
+
'number.precision': '{{#label}}小数位数不能超过{{#limit}}位',
|
|
79
|
+
'number.port': '{{#label}}必须是有效的端口号(1-65535)',
|
|
80
|
+
|
|
81
|
+
// Boolean
|
|
82
|
+
'boolean.base': '{{#label}}必须是布尔类型',
|
|
83
|
+
|
|
84
|
+
// Object
|
|
85
|
+
'object.base': '{{#label}}必须是对象类型',
|
|
86
|
+
'object.min': '{{#label}}至少需要{{#limit}}个属性',
|
|
87
|
+
'object.max': '{{#label}}最多只能有{{#limit}}个属性',
|
|
88
|
+
'object.unknown': '{{#label}}包含未知属性: {{#key}}',
|
|
89
|
+
'object.missing': '{{#label}}缺少必需属性',
|
|
90
|
+
'object.schema': '{{#label}}包含额外属性',
|
|
91
|
+
'additionalProperties': '{{#label}}不允许有额外属性: {{#key}}',
|
|
92
|
+
|
|
93
|
+
// Array
|
|
94
|
+
'array.base': '{{#label}}必须是数组类型',
|
|
95
|
+
'array.min': '{{#label}}至少需要{{#limit}}个元素',
|
|
96
|
+
'array.max': '{{#label}}最多只能有{{#limit}}个元素',
|
|
97
|
+
'array.length': '{{#label}}必须有{{#limit}}个元素',
|
|
98
|
+
'array.unique': '{{#label}}不能包含重复元素',
|
|
99
|
+
'array.sparse': '{{#label}}不能是稀疏数组',
|
|
100
|
+
'array.includesRequired': '{{#label}}必须包含指定元素',
|
|
101
|
+
|
|
102
|
+
// Date
|
|
103
|
+
'date.base': '{{#label}}必须是有效的日期',
|
|
104
|
+
'date.min': '{{#label}}不能早于{{#limit}}',
|
|
105
|
+
'date.max': '{{#label}}不能晚于{{#limit}}',
|
|
106
|
+
'date.format': '{{#label}}日期格式不正确',
|
|
107
|
+
'date.greater': '{{#label}}必须晚于{{#limit}}',
|
|
108
|
+
'date.less': '{{#label}}必须早于{{#limit}}',
|
|
109
|
+
|
|
110
|
+
// Any
|
|
111
|
+
'any.required': '{{#label}}是必填项',
|
|
112
|
+
'any.invalid': '{{#label}}包含无效值',
|
|
113
|
+
'any.only': '{{#label}}必须匹配{{#valids}}',
|
|
114
|
+
'any.unknown': '不允许字段{{#key}}',
|
|
115
|
+
|
|
116
|
+
// Patterns
|
|
117
|
+
'pattern.phone': '请输入有效的手机号',
|
|
118
|
+
'pattern.phone.international': '请输入有效的国际手机号',
|
|
119
|
+
'pattern.idCard': '请输入有效的身份证号码',
|
|
120
|
+
'pattern.creditCard': '无效的信用卡号',
|
|
121
|
+
'pattern.creditCard.visa': '无效的Visa卡号',
|
|
122
|
+
'pattern.creditCard.mastercard': '无效的万事达卡号',
|
|
123
|
+
'pattern.creditCard.amex': '无效的美国运通卡号',
|
|
124
|
+
'pattern.creditCard.discover': '无效的Discover卡号',
|
|
125
|
+
'pattern.creditCard.jcb': '无效的JCB卡号',
|
|
126
|
+
'pattern.creditCard.unionpay': '无效的银联卡号',
|
|
127
|
+
'pattern.licensePlate': '请输入有效的车牌号',
|
|
128
|
+
'pattern.postalCode': '请输入有效的邮政编码',
|
|
129
|
+
'pattern.passport': '请输入有效的护照号码',
|
|
130
|
+
'pattern.objectId': '无效的 ObjectId',
|
|
131
|
+
'pattern.hexColor': '无效的十六进制颜色值',
|
|
132
|
+
'pattern.macAddress': '无效的 MAC 地址',
|
|
133
|
+
'pattern.cron': '无效的 Cron 表达式',
|
|
134
|
+
'pattern.slug': 'URL别名只能包含小写字母、数字和连字符',
|
|
135
|
+
'pattern.domain': '{{#label}}必须是有效的域名',
|
|
136
|
+
'pattern.ip': '{{#label}}必须是有效的IP地址',
|
|
137
|
+
'pattern.base64': '{{#label}}必须是有效的Base64编码',
|
|
138
|
+
'pattern.jwt': '{{#label}}必须是有效的JWT令牌',
|
|
139
|
+
'pattern.json': '{{#label}}必须是有效的JSON字符串',
|
|
140
|
+
'pattern.username': '用户名必须以字母开头,只能包含字母、数字和下划线',
|
|
141
|
+
'pattern.password.weak': '密码至少6位',
|
|
142
|
+
'pattern.password.medium': '密码至少8位,需包含字母和数字',
|
|
143
|
+
'pattern.password.strong': '密码至少8位,需包含大小写字母和数字',
|
|
144
|
+
'pattern.password.veryStrong': '密码至少10位,需包含大小写字母、数字和特殊字符',
|
|
145
|
+
'pattern.emailOrPhone': '必须是邮箱或手机号',
|
|
146
|
+
'pattern.usernameOrEmail': '必须是用户名或邮箱',
|
|
147
|
+
'pattern.httpOrHttps': '必须是 http 或 https 开头的 URL',
|
|
148
|
+
|
|
149
|
+
// oneOf
|
|
150
|
+
oneOf: '{{#label}}必须匹配以下类型之一',
|
|
151
|
+
'oneOf.invalid': '{{#label}}的值不匹配任何允许的类型',
|
|
152
|
+
|
|
153
|
+
// Error fallback
|
|
154
|
+
UNKNOWN_ERROR: '未知的验证错误',
|
|
155
|
+
CUSTOM_VALIDATION_FAILED: '自定义验证失败',
|
|
156
|
+
ASYNC_VALIDATION_NOT_SUPPORTED: '同步验证不支持异步操作',
|
|
157
|
+
VALIDATE_MUST_BE_FUNCTION: 'validate 必须是一个函数',
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export default zhCN
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import type { JSONSchema } from '../types/schema.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* ConstraintParser — parses DSL constraint strings into Partial<JSONSchema>
|
|
5
|
-
*
|
|
6
|
-
* Fixes:
|
|
7
|
-
* DA-03 string:N semantic divergence → always returns exactLength:N (not minLength+maxLength)
|
|
8
|
-
* DB-03 negative range support → regex updated to /^(-?\d*\.?\d*)-(-?\d*\.?\d*)$/
|
|
9
|
-
*
|
|
10
|
-
* Return type is always Partial<JSONSchema>; never returns a raw string (v1 bug)
|
|
11
|
-
*/
|
|
12
|
-
export const ConstraintParser = {
|
|
13
|
-
/**
|
|
14
|
-
* Parse a constraint string
|
|
15
|
-
* @param constraintStr - The constraint portion (type name and '!' already stripped)
|
|
16
|
-
* @param baseType - Base type name ('string' | 'number' | 'integer' | 'array' | ...)
|
|
17
|
-
* @returns Partial<JSONSchema>; returns {} when unparseable (avoids polluting the target schema)
|
|
18
|
-
*/
|
|
19
|
-
parse(constraintStr: string, baseType: string): Partial<JSONSchema> {
|
|
20
|
-
if (!constraintStr) return {}
|
|
21
|
-
|
|
22
|
-
const s = constraintStr.trim()
|
|
23
|
-
if (!s) return {}
|
|
24
|
-
|
|
25
|
-
// ========== 1. Comparison operators (number/integer only, highest priority) ==========
|
|
26
|
-
if (baseType === 'number' || baseType === 'integer') {
|
|
27
|
-
// >= : greater than or equal (supports negative and decimal)
|
|
28
|
-
const gteMatch = /^>=(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
29
|
-
if (gteMatch) return { minimum: parseFloat(gteMatch[1]) }
|
|
30
|
-
|
|
31
|
-
// <= : less than or equal
|
|
32
|
-
const lteMatch = /^<=(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
33
|
-
if (lteMatch) return { maximum: parseFloat(lteMatch[1]) }
|
|
34
|
-
|
|
35
|
-
// > : greater than
|
|
36
|
-
const gtMatch = /^>(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
37
|
-
if (gtMatch) return { exclusiveMinimum: parseFloat(gtMatch[1]) }
|
|
38
|
-
|
|
39
|
-
// < : less than
|
|
40
|
-
const ltMatch = /^<(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
41
|
-
if (ltMatch) return { exclusiveMaximum: parseFloat(ltMatch[1]) }
|
|
42
|
-
|
|
43
|
-
// = : exact equal
|
|
44
|
-
const eqMatch = /^=(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
45
|
-
if (eqMatch) return { enum: [parseFloat(eqMatch[1])] }
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// ========== 2. Enum (x|y|z) ==========
|
|
49
|
-
// Only when '|' is present and it is not a pure numeric range
|
|
50
|
-
if (s.includes('|') && !/^-?\d*\.?\d*--?\d*\.?\d*$/.test(s)) {
|
|
51
|
-
return { enum: s.split('|').map(v => v.trim()) }
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ========== 3. Range constraint (supports negatives — fix DB-03) ==========
|
|
55
|
-
// Format: N-M, N-, -M (N/M may include a minus sign and decimal point)
|
|
56
|
-
// Regex: /^(-?\d*\.?\d*)-(-?\d*\.?\d*)$/
|
|
57
|
-
// Note: in "-M" format the value is treated as an upper bound (absolute value), not a negative lower bound
|
|
58
|
-
const rangeMatch = /^(-?\d*\.?\d*)-(-?\d*\.?\d*)$/.exec(s)
|
|
59
|
-
if (rangeMatch) {
|
|
60
|
-
const [, rawMin, rawMax] = rangeMatch
|
|
61
|
-
const result: Partial<JSONSchema> = {}
|
|
62
|
-
|
|
63
|
-
if (baseType === 'string') {
|
|
64
|
-
// string type: minLength / maxLength (integers, non-negative)
|
|
65
|
-
if (rawMin) result.minLength = Math.max(0, parseInt(rawMin, 10))
|
|
66
|
-
// "-M" format: take absolute value
|
|
67
|
-
if (rawMax) result.maxLength = Math.abs(parseInt(rawMax, 10))
|
|
68
|
-
} else if (baseType === 'array') {
|
|
69
|
-
if (rawMin) result.minItems = Math.max(0, parseInt(rawMin, 10))
|
|
70
|
-
if (rawMax) result.maxItems = Math.abs(parseInt(rawMax, 10))
|
|
71
|
-
} else {
|
|
72
|
-
// number / integer
|
|
73
|
-
if (rawMin) result.minimum = parseFloat(rawMin)
|
|
74
|
-
if (rawMax) result.maximum = parseFloat(rawMax)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return result
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// ========== 4. Single-value constraint ==========
|
|
81
|
-
// Positive integer or decimal, no leading minus (negatives are handled by comparison operators above)
|
|
82
|
-
const singleMatch = /^(\d+(?:\.\d+)?)$/.exec(s)
|
|
83
|
-
if (singleMatch) {
|
|
84
|
-
const value = parseFloat(singleMatch[1])
|
|
85
|
-
|
|
86
|
-
if (baseType === 'string') {
|
|
87
|
-
// v1 compat: string:N → exactLength:N (exact length)
|
|
88
|
-
return { exactLength: Math.floor(value) }
|
|
89
|
-
} else if (baseType === 'array') {
|
|
90
|
-
return { maxItems: Math.floor(value) }
|
|
91
|
-
} else {
|
|
92
|
-
// number/integer single value = upper bound (maximum)
|
|
93
|
-
return { maximum: value }
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// ========== 5. Unparseable: warn and return {} ==========
|
|
98
|
-
console.warn(`[schema-dsl] ConstraintParser: unrecognized constraint "${constraintStr}" for type "${baseType}" — ignored`)
|
|
99
|
-
return {}
|
|
100
|
-
},
|
|
101
|
-
}
|
|
1
|
+
import type { JSONSchema } from '../types/schema.js'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* ConstraintParser — parses DSL constraint strings into Partial<JSONSchema>
|
|
5
|
+
*
|
|
6
|
+
* Fixes:
|
|
7
|
+
* DA-03 string:N semantic divergence → always returns exactLength:N (not minLength+maxLength)
|
|
8
|
+
* DB-03 negative range support → regex updated to /^(-?\d*\.?\d*)-(-?\d*\.?\d*)$/
|
|
9
|
+
*
|
|
10
|
+
* Return type is always Partial<JSONSchema>; never returns a raw string (v1 bug)
|
|
11
|
+
*/
|
|
12
|
+
export const ConstraintParser = {
|
|
13
|
+
/**
|
|
14
|
+
* Parse a constraint string
|
|
15
|
+
* @param constraintStr - The constraint portion (type name and '!' already stripped)
|
|
16
|
+
* @param baseType - Base type name ('string' | 'number' | 'integer' | 'array' | ...)
|
|
17
|
+
* @returns Partial<JSONSchema>; returns {} when unparseable (avoids polluting the target schema)
|
|
18
|
+
*/
|
|
19
|
+
parse(constraintStr: string, baseType: string): Partial<JSONSchema> {
|
|
20
|
+
if (!constraintStr) return {}
|
|
21
|
+
|
|
22
|
+
const s = constraintStr.trim()
|
|
23
|
+
if (!s) return {}
|
|
24
|
+
|
|
25
|
+
// ========== 1. Comparison operators (number/integer only, highest priority) ==========
|
|
26
|
+
if (baseType === 'number' || baseType === 'integer') {
|
|
27
|
+
// >= : greater than or equal (supports negative and decimal)
|
|
28
|
+
const gteMatch = /^>=(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
29
|
+
if (gteMatch) return { minimum: parseFloat(gteMatch[1]) }
|
|
30
|
+
|
|
31
|
+
// <= : less than or equal
|
|
32
|
+
const lteMatch = /^<=(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
33
|
+
if (lteMatch) return { maximum: parseFloat(lteMatch[1]) }
|
|
34
|
+
|
|
35
|
+
// > : greater than
|
|
36
|
+
const gtMatch = /^>(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
37
|
+
if (gtMatch) return { exclusiveMinimum: parseFloat(gtMatch[1]) }
|
|
38
|
+
|
|
39
|
+
// < : less than
|
|
40
|
+
const ltMatch = /^<(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
41
|
+
if (ltMatch) return { exclusiveMaximum: parseFloat(ltMatch[1]) }
|
|
42
|
+
|
|
43
|
+
// = : exact equal
|
|
44
|
+
const eqMatch = /^=(-?\d+(?:\.\d+)?)$/.exec(s)
|
|
45
|
+
if (eqMatch) return { enum: [parseFloat(eqMatch[1])] }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ========== 2. Enum (x|y|z) ==========
|
|
49
|
+
// Only when '|' is present and it is not a pure numeric range
|
|
50
|
+
if (s.includes('|') && !/^-?\d*\.?\d*--?\d*\.?\d*$/.test(s)) {
|
|
51
|
+
return { enum: s.split('|').map(v => v.trim()) }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ========== 3. Range constraint (supports negatives — fix DB-03) ==========
|
|
55
|
+
// Format: N-M, N-, -M (N/M may include a minus sign and decimal point)
|
|
56
|
+
// Regex: /^(-?\d*\.?\d*)-(-?\d*\.?\d*)$/
|
|
57
|
+
// Note: in "-M" format the value is treated as an upper bound (absolute value), not a negative lower bound
|
|
58
|
+
const rangeMatch = /^(-?\d*\.?\d*)-(-?\d*\.?\d*)$/.exec(s)
|
|
59
|
+
if (rangeMatch) {
|
|
60
|
+
const [, rawMin, rawMax] = rangeMatch
|
|
61
|
+
const result: Partial<JSONSchema> = {}
|
|
62
|
+
|
|
63
|
+
if (baseType === 'string') {
|
|
64
|
+
// string type: minLength / maxLength (integers, non-negative)
|
|
65
|
+
if (rawMin) result.minLength = Math.max(0, parseInt(rawMin, 10))
|
|
66
|
+
// "-M" format: take absolute value
|
|
67
|
+
if (rawMax) result.maxLength = Math.abs(parseInt(rawMax, 10))
|
|
68
|
+
} else if (baseType === 'array') {
|
|
69
|
+
if (rawMin) result.minItems = Math.max(0, parseInt(rawMin, 10))
|
|
70
|
+
if (rawMax) result.maxItems = Math.abs(parseInt(rawMax, 10))
|
|
71
|
+
} else {
|
|
72
|
+
// number / integer
|
|
73
|
+
if (rawMin) result.minimum = parseFloat(rawMin)
|
|
74
|
+
if (rawMax) result.maximum = parseFloat(rawMax)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return result
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ========== 4. Single-value constraint ==========
|
|
81
|
+
// Positive integer or decimal, no leading minus (negatives are handled by comparison operators above)
|
|
82
|
+
const singleMatch = /^(\d+(?:\.\d+)?)$/.exec(s)
|
|
83
|
+
if (singleMatch) {
|
|
84
|
+
const value = parseFloat(singleMatch[1])
|
|
85
|
+
|
|
86
|
+
if (baseType === 'string') {
|
|
87
|
+
// v1 compat: string:N → exactLength:N (exact length)
|
|
88
|
+
return { exactLength: Math.floor(value) }
|
|
89
|
+
} else if (baseType === 'array') {
|
|
90
|
+
return { maxItems: Math.floor(value) }
|
|
91
|
+
} else {
|
|
92
|
+
// number/integer single value = upper bound (maximum)
|
|
93
|
+
return { maximum: value }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// ========== 5. Unparseable: warn and return {} ==========
|
|
98
|
+
console.warn(`[schema-dsl] ConstraintParser: unrecognized constraint "${constraintStr}" for type "${baseType}" — ignored`)
|
|
99
|
+
return {}
|
|
100
|
+
},
|
|
101
|
+
}
|