@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.
- package/CHANGELOG.md +94 -0
- package/README.md +2 -2
- package/dist/chunks/{bigint-ZKkWLdmi.js → bigint-CRS0QVsy.js} +242 -16
- package/dist/chunks/bigint-CRS0QVsy.js.map +1 -0
- package/dist/chunks/{date-7Iz2BGsL.js → date-50JV_Mfj.js} +2 -2
- package/dist/chunks/{date-7Iz2BGsL.js.map → date-50JV_Mfj.js.map} +1 -1
- package/dist/chunks/{index-BoV-4S1F.js → index-DO8CFMxD.js} +4 -4
- package/dist/chunks/{index-BoV-4S1F.js.map → index-DO8CFMxD.js.map} +1 -1
- package/dist/chunks/{json-nIhyRPOU.js → json-Cp4WO0M9.js} +226 -139
- package/dist/chunks/json-Cp4WO0M9.js.map +1 -0
- package/dist/chunks/{unknown-CjSRcELI.js → unknown-Dhzri_T-.js} +2 -2
- package/dist/chunks/{unknown-CjSRcELI.js.map → unknown-Dhzri_T-.js.map} +1 -1
- package/dist/cjs/index.cjs +1095 -151
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/mini.cjs +462 -149
- package/dist/cjs/mini.cjs.map +1 -1
- package/dist/cli/commands/validate.d.ts.map +1 -1
- package/dist/codecs/index.js +3 -3
- package/dist/coercion/index.js +2 -2
- package/dist/index.d.ts +244 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +550 -11
- package/dist/index.js.map +1 -1
- package/dist/mini.js +5 -5
- package/dist/utils/json-schema.d.ts +67 -0
- package/dist/utils/json-schema.d.ts.map +1 -0
- package/dist/validators/array.d.ts +5 -0
- package/dist/validators/array.d.ts.map +1 -1
- package/dist/validators/base.d.ts +79 -0
- package/dist/validators/base.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.d.ts +3 -2
- package/dist/validators/index.d.ts.map +1 -1
- package/dist/validators/index.js +514 -4
- package/dist/validators/index.js.map +1 -1
- 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/number.d.ts +30 -0
- package/dist/validators/number.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/promise.d.ts +53 -0
- package/dist/validators/promise.d.ts.map +1 -0
- package/dist/validators/string-formats.d.ts +6 -0
- package/dist/validators/string-formats.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 +6 -5
- package/dist/chunks/bigint-ZKkWLdmi.js.map +0 -1
- package/dist/chunks/index-eWXdjyyy.js +0 -426
- package/dist/chunks/index-eWXdjyyy.js.map +0 -1
- package/dist/chunks/json-nIhyRPOU.js.map +0 -1
package/dist/cjs/mini.cjs
CHANGED
|
@@ -88,6 +88,16 @@ class VldBase {
|
|
|
88
88
|
nullish() {
|
|
89
89
|
return new VldNullish(this);
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Make this validator exactly optional - allows undefined but not missing
|
|
93
|
+
* Unlike .optional() which treats missing as undefined, exactOptional()
|
|
94
|
+
* requires the key to be present but allows undefined as a value
|
|
95
|
+
* Zod 4 API parity
|
|
96
|
+
* @returns A new exact optional validator
|
|
97
|
+
*/
|
|
98
|
+
exactOptional() {
|
|
99
|
+
return new VldExactOptional(this);
|
|
100
|
+
}
|
|
91
101
|
/**
|
|
92
102
|
* Pipe the output of this validator into another validator
|
|
93
103
|
* @param next The next validator to pipe into
|
|
@@ -132,6 +142,49 @@ class VldBase {
|
|
|
132
142
|
apply(fn) {
|
|
133
143
|
return fn(this);
|
|
134
144
|
}
|
|
145
|
+
check(predicate, message) {
|
|
146
|
+
return new VldRefine(this, predicate, message);
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Get or set metadata for this schema
|
|
150
|
+
*/
|
|
151
|
+
meta(data) {
|
|
152
|
+
if (data === undefined) {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
return new VldMeta(this, data);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Add a description to this schema
|
|
159
|
+
* Zod 4 API parity - convenience method for .meta({ description: ... })
|
|
160
|
+
* @param description The description to add
|
|
161
|
+
* @returns A new validator with the description
|
|
162
|
+
*/
|
|
163
|
+
describe(description) {
|
|
164
|
+
return new VldMeta(this, { description });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Metadata validator - wraps a schema with metadata
|
|
169
|
+
*/
|
|
170
|
+
class VldMeta extends VldBase {
|
|
171
|
+
constructor(baseValidator, metadata) {
|
|
172
|
+
super();
|
|
173
|
+
this.baseValidator = baseValidator;
|
|
174
|
+
this.metadata = metadata;
|
|
175
|
+
}
|
|
176
|
+
parse(value) {
|
|
177
|
+
return this.baseValidator.parse(value);
|
|
178
|
+
}
|
|
179
|
+
safeParse(value) {
|
|
180
|
+
return this.baseValidator.safeParse(value);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get the metadata
|
|
184
|
+
*/
|
|
185
|
+
getMeta() {
|
|
186
|
+
return this.metadata;
|
|
187
|
+
}
|
|
135
188
|
}
|
|
136
189
|
/**
|
|
137
190
|
* Readonly validator - marks output as readonly
|
|
@@ -347,6 +400,38 @@ class VldOptional extends VldBase {
|
|
|
347
400
|
return this.baseValidator;
|
|
348
401
|
}
|
|
349
402
|
}
|
|
403
|
+
/**
|
|
404
|
+
* Exact optional validator - allows undefined but requires key presence
|
|
405
|
+
* Unlike Optional which treats missing as undefined, ExactOptional
|
|
406
|
+
* requires the key to be present (not missing from object) but allows undefined
|
|
407
|
+
* Zod 4 API parity
|
|
408
|
+
*/
|
|
409
|
+
class VldExactOptional extends VldBase {
|
|
410
|
+
constructor(baseValidator) {
|
|
411
|
+
super();
|
|
412
|
+
this.baseValidator = baseValidator;
|
|
413
|
+
}
|
|
414
|
+
static create(baseValidator) {
|
|
415
|
+
return new VldExactOptional(baseValidator);
|
|
416
|
+
}
|
|
417
|
+
parse(value) {
|
|
418
|
+
if (value === undefined) {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
return this.baseValidator.parse(value);
|
|
422
|
+
}
|
|
423
|
+
safeParse(value) {
|
|
424
|
+
// Unlike regular optional, undefined is explicitly valid
|
|
425
|
+
// but we still validate through the base validator
|
|
426
|
+
if (value === undefined) {
|
|
427
|
+
return { success: true, data: undefined };
|
|
428
|
+
}
|
|
429
|
+
return this.baseValidator.safeParse(value);
|
|
430
|
+
}
|
|
431
|
+
unwrap() {
|
|
432
|
+
return this.baseValidator;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
350
435
|
/**
|
|
351
436
|
* Nullable validator - allows null
|
|
352
437
|
*/
|
|
@@ -3461,6 +3546,7 @@ const REGEX_PATTERNS = {
|
|
|
3461
3546
|
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
3547
|
/**
|
|
3463
3548
|
* Immutable string validator with chainable methods
|
|
3549
|
+
* Features pre-compiled validation functions for maximum performance
|
|
3464
3550
|
*/
|
|
3465
3551
|
class VldString extends VldBase {
|
|
3466
3552
|
/**
|
|
@@ -3468,12 +3554,100 @@ class VldString extends VldBase {
|
|
|
3468
3554
|
*/
|
|
3469
3555
|
constructor(config) {
|
|
3470
3556
|
super();
|
|
3557
|
+
// Cache for pre-compiled validation function
|
|
3558
|
+
this._compiledValidator = null;
|
|
3471
3559
|
this.config = {
|
|
3472
3560
|
checks: config?.checks || [],
|
|
3473
3561
|
transforms: config?.transforms || [],
|
|
3474
3562
|
errorMessage: config?.errorMessage
|
|
3475
3563
|
};
|
|
3476
3564
|
}
|
|
3565
|
+
/**
|
|
3566
|
+
* Compile all transforms and checks into a single optimized function
|
|
3567
|
+
* This eliminates loop overhead and enables better JIT optimization
|
|
3568
|
+
*/
|
|
3569
|
+
_compileValidator() {
|
|
3570
|
+
const transforms = this.config.transforms;
|
|
3571
|
+
const checks = this.config.checks;
|
|
3572
|
+
const errorMessage = this.config.errorMessage || getMessages().invalidString;
|
|
3573
|
+
// Fast path: no transforms or checks
|
|
3574
|
+
if (transforms.length === 0 && checks.length === 0) {
|
|
3575
|
+
return (value) => ({ success: true, value });
|
|
3576
|
+
}
|
|
3577
|
+
// Fast path: only transforms, no checks
|
|
3578
|
+
if (checks.length === 0) {
|
|
3579
|
+
switch (transforms.length) {
|
|
3580
|
+
case 1:
|
|
3581
|
+
return (value) => ({ success: true, value: transforms[0](value) });
|
|
3582
|
+
case 2:
|
|
3583
|
+
return (value) => ({ success: true, value: transforms[1](transforms[0](value)) });
|
|
3584
|
+
case 3:
|
|
3585
|
+
return (value) => ({ success: true, value: transforms[2](transforms[1](transforms[0](value))) });
|
|
3586
|
+
default:
|
|
3587
|
+
return (value) => {
|
|
3588
|
+
let result = value;
|
|
3589
|
+
for (let i = 0; i < transforms.length; i++) {
|
|
3590
|
+
result = transforms[i](result);
|
|
3591
|
+
}
|
|
3592
|
+
return { success: true, value: result };
|
|
3593
|
+
};
|
|
3594
|
+
}
|
|
3595
|
+
}
|
|
3596
|
+
// Fast path: only checks, no transforms
|
|
3597
|
+
if (transforms.length === 0) {
|
|
3598
|
+
switch (checks.length) {
|
|
3599
|
+
case 1:
|
|
3600
|
+
return (value) => {
|
|
3601
|
+
if (!checks[0](value))
|
|
3602
|
+
return { success: false, error: errorMessage };
|
|
3603
|
+
return { success: true, value };
|
|
3604
|
+
};
|
|
3605
|
+
case 2:
|
|
3606
|
+
return (value) => {
|
|
3607
|
+
if (!checks[0](value) || !checks[1](value))
|
|
3608
|
+
return { success: false, error: errorMessage };
|
|
3609
|
+
return { success: true, value };
|
|
3610
|
+
};
|
|
3611
|
+
case 3:
|
|
3612
|
+
return (value) => {
|
|
3613
|
+
if (!checks[0](value) || !checks[1](value) || !checks[2](value))
|
|
3614
|
+
return { success: false, error: errorMessage };
|
|
3615
|
+
return { success: true, value };
|
|
3616
|
+
};
|
|
3617
|
+
default:
|
|
3618
|
+
return (value) => {
|
|
3619
|
+
for (let i = 0; i < checks.length; i++) {
|
|
3620
|
+
if (!checks[i](value))
|
|
3621
|
+
return { success: false, error: errorMessage };
|
|
3622
|
+
}
|
|
3623
|
+
return { success: true, value };
|
|
3624
|
+
};
|
|
3625
|
+
}
|
|
3626
|
+
}
|
|
3627
|
+
// General case: both transforms and checks
|
|
3628
|
+
return (value) => {
|
|
3629
|
+
let result = value;
|
|
3630
|
+
// Apply transforms
|
|
3631
|
+
for (let i = 0; i < transforms.length; i++) {
|
|
3632
|
+
result = transforms[i](result);
|
|
3633
|
+
}
|
|
3634
|
+
// Apply checks
|
|
3635
|
+
for (let i = 0; i < checks.length; i++) {
|
|
3636
|
+
if (!checks[i](result))
|
|
3637
|
+
return { success: false, error: errorMessage };
|
|
3638
|
+
}
|
|
3639
|
+
return { success: true, value: result };
|
|
3640
|
+
};
|
|
3641
|
+
}
|
|
3642
|
+
/**
|
|
3643
|
+
* Get the cached compiled validator, creating it if necessary
|
|
3644
|
+
*/
|
|
3645
|
+
_getCompiledValidator() {
|
|
3646
|
+
if (!this._compiledValidator) {
|
|
3647
|
+
this._compiledValidator = this._compileValidator();
|
|
3648
|
+
}
|
|
3649
|
+
return this._compiledValidator;
|
|
3650
|
+
}
|
|
3477
3651
|
/**
|
|
3478
3652
|
* Create a new string validator
|
|
3479
3653
|
*/
|
|
@@ -3481,26 +3655,18 @@ class VldString extends VldBase {
|
|
|
3481
3655
|
return new VldString();
|
|
3482
3656
|
}
|
|
3483
3657
|
/**
|
|
3484
|
-
* Parse and validate a string value - ultra-optimized
|
|
3658
|
+
* Parse and validate a string value - ultra-optimized with pre-compiled validator
|
|
3485
3659
|
*/
|
|
3486
3660
|
parse(value) {
|
|
3487
3661
|
if (typeof value !== 'string') {
|
|
3488
3662
|
throw new Error(this.config.errorMessage || getMessages().invalidString);
|
|
3489
3663
|
}
|
|
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
|
-
}
|
|
3664
|
+
// Use pre-compiled validator for maximum performance
|
|
3665
|
+
const result = this._getCompiledValidator()(value);
|
|
3666
|
+
if (!result.success) {
|
|
3667
|
+
throw new Error(result.error);
|
|
3502
3668
|
}
|
|
3503
|
-
return result;
|
|
3669
|
+
return result.value;
|
|
3504
3670
|
}
|
|
3505
3671
|
/**
|
|
3506
3672
|
* Safely parse and validate a string value
|
|
@@ -3914,6 +4080,66 @@ class VldNumber extends VldBase {
|
|
|
3914
4080
|
lte(value, message) {
|
|
3915
4081
|
return this.max(value, message);
|
|
3916
4082
|
}
|
|
4083
|
+
/**
|
|
4084
|
+
* Create a validator for unsigned 32-bit integers
|
|
4085
|
+
* Range: 0 to 4,294,967,295
|
|
4086
|
+
*/
|
|
4087
|
+
uint32(message) {
|
|
4088
|
+
return new VldNumber({
|
|
4089
|
+
checks: [...this.config.checks, (v) => Number.isSafeInteger(v) && v >= 0 && v <= 4294967295],
|
|
4090
|
+
errorMessage: message || 'Expected an unsigned 32-bit integer'
|
|
4091
|
+
});
|
|
4092
|
+
}
|
|
4093
|
+
/**
|
|
4094
|
+
* Create a validator for unsigned 64-bit integers
|
|
4095
|
+
* Range: 0 to 2^53-1 (safe integer limit)
|
|
4096
|
+
*/
|
|
4097
|
+
uint64(message) {
|
|
4098
|
+
return new VldNumber({
|
|
4099
|
+
checks: [...this.config.checks, (v) => Number.isSafeInteger(v) && v >= 0],
|
|
4100
|
+
errorMessage: message || 'Expected an unsigned 64-bit integer'
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
/**
|
|
4104
|
+
* Create a validator for signed 32-bit integers
|
|
4105
|
+
* Range: -2,147,483,648 to 2,147,483,647
|
|
4106
|
+
*/
|
|
4107
|
+
int32(message) {
|
|
4108
|
+
return new VldNumber({
|
|
4109
|
+
checks: [...this.config.checks, (v) => Number.isSafeInteger(v) && v >= -2147483648 && v <= 2147483647],
|
|
4110
|
+
errorMessage: message || 'Expected a signed 32-bit integer'
|
|
4111
|
+
});
|
|
4112
|
+
}
|
|
4113
|
+
/**
|
|
4114
|
+
* Create a validator for signed 64-bit integers
|
|
4115
|
+
* Range: -(2^53-1) to 2^53-1 (safe integer limit)
|
|
4116
|
+
*/
|
|
4117
|
+
int64(message) {
|
|
4118
|
+
return new VldNumber({
|
|
4119
|
+
checks: [...this.config.checks, (v) => Number.isSafeInteger(v)],
|
|
4120
|
+
errorMessage: message || 'Expected a signed 64-bit integer'
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
4123
|
+
/**
|
|
4124
|
+
* Create a validator for 32-bit floats (IEEE 754 single precision)
|
|
4125
|
+
* Range: -3.4e38 to 3.4e38, precision ~7 decimal digits
|
|
4126
|
+
*/
|
|
4127
|
+
float32(message) {
|
|
4128
|
+
return new VldNumber({
|
|
4129
|
+
checks: [...this.config.checks, (v) => Number.isFinite(v) && Math.abs(v) <= 3.4e38],
|
|
4130
|
+
errorMessage: message || 'Expected a 32-bit float'
|
|
4131
|
+
});
|
|
4132
|
+
}
|
|
4133
|
+
/**
|
|
4134
|
+
* Create a validator for 64-bit floats (IEEE 754 double precision)
|
|
4135
|
+
* Alias for standard number validation
|
|
4136
|
+
*/
|
|
4137
|
+
float64(message) {
|
|
4138
|
+
return new VldNumber({
|
|
4139
|
+
checks: [...this.config.checks, (v) => Number.isFinite(v)],
|
|
4140
|
+
errorMessage: message || 'Expected a 64-bit float'
|
|
4141
|
+
});
|
|
4142
|
+
}
|
|
3917
4143
|
}
|
|
3918
4144
|
|
|
3919
4145
|
/**
|
|
@@ -4602,16 +4828,9 @@ class VldArray extends VldBase {
|
|
|
4602
4828
|
}
|
|
4603
4829
|
result[i] = parseResult.data; // Direct assignment is faster than push
|
|
4604
4830
|
}
|
|
4605
|
-
// Check uniqueness if required
|
|
4831
|
+
// Check uniqueness if required - optimized with Map-based approach
|
|
4606
4832
|
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
|
-
}
|
|
4833
|
+
this.checkUnique(result);
|
|
4615
4834
|
}
|
|
4616
4835
|
return result;
|
|
4617
4836
|
}
|
|
@@ -4626,6 +4845,38 @@ class VldArray extends VldBase {
|
|
|
4626
4845
|
return { success: false, error: error };
|
|
4627
4846
|
}
|
|
4628
4847
|
}
|
|
4848
|
+
/**
|
|
4849
|
+
* Optimized uniqueness check using Map-based approach
|
|
4850
|
+
* Avoids repeated stableStringify calls by caching serialized values
|
|
4851
|
+
*/
|
|
4852
|
+
checkUnique(items) {
|
|
4853
|
+
const seen = new Set(); // Set of seen keys
|
|
4854
|
+
const objectKeys = new WeakMap(); // Cache for object->string mappings
|
|
4855
|
+
for (const item of items) {
|
|
4856
|
+
let key;
|
|
4857
|
+
if (typeof item === 'object' && item !== null) {
|
|
4858
|
+
// Check if we've already serialized this object reference
|
|
4859
|
+
const cached = objectKeys.get(item);
|
|
4860
|
+
if (cached !== undefined) {
|
|
4861
|
+
key = cached;
|
|
4862
|
+
}
|
|
4863
|
+
else {
|
|
4864
|
+
// Serialize and cache
|
|
4865
|
+
const serialized = this.stableStringify(item);
|
|
4866
|
+
key = serialized;
|
|
4867
|
+
objectKeys.set(item, serialized);
|
|
4868
|
+
}
|
|
4869
|
+
}
|
|
4870
|
+
else {
|
|
4871
|
+
// Primitives can be used directly as keys
|
|
4872
|
+
key = item;
|
|
4873
|
+
}
|
|
4874
|
+
if (seen.has(key)) {
|
|
4875
|
+
throw new Error('Array must contain unique items');
|
|
4876
|
+
}
|
|
4877
|
+
seen.add(key);
|
|
4878
|
+
}
|
|
4879
|
+
}
|
|
4629
4880
|
/**
|
|
4630
4881
|
* Create a stable string representation of an object for hashing
|
|
4631
4882
|
* Handles circular references and deep nesting gracefully
|
|
@@ -4753,11 +5004,17 @@ class VldEnum extends VldBase {
|
|
|
4753
5004
|
/**
|
|
4754
5005
|
* Private constructor to enforce immutability
|
|
4755
5006
|
*/
|
|
4756
|
-
constructor(
|
|
5007
|
+
constructor(_values, errorMessage) {
|
|
4757
5008
|
super();
|
|
4758
|
-
this.
|
|
5009
|
+
this._values = _values;
|
|
4759
5010
|
this.errorMessage = errorMessage;
|
|
4760
5011
|
}
|
|
5012
|
+
/**
|
|
5013
|
+
* Get the enum values
|
|
5014
|
+
*/
|
|
5015
|
+
get values() {
|
|
5016
|
+
return this._values;
|
|
5017
|
+
}
|
|
4761
5018
|
/**
|
|
4762
5019
|
* Create a new enum validator
|
|
4763
5020
|
*/
|
|
@@ -4771,11 +5028,11 @@ class VldEnum extends VldBase {
|
|
|
4771
5028
|
// BUG-002 FIX: Add type check before includes() to prevent type confusion
|
|
4772
5029
|
if (typeof value !== 'string') {
|
|
4773
5030
|
throw new Error(this.errorMessage ||
|
|
4774
|
-
getMessages().enumExpected([...this.
|
|
5031
|
+
getMessages().enumExpected([...this._values], JSON.stringify(value)));
|
|
4775
5032
|
}
|
|
4776
|
-
if (!this.
|
|
5033
|
+
if (!this._values.includes(value)) {
|
|
4777
5034
|
throw new Error(this.errorMessage ||
|
|
4778
|
-
getMessages().enumExpected([...this.
|
|
5035
|
+
getMessages().enumExpected([...this._values], JSON.stringify(value)));
|
|
4779
5036
|
}
|
|
4780
5037
|
return value;
|
|
4781
5038
|
}
|
|
@@ -4788,16 +5045,16 @@ class VldEnum extends VldBase {
|
|
|
4788
5045
|
return {
|
|
4789
5046
|
success: false,
|
|
4790
5047
|
error: new Error(this.errorMessage ||
|
|
4791
|
-
getMessages().enumExpected([...this.
|
|
5048
|
+
getMessages().enumExpected([...this._values], JSON.stringify(value)))
|
|
4792
5049
|
};
|
|
4793
5050
|
}
|
|
4794
|
-
if (this.
|
|
5051
|
+
if (this._values.includes(value)) {
|
|
4795
5052
|
return { success: true, data: value };
|
|
4796
5053
|
}
|
|
4797
5054
|
return {
|
|
4798
5055
|
success: false,
|
|
4799
5056
|
error: new Error(this.errorMessage ||
|
|
4800
|
-
getMessages().enumExpected([...this.
|
|
5057
|
+
getMessages().enumExpected([...this._values], JSON.stringify(value)))
|
|
4801
5058
|
};
|
|
4802
5059
|
}
|
|
4803
5060
|
/**
|
|
@@ -4805,7 +5062,7 @@ class VldEnum extends VldBase {
|
|
|
4805
5062
|
* Creates a new enum validator without the specified values
|
|
4806
5063
|
*/
|
|
4807
5064
|
exclude(...excludeValues) {
|
|
4808
|
-
const filtered = this.
|
|
5065
|
+
const filtered = this._values.filter(v => !excludeValues.includes(v));
|
|
4809
5066
|
if (filtered.length === 0) {
|
|
4810
5067
|
throw new Error('Cannot exclude all enum values');
|
|
4811
5068
|
}
|
|
@@ -4817,7 +5074,7 @@ class VldEnum extends VldBase {
|
|
|
4817
5074
|
* Creates a new enum validator with only the specified values
|
|
4818
5075
|
*/
|
|
4819
5076
|
extract(...extractValues) {
|
|
4820
|
-
const extracted = this.
|
|
5077
|
+
const extracted = this._values.filter(v => extractValues.includes(v));
|
|
4821
5078
|
if (extracted.length === 0) {
|
|
4822
5079
|
throw new Error('Cannot extract non-existent enum values');
|
|
4823
5080
|
}
|
|
@@ -5338,10 +5595,30 @@ class VldObject extends VldBase {
|
|
|
5338
5595
|
*/
|
|
5339
5596
|
constructor(config) {
|
|
5340
5597
|
super();
|
|
5341
|
-
this.
|
|
5598
|
+
this._config = config;
|
|
5342
5599
|
// Pre-compute shape keys for faster access
|
|
5343
|
-
this.
|
|
5344
|
-
this.
|
|
5600
|
+
this._shapeKeys = Object.keys(config.shape);
|
|
5601
|
+
this._shapeKeysSet = new Set(this._shapeKeys);
|
|
5602
|
+
}
|
|
5603
|
+
/**
|
|
5604
|
+
* Get the validator configuration
|
|
5605
|
+
* @internal Used by discriminated union validator
|
|
5606
|
+
*/
|
|
5607
|
+
get config() {
|
|
5608
|
+
return this._config;
|
|
5609
|
+
}
|
|
5610
|
+
/**
|
|
5611
|
+
* Get the shape keys array
|
|
5612
|
+
*/
|
|
5613
|
+
get shapeKeys() {
|
|
5614
|
+
return this._shapeKeys;
|
|
5615
|
+
}
|
|
5616
|
+
/**
|
|
5617
|
+
* Get the shape keys set for O(1) lookups
|
|
5618
|
+
* @internal Used by discriminated union validator
|
|
5619
|
+
*/
|
|
5620
|
+
get shapeKeysSet() {
|
|
5621
|
+
return this._shapeKeysSet;
|
|
5345
5622
|
}
|
|
5346
5623
|
/**
|
|
5347
5624
|
* Create a new object validator
|
|
@@ -5356,14 +5633,14 @@ class VldObject extends VldBase {
|
|
|
5356
5633
|
parse(value) {
|
|
5357
5634
|
// Fast type check
|
|
5358
5635
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
5359
|
-
throw new Error(this.
|
|
5636
|
+
throw new Error(this._config.errorMessage || getMessages().invalidObject);
|
|
5360
5637
|
}
|
|
5361
5638
|
const obj = value;
|
|
5362
5639
|
const result = {};
|
|
5363
5640
|
// Ultra-optimized field validation with inline fast paths
|
|
5364
|
-
for (let i = 0; i < this.
|
|
5365
|
-
const key = this.
|
|
5366
|
-
const validator = this.
|
|
5641
|
+
for (let i = 0; i < this._shapeKeys.length; i++) {
|
|
5642
|
+
const key = this._shapeKeys[i];
|
|
5643
|
+
const validator = this._config.shape[key];
|
|
5367
5644
|
const fieldValue = obj[key];
|
|
5368
5645
|
// BUG-NEW-002 FIX: Use instanceof instead of constructor.name
|
|
5369
5646
|
// constructor.name breaks in minified builds where class names become 'a', 'b', etc.
|
|
@@ -5420,38 +5697,39 @@ class VldObject extends VldBase {
|
|
|
5420
5697
|
result[key] = parseResult.data;
|
|
5421
5698
|
}
|
|
5422
5699
|
}
|
|
5423
|
-
// Handle strict
|
|
5424
|
-
if (this.
|
|
5700
|
+
// Handle strict/passthrough/catchall modes - optimized single Object.keys() call
|
|
5701
|
+
if (this._config.strict || this._config.passthrough || this._config.catchall) {
|
|
5425
5702
|
const objKeys = Object.keys(obj);
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5703
|
+
// Handle strict mode - optimized with Set
|
|
5704
|
+
if (this._config.strict) {
|
|
5705
|
+
const extraKeys = [];
|
|
5706
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5707
|
+
if (!this._shapeKeysSet.has(objKeys[i])) {
|
|
5708
|
+
extraKeys.push(objKeys[i]);
|
|
5709
|
+
}
|
|
5710
|
+
}
|
|
5711
|
+
if (extraKeys.length > 0) {
|
|
5712
|
+
throw new Error(getMessages().unexpectedKeys(extraKeys));
|
|
5430
5713
|
}
|
|
5431
5714
|
}
|
|
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];
|
|
5715
|
+
// Handle passthrough mode - optimized with comprehensive prototype pollution protection
|
|
5716
|
+
if (this._config.passthrough) {
|
|
5717
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5718
|
+
const key = objKeys[i];
|
|
5719
|
+
// Skip dangerous keys to prevent prototype pollution
|
|
5720
|
+
if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5721
|
+
result[key] = obj[key];
|
|
5722
|
+
}
|
|
5444
5723
|
}
|
|
5445
5724
|
}
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
result[key] = this.config.catchall.parse(obj[key]);
|
|
5725
|
+
// Handle catchall - validate extra keys with catchall validator
|
|
5726
|
+
if (this._config.catchall) {
|
|
5727
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5728
|
+
const key = objKeys[i];
|
|
5729
|
+
// Skip keys already in shape and dangerous keys
|
|
5730
|
+
if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5731
|
+
result[key] = this._config.catchall.parse(obj[key]);
|
|
5732
|
+
}
|
|
5455
5733
|
}
|
|
5456
5734
|
}
|
|
5457
5735
|
}
|
|
@@ -5466,15 +5744,15 @@ class VldObject extends VldBase {
|
|
|
5466
5744
|
if (typeof value !== 'object' || value === null || Array.isArray(value)) {
|
|
5467
5745
|
return {
|
|
5468
5746
|
success: false,
|
|
5469
|
-
error: new Error(this.
|
|
5747
|
+
error: new Error(this._config.errorMessage || getMessages().invalidObject)
|
|
5470
5748
|
};
|
|
5471
5749
|
}
|
|
5472
5750
|
const obj = value;
|
|
5473
5751
|
const result = {};
|
|
5474
5752
|
// Validate all fields
|
|
5475
|
-
for (let i = 0; i < this.
|
|
5476
|
-
const key = this.
|
|
5477
|
-
const validator = this.
|
|
5753
|
+
for (let i = 0; i < this._shapeKeys.length; i++) {
|
|
5754
|
+
const key = this._shapeKeys[i];
|
|
5755
|
+
const validator = this._config.shape[key];
|
|
5478
5756
|
const fieldValue = obj[key];
|
|
5479
5757
|
const parseResult = validator.safeParse(fieldValue);
|
|
5480
5758
|
if (parseResult.success) {
|
|
@@ -5487,48 +5765,49 @@ class VldObject extends VldBase {
|
|
|
5487
5765
|
};
|
|
5488
5766
|
}
|
|
5489
5767
|
}
|
|
5490
|
-
// Handle strict
|
|
5491
|
-
if (this.
|
|
5768
|
+
// Handle strict/passthrough/catchall modes - optimized single Object.keys() call
|
|
5769
|
+
if (this._config.strict || this._config.passthrough || this._config.catchall) {
|
|
5492
5770
|
const objKeys = Object.keys(obj);
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5771
|
+
// Handle strict mode
|
|
5772
|
+
if (this._config.strict) {
|
|
5773
|
+
const extraKeys = [];
|
|
5774
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5775
|
+
if (!this._shapeKeysSet.has(objKeys[i])) {
|
|
5776
|
+
extraKeys.push(objKeys[i]);
|
|
5777
|
+
}
|
|
5778
|
+
}
|
|
5779
|
+
if (extraKeys.length > 0) {
|
|
5780
|
+
return {
|
|
5781
|
+
success: false,
|
|
5782
|
+
error: new Error(getMessages().unexpectedKeys(extraKeys))
|
|
5783
|
+
};
|
|
5497
5784
|
}
|
|
5498
5785
|
}
|
|
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];
|
|
5786
|
+
// Handle passthrough mode with comprehensive prototype pollution protection
|
|
5787
|
+
if (this._config.passthrough) {
|
|
5788
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5789
|
+
const key = objKeys[i];
|
|
5790
|
+
// Skip dangerous keys to prevent prototype pollution
|
|
5791
|
+
if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5792
|
+
result[key] = obj[key];
|
|
5793
|
+
}
|
|
5514
5794
|
}
|
|
5515
5795
|
}
|
|
5516
|
-
|
|
5517
|
-
|
|
5518
|
-
|
|
5519
|
-
|
|
5520
|
-
|
|
5521
|
-
|
|
5522
|
-
|
|
5523
|
-
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5796
|
+
// Handle catchall - validate extra keys with catchall validator
|
|
5797
|
+
if (this._config.catchall) {
|
|
5798
|
+
for (let i = 0; i < objKeys.length; i++) {
|
|
5799
|
+
const key = objKeys[i];
|
|
5800
|
+
// Skip keys already in shape and dangerous keys
|
|
5801
|
+
if (!this._shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
|
|
5802
|
+
const catchallResult = this._config.catchall.safeParse(obj[key]);
|
|
5803
|
+
if (!catchallResult.success) {
|
|
5804
|
+
return {
|
|
5805
|
+
success: false,
|
|
5806
|
+
error: new Error(getMessages().objectField(key, catchallResult.error.message))
|
|
5807
|
+
};
|
|
5808
|
+
}
|
|
5809
|
+
result[key] = catchallResult.data;
|
|
5530
5810
|
}
|
|
5531
|
-
result[key] = catchallResult.data;
|
|
5532
5811
|
}
|
|
5533
5812
|
}
|
|
5534
5813
|
}
|
|
@@ -5593,7 +5872,7 @@ class VldObject extends VldBase {
|
|
|
5593
5872
|
*/
|
|
5594
5873
|
strict(message) {
|
|
5595
5874
|
return new VldObject({
|
|
5596
|
-
...this.
|
|
5875
|
+
...this._config,
|
|
5597
5876
|
strict: true,
|
|
5598
5877
|
passthrough: false,
|
|
5599
5878
|
errorMessage: message
|
|
@@ -5604,7 +5883,7 @@ class VldObject extends VldBase {
|
|
|
5604
5883
|
*/
|
|
5605
5884
|
passthrough() {
|
|
5606
5885
|
return new VldObject({
|
|
5607
|
-
...this.
|
|
5886
|
+
...this._config,
|
|
5608
5887
|
strict: false,
|
|
5609
5888
|
passthrough: true
|
|
5610
5889
|
});
|
|
@@ -5614,11 +5893,11 @@ class VldObject extends VldBase {
|
|
|
5614
5893
|
*/
|
|
5615
5894
|
partial() {
|
|
5616
5895
|
const partialShape = {};
|
|
5617
|
-
for (const key in this.
|
|
5618
|
-
partialShape[key] = new VldOptional(this.
|
|
5896
|
+
for (const key in this._config.shape) {
|
|
5897
|
+
partialShape[key] = new VldOptional(this._config.shape[key]);
|
|
5619
5898
|
}
|
|
5620
5899
|
return new VldObject({
|
|
5621
|
-
...this.
|
|
5900
|
+
...this._config,
|
|
5622
5901
|
shape: partialShape
|
|
5623
5902
|
});
|
|
5624
5903
|
}
|
|
@@ -5627,8 +5906,8 @@ class VldObject extends VldBase {
|
|
|
5627
5906
|
*/
|
|
5628
5907
|
deepPartial() {
|
|
5629
5908
|
const deepPartialShape = {};
|
|
5630
|
-
for (const key in this.
|
|
5631
|
-
const validator = this.
|
|
5909
|
+
for (const key in this._config.shape) {
|
|
5910
|
+
const validator = this._config.shape[key];
|
|
5632
5911
|
if (validator instanceof VldObject) {
|
|
5633
5912
|
deepPartialShape[key] = new VldOptional(validator.deepPartial());
|
|
5634
5913
|
}
|
|
@@ -5637,7 +5916,7 @@ class VldObject extends VldBase {
|
|
|
5637
5916
|
}
|
|
5638
5917
|
}
|
|
5639
5918
|
return new VldObject({
|
|
5640
|
-
...this.
|
|
5919
|
+
...this._config,
|
|
5641
5920
|
shape: deepPartialShape
|
|
5642
5921
|
});
|
|
5643
5922
|
}
|
|
@@ -5647,12 +5926,12 @@ class VldObject extends VldBase {
|
|
|
5647
5926
|
pick(...keys) {
|
|
5648
5927
|
const pickedShape = {};
|
|
5649
5928
|
for (const key of keys) {
|
|
5650
|
-
if (key in this.
|
|
5651
|
-
pickedShape[key] = this.
|
|
5929
|
+
if (key in this._config.shape) {
|
|
5930
|
+
pickedShape[key] = this._config.shape[key];
|
|
5652
5931
|
}
|
|
5653
5932
|
}
|
|
5654
5933
|
return new VldObject({
|
|
5655
|
-
...this.
|
|
5934
|
+
...this._config,
|
|
5656
5935
|
shape: pickedShape
|
|
5657
5936
|
});
|
|
5658
5937
|
}
|
|
@@ -5662,13 +5941,13 @@ class VldObject extends VldBase {
|
|
|
5662
5941
|
omit(...keys) {
|
|
5663
5942
|
const omittedShape = {};
|
|
5664
5943
|
const keysToOmit = new Set(keys);
|
|
5665
|
-
for (const key in this.
|
|
5944
|
+
for (const key in this._config.shape) {
|
|
5666
5945
|
if (!keysToOmit.has(key)) {
|
|
5667
|
-
omittedShape[key] = this.
|
|
5946
|
+
omittedShape[key] = this._config.shape[key];
|
|
5668
5947
|
}
|
|
5669
5948
|
}
|
|
5670
5949
|
return new VldObject({
|
|
5671
|
-
...this.
|
|
5950
|
+
...this._config,
|
|
5672
5951
|
shape: omittedShape
|
|
5673
5952
|
});
|
|
5674
5953
|
}
|
|
@@ -5677,8 +5956,8 @@ class VldObject extends VldBase {
|
|
|
5677
5956
|
*/
|
|
5678
5957
|
extend(extension) {
|
|
5679
5958
|
return new VldObject({
|
|
5680
|
-
...this.
|
|
5681
|
-
shape: { ...this.
|
|
5959
|
+
...this._config,
|
|
5960
|
+
shape: { ...this._config.shape, ...extension }
|
|
5682
5961
|
});
|
|
5683
5962
|
}
|
|
5684
5963
|
/**
|
|
@@ -5686,8 +5965,8 @@ class VldObject extends VldBase {
|
|
|
5686
5965
|
*/
|
|
5687
5966
|
merge(other) {
|
|
5688
5967
|
return new VldObject({
|
|
5689
|
-
...this.
|
|
5690
|
-
shape: { ...this.
|
|
5968
|
+
...this._config,
|
|
5969
|
+
shape: { ...this._config.shape, ...other.config.shape }
|
|
5691
5970
|
});
|
|
5692
5971
|
}
|
|
5693
5972
|
/**
|
|
@@ -5695,8 +5974,8 @@ class VldObject extends VldBase {
|
|
|
5695
5974
|
*/
|
|
5696
5975
|
required() {
|
|
5697
5976
|
const requiredShape = {};
|
|
5698
|
-
for (const key in this.
|
|
5699
|
-
const validator = this.
|
|
5977
|
+
for (const key in this._config.shape) {
|
|
5978
|
+
const validator = this._config.shape[key];
|
|
5700
5979
|
// If it's optional, unwrap it
|
|
5701
5980
|
if (validator instanceof VldOptional) {
|
|
5702
5981
|
// BUG-001 FIX: Add defensive check for baseValidator property
|
|
@@ -5711,7 +5990,7 @@ class VldObject extends VldBase {
|
|
|
5711
5990
|
}
|
|
5712
5991
|
}
|
|
5713
5992
|
return new VldObject({
|
|
5714
|
-
...this.
|
|
5993
|
+
...this._config,
|
|
5715
5994
|
shape: requiredShape
|
|
5716
5995
|
});
|
|
5717
5996
|
}
|
|
@@ -5721,7 +6000,7 @@ class VldObject extends VldBase {
|
|
|
5721
6000
|
*/
|
|
5722
6001
|
catchall(schema) {
|
|
5723
6002
|
return new VldObject({
|
|
5724
|
-
...this.
|
|
6003
|
+
...this._config,
|
|
5725
6004
|
catchall: schema,
|
|
5726
6005
|
passthrough: false // catchall overrides passthrough
|
|
5727
6006
|
});
|
|
@@ -5731,14 +6010,14 @@ class VldObject extends VldBase {
|
|
|
5731
6010
|
* Zod 4 API parity - returns the shape object
|
|
5732
6011
|
*/
|
|
5733
6012
|
get shape() {
|
|
5734
|
-
return this.
|
|
6013
|
+
return this._config.shape;
|
|
5735
6014
|
}
|
|
5736
6015
|
/**
|
|
5737
6016
|
* Create an enum validator from object keys
|
|
5738
6017
|
* Zod 4 API parity - creates literal union of keys
|
|
5739
6018
|
*/
|
|
5740
6019
|
keyof() {
|
|
5741
|
-
const keys = Object.keys(this.
|
|
6020
|
+
const keys = Object.keys(this._config.shape);
|
|
5742
6021
|
if (keys.length === 0) {
|
|
5743
6022
|
throw new Error('Cannot create keyof enum from empty object');
|
|
5744
6023
|
}
|
|
@@ -5757,7 +6036,7 @@ class VldObject extends VldBase {
|
|
|
5757
6036
|
*/
|
|
5758
6037
|
safeExtend(extension) {
|
|
5759
6038
|
// Check for overlapping keys
|
|
5760
|
-
const existingKeys = new Set(Object.keys(this.
|
|
6039
|
+
const existingKeys = new Set(Object.keys(this._config.shape));
|
|
5761
6040
|
const extensionKeys = Object.keys(extension);
|
|
5762
6041
|
const overlappingKeys = [];
|
|
5763
6042
|
for (const key of extensionKeys) {
|
|
@@ -5769,8 +6048,8 @@ class VldObject extends VldBase {
|
|
|
5769
6048
|
throw new Error(`safeExtend: ${getMessages().safeExtendOverlap(overlappingKeys)}`);
|
|
5770
6049
|
}
|
|
5771
6050
|
return new VldObject({
|
|
5772
|
-
...this.
|
|
5773
|
-
shape: { ...this.
|
|
6051
|
+
...this._config,
|
|
6052
|
+
shape: { ...this._config.shape, ...extension }
|
|
5774
6053
|
});
|
|
5775
6054
|
}
|
|
5776
6055
|
}
|
|
@@ -6353,11 +6632,17 @@ class VldLiteral extends VldBase {
|
|
|
6353
6632
|
/**
|
|
6354
6633
|
* Private constructor to enforce immutability
|
|
6355
6634
|
*/
|
|
6356
|
-
constructor(
|
|
6635
|
+
constructor(_literal, errorMessage) {
|
|
6357
6636
|
super();
|
|
6358
|
-
this.
|
|
6637
|
+
this._literal = _literal;
|
|
6359
6638
|
this.errorMessage = errorMessage;
|
|
6360
6639
|
}
|
|
6640
|
+
/**
|
|
6641
|
+
* Get the literal value
|
|
6642
|
+
*/
|
|
6643
|
+
get literal() {
|
|
6644
|
+
return this._literal;
|
|
6645
|
+
}
|
|
6361
6646
|
/**
|
|
6362
6647
|
* Create a new literal validator
|
|
6363
6648
|
*/
|
|
@@ -6368,23 +6653,23 @@ class VldLiteral extends VldBase {
|
|
|
6368
6653
|
* Parse and validate a literal value
|
|
6369
6654
|
*/
|
|
6370
6655
|
parse(value) {
|
|
6371
|
-
if (value !== this.
|
|
6656
|
+
if (value !== this._literal) {
|
|
6372
6657
|
throw new Error(this.errorMessage ||
|
|
6373
|
-
getMessages().literalExpected(JSON.stringify(this.
|
|
6658
|
+
getMessages().literalExpected(JSON.stringify(this._literal), JSON.stringify(value)));
|
|
6374
6659
|
}
|
|
6375
|
-
return this.
|
|
6660
|
+
return this._literal;
|
|
6376
6661
|
}
|
|
6377
6662
|
/**
|
|
6378
6663
|
* Safely parse and validate a literal value
|
|
6379
6664
|
*/
|
|
6380
6665
|
safeParse(value) {
|
|
6381
|
-
if (value === this.
|
|
6382
|
-
return { success: true, data: this.
|
|
6666
|
+
if (value === this._literal) {
|
|
6667
|
+
return { success: true, data: this._literal };
|
|
6383
6668
|
}
|
|
6384
6669
|
return {
|
|
6385
6670
|
success: false,
|
|
6386
6671
|
error: new Error(this.errorMessage ||
|
|
6387
|
-
getMessages().literalExpected(JSON.stringify(this.
|
|
6672
|
+
getMessages().literalExpected(JSON.stringify(this._literal), JSON.stringify(value)))
|
|
6388
6673
|
};
|
|
6389
6674
|
}
|
|
6390
6675
|
}
|
|
@@ -6596,27 +6881,56 @@ class VldNan extends VldBase {
|
|
|
6596
6881
|
/**
|
|
6597
6882
|
* Lazy validator - defers schema evaluation until runtime
|
|
6598
6883
|
* Essential for recursive and self-referencing types
|
|
6884
|
+
*
|
|
6885
|
+
* MEMORY OPTIMIZATION: Uses WeakRef for the cached schema to allow garbage collection
|
|
6886
|
+
* when the validator is no longer in use. This prevents memory leaks in long-running
|
|
6887
|
+
* applications with dynamically created schemas.
|
|
6599
6888
|
*/
|
|
6600
6889
|
class VldLazy extends VldBase {
|
|
6601
6890
|
constructor(_schemaGetter) {
|
|
6602
6891
|
super();
|
|
6603
6892
|
this._schemaGetter = _schemaGetter;
|
|
6604
|
-
|
|
6893
|
+
// Use WeakRef to allow garbage collection of the cached schema
|
|
6894
|
+
this._cachedSchemaRef = null;
|
|
6895
|
+
// Keep a strong reference flag to prevent GC during active use
|
|
6896
|
+
this._strongRef = null;
|
|
6605
6897
|
}
|
|
6606
6898
|
static create(schemaGetter) {
|
|
6607
6899
|
return new VldLazy(schemaGetter);
|
|
6608
6900
|
}
|
|
6609
6901
|
/**
|
|
6610
6902
|
* Get the actual schema, caching it after first retrieval
|
|
6903
|
+
* Uses WeakRef to allow garbage collection when validator is not in use
|
|
6611
6904
|
*/
|
|
6612
6905
|
_getSchema() {
|
|
6613
|
-
if (
|
|
6614
|
-
|
|
6906
|
+
// Check if we have a strong reference first (active use)
|
|
6907
|
+
if (this._strongRef) {
|
|
6908
|
+
return this._strongRef;
|
|
6909
|
+
}
|
|
6910
|
+
// Try to get from WeakRef
|
|
6911
|
+
if (this._cachedSchemaRef) {
|
|
6912
|
+
const cached = this._cachedSchemaRef.deref();
|
|
6913
|
+
if (cached) {
|
|
6914
|
+
// Restore strong reference for active use
|
|
6915
|
+
this._strongRef = cached;
|
|
6916
|
+
return cached;
|
|
6917
|
+
}
|
|
6615
6918
|
}
|
|
6616
|
-
|
|
6919
|
+
// Create new schema
|
|
6920
|
+
const schema = this._schemaGetter();
|
|
6921
|
+
this._cachedSchemaRef = new WeakRef(schema);
|
|
6922
|
+
this._strongRef = schema;
|
|
6923
|
+
// Clear strong reference after a tick to allow GC
|
|
6924
|
+
// This keeps the schema alive during synchronous operations
|
|
6925
|
+
// but allows it to be collected if the validator is discarded
|
|
6926
|
+
Promise.resolve().then(() => {
|
|
6927
|
+
this._strongRef = null;
|
|
6928
|
+
});
|
|
6929
|
+
return schema;
|
|
6617
6930
|
}
|
|
6618
6931
|
/**
|
|
6619
6932
|
* Get the inner schema (unwrap)
|
|
6933
|
+
* Returns a strong reference that will keep the schema alive
|
|
6620
6934
|
*/
|
|
6621
6935
|
unwrap() {
|
|
6622
6936
|
return this._getSchema();
|
|
@@ -6639,11 +6953,10 @@ class VldLazy extends VldBase {
|
|
|
6639
6953
|
*/
|
|
6640
6954
|
function extractLiteralValues(schema) {
|
|
6641
6955
|
if (schema instanceof VldLiteral) {
|
|
6642
|
-
|
|
6643
|
-
return [value];
|
|
6956
|
+
return [schema.literal];
|
|
6644
6957
|
}
|
|
6645
6958
|
if (schema instanceof VldEnum) {
|
|
6646
|
-
return schema.values;
|
|
6959
|
+
return [...schema.values];
|
|
6647
6960
|
}
|
|
6648
6961
|
throw new Error('Discriminator must be a literal or enum schema');
|
|
6649
6962
|
}
|