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/dist/index.js
CHANGED
|
@@ -130,7 +130,16 @@ var init_CacheManager = __esm({
|
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
set options(opts) {
|
|
133
|
-
if (opts.maxSize !== void 0
|
|
133
|
+
if (opts.maxSize !== void 0 && opts.maxSize !== this._maxSize) {
|
|
134
|
+
this._maxSize = opts.maxSize;
|
|
135
|
+
const oldKeys = this._cache.keys();
|
|
136
|
+
const newCache = new MemoryCache({ maxEntries: this._maxSize });
|
|
137
|
+
for (const key of oldKeys) {
|
|
138
|
+
const val = this._cache.get(key);
|
|
139
|
+
if (val !== void 0) newCache.set(key, val);
|
|
140
|
+
}
|
|
141
|
+
this._cache = newCache;
|
|
142
|
+
}
|
|
134
143
|
if (opts.ttl !== void 0) this._ttl = opts.ttl;
|
|
135
144
|
if (opts.enabled !== void 0) this._enabled = opts.enabled;
|
|
136
145
|
if (opts.statsEnabled !== void 0) this._statsEnabled = opts.statsEnabled;
|
|
@@ -3215,7 +3224,7 @@ var init_Validator = __esm({
|
|
|
3215
3224
|
// package.json
|
|
3216
3225
|
var package_default = {
|
|
3217
3226
|
name: "schema-dsl",
|
|
3218
|
-
version: "2.0.
|
|
3227
|
+
version: "2.0.1",
|
|
3219
3228
|
description: "A concise and powerful JSON Schema validation library - DSL syntax + String extensions + convenient validate API",
|
|
3220
3229
|
main: "./dist/index.cjs",
|
|
3221
3230
|
module: "./dist/index.js",
|
|
@@ -3399,6 +3408,7 @@ var JSONSchemaCore = class {
|
|
|
3399
3408
|
init_DslParser();
|
|
3400
3409
|
init_TypeRegistry();
|
|
3401
3410
|
init_patterns();
|
|
3411
|
+
import safeRegex2 from "safe-regex";
|
|
3402
3412
|
var PASSWORD_PATTERNS = {
|
|
3403
3413
|
weak: /.{6,}/,
|
|
3404
3414
|
medium: /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/,
|
|
@@ -3557,7 +3567,15 @@ var DslBuilder = class _DslBuilder {
|
|
|
3557
3567
|
* Add regex validation.
|
|
3558
3568
|
*/
|
|
3559
3569
|
pattern(regex, message) {
|
|
3560
|
-
|
|
3570
|
+
const source = regex instanceof RegExp ? regex.source : regex;
|
|
3571
|
+
if (!safeRegex2(source)) {
|
|
3572
|
+
throw new Error(`[schema-dsl] Unsafe regex pattern rejected (potential ReDoS): ${source}`);
|
|
3573
|
+
}
|
|
3574
|
+
return this._setPattern(source, message);
|
|
3575
|
+
}
|
|
3576
|
+
/** Internal: set pattern without safe-regex check (used by built-in validators with pre-approved patterns). */
|
|
3577
|
+
_setPattern(source, message) {
|
|
3578
|
+
this._baseSchema.pattern = source;
|
|
3561
3579
|
if (message) {
|
|
3562
3580
|
this._customMessages["string.pattern"] = message;
|
|
3563
3581
|
}
|
|
@@ -3719,25 +3737,25 @@ var DslBuilder = class _DslBuilder {
|
|
|
3719
3737
|
domain() {
|
|
3720
3738
|
this._assertStringType("domain");
|
|
3721
3739
|
const cfg = PATTERNS.common.domain;
|
|
3722
|
-
return this.
|
|
3740
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3723
3741
|
}
|
|
3724
3742
|
/** String IP address validation (IPv4 or IPv6). */
|
|
3725
3743
|
ip() {
|
|
3726
3744
|
this._assertStringType("ip");
|
|
3727
3745
|
const cfg = PATTERNS.common.ip;
|
|
3728
|
-
return this.
|
|
3746
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3729
3747
|
}
|
|
3730
3748
|
/** String Base64 encoding validation. */
|
|
3731
3749
|
base64() {
|
|
3732
3750
|
this._assertStringType("base64");
|
|
3733
3751
|
const cfg = PATTERNS.common.base64;
|
|
3734
|
-
return this.
|
|
3752
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3735
3753
|
}
|
|
3736
3754
|
/** String JWT token validation. */
|
|
3737
3755
|
jwt() {
|
|
3738
3756
|
this._assertStringType("jwt");
|
|
3739
3757
|
const cfg = PATTERNS.common.jwt;
|
|
3740
|
-
return this.
|
|
3758
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3741
3759
|
}
|
|
3742
3760
|
// ==================== Identity / Pattern Chain Methods ====================
|
|
3743
3761
|
/** Phone number validation (auto-corrects number → string). */
|
|
@@ -3751,7 +3769,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3751
3769
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country: ${country}`);
|
|
3752
3770
|
if (cfg.min !== void 0 && !this._baseSchema.minLength) this._baseSchema.minLength = cfg.min;
|
|
3753
3771
|
if (cfg.max !== void 0 && !this._baseSchema.maxLength) this._baseSchema.maxLength = cfg.max;
|
|
3754
|
-
return this.
|
|
3772
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3755
3773
|
}
|
|
3756
3774
|
/** phone() alias (BC). */
|
|
3757
3775
|
phoneNumber(country = "cn") {
|
|
@@ -3764,35 +3782,35 @@ var DslBuilder = class _DslBuilder {
|
|
|
3764
3782
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for idCard: ${country}`);
|
|
3765
3783
|
if (cfg.min !== void 0 && !this._baseSchema.minLength) this._baseSchema.minLength = cfg.min;
|
|
3766
3784
|
if (cfg.max !== void 0 && !this._baseSchema.maxLength) this._baseSchema.maxLength = cfg.max;
|
|
3767
|
-
return this.
|
|
3785
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3768
3786
|
}
|
|
3769
3787
|
/** URL slug validation. */
|
|
3770
3788
|
slugChain() {
|
|
3771
|
-
return this.
|
|
3789
|
+
return this._setPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/.source).messages({ pattern: "pattern.slug" });
|
|
3772
3790
|
}
|
|
3773
3791
|
/** Credit card number validation. */
|
|
3774
3792
|
creditCard(type = "visa") {
|
|
3775
3793
|
const cfg = PATTERNS.creditCard[type.toLowerCase()];
|
|
3776
3794
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported credit card type: ${type}`);
|
|
3777
|
-
return this.
|
|
3795
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3778
3796
|
}
|
|
3779
3797
|
/** Vehicle license plate validation. */
|
|
3780
3798
|
licensePlate(country = "cn") {
|
|
3781
3799
|
const cfg = PATTERNS.licensePlate[country.toLowerCase()];
|
|
3782
3800
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for licensePlate: ${country}`);
|
|
3783
|
-
return this.
|
|
3801
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3784
3802
|
}
|
|
3785
3803
|
/** Postal code validation. */
|
|
3786
3804
|
postalCode(country = "cn") {
|
|
3787
3805
|
const cfg = PATTERNS.postalCode[country.toLowerCase()];
|
|
3788
3806
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for postalCode: ${country}`);
|
|
3789
|
-
return this.
|
|
3807
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3790
3808
|
}
|
|
3791
3809
|
/** Passport number validation. */
|
|
3792
3810
|
passport(country = "cn") {
|
|
3793
3811
|
const cfg = PATTERNS.passport[country.toLowerCase()];
|
|
3794
3812
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for passport: ${country}`);
|
|
3795
|
-
return this.
|
|
3813
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3796
3814
|
}
|
|
3797
3815
|
/**
|
|
3798
3816
|
* Username validation.
|
|
@@ -3834,7 +3852,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3834
3852
|
} else {
|
|
3835
3853
|
pat += "[a-zA-Z]*$";
|
|
3836
3854
|
}
|
|
3837
|
-
return this.
|
|
3855
|
+
return this._setPattern(pat).messages({ pattern: "pattern.username" });
|
|
3838
3856
|
}
|
|
3839
3857
|
/**
|
|
3840
3858
|
* Password strength validation.
|
|
@@ -3845,7 +3863,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3845
3863
|
if (!pat) throw new Error(`[schema-dsl] Invalid password strength: ${strength}`);
|
|
3846
3864
|
if (!this._baseSchema.minLength) this._baseSchema.minLength = PASSWORD_MIN_LENGTHS[strength];
|
|
3847
3865
|
if (!this._baseSchema.maxLength) this._baseSchema.maxLength = 64;
|
|
3848
|
-
return this.
|
|
3866
|
+
return this._setPattern(pat.source).messages({ pattern: `pattern.password.${strength}` });
|
|
3849
3867
|
}
|
|
3850
3868
|
// ==================== Number Chain Methods ====================
|
|
3851
3869
|
/** Number decimal places limit. */
|
|
@@ -4936,7 +4954,7 @@ var TypeConverter = class {
|
|
|
4936
4954
|
case "array":
|
|
4937
4955
|
return "JSON";
|
|
4938
4956
|
case "null":
|
|
4939
|
-
return "
|
|
4957
|
+
return "TEXT";
|
|
4940
4958
|
default:
|
|
4941
4959
|
return "VARCHAR(255)";
|
|
4942
4960
|
}
|
|
@@ -4964,7 +4982,7 @@ var TypeConverter = class {
|
|
|
4964
4982
|
case "array":
|
|
4965
4983
|
return "JSONB";
|
|
4966
4984
|
case "null":
|
|
4967
|
-
return "
|
|
4985
|
+
return "TEXT";
|
|
4968
4986
|
default:
|
|
4969
4987
|
return "TEXT";
|
|
4970
4988
|
}
|
|
@@ -5218,6 +5236,7 @@ var MySQLExporter = class _MySQLExporter extends BaseExporter {
|
|
|
5218
5236
|
if (value === null) return "NULL";
|
|
5219
5237
|
if (type === "string") return `'${this._escapeString(String(value))}'`;
|
|
5220
5238
|
if (type === "boolean") return value ? "1" : "0";
|
|
5239
|
+
if (typeof value === "object" && value !== null) return `'${this._escapeString(JSON.stringify(value))}'`;
|
|
5221
5240
|
return String(value);
|
|
5222
5241
|
}
|
|
5223
5242
|
};
|
|
@@ -5363,7 +5382,7 @@ COMMENT ON TABLE ${fullTableName} IS '${this._escapeString(jsonSchema.descriptio
|
|
|
5363
5382
|
if (value === null) return "NULL";
|
|
5364
5383
|
if (type === "string") return `'${this._escapeString(String(value))}'`;
|
|
5365
5384
|
if (type === "boolean") return value ? "TRUE" : "FALSE";
|
|
5366
|
-
if (type === "object" || type === "array") return `'${JSON.stringify(value)}'::JSONB`;
|
|
5385
|
+
if (type === "object" || type === "array") return `'${this._escapeString(JSON.stringify(value))}'::JSONB`;
|
|
5367
5386
|
return String(value);
|
|
5368
5387
|
}
|
|
5369
5388
|
_generateColumnComments(_fullTableName, tableName, schema) {
|
|
@@ -5987,6 +6006,10 @@ var SchemaUtils = class _SchemaUtils {
|
|
|
5987
6006
|
}
|
|
5988
6007
|
/**
|
|
5989
6008
|
* Batch validate using a pre-compiled Ajv validate function.
|
|
6009
|
+
*
|
|
6010
|
+
* Note: the schema is compiled once per call but not cached between calls.
|
|
6011
|
+
* For repeated batch validation of the same schema, prefer `Validator.validateBatch()`
|
|
6012
|
+
* which benefits from the built-in compile cache and returns typed `ValidationResult[]`.
|
|
5990
6013
|
*/
|
|
5991
6014
|
static validateBatch(schema, dataArray, ajvInstance) {
|
|
5992
6015
|
const startTime = Date.now();
|
|
@@ -6024,20 +6047,21 @@ var SchemaUtils = class _SchemaUtils {
|
|
|
6024
6047
|
md += "|-------|------|----------|-------------|\n";
|
|
6025
6048
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
6026
6049
|
const required = schema.required?.includes(key) ? "\u2705" : "\u274C";
|
|
6027
|
-
const type = prop.type ?? "any";
|
|
6050
|
+
const type = _SchemaUtils._escapeMdCell(prop.type ?? "any");
|
|
6028
6051
|
const p = prop;
|
|
6029
|
-
const label = p["_label"] ?? key;
|
|
6030
|
-
|
|
6052
|
+
const label = _SchemaUtils._escapeMdCell(p["_label"] ?? key);
|
|
6053
|
+
const escapedKey = _SchemaUtils._escapeMdCell(key);
|
|
6054
|
+
md += `| ${escapedKey} | ${type} | ${required} | ${label} |
|
|
6031
6055
|
`;
|
|
6032
6056
|
const constraints = [];
|
|
6033
6057
|
if (prop.minLength) constraints.push(`minLength: ${prop.minLength}`);
|
|
6034
6058
|
if (prop.maxLength) constraints.push(`maxLength: ${prop.maxLength}`);
|
|
6035
6059
|
if (prop.minimum !== void 0) constraints.push(`minimum: ${prop.minimum}`);
|
|
6036
6060
|
if (prop.maximum !== void 0) constraints.push(`maximum: ${prop.maximum}`);
|
|
6037
|
-
if (prop.pattern) constraints.push(`pattern: \`${prop.pattern}\``);
|
|
6061
|
+
if (prop.pattern) constraints.push(`pattern: \`${_SchemaUtils._escapeMdCell(String(prop.pattern))}\``);
|
|
6038
6062
|
if (prop.enum) constraints.push(`enum: ${prop.enum.join(", ")}`);
|
|
6039
6063
|
if (constraints.length > 0) {
|
|
6040
|
-
md += `| | | | ${constraints.join("; ")} |
|
|
6064
|
+
md += `| | | | ${_SchemaUtils._escapeMdCell(constraints.join("; "))} |
|
|
6041
6065
|
`;
|
|
6042
6066
|
}
|
|
6043
6067
|
}
|
|
@@ -6052,21 +6076,22 @@ var SchemaUtils = class _SchemaUtils {
|
|
|
6052
6076
|
*/
|
|
6053
6077
|
static toHTML(schema, options = {}) {
|
|
6054
6078
|
const { title = "Schema Documentation" } = options;
|
|
6079
|
+
const safeTitle = _SchemaUtils._escapeHtml(title);
|
|
6055
6080
|
let html = `<!DOCTYPE html>
|
|
6056
6081
|
<html>
|
|
6057
|
-
<head><meta charset="utf-8"><title>${
|
|
6082
|
+
<head><meta charset="utf-8"><title>${safeTitle}</title></head>
|
|
6058
6083
|
<body>
|
|
6059
|
-
<h1>${
|
|
6084
|
+
<h1>${safeTitle}</h1>
|
|
6060
6085
|
`;
|
|
6061
6086
|
if (schema.properties) {
|
|
6062
6087
|
html += '<table border="1" cellpadding="4">\n';
|
|
6063
6088
|
html += "<tr><th>Field</th><th>Type</th><th>Required</th><th>Description</th></tr>\n";
|
|
6064
6089
|
for (const [key, prop] of Object.entries(schema.properties)) {
|
|
6065
6090
|
const required = schema.required?.includes(key) ? "\u2705" : "\u274C";
|
|
6066
|
-
const type = prop.type ?? "any";
|
|
6091
|
+
const type = _SchemaUtils._escapeHtml(prop.type ?? "any");
|
|
6067
6092
|
const p = prop;
|
|
6068
|
-
const label = p["_label"] ?? key;
|
|
6069
|
-
html += `<tr><td>${key}</td><td>${type}</td><td>${required}</td><td>${label}</td></tr>
|
|
6093
|
+
const label = _SchemaUtils._escapeHtml(p["_label"] ?? key);
|
|
6094
|
+
html += `<tr><td>${_SchemaUtils._escapeHtml(key)}</td><td>${type}</td><td>${required}</td><td>${label}</td></tr>
|
|
6070
6095
|
`;
|
|
6071
6096
|
}
|
|
6072
6097
|
html += "</table>\n";
|
|
@@ -6075,6 +6100,12 @@ var SchemaUtils = class _SchemaUtils {
|
|
|
6075
6100
|
return html;
|
|
6076
6101
|
}
|
|
6077
6102
|
// ==================== Private Utilities ====================
|
|
6103
|
+
static _escapeMdCell(str) {
|
|
6104
|
+
return str.replace(/\|/g, "\\|").replace(/\r\n|\r|\n/g, "<br>");
|
|
6105
|
+
}
|
|
6106
|
+
static _escapeHtml(str) {
|
|
6107
|
+
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6108
|
+
}
|
|
6078
6109
|
static _mergeProperties(base, ext) {
|
|
6079
6110
|
const result = { ...base };
|
|
6080
6111
|
for (const [key, extVal] of Object.entries(ext)) {
|
|
@@ -6429,10 +6460,25 @@ function _getDefaultValidator() {
|
|
|
6429
6460
|
function resetDefaultValidator() {
|
|
6430
6461
|
_defaultValidator = null;
|
|
6431
6462
|
}
|
|
6463
|
+
var _INITIAL_PATTERN_KEYS = {
|
|
6464
|
+
phone: new Set(Object.keys(PATTERNS.phone)),
|
|
6465
|
+
idCard: new Set(Object.keys(PATTERNS.idCard)),
|
|
6466
|
+
creditCard: new Set(Object.keys(PATTERNS.creditCard))
|
|
6467
|
+
};
|
|
6432
6468
|
function resetRuntimeState() {
|
|
6433
6469
|
resetDefaultValidator();
|
|
6434
6470
|
DslBuilder.clearCustomTypes();
|
|
6435
6471
|
Locale.reset();
|
|
6472
|
+
TypeRegistry.setStrict(false);
|
|
6473
|
+
for (const key of Object.keys(PATTERNS.phone)) {
|
|
6474
|
+
if (!_INITIAL_PATTERN_KEYS.phone.has(key)) delete PATTERNS.phone[key];
|
|
6475
|
+
}
|
|
6476
|
+
for (const key of Object.keys(PATTERNS.idCard)) {
|
|
6477
|
+
if (!_INITIAL_PATTERN_KEYS.idCard.has(key)) delete PATTERNS.idCard[key];
|
|
6478
|
+
}
|
|
6479
|
+
for (const key of Object.keys(PATTERNS.creditCard)) {
|
|
6480
|
+
if (!_INITIAL_PATTERN_KEYS.creditCard.has(key)) delete PATTERNS.creditCard[key];
|
|
6481
|
+
}
|
|
6436
6482
|
}
|
|
6437
6483
|
function validate(schema, data, options = {}) {
|
|
6438
6484
|
const normalizedSchema = _normalizeSchemaInput(schema);
|
|
@@ -845,7 +845,16 @@ var init_CacheManager = __esm({
|
|
|
845
845
|
};
|
|
846
846
|
}
|
|
847
847
|
set options(opts) {
|
|
848
|
-
if (opts.maxSize !== void 0
|
|
848
|
+
if (opts.maxSize !== void 0 && opts.maxSize !== this._maxSize) {
|
|
849
|
+
this._maxSize = opts.maxSize;
|
|
850
|
+
const oldKeys = this._cache.keys();
|
|
851
|
+
const newCache = new import_cache_hub.MemoryCache({ maxEntries: this._maxSize });
|
|
852
|
+
for (const key of oldKeys) {
|
|
853
|
+
const val = this._cache.get(key);
|
|
854
|
+
if (val !== void 0) newCache.set(key, val);
|
|
855
|
+
}
|
|
856
|
+
this._cache = newCache;
|
|
857
|
+
}
|
|
849
858
|
if (opts.ttl !== void 0) this._ttl = opts.ttl;
|
|
850
859
|
if (opts.enabled !== void 0) this._enabled = opts.enabled;
|
|
851
860
|
if (opts.statsEnabled !== void 0) this._statsEnabled = opts.statsEnabled;
|
|
@@ -3146,6 +3155,7 @@ module.exports = __toCommonJS(custom_format_exports);
|
|
|
3146
3155
|
init_DslParser();
|
|
3147
3156
|
init_TypeRegistry();
|
|
3148
3157
|
init_patterns();
|
|
3158
|
+
var import_safe_regex2 = __toESM(require("safe-regex"), 1);
|
|
3149
3159
|
var PASSWORD_PATTERNS = {
|
|
3150
3160
|
weak: /.{6,}/,
|
|
3151
3161
|
medium: /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/,
|
|
@@ -3304,7 +3314,15 @@ var DslBuilder = class _DslBuilder {
|
|
|
3304
3314
|
* Add regex validation.
|
|
3305
3315
|
*/
|
|
3306
3316
|
pattern(regex, message) {
|
|
3307
|
-
|
|
3317
|
+
const source = regex instanceof RegExp ? regex.source : regex;
|
|
3318
|
+
if (!(0, import_safe_regex2.default)(source)) {
|
|
3319
|
+
throw new Error(`[schema-dsl] Unsafe regex pattern rejected (potential ReDoS): ${source}`);
|
|
3320
|
+
}
|
|
3321
|
+
return this._setPattern(source, message);
|
|
3322
|
+
}
|
|
3323
|
+
/** Internal: set pattern without safe-regex check (used by built-in validators with pre-approved patterns). */
|
|
3324
|
+
_setPattern(source, message) {
|
|
3325
|
+
this._baseSchema.pattern = source;
|
|
3308
3326
|
if (message) {
|
|
3309
3327
|
this._customMessages["string.pattern"] = message;
|
|
3310
3328
|
}
|
|
@@ -3466,25 +3484,25 @@ var DslBuilder = class _DslBuilder {
|
|
|
3466
3484
|
domain() {
|
|
3467
3485
|
this._assertStringType("domain");
|
|
3468
3486
|
const cfg = PATTERNS.common.domain;
|
|
3469
|
-
return this.
|
|
3487
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3470
3488
|
}
|
|
3471
3489
|
/** String IP address validation (IPv4 or IPv6). */
|
|
3472
3490
|
ip() {
|
|
3473
3491
|
this._assertStringType("ip");
|
|
3474
3492
|
const cfg = PATTERNS.common.ip;
|
|
3475
|
-
return this.
|
|
3493
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3476
3494
|
}
|
|
3477
3495
|
/** String Base64 encoding validation. */
|
|
3478
3496
|
base64() {
|
|
3479
3497
|
this._assertStringType("base64");
|
|
3480
3498
|
const cfg = PATTERNS.common.base64;
|
|
3481
|
-
return this.
|
|
3499
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3482
3500
|
}
|
|
3483
3501
|
/** String JWT token validation. */
|
|
3484
3502
|
jwt() {
|
|
3485
3503
|
this._assertStringType("jwt");
|
|
3486
3504
|
const cfg = PATTERNS.common.jwt;
|
|
3487
|
-
return this.
|
|
3505
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3488
3506
|
}
|
|
3489
3507
|
// ==================== Identity / Pattern Chain Methods ====================
|
|
3490
3508
|
/** Phone number validation (auto-corrects number → string). */
|
|
@@ -3498,7 +3516,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3498
3516
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country: ${country}`);
|
|
3499
3517
|
if (cfg.min !== void 0 && !this._baseSchema.minLength) this._baseSchema.minLength = cfg.min;
|
|
3500
3518
|
if (cfg.max !== void 0 && !this._baseSchema.maxLength) this._baseSchema.maxLength = cfg.max;
|
|
3501
|
-
return this.
|
|
3519
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3502
3520
|
}
|
|
3503
3521
|
/** phone() alias (BC). */
|
|
3504
3522
|
phoneNumber(country = "cn") {
|
|
@@ -3511,35 +3529,35 @@ var DslBuilder = class _DslBuilder {
|
|
|
3511
3529
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for idCard: ${country}`);
|
|
3512
3530
|
if (cfg.min !== void 0 && !this._baseSchema.minLength) this._baseSchema.minLength = cfg.min;
|
|
3513
3531
|
if (cfg.max !== void 0 && !this._baseSchema.maxLength) this._baseSchema.maxLength = cfg.max;
|
|
3514
|
-
return this.
|
|
3532
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3515
3533
|
}
|
|
3516
3534
|
/** URL slug validation. */
|
|
3517
3535
|
slugChain() {
|
|
3518
|
-
return this.
|
|
3536
|
+
return this._setPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/.source).messages({ pattern: "pattern.slug" });
|
|
3519
3537
|
}
|
|
3520
3538
|
/** Credit card number validation. */
|
|
3521
3539
|
creditCard(type = "visa") {
|
|
3522
3540
|
const cfg = PATTERNS.creditCard[type.toLowerCase()];
|
|
3523
3541
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported credit card type: ${type}`);
|
|
3524
|
-
return this.
|
|
3542
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3525
3543
|
}
|
|
3526
3544
|
/** Vehicle license plate validation. */
|
|
3527
3545
|
licensePlate(country = "cn") {
|
|
3528
3546
|
const cfg = PATTERNS.licensePlate[country.toLowerCase()];
|
|
3529
3547
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for licensePlate: ${country}`);
|
|
3530
|
-
return this.
|
|
3548
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3531
3549
|
}
|
|
3532
3550
|
/** Postal code validation. */
|
|
3533
3551
|
postalCode(country = "cn") {
|
|
3534
3552
|
const cfg = PATTERNS.postalCode[country.toLowerCase()];
|
|
3535
3553
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for postalCode: ${country}`);
|
|
3536
|
-
return this.
|
|
3554
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3537
3555
|
}
|
|
3538
3556
|
/** Passport number validation. */
|
|
3539
3557
|
passport(country = "cn") {
|
|
3540
3558
|
const cfg = PATTERNS.passport[country.toLowerCase()];
|
|
3541
3559
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for passport: ${country}`);
|
|
3542
|
-
return this.
|
|
3560
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3543
3561
|
}
|
|
3544
3562
|
/**
|
|
3545
3563
|
* Username validation.
|
|
@@ -3581,7 +3599,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3581
3599
|
} else {
|
|
3582
3600
|
pat += "[a-zA-Z]*$";
|
|
3583
3601
|
}
|
|
3584
|
-
return this.
|
|
3602
|
+
return this._setPattern(pat).messages({ pattern: "pattern.username" });
|
|
3585
3603
|
}
|
|
3586
3604
|
/**
|
|
3587
3605
|
* Password strength validation.
|
|
@@ -3592,7 +3610,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3592
3610
|
if (!pat) throw new Error(`[schema-dsl] Invalid password strength: ${strength}`);
|
|
3593
3611
|
if (!this._baseSchema.minLength) this._baseSchema.minLength = PASSWORD_MIN_LENGTHS[strength];
|
|
3594
3612
|
if (!this._baseSchema.maxLength) this._baseSchema.maxLength = 64;
|
|
3595
|
-
return this.
|
|
3613
|
+
return this._setPattern(pat.source).messages({ pattern: `pattern.password.${strength}` });
|
|
3596
3614
|
}
|
|
3597
3615
|
// ==================== Number Chain Methods ====================
|
|
3598
3616
|
/** Number decimal places limit. */
|
|
@@ -3780,10 +3798,8 @@ var customFormatPlugin = {
|
|
|
3780
3798
|
const ajv = getAjvLike(core);
|
|
3781
3799
|
const dslBuilder = getDslBuilderLike(core);
|
|
3782
3800
|
this.addCustomFormats(ajv, dslBuilder);
|
|
3783
|
-
console.log("[Plugin] custom-format v2.0.0 installed (with DSL type registration)");
|
|
3784
3801
|
},
|
|
3785
3802
|
uninstall() {
|
|
3786
|
-
console.log("[Plugin] custom-format uninstalled");
|
|
3787
3803
|
},
|
|
3788
3804
|
addCustomFormats(ajv, dslBuilder) {
|
|
3789
3805
|
for (const [name, config] of Object.entries(FORMATS)) {
|
|
@@ -823,7 +823,16 @@ var init_CacheManager = __esm({
|
|
|
823
823
|
};
|
|
824
824
|
}
|
|
825
825
|
set options(opts) {
|
|
826
|
-
if (opts.maxSize !== void 0
|
|
826
|
+
if (opts.maxSize !== void 0 && opts.maxSize !== this._maxSize) {
|
|
827
|
+
this._maxSize = opts.maxSize;
|
|
828
|
+
const oldKeys = this._cache.keys();
|
|
829
|
+
const newCache = new MemoryCache({ maxEntries: this._maxSize });
|
|
830
|
+
for (const key of oldKeys) {
|
|
831
|
+
const val = this._cache.get(key);
|
|
832
|
+
if (val !== void 0) newCache.set(key, val);
|
|
833
|
+
}
|
|
834
|
+
this._cache = newCache;
|
|
835
|
+
}
|
|
827
836
|
if (opts.ttl !== void 0) this._ttl = opts.ttl;
|
|
828
837
|
if (opts.enabled !== void 0) this._enabled = opts.enabled;
|
|
829
838
|
if (opts.statsEnabled !== void 0) this._statsEnabled = opts.statsEnabled;
|
|
@@ -3116,6 +3125,7 @@ var init_Validator = __esm({
|
|
|
3116
3125
|
init_DslParser();
|
|
3117
3126
|
init_TypeRegistry();
|
|
3118
3127
|
init_patterns();
|
|
3128
|
+
import safeRegex2 from "safe-regex";
|
|
3119
3129
|
var PASSWORD_PATTERNS = {
|
|
3120
3130
|
weak: /.{6,}/,
|
|
3121
3131
|
medium: /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/,
|
|
@@ -3274,7 +3284,15 @@ var DslBuilder = class _DslBuilder {
|
|
|
3274
3284
|
* Add regex validation.
|
|
3275
3285
|
*/
|
|
3276
3286
|
pattern(regex, message) {
|
|
3277
|
-
|
|
3287
|
+
const source = regex instanceof RegExp ? regex.source : regex;
|
|
3288
|
+
if (!safeRegex2(source)) {
|
|
3289
|
+
throw new Error(`[schema-dsl] Unsafe regex pattern rejected (potential ReDoS): ${source}`);
|
|
3290
|
+
}
|
|
3291
|
+
return this._setPattern(source, message);
|
|
3292
|
+
}
|
|
3293
|
+
/** Internal: set pattern without safe-regex check (used by built-in validators with pre-approved patterns). */
|
|
3294
|
+
_setPattern(source, message) {
|
|
3295
|
+
this._baseSchema.pattern = source;
|
|
3278
3296
|
if (message) {
|
|
3279
3297
|
this._customMessages["string.pattern"] = message;
|
|
3280
3298
|
}
|
|
@@ -3436,25 +3454,25 @@ var DslBuilder = class _DslBuilder {
|
|
|
3436
3454
|
domain() {
|
|
3437
3455
|
this._assertStringType("domain");
|
|
3438
3456
|
const cfg = PATTERNS.common.domain;
|
|
3439
|
-
return this.
|
|
3457
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3440
3458
|
}
|
|
3441
3459
|
/** String IP address validation (IPv4 or IPv6). */
|
|
3442
3460
|
ip() {
|
|
3443
3461
|
this._assertStringType("ip");
|
|
3444
3462
|
const cfg = PATTERNS.common.ip;
|
|
3445
|
-
return this.
|
|
3463
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3446
3464
|
}
|
|
3447
3465
|
/** String Base64 encoding validation. */
|
|
3448
3466
|
base64() {
|
|
3449
3467
|
this._assertStringType("base64");
|
|
3450
3468
|
const cfg = PATTERNS.common.base64;
|
|
3451
|
-
return this.
|
|
3469
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3452
3470
|
}
|
|
3453
3471
|
/** String JWT token validation. */
|
|
3454
3472
|
jwt() {
|
|
3455
3473
|
this._assertStringType("jwt");
|
|
3456
3474
|
const cfg = PATTERNS.common.jwt;
|
|
3457
|
-
return this.
|
|
3475
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3458
3476
|
}
|
|
3459
3477
|
// ==================== Identity / Pattern Chain Methods ====================
|
|
3460
3478
|
/** Phone number validation (auto-corrects number → string). */
|
|
@@ -3468,7 +3486,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3468
3486
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country: ${country}`);
|
|
3469
3487
|
if (cfg.min !== void 0 && !this._baseSchema.minLength) this._baseSchema.minLength = cfg.min;
|
|
3470
3488
|
if (cfg.max !== void 0 && !this._baseSchema.maxLength) this._baseSchema.maxLength = cfg.max;
|
|
3471
|
-
return this.
|
|
3489
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3472
3490
|
}
|
|
3473
3491
|
/** phone() alias (BC). */
|
|
3474
3492
|
phoneNumber(country = "cn") {
|
|
@@ -3481,35 +3499,35 @@ var DslBuilder = class _DslBuilder {
|
|
|
3481
3499
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for idCard: ${country}`);
|
|
3482
3500
|
if (cfg.min !== void 0 && !this._baseSchema.minLength) this._baseSchema.minLength = cfg.min;
|
|
3483
3501
|
if (cfg.max !== void 0 && !this._baseSchema.maxLength) this._baseSchema.maxLength = cfg.max;
|
|
3484
|
-
return this.
|
|
3502
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3485
3503
|
}
|
|
3486
3504
|
/** URL slug validation. */
|
|
3487
3505
|
slugChain() {
|
|
3488
|
-
return this.
|
|
3506
|
+
return this._setPattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/.source).messages({ pattern: "pattern.slug" });
|
|
3489
3507
|
}
|
|
3490
3508
|
/** Credit card number validation. */
|
|
3491
3509
|
creditCard(type = "visa") {
|
|
3492
3510
|
const cfg = PATTERNS.creditCard[type.toLowerCase()];
|
|
3493
3511
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported credit card type: ${type}`);
|
|
3494
|
-
return this.
|
|
3512
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3495
3513
|
}
|
|
3496
3514
|
/** Vehicle license plate validation. */
|
|
3497
3515
|
licensePlate(country = "cn") {
|
|
3498
3516
|
const cfg = PATTERNS.licensePlate[country.toLowerCase()];
|
|
3499
3517
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for licensePlate: ${country}`);
|
|
3500
|
-
return this.
|
|
3518
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3501
3519
|
}
|
|
3502
3520
|
/** Postal code validation. */
|
|
3503
3521
|
postalCode(country = "cn") {
|
|
3504
3522
|
const cfg = PATTERNS.postalCode[country.toLowerCase()];
|
|
3505
3523
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for postalCode: ${country}`);
|
|
3506
|
-
return this.
|
|
3524
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3507
3525
|
}
|
|
3508
3526
|
/** Passport number validation. */
|
|
3509
3527
|
passport(country = "cn") {
|
|
3510
3528
|
const cfg = PATTERNS.passport[country.toLowerCase()];
|
|
3511
3529
|
if (!cfg) throw new Error(`[schema-dsl] Unsupported country for passport: ${country}`);
|
|
3512
|
-
return this.
|
|
3530
|
+
return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
|
|
3513
3531
|
}
|
|
3514
3532
|
/**
|
|
3515
3533
|
* Username validation.
|
|
@@ -3551,7 +3569,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3551
3569
|
} else {
|
|
3552
3570
|
pat += "[a-zA-Z]*$";
|
|
3553
3571
|
}
|
|
3554
|
-
return this.
|
|
3572
|
+
return this._setPattern(pat).messages({ pattern: "pattern.username" });
|
|
3555
3573
|
}
|
|
3556
3574
|
/**
|
|
3557
3575
|
* Password strength validation.
|
|
@@ -3562,7 +3580,7 @@ var DslBuilder = class _DslBuilder {
|
|
|
3562
3580
|
if (!pat) throw new Error(`[schema-dsl] Invalid password strength: ${strength}`);
|
|
3563
3581
|
if (!this._baseSchema.minLength) this._baseSchema.minLength = PASSWORD_MIN_LENGTHS[strength];
|
|
3564
3582
|
if (!this._baseSchema.maxLength) this._baseSchema.maxLength = 64;
|
|
3565
|
-
return this.
|
|
3583
|
+
return this._setPattern(pat.source).messages({ pattern: `pattern.password.${strength}` });
|
|
3566
3584
|
}
|
|
3567
3585
|
// ==================== Number Chain Methods ====================
|
|
3568
3586
|
/** Number decimal places limit. */
|
|
@@ -3750,10 +3768,8 @@ var customFormatPlugin = {
|
|
|
3750
3768
|
const ajv = getAjvLike(core);
|
|
3751
3769
|
const dslBuilder = getDslBuilderLike(core);
|
|
3752
3770
|
this.addCustomFormats(ajv, dslBuilder);
|
|
3753
|
-
console.log("[Plugin] custom-format v2.0.0 installed (with DSL type registration)");
|
|
3754
3771
|
},
|
|
3755
3772
|
uninstall() {
|
|
3756
|
-
console.log("[Plugin] custom-format uninstalled");
|
|
3757
3773
|
},
|
|
3758
3774
|
addCustomFormats(ajv, dslBuilder) {
|
|
3759
3775
|
for (const [name, config] of Object.entries(FORMATS)) {
|