@oxog/vld 2.0.1 → 2.0.3

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 (56) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +2 -2
  3. package/dist/chunks/{bigint-ZKkWLdmi.js → bigint-CRS0QVsy.js} +242 -16
  4. package/dist/chunks/bigint-CRS0QVsy.js.map +1 -0
  5. package/dist/chunks/{date-7Iz2BGsL.js → date-50JV_Mfj.js} +2 -2
  6. package/dist/chunks/{date-7Iz2BGsL.js.map → date-50JV_Mfj.js.map} +1 -1
  7. package/dist/chunks/{index-BoV-4S1F.js → index-DO8CFMxD.js} +4 -4
  8. package/dist/chunks/{index-BoV-4S1F.js.map → index-DO8CFMxD.js.map} +1 -1
  9. package/dist/chunks/{json-nIhyRPOU.js → json-Cp4WO0M9.js} +226 -139
  10. package/dist/chunks/json-Cp4WO0M9.js.map +1 -0
  11. package/dist/chunks/{unknown-CjSRcELI.js → unknown-Dhzri_T-.js} +2 -2
  12. package/dist/chunks/{unknown-CjSRcELI.js.map → unknown-Dhzri_T-.js.map} +1 -1
  13. package/dist/cjs/index.cjs +1095 -151
  14. package/dist/cjs/index.cjs.map +1 -1
  15. package/dist/cjs/mini.cjs +462 -149
  16. package/dist/cjs/mini.cjs.map +1 -1
  17. package/dist/cli/commands/validate.d.ts.map +1 -1
  18. package/dist/codecs/index.js +3 -3
  19. package/dist/coercion/index.js +2 -2
  20. package/dist/index.d.ts +244 -0
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +550 -11
  23. package/dist/index.js.map +1 -1
  24. package/dist/mini.js +5 -5
  25. package/dist/utils/json-schema.d.ts +67 -0
  26. package/dist/utils/json-schema.d.ts.map +1 -0
  27. package/dist/validators/array.d.ts +5 -0
  28. package/dist/validators/array.d.ts.map +1 -1
  29. package/dist/validators/base.d.ts +79 -0
  30. package/dist/validators/base.d.ts.map +1 -1
  31. package/dist/validators/discriminated-union.d.ts.map +1 -1
  32. package/dist/validators/enum.d.ts +5 -1
  33. package/dist/validators/enum.d.ts.map +1 -1
  34. package/dist/validators/index.d.ts +3 -2
  35. package/dist/validators/index.d.ts.map +1 -1
  36. package/dist/validators/index.js +514 -4
  37. package/dist/validators/index.js.map +1 -1
  38. package/dist/validators/lazy.d.ts +8 -1
  39. package/dist/validators/lazy.d.ts.map +1 -1
  40. package/dist/validators/literal.d.ts +5 -1
  41. package/dist/validators/literal.d.ts.map +1 -1
  42. package/dist/validators/number.d.ts +30 -0
  43. package/dist/validators/number.d.ts.map +1 -1
  44. package/dist/validators/object.d.ts +30 -3
  45. package/dist/validators/object.d.ts.map +1 -1
  46. package/dist/validators/promise.d.ts +53 -0
  47. package/dist/validators/promise.d.ts.map +1 -0
  48. package/dist/validators/string-formats.d.ts +6 -0
  49. package/dist/validators/string-formats.d.ts.map +1 -1
  50. package/dist/validators/string.d.ts +12 -1
  51. package/dist/validators/string.d.ts.map +1 -1
  52. package/package.json +6 -5
  53. package/dist/chunks/bigint-ZKkWLdmi.js.map +0 -1
  54. package/dist/chunks/index-eWXdjyyy.js +0 -426
  55. package/dist/chunks/index-eWXdjyyy.js.map +0 -1
  56. package/dist/chunks/json-nIhyRPOU.js.map +0 -1
@@ -90,6 +90,16 @@ class VldBase {
90
90
  nullish() {
91
91
  return new VldNullish(this);
92
92
  }
93
+ /**
94
+ * Make this validator exactly optional - allows undefined but not missing
95
+ * Unlike .optional() which treats missing as undefined, exactOptional()
96
+ * requires the key to be present but allows undefined as a value
97
+ * Zod 4 API parity
98
+ * @returns A new exact optional validator
99
+ */
100
+ exactOptional() {
101
+ return new VldExactOptional(this);
102
+ }
93
103
  /**
94
104
  * Pipe the output of this validator into another validator
95
105
  * @param next The next validator to pipe into
@@ -134,6 +144,49 @@ class VldBase {
134
144
  apply(fn) {
135
145
  return fn(this);
136
146
  }
147
+ check(predicate, message) {
148
+ return new VldRefine(this, predicate, message);
149
+ }
150
+ /**
151
+ * Get or set metadata for this schema
152
+ */
153
+ meta(data) {
154
+ if (data === undefined) {
155
+ return undefined;
156
+ }
157
+ return new VldMeta(this, data);
158
+ }
159
+ /**
160
+ * Add a description to this schema
161
+ * Zod 4 API parity - convenience method for .meta({ description: ... })
162
+ * @param description The description to add
163
+ * @returns A new validator with the description
164
+ */
165
+ describe(description) {
166
+ return new VldMeta(this, { description });
167
+ }
168
+ }
169
+ /**
170
+ * Metadata validator - wraps a schema with metadata
171
+ */
172
+ class VldMeta extends VldBase {
173
+ constructor(baseValidator, metadata) {
174
+ super();
175
+ this.baseValidator = baseValidator;
176
+ this.metadata = metadata;
177
+ }
178
+ parse(value) {
179
+ return this.baseValidator.parse(value);
180
+ }
181
+ safeParse(value) {
182
+ return this.baseValidator.safeParse(value);
183
+ }
184
+ /**
185
+ * Get the metadata
186
+ */
187
+ getMeta() {
188
+ return this.metadata;
189
+ }
137
190
  }
138
191
  /**
139
192
  * Readonly validator - marks output as readonly
@@ -349,6 +402,38 @@ class VldOptional extends VldBase {
349
402
  return this.baseValidator;
350
403
  }
351
404
  }
405
+ /**
406
+ * Exact optional validator - allows undefined but requires key presence
407
+ * Unlike Optional which treats missing as undefined, ExactOptional
408
+ * requires the key to be present (not missing from object) but allows undefined
409
+ * Zod 4 API parity
410
+ */
411
+ class VldExactOptional extends VldBase {
412
+ constructor(baseValidator) {
413
+ super();
414
+ this.baseValidator = baseValidator;
415
+ }
416
+ static create(baseValidator) {
417
+ return new VldExactOptional(baseValidator);
418
+ }
419
+ parse(value) {
420
+ if (value === undefined) {
421
+ return undefined;
422
+ }
423
+ return this.baseValidator.parse(value);
424
+ }
425
+ safeParse(value) {
426
+ // Unlike regular optional, undefined is explicitly valid
427
+ // but we still validate through the base validator
428
+ if (value === undefined) {
429
+ return { success: true, data: undefined };
430
+ }
431
+ return this.baseValidator.safeParse(value);
432
+ }
433
+ unwrap() {
434
+ return this.baseValidator;
435
+ }
436
+ }
352
437
  /**
353
438
  * Nullable validator - allows null
354
439
  */
@@ -3478,6 +3563,7 @@ const REGEX_PATTERNS = {
3478
3563
  ipv4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/};
3479
3564
  /**
3480
3565
  * Immutable string validator with chainable methods
3566
+ * Features pre-compiled validation functions for maximum performance
3481
3567
  */
3482
3568
  class VldString extends VldBase {
3483
3569
  /**
@@ -3485,12 +3571,100 @@ class VldString extends VldBase {
3485
3571
  */
3486
3572
  constructor(config) {
3487
3573
  super();
3574
+ // Cache for pre-compiled validation function
3575
+ this._compiledValidator = null;
3488
3576
  this.config = {
3489
3577
  checks: config?.checks || [],
3490
3578
  transforms: config?.transforms || [],
3491
3579
  errorMessage: config?.errorMessage
3492
3580
  };
3493
3581
  }
3582
+ /**
3583
+ * Compile all transforms and checks into a single optimized function
3584
+ * This eliminates loop overhead and enables better JIT optimization
3585
+ */
3586
+ _compileValidator() {
3587
+ const transforms = this.config.transforms;
3588
+ const checks = this.config.checks;
3589
+ const errorMessage = this.config.errorMessage || getMessages().invalidString;
3590
+ // Fast path: no transforms or checks
3591
+ if (transforms.length === 0 && checks.length === 0) {
3592
+ return (value) => ({ success: true, value });
3593
+ }
3594
+ // Fast path: only transforms, no checks
3595
+ if (checks.length === 0) {
3596
+ switch (transforms.length) {
3597
+ case 1:
3598
+ return (value) => ({ success: true, value: transforms[0](value) });
3599
+ case 2:
3600
+ return (value) => ({ success: true, value: transforms[1](transforms[0](value)) });
3601
+ case 3:
3602
+ return (value) => ({ success: true, value: transforms[2](transforms[1](transforms[0](value))) });
3603
+ default:
3604
+ return (value) => {
3605
+ let result = value;
3606
+ for (let i = 0; i < transforms.length; i++) {
3607
+ result = transforms[i](result);
3608
+ }
3609
+ return { success: true, value: result };
3610
+ };
3611
+ }
3612
+ }
3613
+ // Fast path: only checks, no transforms
3614
+ if (transforms.length === 0) {
3615
+ switch (checks.length) {
3616
+ case 1:
3617
+ return (value) => {
3618
+ if (!checks[0](value))
3619
+ return { success: false, error: errorMessage };
3620
+ return { success: true, value };
3621
+ };
3622
+ case 2:
3623
+ return (value) => {
3624
+ if (!checks[0](value) || !checks[1](value))
3625
+ return { success: false, error: errorMessage };
3626
+ return { success: true, value };
3627
+ };
3628
+ case 3:
3629
+ return (value) => {
3630
+ if (!checks[0](value) || !checks[1](value) || !checks[2](value))
3631
+ return { success: false, error: errorMessage };
3632
+ return { success: true, value };
3633
+ };
3634
+ default:
3635
+ return (value) => {
3636
+ for (let i = 0; i < checks.length; i++) {
3637
+ if (!checks[i](value))
3638
+ return { success: false, error: errorMessage };
3639
+ }
3640
+ return { success: true, value };
3641
+ };
3642
+ }
3643
+ }
3644
+ // General case: both transforms and checks
3645
+ return (value) => {
3646
+ let result = value;
3647
+ // Apply transforms
3648
+ for (let i = 0; i < transforms.length; i++) {
3649
+ result = transforms[i](result);
3650
+ }
3651
+ // Apply checks
3652
+ for (let i = 0; i < checks.length; i++) {
3653
+ if (!checks[i](result))
3654
+ return { success: false, error: errorMessage };
3655
+ }
3656
+ return { success: true, value: result };
3657
+ };
3658
+ }
3659
+ /**
3660
+ * Get the cached compiled validator, creating it if necessary
3661
+ */
3662
+ _getCompiledValidator() {
3663
+ if (!this._compiledValidator) {
3664
+ this._compiledValidator = this._compileValidator();
3665
+ }
3666
+ return this._compiledValidator;
3667
+ }
3494
3668
  /**
3495
3669
  * Create a new string validator
3496
3670
  */
@@ -3498,26 +3672,18 @@ class VldString extends VldBase {
3498
3672
  return new VldString();
3499
3673
  }
3500
3674
  /**
3501
- * Parse and validate a string value - ultra-optimized
3675
+ * Parse and validate a string value - ultra-optimized with pre-compiled validator
3502
3676
  */
3503
3677
  parse(value) {
3504
3678
  if (typeof value !== 'string') {
3505
3679
  throw new Error(this.config.errorMessage || getMessages().invalidString);
3506
3680
  }
3507
- let result = value;
3508
- // Apply transformations with optimized loop
3509
- const transformsLength = this.config.transforms.length;
3510
- for (let i = 0; i < transformsLength; i++) {
3511
- result = this.config.transforms[i](result);
3512
- }
3513
- // Apply checks with optimized loop and early termination
3514
- const checksLength = this.config.checks.length;
3515
- for (let i = 0; i < checksLength; i++) {
3516
- if (!this.config.checks[i](result)) {
3517
- throw new Error(this.config.errorMessage || getMessages().invalidString);
3518
- }
3681
+ // Use pre-compiled validator for maximum performance
3682
+ const result = this._getCompiledValidator()(value);
3683
+ if (!result.success) {
3684
+ throw new Error(result.error);
3519
3685
  }
3520
- return result;
3686
+ return result.value;
3521
3687
  }
3522
3688
  /**
3523
3689
  * Safely parse and validate a string value
@@ -3931,6 +4097,66 @@ class VldNumber extends VldBase {
3931
4097
  lte(value, message) {
3932
4098
  return this.max(value, message);
3933
4099
  }
4100
+ /**
4101
+ * Create a validator for unsigned 32-bit integers
4102
+ * Range: 0 to 4,294,967,295
4103
+ */
4104
+ uint32(message) {
4105
+ return new VldNumber({
4106
+ checks: [...this.config.checks, (v) => Number.isSafeInteger(v) && v >= 0 && v <= 4294967295],
4107
+ errorMessage: message || 'Expected an unsigned 32-bit integer'
4108
+ });
4109
+ }
4110
+ /**
4111
+ * Create a validator for unsigned 64-bit integers
4112
+ * Range: 0 to 2^53-1 (safe integer limit)
4113
+ */
4114
+ uint64(message) {
4115
+ return new VldNumber({
4116
+ checks: [...this.config.checks, (v) => Number.isSafeInteger(v) && v >= 0],
4117
+ errorMessage: message || 'Expected an unsigned 64-bit integer'
4118
+ });
4119
+ }
4120
+ /**
4121
+ * Create a validator for signed 32-bit integers
4122
+ * Range: -2,147,483,648 to 2,147,483,647
4123
+ */
4124
+ int32(message) {
4125
+ return new VldNumber({
4126
+ checks: [...this.config.checks, (v) => Number.isSafeInteger(v) && v >= -2147483648 && v <= 2147483647],
4127
+ errorMessage: message || 'Expected a signed 32-bit integer'
4128
+ });
4129
+ }
4130
+ /**
4131
+ * Create a validator for signed 64-bit integers
4132
+ * Range: -(2^53-1) to 2^53-1 (safe integer limit)
4133
+ */
4134
+ int64(message) {
4135
+ return new VldNumber({
4136
+ checks: [...this.config.checks, (v) => Number.isSafeInteger(v)],
4137
+ errorMessage: message || 'Expected a signed 64-bit integer'
4138
+ });
4139
+ }
4140
+ /**
4141
+ * Create a validator for 32-bit floats (IEEE 754 single precision)
4142
+ * Range: -3.4e38 to 3.4e38, precision ~7 decimal digits
4143
+ */
4144
+ float32(message) {
4145
+ return new VldNumber({
4146
+ checks: [...this.config.checks, (v) => Number.isFinite(v) && Math.abs(v) <= 3.4e38],
4147
+ errorMessage: message || 'Expected a 32-bit float'
4148
+ });
4149
+ }
4150
+ /**
4151
+ * Create a validator for 64-bit floats (IEEE 754 double precision)
4152
+ * Alias for standard number validation
4153
+ */
4154
+ float64(message) {
4155
+ return new VldNumber({
4156
+ checks: [...this.config.checks, (v) => Number.isFinite(v)],
4157
+ errorMessage: message || 'Expected a 64-bit float'
4158
+ });
4159
+ }
3934
4160
  }
3935
4161
 
3936
4162
  /**
@@ -4439,16 +4665,9 @@ class VldArray extends VldBase {
4439
4665
  }
4440
4666
  result[i] = parseResult.data; // Direct assignment is faster than push
4441
4667
  }
4442
- // Check uniqueness if required
4668
+ // Check uniqueness if required - optimized with Map-based approach
4443
4669
  if (this.config.unique) {
4444
- const seen = new Set();
4445
- for (const item of result) {
4446
- const key = typeof item === 'object' ? this.stableStringify(item) : item;
4447
- if (seen.has(key)) {
4448
- throw new Error('Array must contain unique items');
4449
- }
4450
- seen.add(key);
4451
- }
4670
+ this.checkUnique(result);
4452
4671
  }
4453
4672
  return result;
4454
4673
  }
@@ -4463,6 +4682,38 @@ class VldArray extends VldBase {
4463
4682
  return { success: false, error: error };
4464
4683
  }
4465
4684
  }
4685
+ /**
4686
+ * Optimized uniqueness check using Map-based approach
4687
+ * Avoids repeated stableStringify calls by caching serialized values
4688
+ */
4689
+ checkUnique(items) {
4690
+ const seen = new Set(); // Set of seen keys
4691
+ const objectKeys = new WeakMap(); // Cache for object->string mappings
4692
+ for (const item of items) {
4693
+ let key;
4694
+ if (typeof item === 'object' && item !== null) {
4695
+ // Check if we've already serialized this object reference
4696
+ const cached = objectKeys.get(item);
4697
+ if (cached !== undefined) {
4698
+ key = cached;
4699
+ }
4700
+ else {
4701
+ // Serialize and cache
4702
+ const serialized = this.stableStringify(item);
4703
+ key = serialized;
4704
+ objectKeys.set(item, serialized);
4705
+ }
4706
+ }
4707
+ else {
4708
+ // Primitives can be used directly as keys
4709
+ key = item;
4710
+ }
4711
+ if (seen.has(key)) {
4712
+ throw new Error('Array must contain unique items');
4713
+ }
4714
+ seen.add(key);
4715
+ }
4716
+ }
4466
4717
  /**
4467
4718
  * Create a stable string representation of an object for hashing
4468
4719
  * Handles circular references and deep nesting gracefully
@@ -4590,11 +4841,17 @@ class VldEnum extends VldBase {
4590
4841
  /**
4591
4842
  * Private constructor to enforce immutability
4592
4843
  */
4593
- constructor(values, errorMessage) {
4844
+ constructor(_values, errorMessage) {
4594
4845
  super();
4595
- this.values = values;
4846
+ this._values = _values;
4596
4847
  this.errorMessage = errorMessage;
4597
4848
  }
4849
+ /**
4850
+ * Get the enum values
4851
+ */
4852
+ get values() {
4853
+ return this._values;
4854
+ }
4598
4855
  /**
4599
4856
  * Create a new enum validator
4600
4857
  */
@@ -4608,11 +4865,11 @@ class VldEnum extends VldBase {
4608
4865
  // BUG-002 FIX: Add type check before includes() to prevent type confusion
4609
4866
  if (typeof value !== 'string') {
4610
4867
  throw new Error(this.errorMessage ||
4611
- getMessages().enumExpected([...this.values], JSON.stringify(value)));
4868
+ getMessages().enumExpected([...this._values], JSON.stringify(value)));
4612
4869
  }
4613
- if (!this.values.includes(value)) {
4870
+ if (!this._values.includes(value)) {
4614
4871
  throw new Error(this.errorMessage ||
4615
- getMessages().enumExpected([...this.values], JSON.stringify(value)));
4872
+ getMessages().enumExpected([...this._values], JSON.stringify(value)));
4616
4873
  }
4617
4874
  return value;
4618
4875
  }
@@ -4625,16 +4882,16 @@ class VldEnum extends VldBase {
4625
4882
  return {
4626
4883
  success: false,
4627
4884
  error: new Error(this.errorMessage ||
4628
- getMessages().enumExpected([...this.values], JSON.stringify(value)))
4885
+ getMessages().enumExpected([...this._values], JSON.stringify(value)))
4629
4886
  };
4630
4887
  }
4631
- if (this.values.includes(value)) {
4888
+ if (this._values.includes(value)) {
4632
4889
  return { success: true, data: value };
4633
4890
  }
4634
4891
  return {
4635
4892
  success: false,
4636
4893
  error: new Error(this.errorMessage ||
4637
- getMessages().enumExpected([...this.values], JSON.stringify(value)))
4894
+ getMessages().enumExpected([...this._values], JSON.stringify(value)))
4638
4895
  };
4639
4896
  }
4640
4897
  /**
@@ -4642,7 +4899,7 @@ class VldEnum extends VldBase {
4642
4899
  * Creates a new enum validator without the specified values
4643
4900
  */
4644
4901
  exclude(...excludeValues) {
4645
- const filtered = this.values.filter(v => !excludeValues.includes(v));
4902
+ const filtered = this._values.filter(v => !excludeValues.includes(v));
4646
4903
  if (filtered.length === 0) {
4647
4904
  throw new Error('Cannot exclude all enum values');
4648
4905
  }
@@ -4654,7 +4911,7 @@ class VldEnum extends VldBase {
4654
4911
  * Creates a new enum validator with only the specified values
4655
4912
  */
4656
4913
  extract(...extractValues) {
4657
- const extracted = this.values.filter(v => extractValues.includes(v));
4914
+ const extracted = this._values.filter(v => extractValues.includes(v));
4658
4915
  if (extracted.length === 0) {
4659
4916
  throw new Error('Cannot extract non-existent enum values');
4660
4917
  }
@@ -5175,10 +5432,30 @@ class VldObject extends VldBase {
5175
5432
  */
5176
5433
  constructor(config) {
5177
5434
  super();
5178
- this.config = config;
5435
+ this._config = config;
5179
5436
  // Pre-compute shape keys for faster access
5180
- this.shapeKeys = Object.keys(config.shape);
5181
- this.shapeKeysSet = new Set(this.shapeKeys);
5437
+ this._shapeKeys = Object.keys(config.shape);
5438
+ this._shapeKeysSet = new Set(this._shapeKeys);
5439
+ }
5440
+ /**
5441
+ * Get the validator configuration
5442
+ * @internal Used by discriminated union validator
5443
+ */
5444
+ get config() {
5445
+ return this._config;
5446
+ }
5447
+ /**
5448
+ * Get the shape keys array
5449
+ */
5450
+ get shapeKeys() {
5451
+ return this._shapeKeys;
5452
+ }
5453
+ /**
5454
+ * Get the shape keys set for O(1) lookups
5455
+ * @internal Used by discriminated union validator
5456
+ */
5457
+ get shapeKeysSet() {
5458
+ return this._shapeKeysSet;
5182
5459
  }
5183
5460
  /**
5184
5461
  * Create a new object validator
@@ -5193,14 +5470,14 @@ class VldObject extends VldBase {
5193
5470
  parse(value) {
5194
5471
  // Fast type check
5195
5472
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
5196
- throw new Error(this.config.errorMessage || getMessages().invalidObject);
5473
+ throw new Error(this._config.errorMessage || getMessages().invalidObject);
5197
5474
  }
5198
5475
  const obj = value;
5199
5476
  const result = {};
5200
5477
  // Ultra-optimized field validation with inline fast paths
5201
- for (let i = 0; i < this.shapeKeys.length; i++) {
5202
- const key = this.shapeKeys[i];
5203
- const validator = this.config.shape[key];
5478
+ for (let i = 0; i < this._shapeKeys.length; i++) {
5479
+ const key = this._shapeKeys[i];
5480
+ const validator = this._config.shape[key];
5204
5481
  const fieldValue = obj[key];
5205
5482
  // BUG-NEW-002 FIX: Use instanceof instead of constructor.name
5206
5483
  // constructor.name breaks in minified builds where class names become 'a', 'b', etc.
@@ -5257,38 +5534,39 @@ class VldObject extends VldBase {
5257
5534
  result[key] = parseResult.data;
5258
5535
  }
5259
5536
  }
5260
- // Handle strict mode - optimized with Set
5261
- if (this.config.strict) {
5537
+ // Handle strict/passthrough/catchall modes - optimized single Object.keys() call
5538
+ if (this._config.strict || this._config.passthrough || this._config.catchall) {
5262
5539
  const objKeys = Object.keys(obj);
5263
- const extraKeys = [];
5264
- for (let i = 0; i < objKeys.length; i++) {
5265
- if (!this.shapeKeysSet.has(objKeys[i])) {
5266
- extraKeys.push(objKeys[i]);
5540
+ // Handle strict mode - optimized with Set
5541
+ if (this._config.strict) {
5542
+ const extraKeys = [];
5543
+ for (let i = 0; i < objKeys.length; i++) {
5544
+ if (!this._shapeKeysSet.has(objKeys[i])) {
5545
+ extraKeys.push(objKeys[i]);
5546
+ }
5547
+ }
5548
+ if (extraKeys.length > 0) {
5549
+ throw new Error(getMessages().unexpectedKeys(extraKeys));
5267
5550
  }
5268
5551
  }
5269
- if (extraKeys.length > 0) {
5270
- throw new Error(getMessages().unexpectedKeys(extraKeys));
5271
- }
5272
- }
5273
- // Handle passthrough mode - optimized with comprehensive prototype pollution protection
5274
- if (this.config.passthrough) {
5275
- const objKeys = Object.keys(obj);
5276
- for (let i = 0; i < objKeys.length; i++) {
5277
- const key = objKeys[i];
5278
- // Skip dangerous keys to prevent prototype pollution
5279
- if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
5280
- result[key] = obj[key];
5552
+ // Handle passthrough mode - optimized with comprehensive prototype pollution protection
5553
+ if (this._config.passthrough) {
5554
+ for (let i = 0; i < objKeys.length; i++) {
5555
+ const key = objKeys[i];
5556
+ // Skip dangerous keys to prevent prototype pollution
5557
+ if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
5558
+ result[key] = obj[key];
5559
+ }
5281
5560
  }
5282
5561
  }
5283
- }
5284
- // Handle catchall - validate extra keys with catchall validator
5285
- if (this.config.catchall) {
5286
- const objKeys = Object.keys(obj);
5287
- for (let i = 0; i < objKeys.length; i++) {
5288
- const key = objKeys[i];
5289
- // Skip keys already in shape and dangerous keys
5290
- if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
5291
- result[key] = this.config.catchall.parse(obj[key]);
5562
+ // Handle catchall - validate extra keys with catchall validator
5563
+ if (this._config.catchall) {
5564
+ for (let i = 0; i < objKeys.length; i++) {
5565
+ const key = objKeys[i];
5566
+ // Skip keys already in shape and dangerous keys
5567
+ if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
5568
+ result[key] = this._config.catchall.parse(obj[key]);
5569
+ }
5292
5570
  }
5293
5571
  }
5294
5572
  }
@@ -5303,15 +5581,15 @@ class VldObject extends VldBase {
5303
5581
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
5304
5582
  return {
5305
5583
  success: false,
5306
- error: new Error(this.config.errorMessage || getMessages().invalidObject)
5584
+ error: new Error(this._config.errorMessage || getMessages().invalidObject)
5307
5585
  };
5308
5586
  }
5309
5587
  const obj = value;
5310
5588
  const result = {};
5311
5589
  // Validate all fields
5312
- for (let i = 0; i < this.shapeKeys.length; i++) {
5313
- const key = this.shapeKeys[i];
5314
- const validator = this.config.shape[key];
5590
+ for (let i = 0; i < this._shapeKeys.length; i++) {
5591
+ const key = this._shapeKeys[i];
5592
+ const validator = this._config.shape[key];
5315
5593
  const fieldValue = obj[key];
5316
5594
  const parseResult = validator.safeParse(fieldValue);
5317
5595
  if (parseResult.success) {
@@ -5324,48 +5602,49 @@ class VldObject extends VldBase {
5324
5602
  };
5325
5603
  }
5326
5604
  }
5327
- // Handle strict mode
5328
- if (this.config.strict) {
5605
+ // Handle strict/passthrough/catchall modes - optimized single Object.keys() call
5606
+ if (this._config.strict || this._config.passthrough || this._config.catchall) {
5329
5607
  const objKeys = Object.keys(obj);
5330
- const extraKeys = [];
5331
- for (let i = 0; i < objKeys.length; i++) {
5332
- if (!this.shapeKeysSet.has(objKeys[i])) {
5333
- extraKeys.push(objKeys[i]);
5608
+ // Handle strict mode
5609
+ if (this._config.strict) {
5610
+ const extraKeys = [];
5611
+ for (let i = 0; i < objKeys.length; i++) {
5612
+ if (!this._shapeKeysSet.has(objKeys[i])) {
5613
+ extraKeys.push(objKeys[i]);
5614
+ }
5615
+ }
5616
+ if (extraKeys.length > 0) {
5617
+ return {
5618
+ success: false,
5619
+ error: new Error(getMessages().unexpectedKeys(extraKeys))
5620
+ };
5334
5621
  }
5335
5622
  }
5336
- if (extraKeys.length > 0) {
5337
- return {
5338
- success: false,
5339
- error: new Error(getMessages().unexpectedKeys(extraKeys))
5340
- };
5341
- }
5342
- }
5343
- // Handle passthrough mode with comprehensive prototype pollution protection
5344
- if (this.config.passthrough) {
5345
- const objKeys = Object.keys(obj);
5346
- for (let i = 0; i < objKeys.length; i++) {
5347
- const key = objKeys[i];
5348
- // Skip dangerous keys to prevent prototype pollution
5349
- if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
5350
- result[key] = obj[key];
5623
+ // Handle passthrough mode with comprehensive prototype pollution protection
5624
+ if (this._config.passthrough) {
5625
+ for (let i = 0; i < objKeys.length; i++) {
5626
+ const key = objKeys[i];
5627
+ // Skip dangerous keys to prevent prototype pollution
5628
+ if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
5629
+ result[key] = obj[key];
5630
+ }
5351
5631
  }
5352
5632
  }
5353
- }
5354
- // Handle catchall - validate extra keys with catchall validator
5355
- if (this.config.catchall) {
5356
- const objKeys = Object.keys(obj);
5357
- for (let i = 0; i < objKeys.length; i++) {
5358
- const key = objKeys[i];
5359
- // Skip keys already in shape and dangerous keys
5360
- if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
5361
- const catchallResult = this.config.catchall.safeParse(obj[key]);
5362
- if (!catchallResult.success) {
5363
- return {
5364
- success: false,
5365
- error: new Error(getMessages().objectField(key, catchallResult.error.message))
5366
- };
5633
+ // Handle catchall - validate extra keys with catchall validator
5634
+ if (this._config.catchall) {
5635
+ for (let i = 0; i < objKeys.length; i++) {
5636
+ const key = objKeys[i];
5637
+ // Skip keys already in shape and dangerous keys
5638
+ if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
5639
+ const catchallResult = this._config.catchall.safeParse(obj[key]);
5640
+ if (!catchallResult.success) {
5641
+ return {
5642
+ success: false,
5643
+ error: new Error(getMessages().objectField(key, catchallResult.error.message))
5644
+ };
5645
+ }
5646
+ result[key] = catchallResult.data;
5367
5647
  }
5368
- result[key] = catchallResult.data;
5369
5648
  }
5370
5649
  }
5371
5650
  }
@@ -5430,7 +5709,7 @@ class VldObject extends VldBase {
5430
5709
  */
5431
5710
  strict(message) {
5432
5711
  return new VldObject({
5433
- ...this.config,
5712
+ ...this._config,
5434
5713
  strict: true,
5435
5714
  passthrough: false,
5436
5715
  errorMessage: message
@@ -5441,7 +5720,7 @@ class VldObject extends VldBase {
5441
5720
  */
5442
5721
  passthrough() {
5443
5722
  return new VldObject({
5444
- ...this.config,
5723
+ ...this._config,
5445
5724
  strict: false,
5446
5725
  passthrough: true
5447
5726
  });
@@ -5451,11 +5730,11 @@ class VldObject extends VldBase {
5451
5730
  */
5452
5731
  partial() {
5453
5732
  const partialShape = {};
5454
- for (const key in this.config.shape) {
5455
- partialShape[key] = new VldOptional(this.config.shape[key]);
5733
+ for (const key in this._config.shape) {
5734
+ partialShape[key] = new VldOptional(this._config.shape[key]);
5456
5735
  }
5457
5736
  return new VldObject({
5458
- ...this.config,
5737
+ ...this._config,
5459
5738
  shape: partialShape
5460
5739
  });
5461
5740
  }
@@ -5464,8 +5743,8 @@ class VldObject extends VldBase {
5464
5743
  */
5465
5744
  deepPartial() {
5466
5745
  const deepPartialShape = {};
5467
- for (const key in this.config.shape) {
5468
- const validator = this.config.shape[key];
5746
+ for (const key in this._config.shape) {
5747
+ const validator = this._config.shape[key];
5469
5748
  if (validator instanceof VldObject) {
5470
5749
  deepPartialShape[key] = new VldOptional(validator.deepPartial());
5471
5750
  }
@@ -5474,7 +5753,7 @@ class VldObject extends VldBase {
5474
5753
  }
5475
5754
  }
5476
5755
  return new VldObject({
5477
- ...this.config,
5756
+ ...this._config,
5478
5757
  shape: deepPartialShape
5479
5758
  });
5480
5759
  }
@@ -5484,12 +5763,12 @@ class VldObject extends VldBase {
5484
5763
  pick(...keys) {
5485
5764
  const pickedShape = {};
5486
5765
  for (const key of keys) {
5487
- if (key in this.config.shape) {
5488
- pickedShape[key] = this.config.shape[key];
5766
+ if (key in this._config.shape) {
5767
+ pickedShape[key] = this._config.shape[key];
5489
5768
  }
5490
5769
  }
5491
5770
  return new VldObject({
5492
- ...this.config,
5771
+ ...this._config,
5493
5772
  shape: pickedShape
5494
5773
  });
5495
5774
  }
@@ -5499,13 +5778,13 @@ class VldObject extends VldBase {
5499
5778
  omit(...keys) {
5500
5779
  const omittedShape = {};
5501
5780
  const keysToOmit = new Set(keys);
5502
- for (const key in this.config.shape) {
5781
+ for (const key in this._config.shape) {
5503
5782
  if (!keysToOmit.has(key)) {
5504
- omittedShape[key] = this.config.shape[key];
5783
+ omittedShape[key] = this._config.shape[key];
5505
5784
  }
5506
5785
  }
5507
5786
  return new VldObject({
5508
- ...this.config,
5787
+ ...this._config,
5509
5788
  shape: omittedShape
5510
5789
  });
5511
5790
  }
@@ -5514,8 +5793,8 @@ class VldObject extends VldBase {
5514
5793
  */
5515
5794
  extend(extension) {
5516
5795
  return new VldObject({
5517
- ...this.config,
5518
- shape: { ...this.config.shape, ...extension }
5796
+ ...this._config,
5797
+ shape: { ...this._config.shape, ...extension }
5519
5798
  });
5520
5799
  }
5521
5800
  /**
@@ -5523,8 +5802,8 @@ class VldObject extends VldBase {
5523
5802
  */
5524
5803
  merge(other) {
5525
5804
  return new VldObject({
5526
- ...this.config,
5527
- shape: { ...this.config.shape, ...other.config.shape }
5805
+ ...this._config,
5806
+ shape: { ...this._config.shape, ...other.config.shape }
5528
5807
  });
5529
5808
  }
5530
5809
  /**
@@ -5532,8 +5811,8 @@ class VldObject extends VldBase {
5532
5811
  */
5533
5812
  required() {
5534
5813
  const requiredShape = {};
5535
- for (const key in this.config.shape) {
5536
- const validator = this.config.shape[key];
5814
+ for (const key in this._config.shape) {
5815
+ const validator = this._config.shape[key];
5537
5816
  // If it's optional, unwrap it
5538
5817
  if (validator instanceof VldOptional) {
5539
5818
  // BUG-001 FIX: Add defensive check for baseValidator property
@@ -5548,7 +5827,7 @@ class VldObject extends VldBase {
5548
5827
  }
5549
5828
  }
5550
5829
  return new VldObject({
5551
- ...this.config,
5830
+ ...this._config,
5552
5831
  shape: requiredShape
5553
5832
  });
5554
5833
  }
@@ -5558,7 +5837,7 @@ class VldObject extends VldBase {
5558
5837
  */
5559
5838
  catchall(schema) {
5560
5839
  return new VldObject({
5561
- ...this.config,
5840
+ ...this._config,
5562
5841
  catchall: schema,
5563
5842
  passthrough: false // catchall overrides passthrough
5564
5843
  });
@@ -5568,14 +5847,14 @@ class VldObject extends VldBase {
5568
5847
  * Zod 4 API parity - returns the shape object
5569
5848
  */
5570
5849
  get shape() {
5571
- return this.config.shape;
5850
+ return this._config.shape;
5572
5851
  }
5573
5852
  /**
5574
5853
  * Create an enum validator from object keys
5575
5854
  * Zod 4 API parity - creates literal union of keys
5576
5855
  */
5577
5856
  keyof() {
5578
- const keys = Object.keys(this.config.shape);
5857
+ const keys = Object.keys(this._config.shape);
5579
5858
  if (keys.length === 0) {
5580
5859
  throw new Error('Cannot create keyof enum from empty object');
5581
5860
  }
@@ -5594,7 +5873,7 @@ class VldObject extends VldBase {
5594
5873
  */
5595
5874
  safeExtend(extension) {
5596
5875
  // Check for overlapping keys
5597
- const existingKeys = new Set(Object.keys(this.config.shape));
5876
+ const existingKeys = new Set(Object.keys(this._config.shape));
5598
5877
  const extensionKeys = Object.keys(extension);
5599
5878
  const overlappingKeys = [];
5600
5879
  for (const key of extensionKeys) {
@@ -5606,8 +5885,8 @@ class VldObject extends VldBase {
5606
5885
  throw new Error(`safeExtend: ${getMessages().safeExtendOverlap(overlappingKeys)}`);
5607
5886
  }
5608
5887
  return new VldObject({
5609
- ...this.config,
5610
- shape: { ...this.config.shape, ...extension }
5888
+ ...this._config,
5889
+ shape: { ...this._config.shape, ...extension }
5611
5890
  });
5612
5891
  }
5613
5892
  }
@@ -5743,11 +6022,17 @@ class VldLiteral extends VldBase {
5743
6022
  /**
5744
6023
  * Private constructor to enforce immutability
5745
6024
  */
5746
- constructor(literal, errorMessage) {
6025
+ constructor(_literal, errorMessage) {
5747
6026
  super();
5748
- this.literal = literal;
6027
+ this._literal = _literal;
5749
6028
  this.errorMessage = errorMessage;
5750
6029
  }
6030
+ /**
6031
+ * Get the literal value
6032
+ */
6033
+ get literal() {
6034
+ return this._literal;
6035
+ }
5751
6036
  /**
5752
6037
  * Create a new literal validator
5753
6038
  */
@@ -5758,23 +6043,23 @@ class VldLiteral extends VldBase {
5758
6043
  * Parse and validate a literal value
5759
6044
  */
5760
6045
  parse(value) {
5761
- if (value !== this.literal) {
6046
+ if (value !== this._literal) {
5762
6047
  throw new Error(this.errorMessage ||
5763
- getMessages().literalExpected(JSON.stringify(this.literal), JSON.stringify(value)));
6048
+ getMessages().literalExpected(JSON.stringify(this._literal), JSON.stringify(value)));
5764
6049
  }
5765
- return this.literal;
6050
+ return this._literal;
5766
6051
  }
5767
6052
  /**
5768
6053
  * Safely parse and validate a literal value
5769
6054
  */
5770
6055
  safeParse(value) {
5771
- if (value === this.literal) {
5772
- return { success: true, data: this.literal };
6056
+ if (value === this._literal) {
6057
+ return { success: true, data: this._literal };
5773
6058
  }
5774
6059
  return {
5775
6060
  success: false,
5776
6061
  error: new Error(this.errorMessage ||
5777
- getMessages().literalExpected(JSON.stringify(this.literal), JSON.stringify(value)))
6062
+ getMessages().literalExpected(JSON.stringify(this._literal), JSON.stringify(value)))
5778
6063
  };
5779
6064
  }
5780
6065
  }
@@ -6613,27 +6898,56 @@ class VldNan extends VldBase {
6613
6898
  /**
6614
6899
  * Lazy validator - defers schema evaluation until runtime
6615
6900
  * Essential for recursive and self-referencing types
6901
+ *
6902
+ * MEMORY OPTIMIZATION: Uses WeakRef for the cached schema to allow garbage collection
6903
+ * when the validator is no longer in use. This prevents memory leaks in long-running
6904
+ * applications with dynamically created schemas.
6616
6905
  */
6617
6906
  class VldLazy extends VldBase {
6618
6907
  constructor(_schemaGetter) {
6619
6908
  super();
6620
6909
  this._schemaGetter = _schemaGetter;
6621
- this._cachedSchema = null;
6910
+ // Use WeakRef to allow garbage collection of the cached schema
6911
+ this._cachedSchemaRef = null;
6912
+ // Keep a strong reference flag to prevent GC during active use
6913
+ this._strongRef = null;
6622
6914
  }
6623
6915
  static create(schemaGetter) {
6624
6916
  return new VldLazy(schemaGetter);
6625
6917
  }
6626
6918
  /**
6627
6919
  * Get the actual schema, caching it after first retrieval
6920
+ * Uses WeakRef to allow garbage collection when validator is not in use
6628
6921
  */
6629
6922
  _getSchema() {
6630
- if (!this._cachedSchema) {
6631
- this._cachedSchema = this._schemaGetter();
6632
- }
6633
- return this._cachedSchema;
6923
+ // Check if we have a strong reference first (active use)
6924
+ if (this._strongRef) {
6925
+ return this._strongRef;
6926
+ }
6927
+ // Try to get from WeakRef
6928
+ if (this._cachedSchemaRef) {
6929
+ const cached = this._cachedSchemaRef.deref();
6930
+ if (cached) {
6931
+ // Restore strong reference for active use
6932
+ this._strongRef = cached;
6933
+ return cached;
6934
+ }
6935
+ }
6936
+ // Create new schema
6937
+ const schema = this._schemaGetter();
6938
+ this._cachedSchemaRef = new WeakRef(schema);
6939
+ this._strongRef = schema;
6940
+ // Clear strong reference after a tick to allow GC
6941
+ // This keeps the schema alive during synchronous operations
6942
+ // but allows it to be collected if the validator is discarded
6943
+ Promise.resolve().then(() => {
6944
+ this._strongRef = null;
6945
+ });
6946
+ return schema;
6634
6947
  }
6635
6948
  /**
6636
6949
  * Get the inner schema (unwrap)
6950
+ * Returns a strong reference that will keep the schema alive
6637
6951
  */
6638
6952
  unwrap() {
6639
6953
  return this._getSchema();
@@ -6656,11 +6970,10 @@ class VldLazy extends VldBase {
6656
6970
  */
6657
6971
  function extractLiteralValues(schema) {
6658
6972
  if (schema instanceof VldLiteral) {
6659
- const value = schema.literal;
6660
- return [value];
6973
+ return [schema.literal];
6661
6974
  }
6662
6975
  if (schema instanceof VldEnum) {
6663
- return schema.values;
6976
+ return [...schema.values];
6664
6977
  }
6665
6978
  throw new Error('Discriminator must be a literal or enum schema');
6666
6979
  }
@@ -7957,6 +8270,87 @@ class VldUint8Array extends VldBase {
7957
8270
  }
7958
8271
  }
7959
8272
 
8273
+ /**
8274
+ * Promise validator - validates Promise values
8275
+ * Validates that the resolved value matches the inner schema
8276
+ */
8277
+ /**
8278
+ * Promise validator - validates Promise<T> where T matches inner schema
8279
+ * Zod 4 API parity - allows async validation with proper type inference
8280
+ *
8281
+ * Validates that the input is a Promise, and validates the resolved value
8282
+ * against the inner schema when parsed.
8283
+ *
8284
+ * Use parseAsync() or safeParseAsync() for proper async handling.
8285
+ */
8286
+ class VldPromise {
8287
+ constructor(inner) {
8288
+ this.inner = inner;
8289
+ }
8290
+ /**
8291
+ * Check if value is thenable (Promise-like)
8292
+ * Must be called BEFORE wrapping in Promise.resolve
8293
+ */
8294
+ _isThenable(value) {
8295
+ return value !== null && typeof value.then === 'function';
8296
+ }
8297
+ /**
8298
+ * Parse and validate a Promise value asynchronously
8299
+ * @param value The Promise to validate
8300
+ * @returns The validated value
8301
+ * @throws {Error} If validation fails when Promise resolves
8302
+ */
8303
+ async parse(value) {
8304
+ // Check if value is thenable BEFORE wrapping in Promise.resolve
8305
+ if (!this._isThenable(value)) {
8306
+ throw new Error('Expected a Promise value');
8307
+ }
8308
+ // Now we can safely await it
8309
+ const resolved = await Promise.resolve(value);
8310
+ return this.inner.parse(resolved);
8311
+ }
8312
+ /**
8313
+ * Safely parse and validate a Promise value asynchronously
8314
+ * @param value The Promise to validate
8315
+ * @returns A Promise resolving to ParseResult containing the validated value
8316
+ */
8317
+ async safeParse(value) {
8318
+ // Check if value is thenable BEFORE wrapping in Promise.resolve
8319
+ if (!this._isThenable(value)) {
8320
+ return {
8321
+ success: false,
8322
+ error: new Error('Expected a Promise value')
8323
+ };
8324
+ }
8325
+ try {
8326
+ const resolved = await Promise.resolve(value);
8327
+ const validated = this.inner.parse(resolved);
8328
+ return {
8329
+ success: true,
8330
+ data: validated
8331
+ };
8332
+ }
8333
+ catch (err) {
8334
+ return {
8335
+ success: false,
8336
+ error: err instanceof Error ? err : new Error(String(err))
8337
+ };
8338
+ }
8339
+ }
8340
+ }
8341
+ /**
8342
+ * Create a Promise validator
8343
+ * @param inner The schema to validate the resolved value against
8344
+ * @returns A new Promise validator
8345
+ * @example
8346
+ * const promiseSchema = v.promise(v.string());
8347
+ * const result = await promiseSchema.parse(Promise.resolve("hello"));
8348
+ * // result is string
8349
+ */
8350
+ function promise(inner) {
8351
+ return new VldPromise(inner);
8352
+ }
8353
+
7960
8354
  /**
7961
8355
  * BigInt coercion validator that attempts to convert values to bigint
7962
8356
  */
@@ -8101,6 +8495,10 @@ const REGEXES = {
8101
8495
  sha256: /^[a-f0-9]{64}$/i,
8102
8496
  sha384: /^[a-f0-9]{96}$/i,
8103
8497
  sha512: /^[a-f0-9]{128}$/i,
8498
+ // Additional formats (Zod v4 parity)
8499
+ xid: /^[A-HJKMNP-TV-Z0-9]{20}$/, // ksort's XID format
8500
+ guid: /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i, // Alias for UUID
8501
+ httpUrl: /^https?:\/\/[^\s/$.?#].[^\s]*$/, // HTTP/HTTPS URL
8104
8502
  };
8105
8503
  /**
8106
8504
  * Generic string format validator
@@ -8161,6 +8559,9 @@ const mac = () => VldStringFormat.create('mac', (val) => REGEXES.mac.test(val));
8161
8559
  const cidrv4 = () => VldStringFormat.create('cidrv4', (val) => REGEXES.cidrv4.test(val));
8162
8560
  const cidrv6 = () => VldStringFormat.create('cidrv6', (val) => REGEXES.cidrv6.test(val));
8163
8561
  const e164 = () => VldStringFormat.create('e164', (val) => REGEXES.e164.test(val));
8562
+ const xid = () => VldStringFormat.create('xid', (val) => REGEXES.xid.test(val));
8563
+ const guid = () => VldStringFormat.create('guid', (val) => REGEXES.guid.test(val));
8564
+ const httpUrl = () => VldStringFormat.create('httpUrl', (val) => REGEXES.httpUrl.test(val));
8164
8565
  const hash = (algorithm) => VldStringFormat.create('hash', (val) => REGEXES[algorithm]?.test(val) ?? false, `Invalid ${algorithm} hash`);
8165
8566
  /**
8166
8567
  * ISO date/time validators
@@ -9599,6 +10000,534 @@ function createNoOpLogger() {
9599
10000
  return createLogger({ level: 'silent' });
9600
10001
  }
9601
10002
 
10003
+ /**
10004
+ * JSON Schema support for VLD validators
10005
+ * Provides conversion between VLD schemas and JSON Schema
10006
+ */
10007
+ /**
10008
+ * Convert a VLD schema to JSON Schema
10009
+ * @param schema The VLD schema to convert
10010
+ * @param options Conversion options
10011
+ * @returns JSON Schema definition
10012
+ */
10013
+ function toJSONSchema(schema, options = {}) {
10014
+ return schemaToJSONSchema(schema, options);
10015
+ }
10016
+ /**
10017
+ * Convert a JSON Schema to a VLD schema
10018
+ * @param json The JSON Schema definition
10019
+ * @returns A VLD schema
10020
+ */
10021
+ function fromJSONSchema(json) {
10022
+ return jsonSchemaToVLD(json);
10023
+ }
10024
+ /**
10025
+ * Internal function to convert VLD schema to JSON Schema
10026
+ */
10027
+ function schemaToJSONSchema(schema, options) {
10028
+ options.target || 'draft-07';
10029
+ // Handle primitives
10030
+ if (schema.constructor.name === 'VldString') {
10031
+ return buildStringSchema();
10032
+ }
10033
+ if (schema.constructor.name === 'VldNumber') {
10034
+ return buildNumberSchema(schema);
10035
+ }
10036
+ if (schema.constructor.name === 'VldBoolean') {
10037
+ return { type: 'boolean' };
10038
+ }
10039
+ if (schema.constructor.name === 'VldBigInt') {
10040
+ return { type: 'integer' };
10041
+ }
10042
+ if (schema.constructor.name === 'VldDate') {
10043
+ return { type: 'string', format: 'date-time' };
10044
+ }
10045
+ if (schema.constructor.name === 'VldArray') {
10046
+ return buildArraySchema(schema, options);
10047
+ }
10048
+ if (schema.constructor.name === 'VldObject') {
10049
+ return buildObjectSchema(schema, options);
10050
+ }
10051
+ // Handle union types
10052
+ if (schema.constructor.name === 'VldUnion') {
10053
+ return buildUnionSchema(schema, options);
10054
+ }
10055
+ // Handle literal types
10056
+ if (schema.constructor.name === 'VldLiteral') {
10057
+ return buildLiteralSchema(schema);
10058
+ }
10059
+ if (schema.constructor.name === 'VldEnum') {
10060
+ return buildEnumSchema(schema);
10061
+ }
10062
+ if (schema.constructor.name === 'VldRecord') {
10063
+ return buildRecordSchema(schema, options);
10064
+ }
10065
+ if (schema.constructor.name === 'VldTuple') {
10066
+ return buildTupleSchema(schema, options);
10067
+ }
10068
+ if (schema.constructor.name === 'VldIntersection') {
10069
+ return buildIntersectionSchema(schema, options);
10070
+ }
10071
+ if (schema.constructor.name === 'VldOptional') {
10072
+ return buildOptionalSchema(schema, options);
10073
+ }
10074
+ if (schema.constructor.name === 'VldNullable') {
10075
+ return buildNullableSchema(schema, options);
10076
+ }
10077
+ if (schema.constructor.name === 'VldNullish') {
10078
+ return buildNullishSchema(schema, options);
10079
+ }
10080
+ if (schema.constructor.name === 'VldExactOptional') {
10081
+ return buildExactOptionalSchema(schema, options);
10082
+ }
10083
+ if (schema.constructor.name === 'VldLazy') {
10084
+ return { type: 'object' }; // Placeholder for recursive schemas
10085
+ }
10086
+ if (schema.constructor.name === 'VldJson') {
10087
+ return {}; // Any JSON
10088
+ }
10089
+ if (schema.constructor.name === 'VldAny') {
10090
+ return {}; // JSON Schema true
10091
+ }
10092
+ if (schema.constructor.name === 'VldUnknown') {
10093
+ return {}; // JSON Schema true
10094
+ }
10095
+ if (schema.constructor.name === 'VldNever') {
10096
+ return { not: {} }; // JSON Schema false
10097
+ }
10098
+ if (schema.constructor.name === 'VldNull') {
10099
+ return { type: 'null' };
10100
+ }
10101
+ if (schema.constructor.name === 'VldUndefined') {
10102
+ return { not: {} }; // Undefined can't be represented in JSON Schema
10103
+ }
10104
+ if (schema.constructor.name === 'VldNan') {
10105
+ return { type: 'number', not: {} }; // NaN is a number type constraint
10106
+ }
10107
+ if (schema.constructor.name === 'VldVoid') {
10108
+ return { not: {} }; // Void/undefined can't be represented
10109
+ }
10110
+ // Handle branded types - unwrap and continue
10111
+ if (schema.constructor.name === 'VldBrand') {
10112
+ return schemaToJSONSchema(schema.baseValidator, options);
10113
+ }
10114
+ // Handle readonly types
10115
+ if (schema.constructor.name === 'VldReadonly') {
10116
+ return schemaToJSONSchema(schema.baseValidator, options);
10117
+ }
10118
+ // Handle transform types
10119
+ if (schema.constructor.name === 'VldTransform') {
10120
+ return schemaToJSONSchema(schema._inner, options);
10121
+ }
10122
+ // Handle meta types - unwrap metadata
10123
+ if (schema.constructor.name === 'VldMeta') {
10124
+ const metaSchema = schema;
10125
+ const result = schemaToJSONSchema(metaSchema.baseValidator, options);
10126
+ if (options.includeMetadata !== false && metaSchema.metadata) {
10127
+ const meta = metaSchema.metadata;
10128
+ if (meta.title)
10129
+ result.title = meta.title;
10130
+ if (meta.description)
10131
+ result.description = meta.description;
10132
+ if (meta.examples && options.includeExamples)
10133
+ result.examples = meta.examples;
10134
+ if (meta.default !== undefined)
10135
+ result.default = meta.default;
10136
+ if (meta.deprecated)
10137
+ result.deprecated = true;
10138
+ if (meta.readOnly)
10139
+ result.readOnly = true;
10140
+ if (meta.writeOnly)
10141
+ result.writeOnly = true;
10142
+ }
10143
+ return result;
10144
+ }
10145
+ // Handle refine/superRefine types
10146
+ if (schema.constructor.name === 'VldRefine' || schema.constructor.name === 'VldSuperRefine') {
10147
+ return schemaToJSONSchema(schema._inner || schema._baseValidator, options);
10148
+ }
10149
+ // Handle pipe types
10150
+ if (schema.constructor.name === 'VldPipe') {
10151
+ return schemaToJSONSchema(schema._next || schema.second, options);
10152
+ }
10153
+ // Handle default/catch types
10154
+ if (schema.constructor.name === 'VldDefault' || schema.constructor.name === 'VldCatch') {
10155
+ return schemaToJSONSchema(schema._inner || schema._baseValidator, options);
10156
+ }
10157
+ // Handle preprocess types
10158
+ if (schema.constructor.name === 'VldPreprocess') {
10159
+ return schemaToJSONSchema(schema._schema, options);
10160
+ }
10161
+ // Handle string format validators
10162
+ if (schema.constructor.name === 'VldStringFormat') {
10163
+ const formatSchema = schema;
10164
+ return { type: 'string', format: formatSchema._format };
10165
+ }
10166
+ // Fallback for unknown types
10167
+ return {};
10168
+ }
10169
+ /**
10170
+ * Build string JSON Schema
10171
+ */
10172
+ function buildStringSchema(_target) {
10173
+ return { type: 'string' };
10174
+ }
10175
+ /**
10176
+ * Build number JSON Schema from VLD number schema
10177
+ */
10178
+ function buildNumberSchema(schema, _target) {
10179
+ const config = schema.config || {};
10180
+ const checks = config.checks || [];
10181
+ const result = { type: 'number' };
10182
+ for (const check of checks) {
10183
+ // Try to extract constraints from closures
10184
+ const checkStr = check.toString();
10185
+ if (checkStr.includes('>=') || checkStr.includes('min')) {
10186
+ result.minimum = 0; // Default, actual value is in closure
10187
+ }
10188
+ if (checkStr.includes('<=') || checkStr.includes('max')) {
10189
+ result.maximum = 0; // Default, actual value is in closure
10190
+ }
10191
+ if (checkStr.includes('isInteger') || checkStr.includes('int')) {
10192
+ result.type = 'integer';
10193
+ }
10194
+ if (checkStr.includes('isSafeInteger')) {
10195
+ result.type = 'integer';
10196
+ }
10197
+ if (checkStr.includes('Number.isFinite')) ;
10198
+ }
10199
+ return result;
10200
+ }
10201
+ /**
10202
+ * Build array JSON Schema
10203
+ */
10204
+ function buildArraySchema(schema, options) {
10205
+ const inner = schema._item || schema._inner;
10206
+ if (inner) {
10207
+ return {
10208
+ type: 'array',
10209
+ items: toJSONSchema(inner, options)
10210
+ };
10211
+ }
10212
+ return { type: 'array' };
10213
+ }
10214
+ /**
10215
+ * Build object JSON Schema
10216
+ */
10217
+ function buildObjectSchema(schema, options) {
10218
+ const shape = schema._shape || schema.shape;
10219
+ if (!shape)
10220
+ return { type: 'object' };
10221
+ const properties = {};
10222
+ const required = [];
10223
+ for (const [key, value] of Object.entries(shape)) {
10224
+ properties[key] = toJSONSchema(value, options);
10225
+ // VLD objects require all keys by default
10226
+ if (schema._strict !== false) {
10227
+ required.push(key);
10228
+ }
10229
+ }
10230
+ const result = {
10231
+ type: 'object',
10232
+ properties
10233
+ };
10234
+ if (required.length > 0) {
10235
+ result.required = required;
10236
+ }
10237
+ // Handle passthrough mode
10238
+ if (schema._passthrough || schema._loose) {
10239
+ result.additionalProperties = true;
10240
+ }
10241
+ else if (schema._strict) {
10242
+ result.additionalProperties = false;
10243
+ }
10244
+ return result;
10245
+ }
10246
+ /**
10247
+ * Build union JSON Schema
10248
+ */
10249
+ function buildUnionSchema(schema, options) {
10250
+ const validators = schema._validators || schema._options || [];
10251
+ return {
10252
+ anyOf: validators.map((v) => toJSONSchema(v, options))
10253
+ };
10254
+ }
10255
+ /**
10256
+ * Build literal JSON Schema
10257
+ */
10258
+ function buildLiteralSchema(schema) {
10259
+ const value = schema._value || schema.value;
10260
+ if (value === null)
10261
+ return { type: 'null' };
10262
+ if (typeof value === 'string')
10263
+ return { type: 'string', const: value };
10264
+ if (typeof value === 'number')
10265
+ return { type: 'number', const: value };
10266
+ if (typeof value === 'boolean')
10267
+ return { type: 'boolean', const: value };
10268
+ return { const: value };
10269
+ }
10270
+ /**
10271
+ * Build enum JSON Schema
10272
+ */
10273
+ function buildEnumSchema(schema) {
10274
+ const values = schema._values || schema.values;
10275
+ if (Array.isArray(values)) {
10276
+ return { enum: [...values] };
10277
+ }
10278
+ return {};
10279
+ }
10280
+ /**
10281
+ * Build record JSON Schema
10282
+ */
10283
+ function buildRecordSchema(schema, options) {
10284
+ const valueValidator = schema._value || schema._inner;
10285
+ if (valueValidator) {
10286
+ return {
10287
+ type: 'object',
10288
+ additionalProperties: toJSONSchema(valueValidator, options)
10289
+ };
10290
+ }
10291
+ return { type: 'object' };
10292
+ }
10293
+ /**
10294
+ * Build tuple JSON Schema
10295
+ */
10296
+ function buildTupleSchema(schema, options) {
10297
+ const items = schema._items || schema.items;
10298
+ if (!items || !Array.isArray(items)) {
10299
+ return { type: 'array' };
10300
+ }
10301
+ return {
10302
+ type: 'array',
10303
+ items: items.map((item) => toJSONSchema(item, options)),
10304
+ minItems: items.length,
10305
+ maxItems: items.length
10306
+ };
10307
+ }
10308
+ /**
10309
+ * Build intersection JSON Schema
10310
+ */
10311
+ function buildIntersectionSchema(schema, options) {
10312
+ const first = schema._first || schema.first;
10313
+ const second = schema._second || schema.second;
10314
+ const schemas = [];
10315
+ if (first)
10316
+ schemas.push(toJSONSchema(first, options));
10317
+ if (second)
10318
+ schemas.push(toJSONSchema(second, options));
10319
+ if (schemas.length === 0)
10320
+ return {};
10321
+ if (schemas.length === 1)
10322
+ return schemas[0];
10323
+ return { allOf: schemas };
10324
+ }
10325
+ /**
10326
+ * Build optional JSON Schema
10327
+ */
10328
+ function buildOptionalSchema(schema, options) {
10329
+ const inner = schema._inner || schema._baseValidator;
10330
+ if (!inner)
10331
+ return {};
10332
+ const result = toJSONSchema(inner, options);
10333
+ // Remove from required array - but we don't track required here
10334
+ return result;
10335
+ }
10336
+ /**
10337
+ * Build nullable JSON Schema
10338
+ */
10339
+ function buildNullableSchema(schema, options) {
10340
+ const inner = schema._inner || schema._baseValidator;
10341
+ if (!inner)
10342
+ return { type: 'null' };
10343
+ const result = toJSONSchema(inner, options);
10344
+ if (typeof result.type === 'string') {
10345
+ return { type: [result.type, 'null'] };
10346
+ }
10347
+ else if (Array.isArray(result.type)) {
10348
+ result.type.push('null');
10349
+ return result;
10350
+ }
10351
+ return {
10352
+ anyOf: [result, { type: 'null' }]
10353
+ };
10354
+ }
10355
+ /**
10356
+ * Build nullish JSON Schema
10357
+ */
10358
+ function buildNullishSchema(schema, options) {
10359
+ const inner = schema._inner || schema._baseValidator;
10360
+ if (!inner)
10361
+ return {};
10362
+ return toJSONSchema(inner, options);
10363
+ }
10364
+ /**
10365
+ * Build exactOptional JSON Schema
10366
+ */
10367
+ function buildExactOptionalSchema(schema, options) {
10368
+ const inner = schema._inner || schema._baseValidator;
10369
+ if (!inner)
10370
+ return {};
10371
+ return toJSONSchema(inner, options);
10372
+ }
10373
+ /**
10374
+ * Internal function to convert JSON Schema to VLD schema
10375
+ */
10376
+ function jsonSchemaToVLD(json) {
10377
+ // This is a simplified implementation
10378
+ // Full implementation would recursively handle all JSON Schema types
10379
+ const VldString = require('../validators/string').VldString;
10380
+ const VldNumber = require('../validators/number').VldNumber;
10381
+ const VldBoolean = require('../validators/boolean').VldBoolean;
10382
+ const VldObject = require('../validators/object').VldObject;
10383
+ const VldArray = require('../validators/array').VldArray;
10384
+ const VldUnion = require('../validators/union').VldUnion;
10385
+ const VldLiteral = require('../validators/literal').VldLiteral;
10386
+ const VldEnum = require('../validators/enum').VldEnum;
10387
+ const VldNull = require('../validators/null').VldNull;
10388
+ const VldAny = require('../validators/any').VldAny;
10389
+ const VldRecord = require('../validators/record').VldRecord;
10390
+ // Handle $ref
10391
+ if (json.$ref) {
10392
+ // For now, return any - full $ref handling requires registry
10393
+ return VldAny.create();
10394
+ }
10395
+ // Handle anyOf/oneOf (union)
10396
+ if (json.anyOf || json.oneOf) {
10397
+ const options = (json.anyOf || json.oneOf)
10398
+ .map((s) => jsonSchemaToVLD(s))
10399
+ .filter(Boolean);
10400
+ if (options.length > 0) {
10401
+ return VldUnion.create(...options);
10402
+ }
10403
+ }
10404
+ // Handle allOf (intersection)
10405
+ if (json.allOf) {
10406
+ // For intersection, we'd need VldIntersection
10407
+ const first = jsonSchemaToVLD(json.allOf[0]);
10408
+ if (json.allOf.length > 1) {
10409
+ const second = jsonSchemaToVLD({ allOf: json.allOf.slice(1) });
10410
+ const VldIntersection = require('../validators/intersection').VldIntersection;
10411
+ return VldIntersection.create(first, second);
10412
+ }
10413
+ return first;
10414
+ }
10415
+ // Handle not
10416
+ if (json.not) {
10417
+ // For negation, we need special handling
10418
+ return VldAny.create();
10419
+ }
10420
+ // Handle const
10421
+ if (json.const !== undefined) {
10422
+ return VldLiteral.create(json.const);
10423
+ }
10424
+ // Handle enum
10425
+ if (json.enum) {
10426
+ return VldEnum.create(json.enum);
10427
+ }
10428
+ // Handle type
10429
+ const type = json.type;
10430
+ if (type === 'string' || type === undefined) {
10431
+ const s = VldString.create();
10432
+ if (json.minLength)
10433
+ s.minLength = json.minLength;
10434
+ if (json.maxLength)
10435
+ s.maxLength = json.maxLength;
10436
+ if (json.pattern)
10437
+ s.pattern = json.pattern;
10438
+ if (json.format) {
10439
+ // Map JSON Schema formats to VLD validators
10440
+ switch (json.format) {
10441
+ case 'date-time':
10442
+ case 'date':
10443
+ case 'time':
10444
+ // Would need DateTime validator
10445
+ break;
10446
+ case 'email':
10447
+ const email = require('../validators/string-formats').email;
10448
+ return email();
10449
+ case 'uri':
10450
+ case 'uri-reference':
10451
+ const httpUrl = require('../validators/string-formats').httpUrl;
10452
+ return httpUrl();
10453
+ case 'uuid':
10454
+ const uuid = require('../validators/string-formats').uuid;
10455
+ return uuid();
10456
+ }
10457
+ }
10458
+ return s;
10459
+ }
10460
+ if (type === 'number' || type === 'integer') {
10461
+ const n = VldNumber.create();
10462
+ if (type === 'integer')
10463
+ n.int();
10464
+ if (json.minimum !== undefined)
10465
+ n.min(json.minimum);
10466
+ if (json.maximum !== undefined)
10467
+ n.max(json.maximum);
10468
+ if (json.exclusiveMinimum !== undefined)
10469
+ n.gt(json.exclusiveMinimum);
10470
+ if (json.exclusiveMaximum !== undefined)
10471
+ n.lt(json.exclusiveMaximum);
10472
+ if (json.multipleOf !== undefined)
10473
+ n.multipleOf(json.multipleOf);
10474
+ return n;
10475
+ }
10476
+ if (type === 'boolean') {
10477
+ return VldBoolean.create();
10478
+ }
10479
+ if (type === 'array') {
10480
+ if (json.items && !Array.isArray(json.items)) {
10481
+ return VldArray.create(jsonSchemaToVLD(json.items));
10482
+ }
10483
+ return VldArray.create(VldAny.create());
10484
+ }
10485
+ if (type === 'object') {
10486
+ if (json.properties) {
10487
+ const shape = {};
10488
+ const required = json.required || [];
10489
+ for (const [key, propSchema] of Object.entries(json.properties)) {
10490
+ shape[key] = jsonSchemaToVLD(propSchema);
10491
+ }
10492
+ let obj = VldObject.create(shape);
10493
+ if (!required.length) {
10494
+ // If no required fields, make all optional
10495
+ for (const key of Object.keys(shape)) {
10496
+ if (!required.includes(key)) ;
10497
+ }
10498
+ }
10499
+ if (json.additionalProperties === false) {
10500
+ obj = obj.strict();
10501
+ }
10502
+ else if (json.additionalProperties === true) {
10503
+ obj = obj.passthrough();
10504
+ }
10505
+ else if (json.additionalProperties) {
10506
+ // additionalProperties is a schema
10507
+ const valueSchema = jsonSchemaToVLD(json.additionalProperties);
10508
+ obj = VldRecord.create(valueSchema);
10509
+ }
10510
+ return obj;
10511
+ }
10512
+ // Empty object schema
10513
+ return VldObject.create({});
10514
+ }
10515
+ if (type === 'null') {
10516
+ return VldNull.create();
10517
+ }
10518
+ if (Array.isArray(type)) {
10519
+ // Union of types
10520
+ const options = type.map((t) => {
10521
+ return jsonSchemaToVLD({ ...json, type: t });
10522
+ });
10523
+ if (options.length > 0) {
10524
+ return VldUnion.create(...options);
10525
+ }
10526
+ }
10527
+ // Fallback to any
10528
+ return VldAny.create();
10529
+ }
10530
+
9602
10531
  /**
9603
10532
  * VLD - Fast, Type-Safe Validation Library
9604
10533
  * Zero dependencies, blazing fast performance
@@ -9624,6 +10553,11 @@ const v = {
9624
10553
  number: () => VldNumber.create(),
9625
10554
  int: () => VldNumber.create().int(),
9626
10555
  int32: () => VldNumber.create().int().min(-2147483648).max(2147483647),
10556
+ uint32: () => VldNumber.create().uint32(),
10557
+ uint64: () => VldNumber.create().uint64(),
10558
+ int64: () => VldNumber.create().int64(),
10559
+ float32: () => VldNumber.create().float32(),
10560
+ float64: () => VldNumber.create().float64(),
9627
10561
  boolean: () => VldBoolean.create(),
9628
10562
  date: () => VldDate.create(),
9629
10563
  bigint: () => VldBigInt.create(),
@@ -9662,6 +10596,7 @@ const v = {
9662
10596
  optional: (validator) => VldOptional.create(validator),
9663
10597
  nullable: (validator) => VldNullable.create(validator),
9664
10598
  nullish: (validator) => VldNullish.create(validator),
10599
+ exactOptional: (validator) => VldExactOptional.create(validator),
9665
10600
  // Recursive schemas
9666
10601
  lazy: (schemaGetter) => VldLazy.create(schemaGetter),
9667
10602
  // JSON validator
@@ -9710,6 +10645,10 @@ const v = {
9710
10645
  duration: () => iso.duration(),
9711
10646
  },
9712
10647
  stringFormat: (name, validator) => stringFormat(name, validator),
10648
+ // Zod v4 parity string formats
10649
+ xid: () => xid(),
10650
+ guid: () => guid(),
10651
+ httpUrl: () => httpUrl(),
9713
10652
  // Template literal validator
9714
10653
  templateLiteral: (...components) => templateLiteral(...components),
9715
10654
  // Codec validators (binary data validators)
@@ -9717,7 +10656,9 @@ const v = {
9717
10656
  hexBytes: () => VldHex.create(),
9718
10657
  uint8Array: () => VldUint8Array.create(),
9719
10658
  // Codec factory
9720
- codec: (inputValidator, outputValidator, transform) => VldCodec.create(inputValidator, outputValidator, transform)
10659
+ codec: (inputValidator, outputValidator, transform) => VldCodec.create(inputValidator, outputValidator, transform),
10660
+ // Promise validator (Zod v4 parity)
10661
+ promise: (inner) => promise(inner)
9721
10662
  };
9722
10663
 
9723
10664
  exports.Err = Err;
@@ -9726,6 +10667,7 @@ exports.ResultUtils = ResultUtils;
9726
10667
  exports.VldBase = VldBase;
9727
10668
  exports.VldError = VldError;
9728
10669
  exports.VldIntersection = VldIntersection;
10670
+ exports.VldMeta = VldMeta;
9729
10671
  exports.all = all;
9730
10672
  exports.base64Json = base64Json;
9731
10673
  exports.base64ToBytes = base64ToBytes;
@@ -9752,6 +10694,7 @@ exports.epochSecondsToDate = epochSecondsToDate;
9752
10694
  exports.failure = failure;
9753
10695
  exports.flatMap = flatMap;
9754
10696
  exports.flattenError = flattenError;
10697
+ exports.fromJSONSchema = fromJSONSchema;
9755
10698
  exports.fromNullable = fromNullable;
9756
10699
  exports.getLocale = getLocale;
9757
10700
  exports.getLogger = getLogger;
@@ -9794,6 +10737,7 @@ exports.stringToUint8Array = stringToUint8Array;
9794
10737
  exports.strip = strip;
9795
10738
  exports.success = success;
9796
10739
  exports.supportsColor = supportsColor;
10740
+ exports.toJSONSchema = toJSONSchema;
9797
10741
  exports.treeifyError = treeifyError;
9798
10742
  exports.tryCatch = tryCatch;
9799
10743
  exports.tryCatchAsync = tryCatchAsync;