@oxog/vld 2.0.1 → 2.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +52 -0
- package/LICENSE +20 -20
- package/README.md +2 -2
- package/dist/chunks/{bigint-ZKkWLdmi.js → bigint-DgsCr2dC.js} +96 -15
- package/dist/chunks/bigint-DgsCr2dC.js.map +1 -0
- package/dist/chunks/{date-7Iz2BGsL.js → date-ODue_rtq.js} +2 -2
- package/dist/chunks/{date-7Iz2BGsL.js.map → date-ODue_rtq.js.map} +1 -1
- package/dist/chunks/{index-eWXdjyyy.js → index-BZqWZvVe.js} +4 -4
- package/dist/chunks/{index-eWXdjyyy.js.map → index-BZqWZvVe.js.map} +1 -1
- package/dist/chunks/{index-BoV-4S1F.js → index-CETyGkrv.js} +3 -3
- package/dist/chunks/index-CETyGkrv.js.map +1 -0
- package/dist/chunks/{json-nIhyRPOU.js → json-o20GFhTh.js} +225 -138
- package/dist/chunks/json-o20GFhTh.js.map +1 -0
- package/dist/chunks/{unknown-CjSRcELI.js → unknown-SLIH1VCf.js} +2 -2
- package/dist/chunks/unknown-SLIH1VCf.js.map +1 -0
- package/dist/cjs/index.cjs +318 -150
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/locales/index.cjs.map +1 -1
- package/dist/cjs/mini.cjs +317 -149
- package/dist/cjs/mini.cjs.map +1 -1
- package/dist/codecs/index.js +3 -3
- package/dist/coercion/index.js +2 -2
- package/dist/coercion/index.js.map +1 -1
- package/dist/index.js +8 -8
- package/dist/mini.js +5 -5
- package/dist/validators/array.d.ts +5 -0
- package/dist/validators/array.d.ts.map +1 -1
- package/dist/validators/discriminated-union.d.ts.map +1 -1
- package/dist/validators/enum.d.ts +5 -1
- package/dist/validators/enum.d.ts.map +1 -1
- package/dist/validators/index.js +4 -4
- package/dist/validators/lazy.d.ts +8 -1
- package/dist/validators/lazy.d.ts.map +1 -1
- package/dist/validators/literal.d.ts +5 -1
- package/dist/validators/literal.d.ts.map +1 -1
- package/dist/validators/object.d.ts +30 -3
- package/dist/validators/object.d.ts.map +1 -1
- package/dist/validators/string.d.ts +12 -1
- package/dist/validators/string.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunks/bigint-ZKkWLdmi.js.map +0 -1
- package/dist/chunks/index-BoV-4S1F.js.map +0 -1
- package/dist/chunks/json-nIhyRPOU.js.map +0 -1
- package/dist/chunks/unknown-CjSRcELI.js.map +0 -1
package/dist/cjs/mini.cjs
CHANGED
|
@@ -3461,6 +3461,7 @@ const REGEX_PATTERNS = {
|
|
|
3461
3461
|
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]?)$/};
|
|
3462
3462
|
/**
|
|
3463
3463
|
* Immutable string validator with chainable methods
|
|
3464
|
+
* Features pre-compiled validation functions for maximum performance
|
|
3464
3465
|
*/
|
|
3465
3466
|
class VldString extends VldBase {
|
|
3466
3467
|
/**
|
|
@@ -3468,12 +3469,100 @@ class VldString extends VldBase {
|
|
|
3468
3469
|
*/
|
|
3469
3470
|
constructor(config) {
|
|
3470
3471
|
super();
|
|
3472
|
+
// Cache for pre-compiled validation function
|
|
3473
|
+
this._compiledValidator = null;
|
|
3471
3474
|
this.config = {
|
|
3472
3475
|
checks: config?.checks || [],
|
|
3473
3476
|
transforms: config?.transforms || [],
|
|
3474
3477
|
errorMessage: config?.errorMessage
|
|
3475
3478
|
};
|
|
3476
3479
|
}
|
|
3480
|
+
/**
|
|
3481
|
+
* Compile all transforms and checks into a single optimized function
|
|
3482
|
+
* This eliminates loop overhead and enables better JIT optimization
|
|
3483
|
+
*/
|
|
3484
|
+
_compileValidator() {
|
|
3485
|
+
const transforms = this.config.transforms;
|
|
3486
|
+
const checks = this.config.checks;
|
|
3487
|
+
const errorMessage = this.config.errorMessage || getMessages().invalidString;
|
|
3488
|
+
// Fast path: no transforms or checks
|
|
3489
|
+
if (transforms.length === 0 && checks.length === 0) {
|
|
3490
|
+
return (value) => ({ success: true, value });
|
|
3491
|
+
}
|
|
3492
|
+
// Fast path: only transforms, no checks
|
|
3493
|
+
if (checks.length === 0) {
|
|
3494
|
+
switch (transforms.length) {
|
|
3495
|
+
case 1:
|
|
3496
|
+
return (value) => ({ success: true, value: transforms[0](value) });
|
|
3497
|
+
case 2:
|
|
3498
|
+
return (value) => ({ success: true, value: transforms[1](transforms[0](value)) });
|
|
3499
|
+
case 3:
|
|
3500
|
+
return (value) => ({ success: true, value: transforms[2](transforms[1](transforms[0](value))) });
|
|
3501
|
+
default:
|
|
3502
|
+
return (value) => {
|
|
3503
|
+
let result = value;
|
|
3504
|
+
for (let i = 0; i < transforms.length; i++) {
|
|
3505
|
+
result = transforms[i](result);
|
|
3506
|
+
}
|
|
3507
|
+
return { success: true, value: result };
|
|
3508
|
+
};
|
|
3509
|
+
}
|
|
3510
|
+
}
|
|
3511
|
+
// Fast path: only checks, no transforms
|
|
3512
|
+
if (transforms.length === 0) {
|
|
3513
|
+
switch (checks.length) {
|
|
3514
|
+
case 1:
|
|
3515
|
+
return (value) => {
|
|
3516
|
+
if (!checks[0](value))
|
|
3517
|
+
return { success: false, error: errorMessage };
|
|
3518
|
+
return { success: true, value };
|
|
3519
|
+
};
|
|
3520
|
+
case 2:
|
|
3521
|
+
return (value) => {
|
|
3522
|
+
if (!checks[0](value) || !checks[1](value))
|
|
3523
|
+
return { success: false, error: errorMessage };
|
|
3524
|
+
return { success: true, value };
|
|
3525
|
+
};
|
|
3526
|
+
case 3:
|
|
3527
|
+
return (value) => {
|
|
3528
|
+
if (!checks[0](value) || !checks[1](value) || !checks[2](value))
|
|
3529
|
+
return { success: false, error: errorMessage };
|
|
3530
|
+
return { success: true, value };
|
|
3531
|
+
};
|
|
3532
|
+
default:
|
|
3533
|
+
return (value) => {
|
|
3534
|
+
for (let i = 0; i < checks.length; i++) {
|
|
3535
|
+
if (!checks[i](value))
|
|
3536
|
+
return { success: false, error: errorMessage };
|
|
3537
|
+
}
|
|
3538
|
+
return { success: true, value };
|
|
3539
|
+
};
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3542
|
+
// General case: both transforms and checks
|
|
3543
|
+
return (value) => {
|
|
3544
|
+
let result = value;
|
|
3545
|
+
// Apply transforms
|
|
3546
|
+
for (let i = 0; i < transforms.length; i++) {
|
|
3547
|
+
result = transforms[i](result);
|
|
3548
|
+
}
|
|
3549
|
+
// Apply checks
|
|
3550
|
+
for (let i = 0; i < checks.length; i++) {
|
|
3551
|
+
if (!checks[i](result))
|
|
3552
|
+
return { success: false, error: errorMessage };
|
|
3553
|
+
}
|
|
3554
|
+
return { success: true, value: result };
|
|
3555
|
+
};
|
|
3556
|
+
}
|
|
3557
|
+
/**
|
|
3558
|
+
* Get the cached compiled validator, creating it if necessary
|
|
3559
|
+
*/
|
|
3560
|
+
_getCompiledValidator() {
|
|
3561
|
+
if (!this._compiledValidator) {
|
|
3562
|
+
this._compiledValidator = this._compileValidator();
|
|
3563
|
+
}
|
|
3564
|
+
return this._compiledValidator;
|
|
3565
|
+
}
|
|
3477
3566
|
/**
|
|
3478
3567
|
* Create a new string validator
|
|
3479
3568
|
*/
|
|
@@ -3481,26 +3570,18 @@ class VldString extends VldBase {
|
|
|
3481
3570
|
return new VldString();
|
|
3482
3571
|
}
|
|
3483
3572
|
/**
|
|
3484
|
-
* Parse and validate a string value - ultra-optimized
|
|
3573
|
+
* Parse and validate a string value - ultra-optimized with pre-compiled validator
|
|
3485
3574
|
*/
|
|
3486
3575
|
parse(value) {
|
|
3487
3576
|
if (typeof value !== 'string') {
|
|
3488
3577
|
throw new Error(this.config.errorMessage || getMessages().invalidString);
|
|
3489
3578
|
}
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3494
|
-
result = this.config.transforms[i](result);
|
|
3495
|
-
}
|
|
3496
|
-
// Apply checks with optimized loop and early termination
|
|
3497
|
-
const checksLength = this.config.checks.length;
|
|
3498
|
-
for (let i = 0; i < checksLength; i++) {
|
|
3499
|
-
if (!this.config.checks[i](result)) {
|
|
3500
|
-
throw new Error(this.config.errorMessage || getMessages().invalidString);
|
|
3501
|
-
}
|
|
3579
|
+
// Use pre-compiled validator for maximum performance
|
|
3580
|
+
const result = this._getCompiledValidator()(value);
|
|
3581
|
+
if (!result.success) {
|
|
3582
|
+
throw new Error(result.error);
|
|
3502
3583
|
}
|
|
3503
|
-
return result;
|
|
3584
|
+
return result.value;
|
|
3504
3585
|
}
|
|
3505
3586
|
/**
|
|
3506
3587
|
* Safely parse and validate a string value
|
|
@@ -4602,16 +4683,9 @@ class VldArray extends VldBase {
|
|
|
4602
4683
|
}
|
|
4603
4684
|
result[i] = parseResult.data; // Direct assignment is faster than push
|
|
4604
4685
|
}
|
|
4605
|
-
// Check uniqueness if required
|
|
4686
|
+
// Check uniqueness if required - optimized with Map-based approach
|
|
4606
4687
|
if (this.config.unique) {
|
|
4607
|
-
|
|
4608
|
-
for (const item of result) {
|
|
4609
|
-
const key = typeof item === 'object' ? this.stableStringify(item) : item;
|
|
4610
|
-
if (seen.has(key)) {
|
|
4611
|
-
throw new Error('Array must contain unique items');
|
|
4612
|
-
}
|
|
4613
|
-
seen.add(key);
|
|
4614
|
-
}
|
|
4688
|
+
this.checkUnique(result);
|
|
4615
4689
|
}
|
|
4616
4690
|
return result;
|
|
4617
4691
|
}
|
|
@@ -4626,6 +4700,38 @@ class VldArray extends VldBase {
|
|
|
4626
4700
|
return { success: false, error: error };
|
|
4627
4701
|
}
|
|
4628
4702
|
}
|
|
4703
|
+
/**
|
|
4704
|
+
* Optimized uniqueness check using Map-based approach
|
|
4705
|
+
* Avoids repeated stableStringify calls by caching serialized values
|
|
4706
|
+
*/
|
|
4707
|
+
checkUnique(items) {
|
|
4708
|
+
const seen = new Set(); // Set of seen keys
|
|
4709
|
+
const objectKeys = new WeakMap(); // Cache for object->string mappings
|
|
4710
|
+
for (const item of items) {
|
|
4711
|
+
let key;
|
|
4712
|
+
if (typeof item === 'object' && item !== null) {
|
|
4713
|
+
// Check if we've already serialized this object reference
|
|
4714
|
+
const cached = objectKeys.get(item);
|
|
4715
|
+
if (cached !== undefined) {
|
|
4716
|
+
key = cached;
|
|
4717
|
+
}
|
|
4718
|
+
else {
|
|
4719
|
+
// Serialize and cache
|
|
4720
|
+
const serialized = this.stableStringify(item);
|
|
4721
|
+
key = serialized;
|
|
4722
|
+
objectKeys.set(item, serialized);
|
|
4723
|
+
}
|
|
4724
|
+
}
|
|
4725
|
+
else {
|
|
4726
|
+
// Primitives can be used directly as keys
|
|
4727
|
+
key = item;
|
|
4728
|
+
}
|
|
4729
|
+
if (seen.has(key)) {
|
|
4730
|
+
throw new Error('Array must contain unique items');
|
|
4731
|
+
}
|
|
4732
|
+
seen.add(key);
|
|
4733
|
+
}
|
|
4734
|
+
}
|
|
4629
4735
|
/**
|
|
4630
4736
|
* Create a stable string representation of an object for hashing
|
|
4631
4737
|
* Handles circular references and deep nesting gracefully
|
|
@@ -4753,11 +4859,17 @@ class VldEnum extends VldBase {
|
|
|
4753
4859
|
/**
|
|
4754
4860
|
* Private constructor to enforce immutability
|
|
4755
4861
|
*/
|
|
4756
|
-
constructor(
|
|
4862
|
+
constructor(_values, errorMessage) {
|
|
4757
4863
|
super();
|
|
4758
|
-
this.
|
|
4864
|
+
this._values = _values;
|
|
4759
4865
|
this.errorMessage = errorMessage;
|
|
4760
4866
|
}
|
|
4867
|
+
/**
|
|
4868
|
+
* Get the enum values
|
|
4869
|
+
*/
|
|
4870
|
+
get values() {
|
|
4871
|
+
return this._values;
|
|
4872
|
+
}
|
|
4761
4873
|
/**
|
|
4762
4874
|
* Create a new enum validator
|
|
4763
4875
|
*/
|
|
@@ -4771,11 +4883,11 @@ class VldEnum extends VldBase {
|
|
|
4771
4883
|
// BUG-002 FIX: Add type check before includes() to prevent type confusion
|
|
4772
4884
|
if (typeof value !== 'string') {
|
|
4773
4885
|
throw new Error(this.errorMessage ||
|
|
4774
|
-
getMessages().enumExpected([...this.
|
|
4886
|
+
getMessages().enumExpected([...this._values], JSON.stringify(value)));
|
|
4775
4887
|
}
|
|
4776
|
-
if (!this.
|
|
4888
|
+
if (!this._values.includes(value)) {
|
|
4777
4889
|
throw new Error(this.errorMessage ||
|
|
4778
|
-
getMessages().enumExpected([...this.
|
|
4890
|
+
getMessages().enumExpected([...this._values], JSON.stringify(value)));
|
|
4779
4891
|
}
|
|
4780
4892
|
return value;
|
|
4781
4893
|
}
|
|
@@ -4788,16 +4900,16 @@ class VldEnum extends VldBase {
|
|
|
4788
4900
|
return {
|
|
4789
4901
|
success: false,
|
|
4790
4902
|
error: new Error(this.errorMessage ||
|
|
4791
|
-
getMessages().enumExpected([...this.
|
|
4903
|
+
getMessages().enumExpected([...this._values], JSON.stringify(value)))
|
|
4792
4904
|
};
|
|
4793
4905
|
}
|
|
4794
|
-
if (this.
|
|
4906
|
+
if (this._values.includes(value)) {
|
|
4795
4907
|
return { success: true, data: value };
|
|
4796
4908
|
}
|
|
4797
4909
|
return {
|
|
4798
4910
|
success: false,
|
|
4799
4911
|
error: new Error(this.errorMessage ||
|
|
4800
|
-
getMessages().enumExpected([...this.
|
|
4912
|
+
getMessages().enumExpected([...this._values], JSON.stringify(value)))
|
|
4801
4913
|
};
|
|
4802
4914
|
}
|
|
4803
4915
|
/**
|
|
@@ -4805,7 +4917,7 @@ class VldEnum extends VldBase {
|
|
|
4805
4917
|
* Creates a new enum validator without the specified values
|
|
4806
4918
|
*/
|
|
4807
4919
|
exclude(...excludeValues) {
|
|
4808
|
-
const filtered = this.
|
|
4920
|
+
const filtered = this._values.filter(v => !excludeValues.includes(v));
|
|
4809
4921
|
if (filtered.length === 0) {
|
|
4810
4922
|
throw new Error('Cannot exclude all enum values');
|
|
4811
4923
|
}
|
|
@@ -4817,7 +4929,7 @@ class VldEnum extends VldBase {
|
|
|
4817
4929
|
* Creates a new enum validator with only the specified values
|
|
4818
4930
|
*/
|
|
4819
4931
|
extract(...extractValues) {
|
|
4820
|
-
const extracted = this.
|
|
4932
|
+
const extracted = this._values.filter(v => extractValues.includes(v));
|
|
4821
4933
|
if (extracted.length === 0) {
|
|
4822
4934
|
throw new Error('Cannot extract non-existent enum values');
|
|
4823
4935
|
}
|
|
@@ -5338,10 +5450,30 @@ class VldObject extends VldBase {
|
|
|
5338
5450
|
*/
|
|
5339
5451
|
constructor(config) {
|
|
5340
5452
|
super();
|
|
5341
|
-
this.
|
|
5453
|
+
this._config = config;
|
|
5342
5454
|
// Pre-compute shape keys for faster access
|
|
5343
|
-
this.
|
|
5344
|
-
this.
|
|
5455
|
+
this._shapeKeys = Object.keys(config.shape);
|
|
5456
|
+
this._shapeKeysSet = new Set(this._shapeKeys);
|
|
5457
|
+
}
|
|
5458
|
+
/**
|
|
5459
|
+
* Get the validator configuration
|
|
5460
|
+
* @internal Used by discriminated union validator
|
|
5461
|
+
*/
|
|
5462
|
+
get config() {
|
|
5463
|
+
return this._config;
|
|
5464
|
+
}
|
|
5465
|
+
/**
|
|
5466
|
+
* Get the shape keys array
|
|
5467
|
+
*/
|
|
5468
|
+
get shapeKeys() {
|
|
5469
|
+
return this._shapeKeys;
|
|
5470
|
+
}
|
|
5471
|
+
/**
|
|
5472
|
+
* Get the shape keys set for O(1) lookups
|
|
5473
|
+
* @internal Used by discriminated union validator
|
|
5474
|
+
*/
|
|
5475
|
+
get shapeKeysSet() {
|
|
5476
|
+
return this._shapeKeysSet;
|
|
5345
5477
|
}
|
|
5346
5478
|
/**
|
|
5347
5479
|
* Create a new object validator
|
|
@@ -5356,14 +5488,14 @@ class VldObject extends VldBase {
|
|
|
5356
5488
|
parse(value) {
|
|
5357
5489
|
// Fast type check
|
|
5358
5490
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
5359
|
-
throw new Error(this.
|
|
5491
|
+
throw new Error(this._config.errorMessage || getMessages().invalidObject);
|
|
5360
5492
|
}
|
|
5361
5493
|
const obj = value;
|
|
5362
5494
|
const result = {};
|
|
5363
5495
|
// Ultra-optimized field validation with inline fast paths
|
|
5364
|
-
for (let i = 0; i < this.
|
|
5365
|
-
const key = this.
|
|
5366
|
-
const validator = this.
|
|
5496
|
+
for (let i = 0; i < this._shapeKeys.length; i++) {
|
|
5497
|
+
const key = this._shapeKeys[i];
|
|
5498
|
+
const validator = this._config.shape[key];
|
|
5367
5499
|
const fieldValue = obj[key];
|
|
5368
5500
|
// BUG-NEW-002 FIX: Use instanceof instead of constructor.name
|
|
5369
5501
|
// constructor.name breaks in minified builds where class names become 'a', 'b', etc.
|
|
@@ -5420,38 +5552,39 @@ class VldObject extends VldBase {
|
|
|
5420
5552
|
result[key] = parseResult.data;
|
|
5421
5553
|
}
|
|
5422
5554
|
}
|
|
5423
|
-
// Handle strict
|
|
5424
|
-
if (this.
|
|
5555
|
+
// Handle strict/passthrough/catchall modes - optimized single Object.keys() call
|
|
5556
|
+
if (this._config.strict || this._config.passthrough || this._config.catchall) {
|
|
5425
5557
|
const objKeys = Object.keys(obj);
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5558
|
+
// Handle strict mode - optimized with Set
|
|
5559
|
+
if (this._config.strict) {
|
|
5560
|
+
const extraKeys = [];
|
|
5561
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5562
|
+
if (!this._shapeKeysSet.has(objKeys[i])) {
|
|
5563
|
+
extraKeys.push(objKeys[i]);
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
if (extraKeys.length > 0) {
|
|
5567
|
+
throw new Error(getMessages().unexpectedKeys(extraKeys));
|
|
5430
5568
|
}
|
|
5431
5569
|
}
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
const key = objKeys[i];
|
|
5441
|
-
// Skip dangerous keys to prevent prototype pollution
|
|
5442
|
-
if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5443
|
-
result[key] = obj[key];
|
|
5570
|
+
// Handle passthrough mode - optimized with comprehensive prototype pollution protection
|
|
5571
|
+
if (this._config.passthrough) {
|
|
5572
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5573
|
+
const key = objKeys[i];
|
|
5574
|
+
// Skip dangerous keys to prevent prototype pollution
|
|
5575
|
+
if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5576
|
+
result[key] = obj[key];
|
|
5577
|
+
}
|
|
5444
5578
|
}
|
|
5445
5579
|
}
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
result[key] = this.config.catchall.parse(obj[key]);
|
|
5580
|
+
// Handle catchall - validate extra keys with catchall validator
|
|
5581
|
+
if (this._config.catchall) {
|
|
5582
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5583
|
+
const key = objKeys[i];
|
|
5584
|
+
// Skip keys already in shape and dangerous keys
|
|
5585
|
+
if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5586
|
+
result[key] = this._config.catchall.parse(obj[key]);
|
|
5587
|
+
}
|
|
5455
5588
|
}
|
|
5456
5589
|
}
|
|
5457
5590
|
}
|
|
@@ -5466,15 +5599,15 @@ class VldObject extends VldBase {
|
|
|
5466
5599
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
5467
5600
|
return {
|
|
5468
5601
|
success: false,
|
|
5469
|
-
error: new Error(this.
|
|
5602
|
+
error: new Error(this._config.errorMessage || getMessages().invalidObject)
|
|
5470
5603
|
};
|
|
5471
5604
|
}
|
|
5472
5605
|
const obj = value;
|
|
5473
5606
|
const result = {};
|
|
5474
5607
|
// Validate all fields
|
|
5475
|
-
for (let i = 0; i < this.
|
|
5476
|
-
const key = this.
|
|
5477
|
-
const validator = this.
|
|
5608
|
+
for (let i = 0; i < this._shapeKeys.length; i++) {
|
|
5609
|
+
const key = this._shapeKeys[i];
|
|
5610
|
+
const validator = this._config.shape[key];
|
|
5478
5611
|
const fieldValue = obj[key];
|
|
5479
5612
|
const parseResult = validator.safeParse(fieldValue);
|
|
5480
5613
|
if (parseResult.success) {
|
|
@@ -5487,48 +5620,49 @@ class VldObject extends VldBase {
|
|
|
5487
5620
|
};
|
|
5488
5621
|
}
|
|
5489
5622
|
}
|
|
5490
|
-
// Handle strict
|
|
5491
|
-
if (this.
|
|
5623
|
+
// Handle strict/passthrough/catchall modes - optimized single Object.keys() call
|
|
5624
|
+
if (this._config.strict || this._config.passthrough || this._config.catchall) {
|
|
5492
5625
|
const objKeys = Object.keys(obj);
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5626
|
+
// Handle strict mode
|
|
5627
|
+
if (this._config.strict) {
|
|
5628
|
+
const extraKeys = [];
|
|
5629
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5630
|
+
if (!this._shapeKeysSet.has(objKeys[i])) {
|
|
5631
|
+
extraKeys.push(objKeys[i]);
|
|
5632
|
+
}
|
|
5633
|
+
}
|
|
5634
|
+
if (extraKeys.length > 0) {
|
|
5635
|
+
return {
|
|
5636
|
+
success: false,
|
|
5637
|
+
error: new Error(getMessages().unexpectedKeys(extraKeys))
|
|
5638
|
+
};
|
|
5497
5639
|
}
|
|
5498
5640
|
}
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
if (this.config.passthrough) {
|
|
5508
|
-
const objKeys = Object.keys(obj);
|
|
5509
|
-
for (let i = 0; i < objKeys.length; i++) {
|
|
5510
|
-
const key = objKeys[i];
|
|
5511
|
-
// Skip dangerous keys to prevent prototype pollution
|
|
5512
|
-
if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5513
|
-
result[key] = obj[key];
|
|
5641
|
+
// Handle passthrough mode with comprehensive prototype pollution protection
|
|
5642
|
+
if (this._config.passthrough) {
|
|
5643
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5644
|
+
const key = objKeys[i];
|
|
5645
|
+
// Skip dangerous keys to prevent prototype pollution
|
|
5646
|
+
if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5647
|
+
result[key] = obj[key];
|
|
5648
|
+
}
|
|
5514
5649
|
}
|
|
5515
5650
|
}
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5651
|
+
// Handle catchall - validate extra keys with catchall validator
|
|
5652
|
+
if (this._config.catchall) {
|
|
5653
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5654
|
+
const key = objKeys[i];
|
|
5655
|
+
// Skip keys already in shape and dangerous keys
|
|
5656
|
+
if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5657
|
+
const catchallResult = this._config.catchall.safeParse(obj[key]);
|
|
5658
|
+
if (!catchallResult.success) {
|
|
5659
|
+
return {
|
|
5660
|
+
success: false,
|
|
5661
|
+
error: new Error(getMessages().objectField(key, catchallResult.error.message))
|
|
5662
|
+
};
|
|
5663
|
+
}
|
|
5664
|
+
result[key] = catchallResult.data;
|
|
5530
5665
|
}
|
|
5531
|
-
result[key] = catchallResult.data;
|
|
5532
5666
|
}
|
|
5533
5667
|
}
|
|
5534
5668
|
}
|
|
@@ -5593,7 +5727,7 @@ class VldObject extends VldBase {
|
|
|
5593
5727
|
*/
|
|
5594
5728
|
strict(message) {
|
|
5595
5729
|
return new VldObject({
|
|
5596
|
-
...this.
|
|
5730
|
+
...this._config,
|
|
5597
5731
|
strict: true,
|
|
5598
5732
|
passthrough: false,
|
|
5599
5733
|
errorMessage: message
|
|
@@ -5604,7 +5738,7 @@ class VldObject extends VldBase {
|
|
|
5604
5738
|
*/
|
|
5605
5739
|
passthrough() {
|
|
5606
5740
|
return new VldObject({
|
|
5607
|
-
...this.
|
|
5741
|
+
...this._config,
|
|
5608
5742
|
strict: false,
|
|
5609
5743
|
passthrough: true
|
|
5610
5744
|
});
|
|
@@ -5614,11 +5748,11 @@ class VldObject extends VldBase {
|
|
|
5614
5748
|
*/
|
|
5615
5749
|
partial() {
|
|
5616
5750
|
const partialShape = {};
|
|
5617
|
-
for (const key in this.
|
|
5618
|
-
partialShape[key] = new VldOptional(this.
|
|
5751
|
+
for (const key in this._config.shape) {
|
|
5752
|
+
partialShape[key] = new VldOptional(this._config.shape[key]);
|
|
5619
5753
|
}
|
|
5620
5754
|
return new VldObject({
|
|
5621
|
-
...this.
|
|
5755
|
+
...this._config,
|
|
5622
5756
|
shape: partialShape
|
|
5623
5757
|
});
|
|
5624
5758
|
}
|
|
@@ -5627,8 +5761,8 @@ class VldObject extends VldBase {
|
|
|
5627
5761
|
*/
|
|
5628
5762
|
deepPartial() {
|
|
5629
5763
|
const deepPartialShape = {};
|
|
5630
|
-
for (const key in this.
|
|
5631
|
-
const validator = this.
|
|
5764
|
+
for (const key in this._config.shape) {
|
|
5765
|
+
const validator = this._config.shape[key];
|
|
5632
5766
|
if (validator instanceof VldObject) {
|
|
5633
5767
|
deepPartialShape[key] = new VldOptional(validator.deepPartial());
|
|
5634
5768
|
}
|
|
@@ -5637,7 +5771,7 @@ class VldObject extends VldBase {
|
|
|
5637
5771
|
}
|
|
5638
5772
|
}
|
|
5639
5773
|
return new VldObject({
|
|
5640
|
-
...this.
|
|
5774
|
+
...this._config,
|
|
5641
5775
|
shape: deepPartialShape
|
|
5642
5776
|
});
|
|
5643
5777
|
}
|
|
@@ -5647,12 +5781,12 @@ class VldObject extends VldBase {
|
|
|
5647
5781
|
pick(...keys) {
|
|
5648
5782
|
const pickedShape = {};
|
|
5649
5783
|
for (const key of keys) {
|
|
5650
|
-
if (key in this.
|
|
5651
|
-
pickedShape[key] = this.
|
|
5784
|
+
if (key in this._config.shape) {
|
|
5785
|
+
pickedShape[key] = this._config.shape[key];
|
|
5652
5786
|
}
|
|
5653
5787
|
}
|
|
5654
5788
|
return new VldObject({
|
|
5655
|
-
...this.
|
|
5789
|
+
...this._config,
|
|
5656
5790
|
shape: pickedShape
|
|
5657
5791
|
});
|
|
5658
5792
|
}
|
|
@@ -5662,13 +5796,13 @@ class VldObject extends VldBase {
|
|
|
5662
5796
|
omit(...keys) {
|
|
5663
5797
|
const omittedShape = {};
|
|
5664
5798
|
const keysToOmit = new Set(keys);
|
|
5665
|
-
for (const key in this.
|
|
5799
|
+
for (const key in this._config.shape) {
|
|
5666
5800
|
if (!keysToOmit.has(key)) {
|
|
5667
|
-
omittedShape[key] = this.
|
|
5801
|
+
omittedShape[key] = this._config.shape[key];
|
|
5668
5802
|
}
|
|
5669
5803
|
}
|
|
5670
5804
|
return new VldObject({
|
|
5671
|
-
...this.
|
|
5805
|
+
...this._config,
|
|
5672
5806
|
shape: omittedShape
|
|
5673
5807
|
});
|
|
5674
5808
|
}
|
|
@@ -5677,8 +5811,8 @@ class VldObject extends VldBase {
|
|
|
5677
5811
|
*/
|
|
5678
5812
|
extend(extension) {
|
|
5679
5813
|
return new VldObject({
|
|
5680
|
-
...this.
|
|
5681
|
-
shape: { ...this.
|
|
5814
|
+
...this._config,
|
|
5815
|
+
shape: { ...this._config.shape, ...extension }
|
|
5682
5816
|
});
|
|
5683
5817
|
}
|
|
5684
5818
|
/**
|
|
@@ -5686,8 +5820,8 @@ class VldObject extends VldBase {
|
|
|
5686
5820
|
*/
|
|
5687
5821
|
merge(other) {
|
|
5688
5822
|
return new VldObject({
|
|
5689
|
-
...this.
|
|
5690
|
-
shape: { ...this.
|
|
5823
|
+
...this._config,
|
|
5824
|
+
shape: { ...this._config.shape, ...other.config.shape }
|
|
5691
5825
|
});
|
|
5692
5826
|
}
|
|
5693
5827
|
/**
|
|
@@ -5695,8 +5829,8 @@ class VldObject extends VldBase {
|
|
|
5695
5829
|
*/
|
|
5696
5830
|
required() {
|
|
5697
5831
|
const requiredShape = {};
|
|
5698
|
-
for (const key in this.
|
|
5699
|
-
const validator = this.
|
|
5832
|
+
for (const key in this._config.shape) {
|
|
5833
|
+
const validator = this._config.shape[key];
|
|
5700
5834
|
// If it's optional, unwrap it
|
|
5701
5835
|
if (validator instanceof VldOptional) {
|
|
5702
5836
|
// BUG-001 FIX: Add defensive check for baseValidator property
|
|
@@ -5711,7 +5845,7 @@ class VldObject extends VldBase {
|
|
|
5711
5845
|
}
|
|
5712
5846
|
}
|
|
5713
5847
|
return new VldObject({
|
|
5714
|
-
...this.
|
|
5848
|
+
...this._config,
|
|
5715
5849
|
shape: requiredShape
|
|
5716
5850
|
});
|
|
5717
5851
|
}
|
|
@@ -5721,7 +5855,7 @@ class VldObject extends VldBase {
|
|
|
5721
5855
|
*/
|
|
5722
5856
|
catchall(schema) {
|
|
5723
5857
|
return new VldObject({
|
|
5724
|
-
...this.
|
|
5858
|
+
...this._config,
|
|
5725
5859
|
catchall: schema,
|
|
5726
5860
|
passthrough: false // catchall overrides passthrough
|
|
5727
5861
|
});
|
|
@@ -5731,14 +5865,14 @@ class VldObject extends VldBase {
|
|
|
5731
5865
|
* Zod 4 API parity - returns the shape object
|
|
5732
5866
|
*/
|
|
5733
5867
|
get shape() {
|
|
5734
|
-
return this.
|
|
5868
|
+
return this._config.shape;
|
|
5735
5869
|
}
|
|
5736
5870
|
/**
|
|
5737
5871
|
* Create an enum validator from object keys
|
|
5738
5872
|
* Zod 4 API parity - creates literal union of keys
|
|
5739
5873
|
*/
|
|
5740
5874
|
keyof() {
|
|
5741
|
-
const keys = Object.keys(this.
|
|
5875
|
+
const keys = Object.keys(this._config.shape);
|
|
5742
5876
|
if (keys.length === 0) {
|
|
5743
5877
|
throw new Error('Cannot create keyof enum from empty object');
|
|
5744
5878
|
}
|
|
@@ -5757,7 +5891,7 @@ class VldObject extends VldBase {
|
|
|
5757
5891
|
*/
|
|
5758
5892
|
safeExtend(extension) {
|
|
5759
5893
|
// Check for overlapping keys
|
|
5760
|
-
const existingKeys = new Set(Object.keys(this.
|
|
5894
|
+
const existingKeys = new Set(Object.keys(this._config.shape));
|
|
5761
5895
|
const extensionKeys = Object.keys(extension);
|
|
5762
5896
|
const overlappingKeys = [];
|
|
5763
5897
|
for (const key of extensionKeys) {
|
|
@@ -5769,8 +5903,8 @@ class VldObject extends VldBase {
|
|
|
5769
5903
|
throw new Error(`safeExtend: ${getMessages().safeExtendOverlap(overlappingKeys)}`);
|
|
5770
5904
|
}
|
|
5771
5905
|
return new VldObject({
|
|
5772
|
-
...this.
|
|
5773
|
-
shape: { ...this.
|
|
5906
|
+
...this._config,
|
|
5907
|
+
shape: { ...this._config.shape, ...extension }
|
|
5774
5908
|
});
|
|
5775
5909
|
}
|
|
5776
5910
|
}
|
|
@@ -6353,11 +6487,17 @@ class VldLiteral extends VldBase {
|
|
|
6353
6487
|
/**
|
|
6354
6488
|
* Private constructor to enforce immutability
|
|
6355
6489
|
*/
|
|
6356
|
-
constructor(
|
|
6490
|
+
constructor(_literal, errorMessage) {
|
|
6357
6491
|
super();
|
|
6358
|
-
this.
|
|
6492
|
+
this._literal = _literal;
|
|
6359
6493
|
this.errorMessage = errorMessage;
|
|
6360
6494
|
}
|
|
6495
|
+
/**
|
|
6496
|
+
* Get the literal value
|
|
6497
|
+
*/
|
|
6498
|
+
get literal() {
|
|
6499
|
+
return this._literal;
|
|
6500
|
+
}
|
|
6361
6501
|
/**
|
|
6362
6502
|
* Create a new literal validator
|
|
6363
6503
|
*/
|
|
@@ -6368,23 +6508,23 @@ class VldLiteral extends VldBase {
|
|
|
6368
6508
|
* Parse and validate a literal value
|
|
6369
6509
|
*/
|
|
6370
6510
|
parse(value) {
|
|
6371
|
-
if (value !== this.
|
|
6511
|
+
if (value !== this._literal) {
|
|
6372
6512
|
throw new Error(this.errorMessage ||
|
|
6373
|
-
getMessages().literalExpected(JSON.stringify(this.
|
|
6513
|
+
getMessages().literalExpected(JSON.stringify(this._literal), JSON.stringify(value)));
|
|
6374
6514
|
}
|
|
6375
|
-
return this.
|
|
6515
|
+
return this._literal;
|
|
6376
6516
|
}
|
|
6377
6517
|
/**
|
|
6378
6518
|
* Safely parse and validate a literal value
|
|
6379
6519
|
*/
|
|
6380
6520
|
safeParse(value) {
|
|
6381
|
-
if (value === this.
|
|
6382
|
-
return { success: true, data: this.
|
|
6521
|
+
if (value === this._literal) {
|
|
6522
|
+
return { success: true, data: this._literal };
|
|
6383
6523
|
}
|
|
6384
6524
|
return {
|
|
6385
6525
|
success: false,
|
|
6386
6526
|
error: new Error(this.errorMessage ||
|
|
6387
|
-
getMessages().literalExpected(JSON.stringify(this.
|
|
6527
|
+
getMessages().literalExpected(JSON.stringify(this._literal), JSON.stringify(value)))
|
|
6388
6528
|
};
|
|
6389
6529
|
}
|
|
6390
6530
|
}
|
|
@@ -6596,27 +6736,56 @@ class VldNan extends VldBase {
|
|
|
6596
6736
|
/**
|
|
6597
6737
|
* Lazy validator - defers schema evaluation until runtime
|
|
6598
6738
|
* Essential for recursive and self-referencing types
|
|
6739
|
+
*
|
|
6740
|
+
* MEMORY OPTIMIZATION: Uses WeakRef for the cached schema to allow garbage collection
|
|
6741
|
+
* when the validator is no longer in use. This prevents memory leaks in long-running
|
|
6742
|
+
* applications with dynamically created schemas.
|
|
6599
6743
|
*/
|
|
6600
6744
|
class VldLazy extends VldBase {
|
|
6601
6745
|
constructor(_schemaGetter) {
|
|
6602
6746
|
super();
|
|
6603
6747
|
this._schemaGetter = _schemaGetter;
|
|
6604
|
-
|
|
6748
|
+
// Use WeakRef to allow garbage collection of the cached schema
|
|
6749
|
+
this._cachedSchemaRef = null;
|
|
6750
|
+
// Keep a strong reference flag to prevent GC during active use
|
|
6751
|
+
this._strongRef = null;
|
|
6605
6752
|
}
|
|
6606
6753
|
static create(schemaGetter) {
|
|
6607
6754
|
return new VldLazy(schemaGetter);
|
|
6608
6755
|
}
|
|
6609
6756
|
/**
|
|
6610
6757
|
* Get the actual schema, caching it after first retrieval
|
|
6758
|
+
* Uses WeakRef to allow garbage collection when validator is not in use
|
|
6611
6759
|
*/
|
|
6612
6760
|
_getSchema() {
|
|
6613
|
-
if (
|
|
6614
|
-
|
|
6761
|
+
// Check if we have a strong reference first (active use)
|
|
6762
|
+
if (this._strongRef) {
|
|
6763
|
+
return this._strongRef;
|
|
6764
|
+
}
|
|
6765
|
+
// Try to get from WeakRef
|
|
6766
|
+
if (this._cachedSchemaRef) {
|
|
6767
|
+
const cached = this._cachedSchemaRef.deref();
|
|
6768
|
+
if (cached) {
|
|
6769
|
+
// Restore strong reference for active use
|
|
6770
|
+
this._strongRef = cached;
|
|
6771
|
+
return cached;
|
|
6772
|
+
}
|
|
6615
6773
|
}
|
|
6616
|
-
|
|
6774
|
+
// Create new schema
|
|
6775
|
+
const schema = this._schemaGetter();
|
|
6776
|
+
this._cachedSchemaRef = new WeakRef(schema);
|
|
6777
|
+
this._strongRef = schema;
|
|
6778
|
+
// Clear strong reference after a tick to allow GC
|
|
6779
|
+
// This keeps the schema alive during synchronous operations
|
|
6780
|
+
// but allows it to be collected if the validator is discarded
|
|
6781
|
+
Promise.resolve().then(() => {
|
|
6782
|
+
this._strongRef = null;
|
|
6783
|
+
});
|
|
6784
|
+
return schema;
|
|
6617
6785
|
}
|
|
6618
6786
|
/**
|
|
6619
6787
|
* Get the inner schema (unwrap)
|
|
6788
|
+
* Returns a strong reference that will keep the schema alive
|
|
6620
6789
|
*/
|
|
6621
6790
|
unwrap() {
|
|
6622
6791
|
return this._getSchema();
|
|
@@ -6639,11 +6808,10 @@ class VldLazy extends VldBase {
|
|
|
6639
6808
|
*/
|
|
6640
6809
|
function extractLiteralValues(schema) {
|
|
6641
6810
|
if (schema instanceof VldLiteral) {
|
|
6642
|
-
|
|
6643
|
-
return [value];
|
|
6811
|
+
return [schema.literal];
|
|
6644
6812
|
}
|
|
6645
6813
|
if (schema instanceof VldEnum) {
|
|
6646
|
-
return schema.values;
|
|
6814
|
+
return [...schema.values];
|
|
6647
6815
|
}
|
|
6648
6816
|
throw new Error('Discriminator must be a literal or enum schema');
|
|
6649
6817
|
}
|