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/lib/errors/I18nError.js
DELETED
|
@@ -1,328 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* I18nError - 多语言错误工具类
|
|
3
|
-
*
|
|
4
|
-
* 提供统一的多语言错误抛出机制,支持:
|
|
5
|
-
* - 多语言 key 自动翻译
|
|
6
|
-
* - 参数插值(如 {{field}}, {{limit}})
|
|
7
|
-
* - 自定义错误代码
|
|
8
|
-
* - Express/Koa 集成
|
|
9
|
-
*
|
|
10
|
-
* @module lib/errors/I18nError
|
|
11
|
-
* @version 1.1.1
|
|
12
|
-
*
|
|
13
|
-
* @example 基础用法
|
|
14
|
-
* const { I18nError } = require('schema-dsl');
|
|
15
|
-
*
|
|
16
|
-
* // 抛出多语言错误
|
|
17
|
-
* throw I18nError.create('error.notFound', { resource: '账户' });
|
|
18
|
-
* // 中文: "找不到账户"
|
|
19
|
-
* // 英文: "Account not found"
|
|
20
|
-
*
|
|
21
|
-
* @example 业务代码中使用
|
|
22
|
-
* function getAccount(id) {
|
|
23
|
-
* const account = db.findAccount(id);
|
|
24
|
-
* if (!account) {
|
|
25
|
-
* throw I18nError.create('account.notFound', { accountId: id });
|
|
26
|
-
* }
|
|
27
|
-
* if (account.balance < 100) {
|
|
28
|
-
* throw I18nError.create('account.insufficientBalance', {
|
|
29
|
-
* balance: account.balance,
|
|
30
|
-
* required: 100
|
|
31
|
-
* });
|
|
32
|
-
* }
|
|
33
|
-
* return account;
|
|
34
|
-
* }
|
|
35
|
-
*
|
|
36
|
-
* @example Express 中间件
|
|
37
|
-
* app.use((error, req, res, next) => {
|
|
38
|
-
* if (error instanceof I18nError) {
|
|
39
|
-
* return res.status(error.statusCode).json(error.toJSON());
|
|
40
|
-
* }
|
|
41
|
-
* next(error);
|
|
42
|
-
* });
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
const Locale = require('../core/Locale');
|
|
46
|
-
const MessageTemplate = require('../core/MessageTemplate');
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* 智能参数识别工具函数
|
|
50
|
-
* @private
|
|
51
|
-
* @param {Object|string} paramsOrLocale - 参数对象 或 语言代码
|
|
52
|
-
* @param {number} statusCode - HTTP 状态码
|
|
53
|
-
* @param {string} locale - 语言环境
|
|
54
|
-
* @returns {Object} 规范化后的参数 { params, statusCode, locale }
|
|
55
|
-
*/
|
|
56
|
-
function normalizeParams(paramsOrLocale, statusCode, locale) {
|
|
57
|
-
let params = {};
|
|
58
|
-
let actualStatusCode = 400;
|
|
59
|
-
let actualLocale = null;
|
|
60
|
-
|
|
61
|
-
if (typeof paramsOrLocale === 'string') {
|
|
62
|
-
// 情况1:第2个参数是字符串 → 视为语言
|
|
63
|
-
actualLocale = paramsOrLocale;
|
|
64
|
-
actualStatusCode = typeof statusCode === 'number' ? statusCode : 400;
|
|
65
|
-
} else if (paramsOrLocale && typeof paramsOrLocale === 'object' && !Array.isArray(paramsOrLocale)) {
|
|
66
|
-
// 情况2:第2个参数是对象(非数组)→ 视为参数对象
|
|
67
|
-
params = paramsOrLocale;
|
|
68
|
-
actualStatusCode = typeof statusCode === 'number' ? statusCode : 400;
|
|
69
|
-
actualLocale = locale;
|
|
70
|
-
} else {
|
|
71
|
-
// 情况3:第2个参数是 null/undefined/数组 → 使用默认值和后续参数
|
|
72
|
-
actualStatusCode = typeof statusCode === 'number' ? statusCode : 400;
|
|
73
|
-
actualLocale = locale;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return { params, statusCode: actualStatusCode, locale: actualLocale };
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* 多语言错误类
|
|
81
|
-
*
|
|
82
|
-
* @class I18nError
|
|
83
|
-
* @extends Error
|
|
84
|
-
*
|
|
85
|
-
* @property {string} name - 错误名称(固定为 'I18nError')
|
|
86
|
-
* @property {string} message - 错误消息(已翻译)
|
|
87
|
-
* @property {string} code - 错误代码(多语言 key)
|
|
88
|
-
* @property {Object} params - 错误参数(用于插值)
|
|
89
|
-
* @property {number} statusCode - HTTP 状态码(默认 400)
|
|
90
|
-
* @property {string} locale - 使用的语言环境
|
|
91
|
-
*/
|
|
92
|
-
class I18nError extends Error {
|
|
93
|
-
/**
|
|
94
|
-
* 构造函数
|
|
95
|
-
* @param {string} key - 错误代码(多语言 key)
|
|
96
|
-
* @param {Object} params - 错误参数(用于插值)
|
|
97
|
-
* @param {number} statusCode - HTTP 状态码(默认 400)
|
|
98
|
-
* @param {string} locale - 语言环境(默认使用当前语言)
|
|
99
|
-
* @version 1.1.5 - 支持对象格式配置
|
|
100
|
-
*/
|
|
101
|
-
constructor(key, params = {}, statusCode = 400, locale = null) {
|
|
102
|
-
// 获取语言环境
|
|
103
|
-
const actualLocale = locale || Locale.getLocale();
|
|
104
|
-
|
|
105
|
-
// 获取消息配置(v1.1.5: 返回对象 { code, message })
|
|
106
|
-
const messageConfig = Locale.getMessage(key, {}, actualLocale);
|
|
107
|
-
|
|
108
|
-
// 判断返回类型(向后兼容)
|
|
109
|
-
let errorCode, template;
|
|
110
|
-
if (typeof messageConfig === 'object' && messageConfig.code && messageConfig.message) {
|
|
111
|
-
// 对象格式:提取 code 和 message
|
|
112
|
-
errorCode = messageConfig.code;
|
|
113
|
-
template = messageConfig.message;
|
|
114
|
-
} else if (typeof messageConfig === 'string') {
|
|
115
|
-
// 字符串格式(向后兼容)
|
|
116
|
-
errorCode = key;
|
|
117
|
-
template = messageConfig;
|
|
118
|
-
} else {
|
|
119
|
-
// 降级处理
|
|
120
|
-
errorCode = key;
|
|
121
|
-
template = key;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// 使用 MessageTemplate 进行参数插值
|
|
125
|
-
const messageTemplate = new MessageTemplate(template);
|
|
126
|
-
const message = messageTemplate.render(params || {});
|
|
127
|
-
|
|
128
|
-
super(message);
|
|
129
|
-
|
|
130
|
-
this.name = 'I18nError';
|
|
131
|
-
this.originalKey = key; // v1.1.5 新增:保留原始 key
|
|
132
|
-
this.code = errorCode; // v1.1.5 修改:从对象提取或使用 key
|
|
133
|
-
this.params = params || {};
|
|
134
|
-
this.statusCode = statusCode;
|
|
135
|
-
this.locale = actualLocale;
|
|
136
|
-
|
|
137
|
-
// 保持堆栈跟踪
|
|
138
|
-
if (Error.captureStackTrace) {
|
|
139
|
-
Error.captureStackTrace(this, I18nError);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* 静态工厂方法 - 创建并抛出错误
|
|
145
|
-
*
|
|
146
|
-
* @param {string} code - 错误代码(多语言 key)
|
|
147
|
-
* @param {Object|string} paramsOrLocale - 错误参数对象 或 语言代码(智能识别)
|
|
148
|
-
* @param {number} statusCode - HTTP 状态码
|
|
149
|
-
* @param {string} locale - 语言环境(仅当第2个参数是对象时有效)
|
|
150
|
-
* @returns {I18nError} 错误实例
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* // 方式1:简化语法(直接传语言)
|
|
154
|
-
* const error = I18nError.create('account.notFound', 'zh-CN');
|
|
155
|
-
* const error = I18nError.create('account.notFound', 'en-US', 404);
|
|
156
|
-
*
|
|
157
|
-
* @example
|
|
158
|
-
* // 方式2:标准语法(带参数对象)
|
|
159
|
-
* const error = I18nError.create('account.notFound', { userId: '123' }, 404, 'zh-CN');
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* // 方式3:省略参数(使用全局语言)
|
|
163
|
-
* const error = I18nError.create('account.notFound');
|
|
164
|
-
*
|
|
165
|
-
* @example
|
|
166
|
-
* // 直接抛出
|
|
167
|
-
* throw I18nError.create('error.notFound', { resource: '用户' });
|
|
168
|
-
*
|
|
169
|
-
* @example
|
|
170
|
-
* // 运行时指定语言
|
|
171
|
-
* const error = I18nError.create('error.notFound', {}, 404, 'en-US');
|
|
172
|
-
*/
|
|
173
|
-
static create(code, paramsOrLocale, statusCode, locale) {
|
|
174
|
-
const { params, statusCode: actualStatusCode, locale: actualLocale } = normalizeParams(paramsOrLocale, statusCode, locale);
|
|
175
|
-
return new I18nError(code, params, actualStatusCode, actualLocale);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* 静态工厂方法 - 快速抛出错误
|
|
180
|
-
*
|
|
181
|
-
* @param {string} code - 错误代码(多语言 key)
|
|
182
|
-
* @param {Object|string} paramsOrLocale - 错误参数对象 或 语言代码(智能识别)
|
|
183
|
-
* @param {number} statusCode - HTTP 状态码
|
|
184
|
-
* @param {string} locale - 语言环境(仅当第2个参数是对象时有效)
|
|
185
|
-
* @throws {I18nError} 直接抛出错误
|
|
186
|
-
*
|
|
187
|
-
* @example
|
|
188
|
-
* // 方式1:简化语法(直接传语言)
|
|
189
|
-
* I18nError.throw('account.notFound', 'zh-CN');
|
|
190
|
-
* I18nError.throw('account.notFound', 'en-US', 404);
|
|
191
|
-
*
|
|
192
|
-
* @example
|
|
193
|
-
* // 方式2:标准语法(带参数对象)
|
|
194
|
-
* I18nError.throw('account.notFound', { userId: '123' }, 404, 'zh-CN');
|
|
195
|
-
*
|
|
196
|
-
* @example
|
|
197
|
-
* // 方式3:省略参数(使用全局语言)
|
|
198
|
-
* I18nError.throw('account.notFound');
|
|
199
|
-
*
|
|
200
|
-
* @example
|
|
201
|
-
* I18nError.throw('error.notFound', { resource: '用户' });
|
|
202
|
-
* // 等同于:throw I18nError.create('error.notFound', { resource: '用户' });
|
|
203
|
-
*
|
|
204
|
-
* @example
|
|
205
|
-
* // 运行时指定语言
|
|
206
|
-
* I18nError.throw('error.notFound', {}, 404, 'en-US');
|
|
207
|
-
*/
|
|
208
|
-
static throw(code, paramsOrLocale, statusCode, locale) {
|
|
209
|
-
const { params, statusCode: actualStatusCode, locale: actualLocale } = normalizeParams(paramsOrLocale, statusCode, locale);
|
|
210
|
-
throw new I18nError(code, params, actualStatusCode, actualLocale);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* 断言方法 - 条件不满足时抛错
|
|
215
|
-
*
|
|
216
|
-
* @param {boolean} condition - 条件表达式
|
|
217
|
-
* @param {string} code - 错误代码(多语言 key)
|
|
218
|
-
* @param {Object|string} paramsOrLocale - 错误参数对象 或 语言代码(智能识别)
|
|
219
|
-
* @param {number} statusCode - HTTP 状态码
|
|
220
|
-
* @param {string} locale - 语言环境(仅当第3个参数是对象时有效)
|
|
221
|
-
* @throws {I18nError} 条件为 false 时抛出错误
|
|
222
|
-
*
|
|
223
|
-
* @example
|
|
224
|
-
* // 方式1:简化语法(直接传语言)
|
|
225
|
-
* I18nError.assert(account, 'account.notFound', 'zh-CN');
|
|
226
|
-
* I18nError.assert(account, 'account.notFound', 'en-US', 404);
|
|
227
|
-
*
|
|
228
|
-
* @example
|
|
229
|
-
* // 方式2:标准语法(带参数对象)
|
|
230
|
-
* I18nError.assert(account, 'account.notFound', { accountId: id }, 404, 'zh-CN');
|
|
231
|
-
* // 等同于:if (!account) throw I18nError.create('account.notFound', { accountId: id });
|
|
232
|
-
*
|
|
233
|
-
* @example
|
|
234
|
-
* I18nError.assert(
|
|
235
|
-
* account.balance >= 100,
|
|
236
|
-
* 'account.insufficientBalance',
|
|
237
|
-
* { balance: account.balance, required: 100 }
|
|
238
|
-
* );
|
|
239
|
-
*
|
|
240
|
-
* @example
|
|
241
|
-
* // 运行时指定语言
|
|
242
|
-
* I18nError.assert(account, 'account.notFound', {}, 404, 'en-US');
|
|
243
|
-
*/
|
|
244
|
-
static assert(condition, code, paramsOrLocale, statusCode, locale) {
|
|
245
|
-
if (!condition) {
|
|
246
|
-
const { params, statusCode: actualStatusCode, locale: actualLocale } = normalizeParams(paramsOrLocale, statusCode, locale);
|
|
247
|
-
throw new I18nError(code, params, actualStatusCode, actualLocale);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* 检查错误是否为指定代码
|
|
253
|
-
*
|
|
254
|
-
* @param {string} codeOrKey - 错误代码或原始 key
|
|
255
|
-
* @returns {boolean} 是否匹配
|
|
256
|
-
*
|
|
257
|
-
* @example
|
|
258
|
-
* try {
|
|
259
|
-
* // ...
|
|
260
|
-
* } catch (error) {
|
|
261
|
-
* if (error instanceof I18nError && error.is('account.notFound')) {
|
|
262
|
-
* // 处理账户不存在的情况
|
|
263
|
-
* }
|
|
264
|
-
*
|
|
265
|
-
* // v1.1.5: 也可以用 code 判断
|
|
266
|
-
* if (error instanceof I18nError && error.is('ACCOUNT_NOT_FOUND')) {
|
|
267
|
-
* // 也能匹配
|
|
268
|
-
* }
|
|
269
|
-
* }
|
|
270
|
-
*/
|
|
271
|
-
is(codeOrKey) {
|
|
272
|
-
// v1.1.5: 同时比较 code 和 originalKey(向后兼容)
|
|
273
|
-
return this.code === codeOrKey || this.originalKey === codeOrKey;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/**
|
|
277
|
-
* 转换为 JSON 格式(用于 API 响应)
|
|
278
|
-
*
|
|
279
|
-
* @returns {Object} JSON 对象
|
|
280
|
-
* @returns {string} return.error - 错误名称
|
|
281
|
-
* @returns {string} return.originalKey - 原始 key(v1.1.5 新增)
|
|
282
|
-
* @returns {string} return.code - 错误代码
|
|
283
|
-
* @returns {string} return.message - 错误消息(已翻译)
|
|
284
|
-
* @returns {Object} return.params - 错误参数
|
|
285
|
-
* @returns {number} return.statusCode - 状态码
|
|
286
|
-
* @returns {string} return.locale - 语言环境
|
|
287
|
-
*
|
|
288
|
-
* @example
|
|
289
|
-
* const json = error.toJSON();
|
|
290
|
-
* res.status(error.statusCode).json(json);
|
|
291
|
-
* // {
|
|
292
|
-
* // error: 'I18nError',
|
|
293
|
-
* // originalKey: 'account.notFound', // v1.1.5 新增
|
|
294
|
-
* // code: 'ACCOUNT_NOT_FOUND',
|
|
295
|
-
* // message: '找不到账户',
|
|
296
|
-
* // params: { accountId: '123' },
|
|
297
|
-
* // statusCode: 404,
|
|
298
|
-
* // locale: 'zh-CN'
|
|
299
|
-
* // }
|
|
300
|
-
*/
|
|
301
|
-
toJSON() {
|
|
302
|
-
return {
|
|
303
|
-
error: this.name,
|
|
304
|
-
originalKey: this.originalKey, // v1.1.5 新增
|
|
305
|
-
code: this.code,
|
|
306
|
-
message: this.message,
|
|
307
|
-
params: this.params,
|
|
308
|
-
statusCode: this.statusCode,
|
|
309
|
-
locale: this.locale
|
|
310
|
-
};
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* 转换为字符串
|
|
315
|
-
*
|
|
316
|
-
* @returns {string} 格式化的错误信息
|
|
317
|
-
*
|
|
318
|
-
* @example
|
|
319
|
-
* console.log(error.toString());
|
|
320
|
-
* // "I18nError [account.notFound]: 找不到账户"
|
|
321
|
-
*/
|
|
322
|
-
toString() {
|
|
323
|
-
return `${this.name} [${this.code}]: ${this.message}`;
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
module.exports = I18nError;
|
|
328
|
-
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ValidationError - 验证错误类
|
|
3
|
-
*
|
|
4
|
-
* 用于 validateAsync() 方法,验证失败时自动抛出
|
|
5
|
-
*
|
|
6
|
-
* @module lib/errors/ValidationError
|
|
7
|
-
* @version 1.0.3
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* 验证错误类
|
|
12
|
-
*
|
|
13
|
-
* @class ValidationError
|
|
14
|
-
* @extends Error
|
|
15
|
-
*
|
|
16
|
-
* @property {string} name - 错误名称(固定为 'ValidationError')
|
|
17
|
-
* @property {string} message - 错误消息(所有错误的汇总)
|
|
18
|
-
* @property {Array<Object>} errors - 详细错误列表
|
|
19
|
-
* @property {*} data - 原始验证数据
|
|
20
|
-
* @property {number} statusCode - HTTP 状态码(默认 400)
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* // 创建错误
|
|
24
|
-
* const errors = [
|
|
25
|
-
* { path: '/name', message: '字段必填', keyword: 'required' }
|
|
26
|
-
* ];
|
|
27
|
-
* const error = new ValidationError(errors, inputData);
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* // 在 Express 中使用
|
|
31
|
-
* app.use((error, req, res, next) => {
|
|
32
|
-
* if (error instanceof ValidationError) {
|
|
33
|
-
* return res.status(error.statusCode).json(error.toJSON());
|
|
34
|
-
* }
|
|
35
|
-
* next(error);
|
|
36
|
-
* });
|
|
37
|
-
*/
|
|
38
|
-
class ValidationError extends Error {
|
|
39
|
-
/**
|
|
40
|
-
* 构造函数
|
|
41
|
-
* @param {Array<Object>} errors - 错误列表
|
|
42
|
-
* @param {string} errors[].path - 字段路径(如 '/name')
|
|
43
|
-
* @param {string} errors[].message - 错误消息
|
|
44
|
-
* @param {string} errors[].keyword - 验证关键字(如 'required', 'format')
|
|
45
|
-
* @param {Object} errors[].params - 错误参数
|
|
46
|
-
* @param {*} data - 原始数据
|
|
47
|
-
*/
|
|
48
|
-
constructor(errors, data) {
|
|
49
|
-
// 生成友好的错误消息
|
|
50
|
-
const messages = errors.map(e => {
|
|
51
|
-
if (e.path) {
|
|
52
|
-
const field = e.path.replace(/^\//, '');
|
|
53
|
-
// 只有字段名非空时才添加前缀
|
|
54
|
-
return field ? `${field}: ${e.message}` : e.message;
|
|
55
|
-
}
|
|
56
|
-
return e.message;
|
|
57
|
-
}).join('; ');
|
|
58
|
-
|
|
59
|
-
// 检查是否所有错误都完全没有 path 属性(而不是空路径)
|
|
60
|
-
const hasNoPath = errors.every(e => e.path === undefined || e.path === null);
|
|
61
|
-
|
|
62
|
-
// 如果都是无 path 属性的错误,使用简单格式;否则使用标准格式
|
|
63
|
-
super(hasNoPath ? `Validation failed - ${messages}` : `Validation failed: ${messages}`);
|
|
64
|
-
|
|
65
|
-
this.name = 'ValidationError';
|
|
66
|
-
this.errors = errors;
|
|
67
|
-
this.data = data;
|
|
68
|
-
this.statusCode = 400;
|
|
69
|
-
|
|
70
|
-
// 保持堆栈跟踪
|
|
71
|
-
if (Error.captureStackTrace) {
|
|
72
|
-
Error.captureStackTrace(this, ValidationError);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* 转换为 JSON 格式(用于 API 响应)
|
|
78
|
-
*
|
|
79
|
-
* @returns {Object} JSON 对象
|
|
80
|
-
* @returns {string} return.error - 错误名称
|
|
81
|
-
* @returns {string} return.message - 错误消息
|
|
82
|
-
* @returns {number} return.statusCode - 状态码
|
|
83
|
-
* @returns {Array<Object>} return.details - 详细错误列表
|
|
84
|
-
*
|
|
85
|
-
* @example
|
|
86
|
-
* const json = error.toJSON();
|
|
87
|
-
* res.status(400).json(json);
|
|
88
|
-
* // {
|
|
89
|
-
* // error: 'ValidationError',
|
|
90
|
-
* // message: 'Validation failed: name: 字段必填',
|
|
91
|
-
* // statusCode: 400,
|
|
92
|
-
* // details: [...]
|
|
93
|
-
* // }
|
|
94
|
-
*/
|
|
95
|
-
toJSON() {
|
|
96
|
-
return {
|
|
97
|
-
error: this.name,
|
|
98
|
-
message: this.message,
|
|
99
|
-
statusCode: this.statusCode,
|
|
100
|
-
details: this.errors.map(e => ({
|
|
101
|
-
field: e.path ? e.path.replace(/^\//, '') : null,
|
|
102
|
-
message: e.message,
|
|
103
|
-
keyword: e.keyword,
|
|
104
|
-
params: e.params
|
|
105
|
-
}))
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* 获取指定字段的错误
|
|
111
|
-
*
|
|
112
|
-
* @param {string} field - 字段名称(如 'name' 或 '/name')
|
|
113
|
-
* @returns {Object|null} 错误对象或 null
|
|
114
|
-
*
|
|
115
|
-
* @example
|
|
116
|
-
* const nameError = error.getFieldError('name');
|
|
117
|
-
* if (nameError) {
|
|
118
|
-
* console.log('姓名错误:', nameError.message);
|
|
119
|
-
* }
|
|
120
|
-
*/
|
|
121
|
-
getFieldError(field) {
|
|
122
|
-
// 规范化字段名(移除前导斜杠)
|
|
123
|
-
const normalizedField = field.replace(/^\//, '');
|
|
124
|
-
|
|
125
|
-
// 查找匹配的错误(支持多种路径格式)
|
|
126
|
-
return this.errors.find(e => {
|
|
127
|
-
if (!e.path) return false;
|
|
128
|
-
const errorField = e.path.replace(/^\//, '');
|
|
129
|
-
return errorField === normalizedField;
|
|
130
|
-
}) || null;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* 获取所有字段的错误映射
|
|
135
|
-
*
|
|
136
|
-
* @returns {Object} 字段错误映射 { fieldName: errorMessage }
|
|
137
|
-
*
|
|
138
|
-
* @example
|
|
139
|
-
* const fieldErrors = error.getFieldErrors();
|
|
140
|
-
* // { name: '字段必填', email: '邮箱格式错误' }
|
|
141
|
-
*/
|
|
142
|
-
getFieldErrors() {
|
|
143
|
-
const result = {};
|
|
144
|
-
this.errors.forEach(e => {
|
|
145
|
-
if (e.path) {
|
|
146
|
-
const field = e.path.replace(/^\//, '');
|
|
147
|
-
if (field) {
|
|
148
|
-
result[field] = e.message;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
return result;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* 检查是否包含指定字段的错误
|
|
157
|
-
*
|
|
158
|
-
* @param {string} field - 字段名称
|
|
159
|
-
* @returns {boolean} 是否包含错误
|
|
160
|
-
*
|
|
161
|
-
* @example
|
|
162
|
-
* if (error.hasFieldError('name')) {
|
|
163
|
-
* console.log('姓名字段有错误');
|
|
164
|
-
* }
|
|
165
|
-
*/
|
|
166
|
-
hasFieldError(field) {
|
|
167
|
-
return this.getFieldError(field) !== null;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* 获取错误数量
|
|
172
|
-
*
|
|
173
|
-
* @returns {number} 错误数量
|
|
174
|
-
*
|
|
175
|
-
* @example
|
|
176
|
-
* console.log(`共 ${error.getErrorCount()} 个错误`);
|
|
177
|
-
*/
|
|
178
|
-
getErrorCount() {
|
|
179
|
-
return this.errors.length;
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
// Support calling without new
|
|
184
|
-
const ValidationErrorProxy = new Proxy(ValidationError, {
|
|
185
|
-
apply: function (target, thisArg, argumentsList) {
|
|
186
|
-
return new target(...argumentsList);
|
|
187
|
-
}
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
module.exports = ValidationErrorProxy;
|
|
191
|
-
|