schema-dsl 1.2.4 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +87 -210
- package/README.md +391 -2249
- package/dist/DslBuilder-DQDN0ZxZ.d.cts +341 -0
- package/dist/DslBuilder-DkLaOo9Q.d.ts +341 -0
- package/dist/Validator-C7GsVQOH.d.cts +192 -0
- package/dist/Validator-hFWKGxir.d.ts +192 -0
- package/dist/index.cjs +6594 -0
- package/dist/index.d.cts +1145 -0
- package/dist/index.d.ts +1145 -0
- package/dist/index.js +6528 -0
- package/dist/plugin-CIKtTMtS.d.cts +246 -0
- package/dist/plugin-CIKtTMtS.d.ts +246 -0
- package/dist/plugins/custom-format.cjs +3802 -0
- package/dist/plugins/custom-format.d.cts +12 -0
- package/dist/plugins/custom-format.d.ts +12 -0
- package/dist/plugins/custom-format.js +3772 -0
- package/dist/plugins/custom-type-example.cjs +3795 -0
- package/dist/plugins/custom-type-example.d.cts +8 -0
- package/dist/plugins/custom-type-example.d.ts +8 -0
- package/dist/plugins/custom-type-example.js +3765 -0
- package/dist/plugins/custom-validator.cjs +146 -0
- package/dist/plugins/custom-validator.d.cts +10 -0
- package/dist/plugins/custom-validator.d.ts +10 -0
- package/dist/plugins/custom-validator.js +121 -0
- package/docs/FEATURE-INDEX.md +102 -68
- package/docs/add-custom-locale.md +48 -35
- package/docs/add-keyword.md +24 -0
- package/docs/api-reference.md +396 -154
- package/docs/api.md +13 -0
- package/docs/best-practices-project-structure.md +19 -10
- package/docs/best-practices.md +93 -53
- package/docs/cache-manager.md +23 -15
- package/docs/compile.md +45 -0
- package/docs/conditional-api.md +40 -11
- package/docs/custom-extensions-guide.md +80 -152
- package/docs/design-philosophy.md +76 -71
- package/docs/doc-index.md +324 -0
- package/docs/dsl-syntax.md +69 -19
- package/docs/dynamic-locale.md +24 -14
- package/docs/enum.md +12 -5
- package/docs/error-handling.md +53 -44
- package/docs/export-guide.md +47 -8
- package/docs/export-limitations.md +27 -11
- package/docs/faq.md +86 -67
- package/docs/frontend-i18n-guide.md +26 -12
- package/docs/i18n-user-guide.md +60 -47
- package/docs/i18n.md +51 -32
- package/docs/index.md +48 -0
- package/docs/json-schema-basics.md +40 -0
- package/docs/label-vs-description.md +12 -3
- package/docs/markdown-exporter.md +15 -6
- package/docs/mongodb-exporter.md +11 -4
- package/docs/multi-language.md +26 -0
- package/docs/multi-type-support.md +26 -33
- package/docs/mysql-exporter.md +9 -2
- package/docs/number-operators.md +12 -5
- package/docs/optional-marker-guide.md +28 -23
- package/docs/performance-guide.md +49 -0
- package/docs/plugin-system.md +205 -366
- package/docs/plugin-type-registration.md +34 -0
- package/docs/postgresql-exporter.md +9 -2
- package/docs/public/favicon.svg +5 -0
- package/docs/quick-start.md +37 -363
- package/docs/runtime-locale-support.md +20 -9
- package/docs/schema-helper.md +10 -5
- package/docs/schema-utils-advanced-issues.md +23 -0
- package/docs/schema-utils-best-practices.md +20 -0
- package/docs/schema-utils-chaining.md +7 -0
- package/docs/schema-utils.md +76 -42
- package/docs/security-checklist.md +20 -0
- package/docs/string-extensions.md +17 -9
- package/docs/troubleshooting.md +36 -21
- package/docs/type-converter.md +41 -50
- package/docs/type-reference.md +38 -15
- package/docs/typescript-guide.md +53 -42
- package/docs/union-type-guide.md +11 -1
- package/docs/union-types.md +10 -3
- package/docs/validate-async.md +36 -25
- package/docs/validate-batch.md +49 -0
- package/docs/validate-dsl-object-support.md +33 -28
- package/docs/validate.md +36 -16
- package/docs/validation-guide.md +25 -7
- package/docs/validator.md +39 -0
- package/package.json +85 -27
- package/plugins/custom-format.cjs +8 -0
- package/plugins/custom-type-example.cjs +8 -0
- package/plugins/custom-validator.cjs +8 -0
- package/src/adapters/DslAdapter.ts +111 -0
- package/src/adapters/index.ts +1 -0
- package/src/config/constants.ts +83 -0
- package/src/config/index.ts +2 -0
- package/src/config/patterns.ts +77 -0
- package/src/core/CacheManager.ts +159 -0
- package/src/core/ConditionalBuilder.ts +382 -0
- package/src/core/ConditionalRuntime.ts +28 -0
- package/src/core/ConditionalValidator.ts +255 -0
- package/src/core/DslBuilder.ts +677 -0
- package/src/core/ErrorCodes.ts +38 -0
- package/src/core/ErrorFormatter.ts +271 -0
- package/src/core/JSONSchemaCore.ts +65 -0
- package/src/core/Locale.ts +187 -0
- package/src/core/MessageTemplate.ts +42 -0
- package/src/core/ObjectDslBuilder.ts +64 -0
- package/src/core/PluginManager.ts +326 -0
- package/src/core/StringExtensions.ts +140 -0
- package/src/core/TemplateEngine.ts +44 -0
- package/src/core/Validator.ts +448 -0
- package/src/errors/I18nError.ts +159 -0
- package/src/errors/ValidationError.ts +105 -0
- package/src/exporters/BaseExporter.ts +60 -0
- package/src/exporters/MarkdownExporter.ts +305 -0
- package/src/exporters/MongoDBExporter.ts +126 -0
- package/src/exporters/MySQLExporter.ts +155 -0
- package/src/exporters/PostgreSQLExporter.ts +222 -0
- package/src/exporters/index.ts +18 -0
- package/src/index.ts +633 -0
- package/{lib/locales/en-US.js → src/locales/en-US.ts} +21 -37
- package/{lib/locales/es-ES.js → src/locales/es-ES.ts} +63 -16
- package/{lib/locales/fr-FR.js → src/locales/fr-FR.ts} +74 -27
- package/src/locales/index.ts +103 -0
- package/{lib/locales/ja-JP.js → src/locales/ja-JP.ts} +59 -17
- package/src/locales/types.ts +156 -0
- package/{lib/locales/zh-CN.js → src/locales/zh-CN.ts} +21 -38
- package/src/parser/ConstraintParser.ts +101 -0
- package/src/parser/DslParser.ts +470 -0
- package/src/parser/SchemaCompiler.ts +66 -0
- package/src/parser/TypeRegistry.ts +250 -0
- package/src/parser/index.ts +6 -0
- package/src/plugins/custom-format.ts +126 -0
- package/src/plugins/custom-type-example.ts +108 -0
- package/src/plugins/custom-validator.ts +140 -0
- package/src/types/conditional.ts +28 -0
- package/src/types/config.ts +59 -0
- package/src/types/dsl.ts +131 -0
- package/src/types/error.ts +60 -0
- package/src/types/index.ts +17 -0
- package/src/types/infer.ts +128 -0
- package/src/types/plugin.ts +58 -0
- package/src/types/safe-regex.d.ts +9 -0
- package/src/types/schema.ts +66 -0
- package/src/types/validate.ts +71 -0
- package/src/utils/SchemaHelper.ts +196 -0
- package/src/utils/SchemaUtils.ts +346 -0
- package/src/utils/TypeConverter.ts +215 -0
- package/src/utils/index.ts +10 -0
- package/src/validators/CustomKeywords.ts +477 -0
- package/.eslintignore +0 -11
- package/.eslintrc.json +0 -27
- package/CONTRIBUTING.md +0 -368
- package/STATUS.md +0 -491
- package/changelogs/v1.0.0.md +0 -328
- package/changelogs/v1.0.9.md +0 -367
- package/changelogs/v1.1.0.md +0 -389
- package/changelogs/v1.1.1.md +0 -308
- package/changelogs/v1.1.2.md +0 -183
- package/changelogs/v1.1.3.md +0 -161
- package/changelogs/v1.1.4.md +0 -432
- package/changelogs/v1.1.5.md +0 -493
- package/changelogs/v1.1.6.md +0 -211
- package/changelogs/v1.1.8.md +0 -376
- package/changelogs/v1.2.3.md +0 -124
- package/docs/INDEX.md +0 -252
- package/docs/issues-resolved-summary.md +0 -196
- package/docs/performance-benchmark-report.md +0 -179
- package/docs/performance-quick-reference.md +0 -123
- package/docs/user-questions-answered.md +0 -353
- package/docs/validation-rules-v1.0.2.md +0 -1608
- package/examples/README.md +0 -81
- package/examples/array-dsl-example.js +0 -227
- package/examples/conditional-example.js +0 -288
- package/examples/conditional-non-object.js +0 -129
- package/examples/conditional-validate-example.js +0 -321
- package/examples/custom-extension.js +0 -85
- package/examples/dsl-match-example.js +0 -74
- package/examples/dsl-style.js +0 -118
- package/examples/dynamic-locale-configuration.js +0 -348
- package/examples/dynamic-locale-example.js +0 -287
- package/examples/enum.examples.js +0 -324
- package/examples/export-demo.js +0 -130
- package/examples/express-integration.js +0 -376
- package/examples/i18n-error-handling-complete.js +0 -381
- package/examples/i18n-error-handling-quickstart.md +0 -0
- package/examples/i18n-error.examples.js +0 -181
- package/examples/i18n-full-demo.js +0 -301
- package/examples/i18n-memory-safety.examples.js +0 -268
- package/examples/markdown-export.js +0 -71
- package/examples/middleware-usage.js +0 -93
- package/examples/new-features-comparison.js +0 -315
- package/examples/password-reset/README.md +0 -153
- package/examples/password-reset/schema.js +0 -26
- package/examples/password-reset/test.js +0 -101
- package/examples/plugin-system.examples.js +0 -205
- package/examples/schema-utils-chaining.examples.js +0 -250
- package/examples/simple-example.js +0 -122
- package/examples/slug.examples.js +0 -179
- package/examples/string-extensions.js +0 -297
- package/examples/union-type-example.js +0 -127
- package/examples/union-types-example.js +0 -77
- package/examples/user-registration/README.md +0 -156
- package/examples/user-registration/routes.js +0 -92
- package/examples/user-registration/schema.js +0 -150
- package/examples/user-registration/server.js +0 -74
- package/index.d.ts +0 -3540
- package/index.js +0 -457
- package/index.mjs +0 -60
- package/lib/adapters/DslAdapter.js +0 -871
- package/lib/adapters/index.js +0 -20
- package/lib/config/constants.js +0 -286
- package/lib/config/patterns/common.js +0 -47
- package/lib/config/patterns/creditCard.js +0 -9
- package/lib/config/patterns/idCard.js +0 -9
- package/lib/config/patterns/index.js +0 -9
- package/lib/config/patterns/licensePlate.js +0 -4
- package/lib/config/patterns/passport.js +0 -4
- package/lib/config/patterns/phone.js +0 -9
- package/lib/config/patterns/postalCode.js +0 -5
- package/lib/core/CacheManager.js +0 -376
- package/lib/core/ConditionalBuilder.js +0 -503
- package/lib/core/DslBuilder.js +0 -1400
- package/lib/core/ErrorCodes.js +0 -233
- package/lib/core/ErrorFormatter.js +0 -445
- package/lib/core/JSONSchemaCore.js +0 -347
- package/lib/core/Locale.js +0 -130
- package/lib/core/MessageTemplate.js +0 -98
- package/lib/core/PluginManager.js +0 -448
- package/lib/core/StringExtensions.js +0 -240
- package/lib/core/Validator.js +0 -654
- package/lib/errors/I18nError.js +0 -328
- package/lib/errors/ValidationError.js +0 -191
- package/lib/exporters/MarkdownExporter.js +0 -420
- package/lib/exporters/MongoDBExporter.js +0 -162
- package/lib/exporters/MySQLExporter.js +0 -212
- package/lib/exporters/PostgreSQLExporter.js +0 -289
- package/lib/exporters/index.js +0 -24
- package/lib/locales/index.js +0 -8
- package/lib/utils/LRUCache.js +0 -174
- package/lib/utils/SchemaHelper.js +0 -240
- package/lib/utils/SchemaUtils.js +0 -445
- package/lib/utils/TypeConverter.js +0 -245
- package/lib/utils/index.js +0 -13
- package/lib/validators/CustomKeywords.js +0 -616
- package/lib/validators/index.js +0 -11
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { LocaleMessages } from './types.js'
|
|
2
|
+
|
|
3
|
+
const jaJP: LocaleMessages = {
|
|
2
4
|
// Generic
|
|
3
5
|
required: '{{#label}}は必須です',
|
|
4
6
|
type: '{{#label}}は{{#expected}}型である必要がありますが、{{#actual}}が渡されました',
|
|
@@ -12,12 +14,40 @@ module.exports = {
|
|
|
12
14
|
'max-depth': '{{#label}}で最大再帰深度 ({{#depth}}) を超えました',
|
|
13
15
|
exception: '{{#label}}の検証例外: {{#message}}',
|
|
14
16
|
|
|
15
|
-
// Conditional
|
|
17
|
+
// Conditional
|
|
16
18
|
'conditional.underAge': '未成年者は登録できません',
|
|
17
19
|
'conditional.blocked': 'アカウントがブロックされています',
|
|
18
20
|
'conditional.notAllowed': '登録は許可されていません',
|
|
19
21
|
|
|
20
|
-
//
|
|
22
|
+
// I18nError — generic (v2 additions)
|
|
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 (v2 additions)
|
|
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 (v2 additions)
|
|
41
|
+
'user.notFound': 'ユーザーが見つかりません',
|
|
42
|
+
'user.notVerified': 'ユーザーが確認されていません',
|
|
43
|
+
'user.noPermission': '管理者権限がありません',
|
|
44
|
+
|
|
45
|
+
// Order (v2 additions)
|
|
46
|
+
'order.notPaid': { code: 'ORDER_NOT_PAID', message: '注文が支払われていません' },
|
|
47
|
+
'order.paymentMissing': '支払い情報がありません',
|
|
48
|
+
'order.addressMissing': '配送先住所がありません',
|
|
49
|
+
|
|
50
|
+
// Format
|
|
21
51
|
'format.email': '{{#label}}は有効なメールアドレスである必要があります',
|
|
22
52
|
'format.url': '{{#label}}は有効なURLである必要があります',
|
|
23
53
|
'format.uuid': '{{#label}}は有効なUUIDである必要があります',
|
|
@@ -32,6 +62,11 @@ module.exports = {
|
|
|
32
62
|
'string.hostname': '{{#label}}は有効なホスト名である必要があります',
|
|
33
63
|
'string.pattern': '{{#label}}の形式が必要なパターンと一致しません',
|
|
34
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}}は大文字である必要があります',
|
|
35
70
|
|
|
36
71
|
// Number
|
|
37
72
|
'number.base': '{{#label}}は数値である必要があります',
|
|
@@ -40,6 +75,8 @@ module.exports = {
|
|
|
40
75
|
'number.integer': '{{#label}}は整数である必要があります',
|
|
41
76
|
'number.positive': '{{#label}}は正の数である必要があります',
|
|
42
77
|
'number.negative': '{{#label}}は負の数である必要があります',
|
|
78
|
+
'number.precision': '{{#label}}の小数点以下は{{#limit}}桁以内である必要があります',
|
|
79
|
+
'number.port': '{{#label}}は有効なポート番号 (1-65535) である必要があります',
|
|
43
80
|
|
|
44
81
|
// Boolean
|
|
45
82
|
'boolean.base': '{{#label}}はブール値である必要があります',
|
|
@@ -49,7 +86,8 @@ module.exports = {
|
|
|
49
86
|
'object.min': '{{#label}}は少なくとも{{#limit}}個のプロパティを持つ必要があります',
|
|
50
87
|
'object.max': '{{#label}}は最大{{#limit}}個のプロパティを持つことができます',
|
|
51
88
|
'object.unknown': '{{#label}}に未知のプロパティが含まれています: {{#key}}',
|
|
52
|
-
|
|
89
|
+
'object.missing': '{{#label}}には必須プロパティがありません',
|
|
90
|
+
'object.schema': '{{#label}}に追加のプロパティが含まれています',
|
|
53
91
|
'additionalProperties': '{{#label}}に追加のプロパティを含めてはいけません: {{#key}}',
|
|
54
92
|
|
|
55
93
|
// Array
|
|
@@ -58,11 +96,16 @@ module.exports = {
|
|
|
58
96
|
'array.max': '{{#label}}は最大{{#limit}}個の要素を持つことができます',
|
|
59
97
|
'array.length': '{{#label}}は正確に{{#limit}}個の要素を持つ必要があります',
|
|
60
98
|
'array.unique': '{{#label}}に重複する要素を含めることはできません',
|
|
99
|
+
'array.sparse': '{{#label}}はスパース配列であってはなりません',
|
|
100
|
+
'array.includesRequired': '{{#label}}には必須アイテムを含める必要があります',
|
|
61
101
|
|
|
62
102
|
// Date
|
|
63
103
|
'date.base': '{{#label}}は有効な日付である必要があります',
|
|
64
104
|
'date.min': '{{#label}}は{{#limit}}より前であってはなりません',
|
|
65
105
|
'date.max': '{{#label}}は{{#limit}}より後であってはなりません',
|
|
106
|
+
'date.format': '{{#label}}の日付形式が無効です',
|
|
107
|
+
'date.greater': '{{#label}}は{{#limit}}より後である必要があります',
|
|
108
|
+
'date.less': '{{#label}}は{{#limit}}より前である必要があります',
|
|
66
109
|
|
|
67
110
|
// Any
|
|
68
111
|
'any.required': '{{#label}}は必須です',
|
|
@@ -70,7 +113,7 @@ module.exports = {
|
|
|
70
113
|
'any.only': '{{#label}}は{{#valids}}と一致する必要があります',
|
|
71
114
|
'any.unknown': 'フィールド{{#key}}は許可されていません',
|
|
72
115
|
|
|
73
|
-
// Patterns
|
|
116
|
+
// Patterns
|
|
74
117
|
'pattern.phone': '無効な電話番号',
|
|
75
118
|
'pattern.phone.international': '無効な国際電話番号',
|
|
76
119
|
'pattern.idCard': '無効なIDカード番号',
|
|
@@ -89,30 +132,29 @@ module.exports = {
|
|
|
89
132
|
'pattern.macAddress': '無効なMACアドレス',
|
|
90
133
|
'pattern.cron': '無効なCron式',
|
|
91
134
|
'pattern.slug': 'URLスラッグには小文字、数字、ハイフンのみを含めることができます',
|
|
92
|
-
// v1.0.2 新増
|
|
93
135
|
'pattern.domain': '{{#label}}は有効なドメイン名である必要があります',
|
|
94
136
|
'pattern.ip': '{{#label}}は有効なIPアドレスである必要があります',
|
|
95
137
|
'pattern.base64': '{{#label}}は有効なBase64文字列である必要があります',
|
|
96
138
|
'pattern.jwt': '{{#label}}は有効なJWTトークンである必要があります',
|
|
97
139
|
'pattern.json': '{{#label}}は有効なJSON文字列である必要があります',
|
|
98
|
-
|
|
99
|
-
// Username & Password
|
|
100
140
|
'pattern.username': 'ユーザー名は文字で始まり、文字、数字、アンダースコアのみを含む必要があります',
|
|
101
141
|
'pattern.password.weak': 'パスワードは少なくとも6文字である必要があります',
|
|
102
142
|
'pattern.password.medium': 'パスワードは少なくとも8文字で、文字と数字を含む必要があります',
|
|
103
143
|
'pattern.password.strong': 'パスワードは少なくとも8文字で、大文字、小文字、数字を含む必要があります',
|
|
104
144
|
'pattern.password.veryStrong': 'パスワードは少なくとも10文字で、大文字、小文字、数字、特殊文字を含む必要があります',
|
|
145
|
+
'pattern.emailOrPhone': 'メールアドレスまたは電話番号である必要があります',
|
|
146
|
+
'pattern.usernameOrEmail': 'ユーザー名またはメールアドレスである必要があります',
|
|
147
|
+
'pattern.httpOrHttps': 'http または https で始まるURLである必要があります',
|
|
105
148
|
|
|
106
|
-
// oneOf
|
|
149
|
+
// oneOf
|
|
107
150
|
oneOf: '{{#label}}は次のいずれかの型に一致する必要があります',
|
|
108
151
|
'oneOf.invalid': '{{#label}}の値は許可された型のいずれとも一致しません',
|
|
109
152
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
'VALIDATE_MUST_BE_FUNCTION': 'validateは関数である必要があります'
|
|
117
|
-
};
|
|
153
|
+
// Error fallback
|
|
154
|
+
UNKNOWN_ERROR: '不明な検証エラー',
|
|
155
|
+
CUSTOM_VALIDATION_FAILED: '検証に失敗しました',
|
|
156
|
+
ASYNC_VALIDATION_NOT_SUPPORTED: '同期validate()では非同期検証はサポートされていません',
|
|
157
|
+
VALIDATE_MUST_BE_FUNCTION: 'validateは関数である必要があります',
|
|
158
|
+
}
|
|
118
159
|
|
|
160
|
+
export default jaJP
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocaleKey — union type of all locale message keys (118 keys)
|
|
3
|
+
*
|
|
4
|
+
* TypeScript compile-time completeness enforcement: every locale file must implement
|
|
5
|
+
* the LocaleMessages interface; adding a new key causes a compiler error in all locales.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ─── Base message value type ──────────────────────────────────────────────────
|
|
9
|
+
/** A message is either a plain string or an object with code (v1.1.5+ format) */
|
|
10
|
+
export type LocaleMessage = string | { code: string | number; message: string }
|
|
11
|
+
|
|
12
|
+
// ─── Key union (for precise key inference in getMessage) ─────────────────────
|
|
13
|
+
export type LocaleKey =
|
|
14
|
+
// Generic
|
|
15
|
+
| 'required'
|
|
16
|
+
| 'type'
|
|
17
|
+
| 'min'
|
|
18
|
+
| 'max'
|
|
19
|
+
| 'length'
|
|
20
|
+
| 'pattern'
|
|
21
|
+
| 'enum'
|
|
22
|
+
| 'custom'
|
|
23
|
+
| 'circular'
|
|
24
|
+
| 'max-depth'
|
|
25
|
+
| 'exception'
|
|
26
|
+
// Conditional
|
|
27
|
+
| 'conditional.underAge'
|
|
28
|
+
| 'conditional.blocked'
|
|
29
|
+
| 'conditional.notAllowed'
|
|
30
|
+
// I18nError — generic
|
|
31
|
+
| 'error.notFound'
|
|
32
|
+
| 'error.forbidden'
|
|
33
|
+
| 'error.unauthorized'
|
|
34
|
+
| 'error.invalid'
|
|
35
|
+
| 'error.duplicate'
|
|
36
|
+
| 'error.conflict'
|
|
37
|
+
// Account
|
|
38
|
+
| 'account.notFound'
|
|
39
|
+
| 'account.inactive'
|
|
40
|
+
| 'account.banned'
|
|
41
|
+
| 'account.insufficientBalance'
|
|
42
|
+
| 'account.insufficientCredits'
|
|
43
|
+
// User
|
|
44
|
+
| 'user.notFound'
|
|
45
|
+
| 'user.notVerified'
|
|
46
|
+
| 'user.noPermission'
|
|
47
|
+
// Order
|
|
48
|
+
| 'order.notPaid'
|
|
49
|
+
| 'order.paymentMissing'
|
|
50
|
+
| 'order.addressMissing'
|
|
51
|
+
// Format
|
|
52
|
+
| 'format.email'
|
|
53
|
+
| 'format.url'
|
|
54
|
+
| 'format.uuid'
|
|
55
|
+
| 'format.date'
|
|
56
|
+
| 'format.datetime'
|
|
57
|
+
| 'format.time'
|
|
58
|
+
| 'format.ipv4'
|
|
59
|
+
| 'format.ipv6'
|
|
60
|
+
| 'format.binary'
|
|
61
|
+
// String
|
|
62
|
+
| 'string.hostname'
|
|
63
|
+
| 'string.pattern'
|
|
64
|
+
| 'string.enum'
|
|
65
|
+
| 'string.length'
|
|
66
|
+
| 'string.alphanum'
|
|
67
|
+
| 'string.trim'
|
|
68
|
+
| 'string.lowercase'
|
|
69
|
+
| 'string.uppercase'
|
|
70
|
+
// Number
|
|
71
|
+
| 'number.base'
|
|
72
|
+
| 'number.min'
|
|
73
|
+
| 'number.max'
|
|
74
|
+
| 'number.integer'
|
|
75
|
+
| 'number.positive'
|
|
76
|
+
| 'number.negative'
|
|
77
|
+
| 'number.precision'
|
|
78
|
+
| 'number.port'
|
|
79
|
+
// Boolean
|
|
80
|
+
| 'boolean.base'
|
|
81
|
+
// Object
|
|
82
|
+
| 'object.base'
|
|
83
|
+
| 'object.min'
|
|
84
|
+
| 'object.max'
|
|
85
|
+
| 'object.unknown'
|
|
86
|
+
| 'object.missing'
|
|
87
|
+
| 'object.schema'
|
|
88
|
+
| 'additionalProperties'
|
|
89
|
+
// Array
|
|
90
|
+
| 'array.base'
|
|
91
|
+
| 'array.min'
|
|
92
|
+
| 'array.max'
|
|
93
|
+
| 'array.length'
|
|
94
|
+
| 'array.unique'
|
|
95
|
+
| 'array.sparse'
|
|
96
|
+
| 'array.includesRequired'
|
|
97
|
+
// Date
|
|
98
|
+
| 'date.base'
|
|
99
|
+
| 'date.min'
|
|
100
|
+
| 'date.max'
|
|
101
|
+
| 'date.format'
|
|
102
|
+
| 'date.greater'
|
|
103
|
+
| 'date.less'
|
|
104
|
+
// Any
|
|
105
|
+
| 'any.required'
|
|
106
|
+
| 'any.invalid'
|
|
107
|
+
| 'any.only'
|
|
108
|
+
| 'any.unknown'
|
|
109
|
+
// Patterns — formats
|
|
110
|
+
| 'pattern.phone'
|
|
111
|
+
| 'pattern.phone.international'
|
|
112
|
+
| 'pattern.idCard'
|
|
113
|
+
| 'pattern.creditCard'
|
|
114
|
+
| 'pattern.creditCard.visa'
|
|
115
|
+
| 'pattern.creditCard.mastercard'
|
|
116
|
+
| 'pattern.creditCard.amex'
|
|
117
|
+
| 'pattern.creditCard.discover'
|
|
118
|
+
| 'pattern.creditCard.jcb'
|
|
119
|
+
| 'pattern.creditCard.unionpay'
|
|
120
|
+
| 'pattern.licensePlate'
|
|
121
|
+
| 'pattern.postalCode'
|
|
122
|
+
| 'pattern.passport'
|
|
123
|
+
| 'pattern.objectId'
|
|
124
|
+
| 'pattern.hexColor'
|
|
125
|
+
| 'pattern.macAddress'
|
|
126
|
+
| 'pattern.cron'
|
|
127
|
+
| 'pattern.slug'
|
|
128
|
+
| 'pattern.domain'
|
|
129
|
+
| 'pattern.ip'
|
|
130
|
+
| 'pattern.base64'
|
|
131
|
+
| 'pattern.jwt'
|
|
132
|
+
| 'pattern.json'
|
|
133
|
+
// Patterns — username & password
|
|
134
|
+
| 'pattern.username'
|
|
135
|
+
| 'pattern.password.weak'
|
|
136
|
+
| 'pattern.password.medium'
|
|
137
|
+
| 'pattern.password.strong'
|
|
138
|
+
| 'pattern.password.veryStrong'
|
|
139
|
+
// Patterns — union
|
|
140
|
+
| 'pattern.emailOrPhone'
|
|
141
|
+
| 'pattern.usernameOrEmail'
|
|
142
|
+
| 'pattern.httpOrHttps'
|
|
143
|
+
// oneOf
|
|
144
|
+
| 'oneOf'
|
|
145
|
+
| 'oneOf.invalid'
|
|
146
|
+
// Error fallback
|
|
147
|
+
| 'UNKNOWN_ERROR'
|
|
148
|
+
| 'CUSTOM_VALIDATION_FAILED'
|
|
149
|
+
| 'ASYNC_VALIDATION_NOT_SUPPORTED'
|
|
150
|
+
| 'VALIDATE_MUST_BE_FUNCTION'
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Complete locale interface — every locale file must implement this (Record<LocaleKey, LocaleMessage>).
|
|
154
|
+
* TypeScript compile-time guarantee: no key can be missing.
|
|
155
|
+
*/
|
|
156
|
+
export type LocaleMessages = Record<LocaleKey, LocaleMessage>
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
import type { LocaleMessages } from './types.js'
|
|
2
|
+
|
|
3
|
+
const zhCN: LocaleMessages = {
|
|
2
4
|
// Generic
|
|
3
5
|
required: '{{#label}}不能为空',
|
|
4
6
|
type: '{{#label}}应该是 {{#expected}} 类型',
|
|
@@ -12,13 +14,12 @@ module.exports = {
|
|
|
12
14
|
'max-depth': '超过最大递归深度 ({{#depth}}) at {{#label}}',
|
|
13
15
|
exception: '{{#label}}验证异常: {{#message}}',
|
|
14
16
|
|
|
15
|
-
// Conditional
|
|
17
|
+
// Conditional
|
|
16
18
|
'conditional.underAge': '未成年用户不能注册',
|
|
17
19
|
'conditional.blocked': '账号已被封禁',
|
|
18
20
|
'conditional.notAllowed': '不允许注册',
|
|
19
21
|
|
|
20
|
-
// I18nError
|
|
21
|
-
// I18nError - 通用错误消息 (v1.1.1)
|
|
22
|
+
// I18nError — generic
|
|
22
23
|
'error.notFound': '找不到{{#resource}}',
|
|
23
24
|
'error.forbidden': '没有权限访问{{#resource}}',
|
|
24
25
|
'error.unauthorized': '未授权,请先登录',
|
|
@@ -26,34 +27,27 @@ module.exports = {
|
|
|
26
27
|
'error.duplicate': '{{#resource}}已存在',
|
|
27
28
|
'error.conflict': '操作冲突: {{#reason}}',
|
|
28
29
|
|
|
29
|
-
//
|
|
30
|
-
|
|
31
|
-
'account.notFound': {
|
|
32
|
-
code: 'ACCOUNT_NOT_FOUND',
|
|
33
|
-
message: '账户不存在'
|
|
34
|
-
},
|
|
30
|
+
// Account
|
|
31
|
+
'account.notFound': { code: 'ACCOUNT_NOT_FOUND', message: '账户不存在' },
|
|
35
32
|
'account.inactive': '账户未激活',
|
|
36
33
|
'account.banned': '账户已被封禁',
|
|
37
34
|
'account.insufficientBalance': {
|
|
38
35
|
code: 'INSUFFICIENT_BALANCE',
|
|
39
|
-
message: '余额不足,当前余额{{#balance}},需要{{#required}}'
|
|
36
|
+
message: '余额不足,当前余额{{#balance}},需要{{#required}}',
|
|
40
37
|
},
|
|
41
38
|
'account.insufficientCredits': '积分不足,当前积分{{#credits}},需要{{#required}}',
|
|
42
39
|
|
|
43
|
-
//
|
|
40
|
+
// User
|
|
44
41
|
'user.notFound': '用户不存在',
|
|
45
42
|
'user.notVerified': '用户未验证',
|
|
46
43
|
'user.noPermission': '没有管理员权限',
|
|
47
44
|
|
|
48
|
-
//
|
|
49
|
-
'order.notPaid': {
|
|
50
|
-
code: 'ORDER_NOT_PAID',
|
|
51
|
-
message: '订单未支付'
|
|
52
|
-
},
|
|
45
|
+
// Order
|
|
46
|
+
'order.notPaid': { code: 'ORDER_NOT_PAID', message: '订单未支付' },
|
|
53
47
|
'order.paymentMissing': '缺少支付信息',
|
|
54
48
|
'order.addressMissing': '缺少收货地址',
|
|
55
49
|
|
|
56
|
-
//
|
|
50
|
+
// Format
|
|
57
51
|
'format.email': '{{#label}}必须是有效的邮箱地址',
|
|
58
52
|
'format.url': '{{#label}}必须是有效的URL地址',
|
|
59
53
|
'format.uuid': '{{#label}}必须是有效的UUID',
|
|
@@ -68,7 +62,6 @@ module.exports = {
|
|
|
68
62
|
'string.hostname': '{{#label}}必须是有效的主机名',
|
|
69
63
|
'string.pattern': '{{#label}}格式不符合要求',
|
|
70
64
|
'string.enum': '{{#label}}必须是以下值之一: {{#valids}}',
|
|
71
|
-
// v1.0.2新增
|
|
72
65
|
'string.length': '{{#label}}长度必须是{{#limit}}个字符',
|
|
73
66
|
'string.alphanum': '{{#label}}只能包含字母和数字',
|
|
74
67
|
'string.trim': '{{#label}}不能包含前后空格',
|
|
@@ -82,7 +75,6 @@ module.exports = {
|
|
|
82
75
|
'number.integer': '{{#label}}必须是整数',
|
|
83
76
|
'number.positive': '{{#label}}必须是正数',
|
|
84
77
|
'number.negative': '{{#label}}必须是负数',
|
|
85
|
-
// v1.0.2新增
|
|
86
78
|
'number.precision': '{{#label}}小数位数不能超过{{#limit}}位',
|
|
87
79
|
'number.port': '{{#label}}必须是有效的端口号(1-65535)',
|
|
88
80
|
|
|
@@ -94,10 +86,8 @@ module.exports = {
|
|
|
94
86
|
'object.min': '{{#label}}至少需要{{#limit}}个属性',
|
|
95
87
|
'object.max': '{{#label}}最多只能有{{#limit}}个属性',
|
|
96
88
|
'object.unknown': '{{#label}}包含未知属性: {{#key}}',
|
|
97
|
-
// v1.0.2新增
|
|
98
89
|
'object.missing': '{{#label}}缺少必需属性',
|
|
99
90
|
'object.schema': '{{#label}}包含额外属性',
|
|
100
|
-
// v1.1.6新增 - additionalProperties
|
|
101
91
|
'additionalProperties': '{{#label}}不允许有额外属性: {{#key}}',
|
|
102
92
|
|
|
103
93
|
// Array
|
|
@@ -106,7 +96,6 @@ module.exports = {
|
|
|
106
96
|
'array.max': '{{#label}}最多只能有{{#limit}}个元素',
|
|
107
97
|
'array.length': '{{#label}}必须有{{#limit}}个元素',
|
|
108
98
|
'array.unique': '{{#label}}不能包含重复元素',
|
|
109
|
-
// v1.0.2新增
|
|
110
99
|
'array.sparse': '{{#label}}不能是稀疏数组',
|
|
111
100
|
'array.includesRequired': '{{#label}}必须包含指定元素',
|
|
112
101
|
|
|
@@ -114,7 +103,6 @@ module.exports = {
|
|
|
114
103
|
'date.base': '{{#label}}必须是有效的日期',
|
|
115
104
|
'date.min': '{{#label}}不能早于{{#limit}}',
|
|
116
105
|
'date.max': '{{#label}}不能晚于{{#limit}}',
|
|
117
|
-
// v1.0.2新增
|
|
118
106
|
'date.format': '{{#label}}日期格式不正确',
|
|
119
107
|
'date.greater': '{{#label}}必须晚于{{#limit}}',
|
|
120
108
|
'date.less': '{{#label}}必须早于{{#limit}}',
|
|
@@ -125,7 +113,7 @@ module.exports = {
|
|
|
125
113
|
'any.only': '{{#label}}必须匹配{{#valids}}',
|
|
126
114
|
'any.unknown': '不允许字段{{#key}}',
|
|
127
115
|
|
|
128
|
-
// Patterns
|
|
116
|
+
// Patterns
|
|
129
117
|
'pattern.phone': '请输入有效的手机号',
|
|
130
118
|
'pattern.phone.international': '请输入有效的国际手机号',
|
|
131
119
|
'pattern.idCard': '请输入有效的身份证号码',
|
|
@@ -144,34 +132,29 @@ module.exports = {
|
|
|
144
132
|
'pattern.macAddress': '无效的 MAC 地址',
|
|
145
133
|
'pattern.cron': '无效的 Cron 表达式',
|
|
146
134
|
'pattern.slug': 'URL别名只能包含小写字母、数字和连字符',
|
|
147
|
-
// v1.0.2新增
|
|
148
135
|
'pattern.domain': '{{#label}}必须是有效的域名',
|
|
149
136
|
'pattern.ip': '{{#label}}必须是有效的IP地址',
|
|
150
137
|
'pattern.base64': '{{#label}}必须是有效的Base64编码',
|
|
151
138
|
'pattern.jwt': '{{#label}}必须是有效的JWT令牌',
|
|
152
139
|
'pattern.json': '{{#label}}必须是有效的JSON字符串',
|
|
153
|
-
|
|
154
|
-
// Username & Password
|
|
155
140
|
'pattern.username': '用户名必须以字母开头,只能包含字母、数字和下划线',
|
|
156
141
|
'pattern.password.weak': '密码至少6位',
|
|
157
142
|
'pattern.password.medium': '密码至少8位,需包含字母和数字',
|
|
158
143
|
'pattern.password.strong': '密码至少8位,需包含大小写字母和数字',
|
|
159
144
|
'pattern.password.veryStrong': '密码至少10位,需包含大小写字母、数字和特殊字符',
|
|
160
|
-
|
|
161
|
-
// Union Type (联合类型)
|
|
162
145
|
'pattern.emailOrPhone': '必须是邮箱或手机号',
|
|
163
146
|
'pattern.usernameOrEmail': '必须是用户名或邮箱',
|
|
164
147
|
'pattern.httpOrHttps': '必须是 http 或 https 开头的 URL',
|
|
165
148
|
|
|
166
|
-
// oneOf
|
|
149
|
+
// oneOf
|
|
167
150
|
oneOf: '{{#label}}必须匹配以下类型之一',
|
|
168
151
|
'oneOf.invalid': '{{#label}}的值不匹配任何允许的类型',
|
|
169
152
|
|
|
170
|
-
//
|
|
171
|
-
|
|
153
|
+
// Error fallback
|
|
154
|
+
UNKNOWN_ERROR: '未知的验证错误',
|
|
155
|
+
CUSTOM_VALIDATION_FAILED: '自定义验证失败',
|
|
156
|
+
ASYNC_VALIDATION_NOT_SUPPORTED: '同步验证不支持异步操作',
|
|
157
|
+
VALIDATE_MUST_BE_FUNCTION: 'validate 必须是一个函数',
|
|
158
|
+
}
|
|
172
159
|
|
|
173
|
-
|
|
174
|
-
'CUSTOM_VALIDATION_FAILED': '自定义验证失败',
|
|
175
|
-
'ASYNC_VALIDATION_NOT_SUPPORTED': '同步验证不支持异步操作',
|
|
176
|
-
'VALIDATE_MUST_BE_FUNCTION': 'validate 必须是一个函数'
|
|
177
|
-
};
|
|
160
|
+
export default zhCN
|
|
@@ -0,0 +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
|
+
}
|