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.
Files changed (145) hide show
  1. package/CHANGELOG.md +130 -113
  2. package/LICENSE +21 -21
  3. package/README.md +628 -628
  4. package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
  5. package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
  6. package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
  7. package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
  8. package/dist/index.cjs +75 -29
  9. package/dist/index.d.cts +10 -4
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.js +75 -29
  12. package/dist/plugins/custom-format.cjs +33 -17
  13. package/dist/plugins/custom-format.d.cts +1 -1
  14. package/dist/plugins/custom-format.d.ts +1 -1
  15. package/dist/plugins/custom-format.js +33 -17
  16. package/dist/plugins/custom-type-example.cjs +33 -17
  17. package/dist/plugins/custom-type-example.d.cts +1 -1
  18. package/dist/plugins/custom-type-example.d.ts +1 -1
  19. package/dist/plugins/custom-type-example.js +33 -17
  20. package/dist/plugins/custom-validator.cjs +0 -2
  21. package/dist/plugins/custom-validator.d.cts +1 -1
  22. package/dist/plugins/custom-validator.d.ts +1 -1
  23. package/dist/plugins/custom-validator.js +0 -2
  24. package/docs/FEATURE-INDEX.md +553 -553
  25. package/docs/add-custom-locale.md +496 -496
  26. package/docs/add-keyword.md +24 -24
  27. package/docs/api-reference.md +1047 -1047
  28. package/docs/api.md +13 -13
  29. package/docs/best-practices-project-structure.md +417 -417
  30. package/docs/best-practices.md +712 -712
  31. package/docs/cache-manager.md +344 -344
  32. package/docs/compile.md +45 -45
  33. package/docs/conditional-api.md +1307 -1307
  34. package/docs/custom-extensions-guide.md +339 -339
  35. package/docs/design-philosophy.md +606 -606
  36. package/docs/doc-index.md +324 -324
  37. package/docs/dsl-syntax.md +714 -714
  38. package/docs/dynamic-locale.md +608 -608
  39. package/docs/enum.md +482 -482
  40. package/docs/error-handling.md +1975 -1975
  41. package/docs/export-guide.md +501 -501
  42. package/docs/export-limitations.md +567 -567
  43. package/docs/faq.md +596 -596
  44. package/docs/frontend-i18n-guide.md +307 -307
  45. package/docs/i18n-user-guide.md +487 -487
  46. package/docs/i18n.md +476 -476
  47. package/docs/index.md +48 -48
  48. package/docs/json-schema-basics.md +40 -40
  49. package/docs/label-vs-description.md +271 -271
  50. package/docs/markdown-exporter.md +406 -406
  51. package/docs/mongodb-exporter.md +302 -302
  52. package/docs/multi-language.md +26 -26
  53. package/docs/multi-type-support.md +322 -322
  54. package/docs/mysql-exporter.md +280 -280
  55. package/docs/number-operators.md +449 -449
  56. package/docs/optional-marker-guide.md +326 -326
  57. package/docs/performance-guide.md +49 -49
  58. package/docs/plugin-system.md +381 -381
  59. package/docs/plugin-type-registration.md +34 -34
  60. package/docs/postgresql-exporter.md +311 -311
  61. package/docs/public/favicon.svg +4 -4
  62. package/docs/quick-start.md +435 -435
  63. package/docs/runtime-locale-support.md +532 -532
  64. package/docs/schema-helper.md +345 -345
  65. package/docs/schema-utils-advanced-issues.md +23 -23
  66. package/docs/schema-utils-best-practices.md +20 -20
  67. package/docs/schema-utils-chaining.md +150 -150
  68. package/docs/schema-utils.md +524 -524
  69. package/docs/security-checklist.md +20 -20
  70. package/docs/string-extensions.md +488 -488
  71. package/docs/troubleshooting.md +486 -486
  72. package/docs/type-converter.md +310 -310
  73. package/docs/type-reference.md +242 -242
  74. package/docs/typescript-guide.md +584 -584
  75. package/docs/union-type-guide.md +157 -157
  76. package/docs/union-types.md +284 -284
  77. package/docs/validate-async.md +491 -491
  78. package/docs/validate-batch.md +49 -49
  79. package/docs/validate-dsl-object-support.md +578 -578
  80. package/docs/validate.md +506 -506
  81. package/docs/validation-guide.md +502 -502
  82. package/docs/validator.md +39 -39
  83. package/package.json +131 -131
  84. package/plugins/custom-format.cjs +8 -8
  85. package/plugins/custom-type-example.cjs +8 -8
  86. package/plugins/custom-validator.cjs +8 -8
  87. package/src/adapters/DslAdapter.ts +111 -111
  88. package/src/adapters/index.ts +1 -1
  89. package/src/config/constants.ts +83 -83
  90. package/src/config/index.ts +2 -2
  91. package/src/config/patterns.ts +77 -77
  92. package/src/core/CacheManager.ts +169 -159
  93. package/src/core/ConditionalBuilder.ts +382 -382
  94. package/src/core/ConditionalRuntime.ts +27 -27
  95. package/src/core/ConditionalValidator.ts +254 -254
  96. package/src/core/DslBuilder.ts +687 -677
  97. package/src/core/ErrorCodes.ts +38 -38
  98. package/src/core/ErrorFormatter.ts +271 -271
  99. package/src/core/JSONSchemaCore.ts +65 -65
  100. package/src/core/Locale.ts +187 -187
  101. package/src/core/MessageTemplate.ts +42 -42
  102. package/src/core/ObjectDslBuilder.ts +64 -64
  103. package/src/core/PluginManager.ts +326 -326
  104. package/src/core/StringExtensions.ts +140 -140
  105. package/src/core/TemplateEngine.ts +44 -44
  106. package/src/core/Validator.ts +448 -448
  107. package/src/errors/I18nError.ts +159 -159
  108. package/src/errors/ValidationError.ts +105 -105
  109. package/src/exporters/BaseExporter.ts +60 -60
  110. package/src/exporters/MarkdownExporter.ts +305 -305
  111. package/src/exporters/MongoDBExporter.ts +126 -126
  112. package/src/exporters/MySQLExporter.ts +156 -155
  113. package/src/exporters/PostgreSQLExporter.ts +222 -222
  114. package/src/exporters/index.ts +18 -18
  115. package/src/index.ts +651 -633
  116. package/src/locales/en-US.ts +160 -160
  117. package/src/locales/es-ES.ts +160 -160
  118. package/src/locales/fr-FR.ts +160 -160
  119. package/src/locales/index.ts +103 -103
  120. package/src/locales/ja-JP.ts +160 -160
  121. package/src/locales/types.ts +156 -156
  122. package/src/locales/zh-CN.ts +160 -160
  123. package/src/parser/ConstraintParser.ts +101 -101
  124. package/src/parser/DslParser.ts +470 -470
  125. package/src/parser/SchemaCompiler.ts +66 -66
  126. package/src/parser/TypeRegistry.ts +250 -250
  127. package/src/parser/index.ts +6 -6
  128. package/src/plugins/custom-format.ts +124 -126
  129. package/src/plugins/custom-type-example.ts +106 -108
  130. package/src/plugins/custom-validator.ts +138 -140
  131. package/src/types/conditional.ts +28 -28
  132. package/src/types/config.ts +59 -59
  133. package/src/types/dsl.ts +131 -131
  134. package/src/types/error.ts +60 -60
  135. package/src/types/index.ts +17 -17
  136. package/src/types/infer.ts +127 -127
  137. package/src/types/plugin.ts +58 -58
  138. package/src/types/safe-regex.d.ts +9 -9
  139. package/src/types/schema.ts +66 -66
  140. package/src/types/validate.ts +71 -71
  141. package/src/utils/SchemaHelper.ts +196 -196
  142. package/src/utils/SchemaUtils.ts +365 -346
  143. package/src/utils/TypeConverter.ts +215 -215
  144. package/src/utils/index.ts +10 -10
  145. 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) this._maxSize = opts.maxSize;
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.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
- this._baseSchema.pattern = regex instanceof RegExp ? regex.source : regex;
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
3785
+ return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
3768
3786
  }
3769
3787
  /** URL slug validation. */
3770
3788
  slugChain() {
3771
- return this.pattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).messages({ pattern: "pattern.slug" });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(new RegExp(pat)).messages({ pattern: "pattern.username" });
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.pattern(pat).messages({ pattern: `pattern.password.${strength}` });
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 "NULL";
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 "NULL";
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
- md += `| ${key} | ${type} | ${required} | ${label} |
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>${title}</title></head>
6082
+ <head><meta charset="utf-8"><title>${safeTitle}</title></head>
6058
6083
  <body>
6059
- <h1>${title}</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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#x27;");
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) this._maxSize = opts.maxSize;
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
- this._baseSchema.pattern = regex instanceof RegExp ? regex.source : regex;
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
3532
+ return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
3515
3533
  }
3516
3534
  /** URL slug validation. */
3517
3535
  slugChain() {
3518
- return this.pattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).messages({ pattern: "pattern.slug" });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(new RegExp(pat)).messages({ pattern: "pattern.username" });
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.pattern(pat).messages({ pattern: `pattern.password.${strength}` });
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)) {
@@ -1,5 +1,5 @@
1
1
  import { P as Plugin } from '../plugin-CIKtTMtS.cjs';
2
- import { D as DslBuilder } from '../DslBuilder-DQDN0ZxZ.cjs';
2
+ import { D as DslBuilder } from '../DslBuilder-CjHTucNQ.cjs';
3
3
 
4
4
  declare const customFormatPlugin: Plugin & {
5
5
  addCustomFormats: (ajv: {
@@ -1,5 +1,5 @@
1
1
  import { P as Plugin } from '../plugin-CIKtTMtS.js';
2
- import { D as DslBuilder } from '../DslBuilder-DkLaOo9Q.js';
2
+ import { D as DslBuilder } from '../DslBuilder-BIgQOAXp.js';
3
3
 
4
4
  declare const customFormatPlugin: Plugin & {
5
5
  addCustomFormats: (ajv: {
@@ -823,7 +823,16 @@ var init_CacheManager = __esm({
823
823
  };
824
824
  }
825
825
  set options(opts) {
826
- if (opts.maxSize !== void 0) this._maxSize = opts.maxSize;
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
- this._baseSchema.pattern = regex instanceof RegExp ? regex.source : regex;
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
3502
+ return this._setPattern(cfg.pattern.source).messages({ pattern: cfg.key });
3485
3503
  }
3486
3504
  /** URL slug validation. */
3487
3505
  slugChain() {
3488
- return this.pattern(/^[a-z0-9]+(?:-[a-z0-9]+)*$/).messages({ pattern: "pattern.slug" });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(cfg.pattern).messages({ pattern: cfg.key });
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.pattern(new RegExp(pat)).messages({ pattern: "pattern.username" });
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.pattern(pat).messages({ pattern: `pattern.password.${strength}` });
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)) {