@oxog/vld 1.4.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) hide show
  1. package/CHANGELOG.md +449 -333
  2. package/README.md +1171 -1130
  3. package/dist/chunks/bigint-ZKkWLdmi.js +1628 -0
  4. package/dist/chunks/bigint-ZKkWLdmi.js.map +1 -0
  5. package/dist/{coercion/string.js → chunks/date-7Iz2BGsL.js} +271 -5
  6. package/dist/chunks/date-7Iz2BGsL.js.map +1 -0
  7. package/dist/chunks/index-BoV-4S1F.js +894 -0
  8. package/dist/chunks/index-BoV-4S1F.js.map +1 -0
  9. package/dist/chunks/index-eWXdjyyy.js +426 -0
  10. package/dist/chunks/index-eWXdjyyy.js.map +1 -0
  11. package/dist/chunks/json-nIhyRPOU.js +1934 -0
  12. package/dist/chunks/json-nIhyRPOU.js.map +1 -0
  13. package/dist/{validators/unknown.js → chunks/unknown-CjSRcELI.js} +6 -3
  14. package/dist/chunks/unknown-CjSRcELI.js.map +1 -0
  15. package/dist/cjs/errors.cjs +387 -0
  16. package/dist/cjs/errors.cjs.map +1 -0
  17. package/dist/cjs/index.cjs +9815 -0
  18. package/dist/cjs/index.cjs.map +1 -0
  19. package/dist/cjs/locales/index.cjs +2877 -0
  20. package/dist/cjs/locales/index.cjs.map +1 -0
  21. package/dist/cjs/locales/lazy.cjs +3169 -0
  22. package/dist/cjs/locales/lazy.cjs.map +1 -0
  23. package/dist/cjs/mini.cjs +7161 -0
  24. package/dist/cjs/mini.cjs.map +1 -0
  25. package/dist/cli/bin.d.ts +8 -0
  26. package/dist/cli/bin.d.ts.map +1 -0
  27. package/dist/cli/commands/benchmark.d.ts +11 -0
  28. package/dist/cli/commands/benchmark.d.ts.map +1 -0
  29. package/dist/cli/commands/validate.d.ts +11 -0
  30. package/dist/cli/commands/validate.d.ts.map +1 -0
  31. package/dist/cli/index.d.ts +59 -0
  32. package/dist/cli/index.d.ts.map +1 -0
  33. package/dist/codecs/index.d.ts.map +1 -1
  34. package/dist/codecs/index.js +4 -256
  35. package/dist/codecs/index.js.map +1 -1
  36. package/dist/coercion/boolean.d.ts.map +1 -1
  37. package/dist/coercion/index.d.ts +11 -0
  38. package/dist/coercion/index.d.ts.map +1 -0
  39. package/dist/coercion/{bigint.js → index.js} +7 -4
  40. package/dist/coercion/index.js.map +1 -0
  41. package/dist/compat/emitter.d.ts +73 -0
  42. package/dist/compat/emitter.d.ts.map +1 -0
  43. package/dist/compat/emitter.js +181 -0
  44. package/dist/compat/emitter.js.map +1 -0
  45. package/dist/compat/index.d.ts +15 -0
  46. package/dist/compat/index.d.ts.map +1 -0
  47. package/dist/compat/result.d.ts +128 -0
  48. package/dist/compat/result.d.ts.map +1 -0
  49. package/dist/compat/result.js +171 -0
  50. package/dist/compat/result.js.map +1 -0
  51. package/dist/errors.d.ts +58 -1
  52. package/dist/errors.d.ts.map +1 -1
  53. package/dist/errors.js +106 -15
  54. package/dist/errors.js.map +1 -1
  55. package/dist/events.d.ts +140 -0
  56. package/dist/events.d.ts.map +1 -0
  57. package/dist/index.d.ts +13 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +230 -103
  60. package/dist/index.js.map +1 -1
  61. package/dist/kernel.d.ts +29 -0
  62. package/dist/kernel.d.ts.map +1 -0
  63. package/dist/kernel.js +405 -0
  64. package/dist/kernel.js.map +1 -0
  65. package/dist/locales/af.js +4 -2
  66. package/dist/locales/af.js.map +1 -1
  67. package/dist/locales/ar.js +4 -2
  68. package/dist/locales/ar.js.map +1 -1
  69. package/dist/locales/bn.js +4 -2
  70. package/dist/locales/bn.js.map +1 -1
  71. package/dist/locales/da.js +4 -2
  72. package/dist/locales/da.js.map +1 -1
  73. package/dist/locales/de.js +4 -2
  74. package/dist/locales/de.js.map +1 -1
  75. package/dist/locales/en.js +4 -2
  76. package/dist/locales/en.js.map +1 -1
  77. package/dist/locales/es-MX.js +4 -2
  78. package/dist/locales/es-MX.js.map +1 -1
  79. package/dist/locales/es.js +4 -2
  80. package/dist/locales/es.js.map +1 -1
  81. package/dist/locales/fi.js +4 -2
  82. package/dist/locales/fi.js.map +1 -1
  83. package/dist/locales/fr.js +4 -2
  84. package/dist/locales/fr.js.map +1 -1
  85. package/dist/locales/hi.js +4 -2
  86. package/dist/locales/hi.js.map +1 -1
  87. package/dist/locales/id.js +4 -2
  88. package/dist/locales/id.js.map +1 -1
  89. package/dist/locales/index.js +3 -91
  90. package/dist/locales/index.js.map +1 -1
  91. package/dist/locales/it.js +4 -2
  92. package/dist/locales/it.js.map +1 -1
  93. package/dist/locales/ja.js +4 -2
  94. package/dist/locales/ja.js.map +1 -1
  95. package/dist/locales/ko.js +4 -2
  96. package/dist/locales/ko.js.map +1 -1
  97. package/dist/locales/lazy.d.ts +111 -0
  98. package/dist/locales/lazy.d.ts.map +1 -0
  99. package/dist/locales/lazy.js +220 -0
  100. package/dist/locales/lazy.js.map +1 -0
  101. package/dist/locales/nl.js +4 -2
  102. package/dist/locales/nl.js.map +1 -1
  103. package/dist/locales/no.js +4 -2
  104. package/dist/locales/no.js.map +1 -1
  105. package/dist/locales/pl.js +4 -2
  106. package/dist/locales/pl.js.map +1 -1
  107. package/dist/locales/pt-BR.js +4 -2
  108. package/dist/locales/pt-BR.js.map +1 -1
  109. package/dist/locales/pt.js +4 -2
  110. package/dist/locales/pt.js.map +1 -1
  111. package/dist/locales/ru.js +4 -2
  112. package/dist/locales/ru.js.map +1 -1
  113. package/dist/locales/sv.js +4 -2
  114. package/dist/locales/sv.js.map +1 -1
  115. package/dist/locales/sw.js +4 -2
  116. package/dist/locales/sw.js.map +1 -1
  117. package/dist/locales/th.js +4 -2
  118. package/dist/locales/th.js.map +1 -1
  119. package/dist/locales/tr.js +4 -2
  120. package/dist/locales/tr.js.map +1 -1
  121. package/dist/locales/vi.js +4 -2
  122. package/dist/locales/vi.js.map +1 -1
  123. package/dist/locales/zh.js +4 -2
  124. package/dist/locales/zh.js.map +1 -1
  125. package/dist/logger.d.ts +94 -0
  126. package/dist/logger.d.ts.map +1 -0
  127. package/dist/mini.d.ts +182 -0
  128. package/dist/mini.d.ts.map +1 -0
  129. package/dist/mini.js +132 -0
  130. package/dist/mini.js.map +1 -0
  131. package/dist/pigment.d.ts +124 -0
  132. package/dist/pigment.d.ts.map +1 -0
  133. package/dist/pigment.js +178 -0
  134. package/dist/pigment.js.map +1 -0
  135. package/dist/plugins/index.d.ts +8 -0
  136. package/dist/plugins/index.d.ts.map +1 -0
  137. package/dist/plugins/types.d.ts +186 -0
  138. package/dist/plugins/types.d.ts.map +1 -0
  139. package/dist/utils/codec-utils.d.ts.map +1 -1
  140. package/dist/utils/ip-validation.d.ts.map +1 -1
  141. package/dist/validators/base.d.ts +3 -0
  142. package/dist/validators/base.d.ts.map +1 -1
  143. package/dist/validators/index.js +5 -45
  144. package/dist/validators/index.js.map +1 -1
  145. package/dist/validators/object.d.ts.map +1 -1
  146. package/dist/validators/template-literal.d.ts.map +1 -1
  147. package/package.json +153 -79
  148. package/dist/coercion/bigint.js.map +0 -1
  149. package/dist/coercion/boolean.js +0 -67
  150. package/dist/coercion/boolean.js.map +0 -1
  151. package/dist/coercion/date.js +0 -52
  152. package/dist/coercion/date.js.map +0 -1
  153. package/dist/coercion/number.js +0 -160
  154. package/dist/coercion/number.js.map +0 -1
  155. package/dist/coercion/string.js.map +0 -1
  156. package/dist/locales/backup-en.js +0 -105
  157. package/dist/locales/backup-en.js.map +0 -1
  158. package/dist/locales/types.js +0 -2
  159. package/dist/locales/types.js.map +0 -1
  160. package/dist/utils/codec-utils.js +0 -223
  161. package/dist/utils/codec-utils.js.map +0 -1
  162. package/dist/utils/deep-merge.js +0 -76
  163. package/dist/utils/deep-merge.js.map +0 -1
  164. package/dist/utils/ip-validation.js +0 -78
  165. package/dist/utils/ip-validation.js.map +0 -1
  166. package/dist/utils/security.js +0 -61
  167. package/dist/utils/security.js.map +0 -1
  168. package/dist/validators/any.js +0 -25
  169. package/dist/validators/any.js.map +0 -1
  170. package/dist/validators/array.js +0 -190
  171. package/dist/validators/array.js.map +0 -1
  172. package/dist/validators/base.js +0 -491
  173. package/dist/validators/base.js.map +0 -1
  174. package/dist/validators/base64.js +0 -65
  175. package/dist/validators/base64.js.map +0 -1
  176. package/dist/validators/bigint.js +0 -142
  177. package/dist/validators/bigint.js.map +0 -1
  178. package/dist/validators/boolean.js +0 -114
  179. package/dist/validators/boolean.js.map +0 -1
  180. package/dist/validators/codec.js +0 -182
  181. package/dist/validators/codec.js.map +0 -1
  182. package/dist/validators/custom.js +0 -50
  183. package/dist/validators/custom.js.map +0 -1
  184. package/dist/validators/date.js +0 -227
  185. package/dist/validators/date.js.map +0 -1
  186. package/dist/validators/discriminated-union.js +0 -98
  187. package/dist/validators/discriminated-union.js.map +0 -1
  188. package/dist/validators/enum.js +0 -82
  189. package/dist/validators/enum.js.map +0 -1
  190. package/dist/validators/file.js +0 -114
  191. package/dist/validators/file.js.map +0 -1
  192. package/dist/validators/function.js +0 -45
  193. package/dist/validators/function.js.map +0 -1
  194. package/dist/validators/hex.js +0 -62
  195. package/dist/validators/hex.js.map +0 -1
  196. package/dist/validators/intersection.js +0 -64
  197. package/dist/validators/intersection.js.map +0 -1
  198. package/dist/validators/json.js +0 -64
  199. package/dist/validators/json.js.map +0 -1
  200. package/dist/validators/lazy.js +0 -42
  201. package/dist/validators/lazy.js.map +0 -1
  202. package/dist/validators/literal.js +0 -45
  203. package/dist/validators/literal.js.map +0 -1
  204. package/dist/validators/map.js +0 -54
  205. package/dist/validators/map.js.map +0 -1
  206. package/dist/validators/nan.js +0 -32
  207. package/dist/validators/nan.js.map +0 -1
  208. package/dist/validators/never.js +0 -29
  209. package/dist/validators/never.js.map +0 -1
  210. package/dist/validators/null.js +0 -31
  211. package/dist/validators/null.js.map +0 -1
  212. package/dist/validators/number.js +0 -225
  213. package/dist/validators/number.js.map +0 -1
  214. package/dist/validators/object.js +0 -458
  215. package/dist/validators/object.js.map +0 -1
  216. package/dist/validators/record.js +0 -128
  217. package/dist/validators/record.js.map +0 -1
  218. package/dist/validators/set.js +0 -51
  219. package/dist/validators/set.js.map +0 -1
  220. package/dist/validators/string-bool.js +0 -127
  221. package/dist/validators/string-bool.js.map +0 -1
  222. package/dist/validators/string-formats.js +0 -120
  223. package/dist/validators/string-formats.js.map +0 -1
  224. package/dist/validators/string.js +0 -253
  225. package/dist/validators/string.js.map +0 -1
  226. package/dist/validators/symbol.js +0 -42
  227. package/dist/validators/symbol.js.map +0 -1
  228. package/dist/validators/template-literal.js +0 -101
  229. package/dist/validators/template-literal.js.map +0 -1
  230. package/dist/validators/tuple.js +0 -55
  231. package/dist/validators/tuple.js.map +0 -1
  232. package/dist/validators/uint8array.js +0 -98
  233. package/dist/validators/uint8array.js.map +0 -1
  234. package/dist/validators/undefined.js +0 -31
  235. package/dist/validators/undefined.js.map +0 -1
  236. package/dist/validators/union.js +0 -126
  237. package/dist/validators/union.js.map +0 -1
  238. package/dist/validators/unknown.js.map +0 -1
  239. package/dist/validators/void.js +0 -35
  240. package/dist/validators/void.js.map +0 -1
  241. package/dist/validators/xor.js +0 -64
  242. package/dist/validators/xor.js.map +0 -1
@@ -0,0 +1,1934 @@
1
+ import { i as VldBase, t as getMessages, V as VldString, a as VldNumber, b as VldBoolean, c as VldDate, e as VldOptional } from './bigint-ZKkWLdmi.js';
2
+ import { c as VldCoerceString, b as VldCoerceNumber, a as VldCoerceBoolean, V as VldCoerceDate } from './date-7Iz2BGsL.js';
3
+
4
+ /**
5
+ * Default truthy and falsy value sets
6
+ */
7
+ const DEFAULT_TRUTHY = ['true', '1', 'yes', 'on', 'y', 'enabled'];
8
+ const DEFAULT_FALSY = ['false', '0', 'no', 'off', 'n', 'disabled'];
9
+ /**
10
+ * Immutable string boolean validator for flexible boolean parsing
11
+ * Supports string representations like "true", "yes", "on", "1", etc.
12
+ */
13
+ class VldStringBool extends VldBase {
14
+ constructor(options) {
15
+ super();
16
+ this.options = options;
17
+ }
18
+ /**
19
+ * Create a new string boolean validator
20
+ */
21
+ static create(options = {}) {
22
+ return new VldStringBool(options);
23
+ }
24
+ /**
25
+ * Normalize a string value based on case sensitivity setting
26
+ */
27
+ normalizeValue(value) {
28
+ return this.options.caseSensitive ? value : value.toLowerCase();
29
+ }
30
+ /**
31
+ * Get the effective truthy values (custom or default)
32
+ */
33
+ getTruthyValues() {
34
+ return this.options.truthy ?? DEFAULT_TRUTHY;
35
+ }
36
+ /**
37
+ * Get the effective falsy values (custom or default)
38
+ */
39
+ getFalsyValues() {
40
+ return this.options.falsy ?? DEFAULT_FALSY;
41
+ }
42
+ /**
43
+ * Parse and validate a string boolean value
44
+ */
45
+ parse(value) {
46
+ // First, ensure we have a string
47
+ if (typeof value !== 'string') {
48
+ // Also accept actual boolean values for convenience
49
+ if (typeof value === 'boolean') {
50
+ return value;
51
+ }
52
+ throw new Error(getMessages().stringExpected(typeof value, 'string'));
53
+ }
54
+ const normalized = this.normalizeValue(value);
55
+ const truthyValues = this.getTruthyValues();
56
+ const falsyValues = this.getFalsyValues();
57
+ // Normalize the value sets for comparison
58
+ const normalizedTruthy = this.options.caseSensitive
59
+ ? truthyValues
60
+ : truthyValues.map(v => v.toLowerCase());
61
+ const normalizedFalsy = this.options.caseSensitive
62
+ ? falsyValues
63
+ : falsyValues.map(v => v.toLowerCase());
64
+ // Check if value is in truthy set
65
+ if (normalizedTruthy.includes(normalized)) {
66
+ return true;
67
+ }
68
+ // Check if value is in falsy set
69
+ if (normalizedFalsy.includes(normalized)) {
70
+ return false;
71
+ }
72
+ // Value is not recognized
73
+ const allValidValues = [...normalizedTruthy, ...normalizedFalsy];
74
+ throw new Error(getMessages().stringBoolExpected(this.options.caseSensitive ? allValidValues.join(', ') : allValidValues.join(', '), value));
75
+ }
76
+ /**
77
+ * Safely parse and validate a string boolean value
78
+ */
79
+ safeParse(value) {
80
+ try {
81
+ const result = this.parse(value);
82
+ return { success: true, data: result };
83
+ }
84
+ catch (error) {
85
+ return {
86
+ success: false,
87
+ error: error
88
+ };
89
+ }
90
+ }
91
+ /**
92
+ * Create a new validator with custom truthy values
93
+ */
94
+ withTruthy(values) {
95
+ return new VldStringBool({
96
+ ...this.options,
97
+ truthy: values
98
+ });
99
+ }
100
+ /**
101
+ * Create a new validator with custom falsy values
102
+ */
103
+ withFalsy(values) {
104
+ return new VldStringBool({
105
+ ...this.options,
106
+ falsy: values
107
+ });
108
+ }
109
+ /**
110
+ * Create a new validator with case-sensitive matching
111
+ */
112
+ caseSensitive() {
113
+ return new VldStringBool({
114
+ ...this.options,
115
+ caseSensitive: true
116
+ });
117
+ }
118
+ /**
119
+ * Create a new validator with case-insensitive matching (default)
120
+ */
121
+ caseInsensitive() {
122
+ return new VldStringBool({
123
+ ...this.options,
124
+ caseSensitive: false
125
+ });
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Immutable array validator with chainable methods
131
+ */
132
+ class VldArray extends VldBase {
133
+ /**
134
+ * Private constructor to enforce immutability
135
+ */
136
+ constructor(config) {
137
+ super();
138
+ this.config = config;
139
+ }
140
+ /**
141
+ * Create a new array validator
142
+ */
143
+ static create(itemValidator) {
144
+ return new VldArray({ itemValidator });
145
+ }
146
+ /**
147
+ * Parse and validate an array value
148
+ */
149
+ parse(value) {
150
+ if (!Array.isArray(value)) {
151
+ throw new Error(this.config.errorMessage || getMessages().invalidArray);
152
+ }
153
+ // Validate length constraints
154
+ if (this.config.exactLength !== undefined && value.length !== this.config.exactLength) {
155
+ throw new Error(this.config.errorMessage || getMessages().arrayLength(this.config.exactLength));
156
+ }
157
+ if (this.config.minLength !== undefined && value.length < this.config.minLength) {
158
+ throw new Error(this.config.errorMessage || getMessages().arrayMin(this.config.minLength));
159
+ }
160
+ if (this.config.maxLength !== undefined && value.length > this.config.maxLength) {
161
+ throw new Error(this.config.errorMessage || getMessages().arrayMax(this.config.maxLength));
162
+ }
163
+ // Validate each item with optimized loop and safeParse
164
+ const result = [];
165
+ const length = value.length;
166
+ for (let i = 0; i < length; i++) {
167
+ const parseResult = this.config.itemValidator.safeParse(value[i]);
168
+ if (!parseResult.success) {
169
+ throw new Error(getMessages().arrayItem(i, parseResult.error.message));
170
+ }
171
+ result[i] = parseResult.data; // Direct assignment is faster than push
172
+ }
173
+ // Check uniqueness if required
174
+ if (this.config.unique) {
175
+ const seen = new Set();
176
+ for (const item of result) {
177
+ const key = typeof item === 'object' ? this.stableStringify(item) : item;
178
+ if (seen.has(key)) {
179
+ throw new Error('Array must contain unique items');
180
+ }
181
+ seen.add(key);
182
+ }
183
+ }
184
+ return result;
185
+ }
186
+ /**
187
+ * Safely parse and validate an array value
188
+ */
189
+ safeParse(value) {
190
+ try {
191
+ return { success: true, data: this.parse(value) };
192
+ }
193
+ catch (error) {
194
+ return { success: false, error: error };
195
+ }
196
+ }
197
+ /**
198
+ * Create a stable string representation of an object for hashing
199
+ * Handles circular references and deep nesting gracefully
200
+ * BUG-006 FIX: Added depth limit to prevent stack overflow
201
+ * BUG-NEW-003 FIX: Fixed depth tracking to properly track recursion depth
202
+ */
203
+ stableStringify(obj) {
204
+ const seen = new WeakSet();
205
+ const MAX_DEPTH = 100; // Reasonable depth limit to prevent stack overflow
206
+ // Helper function to sort keys and create stable representation
207
+ const sortedStringify = (value, depth = 0) => {
208
+ // Check depth limit
209
+ if (depth > MAX_DEPTH) {
210
+ return '"[Max Depth Exceeded]"';
211
+ }
212
+ // Handle primitives
213
+ if (value === null)
214
+ return 'null';
215
+ if (value === undefined)
216
+ return 'undefined';
217
+ if (typeof value !== 'object')
218
+ return JSON.stringify(value);
219
+ // Handle circular references
220
+ if (seen.has(value)) {
221
+ return '"[Circular]"';
222
+ }
223
+ seen.add(value);
224
+ try {
225
+ // Handle arrays
226
+ if (Array.isArray(value)) {
227
+ const items = value.map(item => sortedStringify(item, depth + 1));
228
+ return `[${items.join(',')}]`;
229
+ }
230
+ // Handle objects - sort keys for stability
231
+ const keys = Object.keys(value).sort();
232
+ const pairs = keys.map(key => {
233
+ const serializedKey = JSON.stringify(key);
234
+ const serializedValue = sortedStringify(value[key], depth + 1);
235
+ return `${serializedKey}:${serializedValue}`;
236
+ });
237
+ return `{${pairs.join(',')}}`;
238
+ }
239
+ finally {
240
+ // Clean up seen set for this branch
241
+ seen.delete(value);
242
+ }
243
+ };
244
+ try {
245
+ return sortedStringify(obj);
246
+ }
247
+ catch (error) {
248
+ // Fallback to safe representation if stringify fails
249
+ return String(obj);
250
+ }
251
+ }
252
+ /**
253
+ * Create a new validator with minimum length constraint
254
+ */
255
+ min(length, message) {
256
+ return new VldArray({
257
+ ...this.config,
258
+ minLength: length,
259
+ errorMessage: message || getMessages().arrayMin(length)
260
+ });
261
+ }
262
+ /**
263
+ * Create a new validator with maximum length constraint
264
+ */
265
+ max(length, message) {
266
+ return new VldArray({
267
+ ...this.config,
268
+ maxLength: length,
269
+ errorMessage: message || getMessages().arrayMax(length)
270
+ });
271
+ }
272
+ /**
273
+ * Create a new validator with exact length constraint
274
+ */
275
+ length(length, message) {
276
+ return new VldArray({
277
+ ...this.config,
278
+ exactLength: length,
279
+ minLength: undefined,
280
+ maxLength: undefined,
281
+ errorMessage: message || getMessages().arrayLength(length)
282
+ });
283
+ }
284
+ /**
285
+ * Create a new validator that ensures array is not empty
286
+ */
287
+ nonempty(message) {
288
+ return new VldArray({
289
+ ...this.config,
290
+ minLength: 1,
291
+ errorMessage: message || getMessages().arrayEmpty
292
+ });
293
+ }
294
+ /**
295
+ * Create a new validator that ensures array contains unique items
296
+ */
297
+ unique(message) {
298
+ return new VldArray({
299
+ ...this.config,
300
+ unique: true,
301
+ errorMessage: message || 'Array must contain unique items'
302
+ });
303
+ }
304
+ /**
305
+ * Create a new validator with a range constraint for length
306
+ */
307
+ between(min, max, message) {
308
+ return new VldArray({
309
+ ...this.config,
310
+ minLength: min,
311
+ maxLength: max,
312
+ errorMessage: message || `Array length must be between ${min} and ${max}`
313
+ });
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Immutable enum validator for string enum values
319
+ */
320
+ class VldEnum extends VldBase {
321
+ /**
322
+ * Private constructor to enforce immutability
323
+ */
324
+ constructor(values, errorMessage) {
325
+ super();
326
+ this.values = values;
327
+ this.errorMessage = errorMessage;
328
+ }
329
+ /**
330
+ * Create a new enum validator
331
+ */
332
+ static create(values) {
333
+ return new VldEnum(values);
334
+ }
335
+ /**
336
+ * Parse and validate an enum value
337
+ */
338
+ parse(value) {
339
+ // BUG-002 FIX: Add type check before includes() to prevent type confusion
340
+ if (typeof value !== 'string') {
341
+ throw new Error(this.errorMessage ||
342
+ getMessages().enumExpected([...this.values], JSON.stringify(value)));
343
+ }
344
+ if (!this.values.includes(value)) {
345
+ throw new Error(this.errorMessage ||
346
+ getMessages().enumExpected([...this.values], JSON.stringify(value)));
347
+ }
348
+ return value;
349
+ }
350
+ /**
351
+ * Safely parse and validate an enum value
352
+ */
353
+ safeParse(value) {
354
+ // BUG-002 FIX: Add type check before includes() to prevent type confusion
355
+ if (typeof value !== 'string') {
356
+ return {
357
+ success: false,
358
+ error: new Error(this.errorMessage ||
359
+ getMessages().enumExpected([...this.values], JSON.stringify(value)))
360
+ };
361
+ }
362
+ if (this.values.includes(value)) {
363
+ return { success: true, data: value };
364
+ }
365
+ return {
366
+ success: false,
367
+ error: new Error(this.errorMessage ||
368
+ getMessages().enumExpected([...this.values], JSON.stringify(value)))
369
+ };
370
+ }
371
+ /**
372
+ * Exclude specific values from the enum
373
+ * Creates a new enum validator without the specified values
374
+ */
375
+ exclude(...excludeValues) {
376
+ const filtered = this.values.filter(v => !excludeValues.includes(v));
377
+ if (filtered.length === 0) {
378
+ throw new Error('Cannot exclude all enum values');
379
+ }
380
+ // Type assertion needed because TypeScript can't guarantee the filtered array maintains the required type
381
+ return new VldEnum(filtered, this.errorMessage);
382
+ }
383
+ /**
384
+ * Extract specific values from the enum
385
+ * Creates a new enum validator with only the specified values
386
+ */
387
+ extract(...extractValues) {
388
+ const extracted = this.values.filter(v => extractValues.includes(v));
389
+ if (extracted.length === 0) {
390
+ throw new Error('Cannot extract non-existent enum values');
391
+ }
392
+ // Type assertion needed because TypeScript can't guarantee the extracted array has the required type
393
+ return new VldEnum(extracted, this.errorMessage);
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Optimized immutable object validator with chainable methods
399
+ * Features pre-computed keys and Set-based lookups for better performance
400
+ */
401
+ class VldObject extends VldBase {
402
+ /**
403
+ * Private constructor to enforce immutability
404
+ */
405
+ constructor(config) {
406
+ super();
407
+ this.config = config;
408
+ // Pre-compute shape keys for faster access
409
+ this.shapeKeys = Object.keys(config.shape);
410
+ this.shapeKeysSet = new Set(this.shapeKeys);
411
+ }
412
+ /**
413
+ * Create a new object validator
414
+ */
415
+ static create(shape) {
416
+ return new VldObject({ shape });
417
+ }
418
+ /**
419
+ * Parse and validate an object value
420
+ * Ultra-optimized with inline type checks and minimal overhead
421
+ */
422
+ parse(value) {
423
+ // Fast type check
424
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
425
+ throw new Error(this.config.errorMessage || getMessages().invalidObject);
426
+ }
427
+ const obj = value;
428
+ const result = {};
429
+ // Ultra-optimized field validation with inline fast paths
430
+ for (let i = 0; i < this.shapeKeys.length; i++) {
431
+ const key = this.shapeKeys[i];
432
+ const validator = this.config.shape[key];
433
+ const fieldValue = obj[key];
434
+ // BUG-NEW-002 FIX: Use instanceof instead of constructor.name
435
+ // constructor.name breaks in minified builds where class names become 'a', 'b', etc.
436
+ // instanceof checks are reliable regardless of minification
437
+ //
438
+ // Note: Coercion validators extend their base validators, so we need to check for
439
+ // coercion types first (more specific) before checking base types
440
+ // Check for coercion validators first - they need full safeParse for type conversion
441
+ if (validator instanceof VldCoerceString ||
442
+ validator instanceof VldCoerceNumber ||
443
+ validator instanceof VldCoerceBoolean ||
444
+ validator instanceof VldCoerceDate) {
445
+ // Coercion validators need safeParse to handle type conversion
446
+ const parseResult = validator.safeParse(fieldValue);
447
+ if (!parseResult.success) {
448
+ throw new Error(getMessages().objectField(key, parseResult.error.message));
449
+ }
450
+ result[key] = parseResult.data;
451
+ }
452
+ else if (validator instanceof VldString) {
453
+ // For string with validations, use safeParse to handle email, regex etc.
454
+ const parseResult = validator.safeParse(fieldValue);
455
+ if (!parseResult.success) {
456
+ throw new Error(getMessages().objectField(key, parseResult.error.message));
457
+ }
458
+ result[key] = parseResult.data;
459
+ }
460
+ else if (validator instanceof VldNumber) {
461
+ if (typeof fieldValue !== 'number' || isNaN(fieldValue)) {
462
+ throw new Error(getMessages().objectField(key, getMessages().invalidNumber));
463
+ }
464
+ result[key] = fieldValue;
465
+ }
466
+ else if (validator instanceof VldBoolean) {
467
+ if (typeof fieldValue !== 'boolean') {
468
+ throw new Error(getMessages().objectField(key, getMessages().invalidBoolean));
469
+ }
470
+ result[key] = fieldValue;
471
+ }
472
+ else if (validator instanceof VldDate) {
473
+ // Use the actual validator for Date parsing (handles string conversion)
474
+ const parseResult = validator.safeParse(fieldValue);
475
+ if (!parseResult.success) {
476
+ throw new Error(getMessages().objectField(key, parseResult.error.message));
477
+ }
478
+ result[key] = parseResult.data;
479
+ }
480
+ else {
481
+ // Fallback to safeParse for complex validators
482
+ const parseResult = validator.safeParse(fieldValue);
483
+ if (!parseResult.success) {
484
+ throw new Error(getMessages().objectField(key, parseResult.error.message));
485
+ }
486
+ result[key] = parseResult.data;
487
+ }
488
+ }
489
+ // Handle strict mode - optimized with Set
490
+ if (this.config.strict) {
491
+ const objKeys = Object.keys(obj);
492
+ const extraKeys = [];
493
+ for (let i = 0; i < objKeys.length; i++) {
494
+ if (!this.shapeKeysSet.has(objKeys[i])) {
495
+ extraKeys.push(objKeys[i]);
496
+ }
497
+ }
498
+ if (extraKeys.length > 0) {
499
+ throw new Error(getMessages().unexpectedKeys(extraKeys));
500
+ }
501
+ }
502
+ // Handle passthrough mode - optimized with comprehensive prototype pollution protection
503
+ if (this.config.passthrough) {
504
+ const objKeys = Object.keys(obj);
505
+ for (let i = 0; i < objKeys.length; i++) {
506
+ const key = objKeys[i];
507
+ // Skip dangerous keys to prevent prototype pollution
508
+ if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
509
+ result[key] = obj[key];
510
+ }
511
+ }
512
+ }
513
+ // Handle catchall - validate extra keys with catchall validator
514
+ if (this.config.catchall) {
515
+ const objKeys = Object.keys(obj);
516
+ for (let i = 0; i < objKeys.length; i++) {
517
+ const key = objKeys[i];
518
+ // Skip keys already in shape and dangerous keys
519
+ if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
520
+ result[key] = this.config.catchall.parse(obj[key]);
521
+ }
522
+ }
523
+ }
524
+ return result;
525
+ }
526
+ /**
527
+ * Safely parse and validate an object value
528
+ * Optimized version using pre-computed keys
529
+ */
530
+ safeParse(value) {
531
+ // Fast type check
532
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
533
+ return {
534
+ success: false,
535
+ error: new Error(this.config.errorMessage || getMessages().invalidObject)
536
+ };
537
+ }
538
+ const obj = value;
539
+ const result = {};
540
+ // Validate all fields
541
+ for (let i = 0; i < this.shapeKeys.length; i++) {
542
+ const key = this.shapeKeys[i];
543
+ const validator = this.config.shape[key];
544
+ const fieldValue = obj[key];
545
+ const parseResult = validator.safeParse(fieldValue);
546
+ if (parseResult.success) {
547
+ result[key] = parseResult.data;
548
+ }
549
+ else {
550
+ return {
551
+ success: false,
552
+ error: new Error(getMessages().objectField(key, parseResult.error.message))
553
+ };
554
+ }
555
+ }
556
+ // Handle strict mode
557
+ if (this.config.strict) {
558
+ const objKeys = Object.keys(obj);
559
+ const extraKeys = [];
560
+ for (let i = 0; i < objKeys.length; i++) {
561
+ if (!this.shapeKeysSet.has(objKeys[i])) {
562
+ extraKeys.push(objKeys[i]);
563
+ }
564
+ }
565
+ if (extraKeys.length > 0) {
566
+ return {
567
+ success: false,
568
+ error: new Error(getMessages().unexpectedKeys(extraKeys))
569
+ };
570
+ }
571
+ }
572
+ // Handle passthrough mode with comprehensive prototype pollution protection
573
+ if (this.config.passthrough) {
574
+ const objKeys = Object.keys(obj);
575
+ for (let i = 0; i < objKeys.length; i++) {
576
+ const key = objKeys[i];
577
+ // Skip dangerous keys to prevent prototype pollution
578
+ if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
579
+ result[key] = obj[key];
580
+ }
581
+ }
582
+ }
583
+ // Handle catchall - validate extra keys with catchall validator
584
+ if (this.config.catchall) {
585
+ const objKeys = Object.keys(obj);
586
+ for (let i = 0; i < objKeys.length; i++) {
587
+ const key = objKeys[i];
588
+ // Skip keys already in shape and dangerous keys
589
+ if (!this.shapeKeysSet.has(key) && !this.isDangerousKey(key)) {
590
+ const catchallResult = this.config.catchall.safeParse(obj[key]);
591
+ if (!catchallResult.success) {
592
+ return {
593
+ success: false,
594
+ error: new Error(getMessages().objectField(key, catchallResult.error.message))
595
+ };
596
+ }
597
+ result[key] = catchallResult.data;
598
+ }
599
+ }
600
+ }
601
+ return { success: true, data: result };
602
+ }
603
+ /**
604
+ * Comprehensive prototype pollution protection
605
+ * Checks for dangerous keys that could modify Object.prototype
606
+ */
607
+ isDangerousKey(key) {
608
+ // Direct dangerous keys
609
+ const directDangerousKeys = ['__proto__', 'constructor', 'prototype'];
610
+ if (directDangerousKeys.includes(key)) {
611
+ return true;
612
+ }
613
+ // Nested prototype manipulation vectors
614
+ // These patterns could allow prototype pollution through nested access
615
+ const nestedPatterns = [
616
+ 'constructor.prototype',
617
+ '__proto__.toString',
618
+ 'prototype.constructor',
619
+ '__defineGetter__',
620
+ '__defineSetter__',
621
+ '__lookupGetter__',
622
+ '__lookupSetter__'
623
+ ];
624
+ // Check for nested patterns
625
+ for (const pattern of nestedPatterns) {
626
+ if (key.includes(pattern)) {
627
+ return true;
628
+ }
629
+ }
630
+ // Check for property access chains that could lead to prototype pollution
631
+ // This covers patterns like "x.constructor.prototype.polluted"
632
+ const dangerousChains = [
633
+ 'constructor.',
634
+ '__proto__.',
635
+ 'prototype.'
636
+ ];
637
+ for (const chain of dangerousChains) {
638
+ if (key.includes(chain)) {
639
+ return true;
640
+ }
641
+ }
642
+ // Additional protection: reject keys that could be used for property shadowing
643
+ const shadowingPatterns = [
644
+ 'hasOwnProperty',
645
+ 'toString',
646
+ 'valueOf',
647
+ 'isPrototypeOf',
648
+ 'propertyIsEnumerable'
649
+ ];
650
+ for (const pattern of shadowingPatterns) {
651
+ if (key === pattern || key.includes(`.${pattern}`)) {
652
+ return true;
653
+ }
654
+ }
655
+ return false;
656
+ }
657
+ /**
658
+ * Create a new validator in strict mode (no extra keys allowed)
659
+ */
660
+ strict(message) {
661
+ return new VldObject({
662
+ ...this.config,
663
+ strict: true,
664
+ passthrough: false,
665
+ errorMessage: message
666
+ });
667
+ }
668
+ /**
669
+ * Create a new validator in passthrough mode (extra keys are preserved)
670
+ */
671
+ passthrough() {
672
+ return new VldObject({
673
+ ...this.config,
674
+ strict: false,
675
+ passthrough: true
676
+ });
677
+ }
678
+ /**
679
+ * Create a new validator with all fields optional
680
+ */
681
+ partial() {
682
+ const partialShape = {};
683
+ for (const key in this.config.shape) {
684
+ partialShape[key] = new VldOptional(this.config.shape[key]);
685
+ }
686
+ return new VldObject({
687
+ ...this.config,
688
+ shape: partialShape
689
+ });
690
+ }
691
+ /**
692
+ * Create a new validator with deep partial (nested objects also partial)
693
+ */
694
+ deepPartial() {
695
+ const deepPartialShape = {};
696
+ for (const key in this.config.shape) {
697
+ const validator = this.config.shape[key];
698
+ if (validator instanceof VldObject) {
699
+ deepPartialShape[key] = new VldOptional(validator.deepPartial());
700
+ }
701
+ else {
702
+ deepPartialShape[key] = new VldOptional(validator);
703
+ }
704
+ }
705
+ return new VldObject({
706
+ ...this.config,
707
+ shape: deepPartialShape
708
+ });
709
+ }
710
+ /**
711
+ * Create a new validator with only specified keys
712
+ */
713
+ pick(...keys) {
714
+ const pickedShape = {};
715
+ for (const key of keys) {
716
+ if (key in this.config.shape) {
717
+ pickedShape[key] = this.config.shape[key];
718
+ }
719
+ }
720
+ return new VldObject({
721
+ ...this.config,
722
+ shape: pickedShape
723
+ });
724
+ }
725
+ /**
726
+ * Create a new validator without specified keys
727
+ */
728
+ omit(...keys) {
729
+ const omittedShape = {};
730
+ const keysToOmit = new Set(keys);
731
+ for (const key in this.config.shape) {
732
+ if (!keysToOmit.has(key)) {
733
+ omittedShape[key] = this.config.shape[key];
734
+ }
735
+ }
736
+ return new VldObject({
737
+ ...this.config,
738
+ shape: omittedShape
739
+ });
740
+ }
741
+ /**
742
+ * Create a new validator with additional fields
743
+ */
744
+ extend(extension) {
745
+ return new VldObject({
746
+ ...this.config,
747
+ shape: { ...this.config.shape, ...extension }
748
+ });
749
+ }
750
+ /**
751
+ * Create a new validator by merging with another object validator
752
+ */
753
+ merge(other) {
754
+ return new VldObject({
755
+ ...this.config,
756
+ shape: { ...this.config.shape, ...other.config.shape }
757
+ });
758
+ }
759
+ /**
760
+ * Create a new validator with all fields required (removes optional)
761
+ */
762
+ required() {
763
+ const requiredShape = {};
764
+ for (const key in this.config.shape) {
765
+ const validator = this.config.shape[key];
766
+ // If it's optional, unwrap it
767
+ if (validator instanceof VldOptional) {
768
+ // BUG-001 FIX: Add defensive check for baseValidator property
769
+ const unwrapped = validator.baseValidator;
770
+ if (!unwrapped || typeof unwrapped.parse !== 'function') {
771
+ throw new Error(`Invalid VldOptional structure for field "${key}": missing or invalid baseValidator`);
772
+ }
773
+ requiredShape[key] = unwrapped;
774
+ }
775
+ else {
776
+ requiredShape[key] = validator;
777
+ }
778
+ }
779
+ return new VldObject({
780
+ ...this.config,
781
+ shape: requiredShape
782
+ });
783
+ }
784
+ /**
785
+ * Create a new validator with a catchall validator for extra keys
786
+ * Zod 4 API parity - validates unknown keys with provided schema
787
+ */
788
+ catchall(schema) {
789
+ return new VldObject({
790
+ ...this.config,
791
+ catchall: schema,
792
+ passthrough: false // catchall overrides passthrough
793
+ });
794
+ }
795
+ /**
796
+ * Access the inner shape schemas
797
+ * Zod 4 API parity - returns the shape object
798
+ */
799
+ get shape() {
800
+ return this.config.shape;
801
+ }
802
+ /**
803
+ * Create an enum validator from object keys
804
+ * Zod 4 API parity - creates literal union of keys
805
+ */
806
+ keyof() {
807
+ const keys = Object.keys(this.config.shape);
808
+ if (keys.length === 0) {
809
+ throw new Error('Cannot create keyof enum from empty object');
810
+ }
811
+ return VldEnum.create(keys);
812
+ }
813
+ /**
814
+ * Type-safe extend that throws an error if any key already exists
815
+ * Zod 4 API parity - prevents accidental field override
816
+ * @param extension The extension shape to add
817
+ * @returns A new validator with extended shape
818
+ * @throws {Error} If any extension key already exists in the shape
819
+ * @example
820
+ * const base = v.object({ name: v.string() });
821
+ * const extended = base.safeExtend({ age: v.number() }); // OK
822
+ * const invalid = base.safeExtend({ name: v.number() }); // Throws error
823
+ */
824
+ safeExtend(extension) {
825
+ // Check for overlapping keys
826
+ const existingKeys = new Set(Object.keys(this.config.shape));
827
+ const extensionKeys = Object.keys(extension);
828
+ const overlappingKeys = [];
829
+ for (const key of extensionKeys) {
830
+ if (existingKeys.has(key)) {
831
+ overlappingKeys.push(key);
832
+ }
833
+ }
834
+ if (overlappingKeys.length > 0) {
835
+ throw new Error(`safeExtend: ${getMessages().safeExtendOverlap(overlappingKeys)}`);
836
+ }
837
+ return new VldObject({
838
+ ...this.config,
839
+ shape: { ...this.config.shape, ...extension }
840
+ });
841
+ }
842
+ }
843
+
844
+ /**
845
+ * Optimized immutable union validator for multiple type options
846
+ * Features type-checking shortcuts for 110x performance improvement
847
+ */
848
+ class VldUnion extends VldBase {
849
+ /**
850
+ * Private constructor to enforce immutability
851
+ */
852
+ constructor(validators, errorMessage) {
853
+ super();
854
+ this.validators = validators;
855
+ this.errorMessage = errorMessage;
856
+ this.typeCheckers = new Map();
857
+ // Pre-compile type checkers for common types
858
+ for (const validator of validators) {
859
+ this.typeCheckers.set(validator, this.createTypeChecker(validator));
860
+ }
861
+ }
862
+ /**
863
+ * Create type checker based on validator type for fast path optimization
864
+ * Uses a safer approach that's less prone to spoofing
865
+ */
866
+ createTypeChecker(validator) {
867
+ // More conservative approach that prioritizes security and stability
868
+ // Only use fast path checks for very common, unambiguous types
869
+ try {
870
+ // Test basic types with safe parsing to validate the validator
871
+ const stringTest = validator.safeParse('test');
872
+ if (stringTest.success) {
873
+ return (v) => typeof v === 'string';
874
+ }
875
+ const numberTest = validator.safeParse(123);
876
+ if (numberTest.success) {
877
+ return (v) => typeof v === 'number' && !isNaN(v);
878
+ }
879
+ const booleanTest = validator.safeParse(true);
880
+ if (booleanTest.success) {
881
+ return (v) => typeof v === 'boolean';
882
+ }
883
+ const arrayTest = validator.safeParse([]);
884
+ if (arrayTest.success) {
885
+ return (v) => Array.isArray(v);
886
+ }
887
+ const objectTest = validator.safeParse({});
888
+ if (objectTest.success) {
889
+ return (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
890
+ }
891
+ const nullTest = validator.safeParse(null);
892
+ if (nullTest.success) {
893
+ return (v) => v === null;
894
+ }
895
+ const undefinedTest = validator.safeParse(undefined);
896
+ if (undefinedTest.success) {
897
+ return (v) => v === undefined;
898
+ }
899
+ }
900
+ catch {
901
+ // If safe testing fails, fall back to safe validation
902
+ }
903
+ // Fallback: no quick check available - use safe validation
904
+ return () => true;
905
+ }
906
+ /**
907
+ * Create a new union validator
908
+ */
909
+ static create(...validators) {
910
+ return new VldUnion(validators);
911
+ }
912
+ /**
913
+ * Parse and validate a value against union options
914
+ * Optimized with type checking and safeParse to avoid try-catch overhead
915
+ * BUG-NEW-013 FIX: Single-pass error collection to avoid double parsing
916
+ */
917
+ parse(value) {
918
+ // Single pass: collect errors during validation
919
+ const errors = [];
920
+ for (const validator of this.validators) {
921
+ const typeChecker = this.typeCheckers.get(validator);
922
+ // Skip validators that definitely won't match based on type
923
+ if (typeChecker && !typeChecker(value)) {
924
+ continue;
925
+ }
926
+ // Use safeParse to avoid try-catch overhead
927
+ const result = validator.safeParse(value);
928
+ if (result.success) {
929
+ return result.data;
930
+ }
931
+ // Collect error for final message if all validators fail
932
+ errors.push(result.error.message);
933
+ }
934
+ // All validators failed - throw with collected errors
935
+ throw new Error(this.errorMessage ||
936
+ getMessages().unionNoMatch(errors));
937
+ }
938
+ /**
939
+ * Safely parse and validate a value against union options
940
+ * Optimized version using type checking shortcuts
941
+ * BUG-NEW-013 FIX: Single-pass error collection to avoid double parsing
942
+ */
943
+ safeParse(value) {
944
+ // Single pass: collect errors during validation
945
+ const errors = [];
946
+ for (const validator of this.validators) {
947
+ const typeChecker = this.typeCheckers.get(validator);
948
+ // Skip validators that definitely won't match based on type
949
+ if (typeChecker && !typeChecker(value)) {
950
+ continue;
951
+ }
952
+ const result = validator.safeParse(value);
953
+ if (result.success) {
954
+ return result;
955
+ }
956
+ // Collect error for final message if all validators fail
957
+ errors.push(result.error.message);
958
+ }
959
+ // All validators failed - return error with collected messages
960
+ return {
961
+ success: false,
962
+ error: new Error(this.errorMessage ||
963
+ getMessages().unionNoMatch(errors))
964
+ };
965
+ }
966
+ }
967
+
968
+ /**
969
+ * Immutable literal validator for exact value matching
970
+ */
971
+ class VldLiteral extends VldBase {
972
+ /**
973
+ * Private constructor to enforce immutability
974
+ */
975
+ constructor(literal, errorMessage) {
976
+ super();
977
+ this.literal = literal;
978
+ this.errorMessage = errorMessage;
979
+ }
980
+ /**
981
+ * Create a new literal validator
982
+ */
983
+ static create(literal) {
984
+ return new VldLiteral(literal);
985
+ }
986
+ /**
987
+ * Parse and validate a literal value
988
+ */
989
+ parse(value) {
990
+ if (value !== this.literal) {
991
+ throw new Error(this.errorMessage ||
992
+ getMessages().literalExpected(JSON.stringify(this.literal), JSON.stringify(value)));
993
+ }
994
+ return this.literal;
995
+ }
996
+ /**
997
+ * Safely parse and validate a literal value
998
+ */
999
+ safeParse(value) {
1000
+ if (value === this.literal) {
1001
+ return { success: true, data: this.literal };
1002
+ }
1003
+ return {
1004
+ success: false,
1005
+ error: new Error(this.errorMessage ||
1006
+ getMessages().literalExpected(JSON.stringify(this.literal), JSON.stringify(value)))
1007
+ };
1008
+ }
1009
+ }
1010
+
1011
+ /**
1012
+ * Immutable symbol validator
1013
+ */
1014
+ class VldSymbol extends VldBase {
1015
+ /**
1016
+ * Private constructor to enforce immutability
1017
+ */
1018
+ constructor(errorMessage) {
1019
+ super();
1020
+ this.errorMessage = errorMessage;
1021
+ }
1022
+ /**
1023
+ * Create a new symbol validator
1024
+ */
1025
+ static create() {
1026
+ return new VldSymbol();
1027
+ }
1028
+ /**
1029
+ * Parse and validate a symbol value
1030
+ */
1031
+ parse(value) {
1032
+ if (typeof value !== 'symbol') {
1033
+ throw new Error(this.errorMessage || getMessages().invalidSymbol);
1034
+ }
1035
+ return value;
1036
+ }
1037
+ /**
1038
+ * Safely parse and validate a symbol value
1039
+ */
1040
+ safeParse(value) {
1041
+ if (typeof value === 'symbol') {
1042
+ return { success: true, data: value };
1043
+ }
1044
+ return {
1045
+ success: false,
1046
+ error: new Error(this.errorMessage || getMessages().invalidSymbol)
1047
+ };
1048
+ }
1049
+ }
1050
+
1051
+ /**
1052
+ * Immutable tuple validator for fixed-length arrays
1053
+ */
1054
+ class VldTuple extends VldBase {
1055
+ /**
1056
+ * Private constructor to enforce immutability
1057
+ */
1058
+ constructor(validators, errorMessage) {
1059
+ super();
1060
+ this.validators = validators;
1061
+ this.errorMessage = errorMessage;
1062
+ }
1063
+ /**
1064
+ * Create a new tuple validator
1065
+ */
1066
+ static create(...validators) {
1067
+ return new VldTuple(validators);
1068
+ }
1069
+ /**
1070
+ * Parse and validate a tuple value
1071
+ */
1072
+ parse(value) {
1073
+ if (!Array.isArray(value)) {
1074
+ throw new Error(this.errorMessage || getMessages().invalidTuple);
1075
+ }
1076
+ if (value.length !== this.validators.length) {
1077
+ throw new Error(this.errorMessage ||
1078
+ getMessages().tupleLength(this.validators.length, value.length));
1079
+ }
1080
+ const result = [];
1081
+ for (let i = 0; i < this.validators.length; i++) {
1082
+ try {
1083
+ result[i] = this.validators[i].parse(value[i]);
1084
+ }
1085
+ catch (error) {
1086
+ throw new Error(getMessages().arrayItem(i, error.message));
1087
+ }
1088
+ }
1089
+ return result;
1090
+ }
1091
+ /**
1092
+ * Safely parse and validate a tuple value
1093
+ */
1094
+ safeParse(value) {
1095
+ try {
1096
+ return { success: true, data: this.parse(value) };
1097
+ }
1098
+ catch (error) {
1099
+ return { success: false, error: error };
1100
+ }
1101
+ }
1102
+ }
1103
+
1104
+ /**
1105
+ * Security utilities for prototype pollution protection
1106
+ * BUG-NEW-018 & BUG-NEW-020 FIX: Centralized comprehensive dangerous key detection
1107
+ */
1108
+ /**
1109
+ * Comprehensive prototype pollution protection
1110
+ * Checks for dangerous keys that could modify Object.prototype or built-in prototypes
1111
+ * @param key The property key to check
1112
+ * @returns true if the key is dangerous, false otherwise
1113
+ */
1114
+ function isDangerousKey(key) {
1115
+ // Direct dangerous keys
1116
+ const directDangerousKeys = ['__proto__', 'constructor', 'prototype'];
1117
+ if (directDangerousKeys.includes(key)) {
1118
+ return true;
1119
+ }
1120
+ // Nested prototype manipulation vectors
1121
+ // These patterns could allow prototype pollution through nested access
1122
+ const nestedPatterns = [
1123
+ 'constructor.prototype',
1124
+ '__proto__.toString',
1125
+ 'prototype.constructor',
1126
+ '__defineGetter__',
1127
+ '__defineSetter__',
1128
+ '__lookupGetter__',
1129
+ '__lookupSetter__'
1130
+ ];
1131
+ // Check for nested patterns
1132
+ for (const pattern of nestedPatterns) {
1133
+ if (key.includes(pattern)) {
1134
+ return true;
1135
+ }
1136
+ }
1137
+ // Check for property access chains that could lead to prototype pollution
1138
+ // This covers patterns like "x.constructor.prototype.polluted"
1139
+ const dangerousChains = [
1140
+ 'constructor.',
1141
+ '__proto__.',
1142
+ 'prototype.'
1143
+ ];
1144
+ for (const chain of dangerousChains) {
1145
+ if (key.includes(chain)) {
1146
+ return true;
1147
+ }
1148
+ }
1149
+ // Additional protection: reject keys that could be used for property shadowing
1150
+ const shadowingPatterns = [
1151
+ 'hasOwnProperty',
1152
+ 'toString',
1153
+ 'valueOf',
1154
+ 'isPrototypeOf',
1155
+ 'propertyIsEnumerable'
1156
+ ];
1157
+ for (const pattern of shadowingPatterns) {
1158
+ if (key === pattern || key.includes(`.${pattern}`)) {
1159
+ return true;
1160
+ }
1161
+ }
1162
+ return false;
1163
+ }
1164
+
1165
+ /**
1166
+ * Immutable record validator for key-value pairs
1167
+ * BUG-NEW-018 FIX: Uses comprehensive dangerous key protection
1168
+ */
1169
+ class VldRecord extends VldBase {
1170
+ /**
1171
+ * Private constructor to enforce immutability
1172
+ */
1173
+ constructor(valueValidator, errorMessage) {
1174
+ super();
1175
+ this.valueValidator = valueValidator;
1176
+ this.errorMessage = errorMessage;
1177
+ }
1178
+ /**
1179
+ * Create a new record validator
1180
+ */
1181
+ static create(valueValidator) {
1182
+ return new VldRecord(valueValidator);
1183
+ }
1184
+ /**
1185
+ * Parse and validate a record value
1186
+ * BUG-NEW-018 FIX: Use comprehensive dangerous key protection
1187
+ */
1188
+ parse(value) {
1189
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
1190
+ throw new Error(this.errorMessage || getMessages().invalidRecord);
1191
+ }
1192
+ const result = {};
1193
+ const obj = value;
1194
+ for (const [key, val] of Object.entries(obj)) {
1195
+ // Skip dangerous keys to prevent prototype pollution
1196
+ // Now using comprehensive protection from shared utility
1197
+ if (isDangerousKey(key)) {
1198
+ continue;
1199
+ }
1200
+ try {
1201
+ result[key] = this.valueValidator.parse(val);
1202
+ }
1203
+ catch (error) {
1204
+ throw new Error(getMessages().objectField(key, error.message));
1205
+ }
1206
+ }
1207
+ return result;
1208
+ }
1209
+ /**
1210
+ * Safely parse and validate a record value
1211
+ */
1212
+ safeParse(value) {
1213
+ try {
1214
+ return { success: true, data: this.parse(value) };
1215
+ }
1216
+ catch (error) {
1217
+ return { success: false, error: error };
1218
+ }
1219
+ }
1220
+ /**
1221
+ * Create a partial record variant (all keys optional)
1222
+ * Similar to v.object().partial() but for records
1223
+ */
1224
+ partial() {
1225
+ const optionalValidator = this.valueValidator.optional();
1226
+ return new VldRecord(optionalValidator);
1227
+ }
1228
+ /**
1229
+ * Create a loose record variant (allows extra keys)
1230
+ * Similar to v.object().passthrough() but for records
1231
+ * Note: Records already allow any keys, so this mainly affects error handling
1232
+ */
1233
+ loose() {
1234
+ // For records, "loose" means we don't throw errors for validation failures
1235
+ // We return a modified version that catches validation errors
1236
+ return new VldLooseRecord(this.valueValidator);
1237
+ }
1238
+ }
1239
+ /**
1240
+ * Loose record variant that allows validation failures
1241
+ * Used internally by .loose() method
1242
+ */
1243
+ class VldLooseRecord extends VldBase {
1244
+ constructor(valueValidator) {
1245
+ super();
1246
+ this.valueValidator = valueValidator;
1247
+ }
1248
+ parse(value) {
1249
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
1250
+ throw new Error(getMessages().invalidRecord);
1251
+ }
1252
+ const result = {};
1253
+ const obj = value;
1254
+ for (const [key, val] of Object.entries(obj)) {
1255
+ // Skip dangerous keys
1256
+ if (isDangerousKey(key)) {
1257
+ continue;
1258
+ }
1259
+ // For loose records, skip invalid values instead of throwing
1260
+ const parseResult = this.valueValidator.safeParse(val);
1261
+ if (parseResult.success) {
1262
+ result[key] = parseResult.data;
1263
+ }
1264
+ }
1265
+ return result;
1266
+ }
1267
+ safeParse(value) {
1268
+ try {
1269
+ return { success: true, data: this.parse(value) };
1270
+ }
1271
+ catch (error) {
1272
+ return { success: false, error: error };
1273
+ }
1274
+ }
1275
+ /**
1276
+ * Create a partial variant (all keys optional)
1277
+ */
1278
+ partial() {
1279
+ const optionalValidator = this.valueValidator.optional();
1280
+ return new VldLooseRecord(optionalValidator);
1281
+ }
1282
+ /**
1283
+ * Return self (already loose)
1284
+ */
1285
+ loose() {
1286
+ return this;
1287
+ }
1288
+ }
1289
+
1290
+ /**
1291
+ * Immutable Set validator
1292
+ */
1293
+ class VldSet extends VldBase {
1294
+ /**
1295
+ * Private constructor to enforce immutability
1296
+ */
1297
+ constructor(itemValidator, errorMessage) {
1298
+ super();
1299
+ this.itemValidator = itemValidator;
1300
+ this.errorMessage = errorMessage;
1301
+ }
1302
+ /**
1303
+ * Create a new Set validator
1304
+ */
1305
+ static create(itemValidator) {
1306
+ return new VldSet(itemValidator);
1307
+ }
1308
+ /**
1309
+ * Parse and validate a Set value
1310
+ */
1311
+ parse(value) {
1312
+ if (!(value instanceof Set)) {
1313
+ throw new Error(this.errorMessage || getMessages().invalidSet);
1314
+ }
1315
+ const result = new Set();
1316
+ for (const item of value) {
1317
+ try {
1318
+ result.add(this.itemValidator.parse(item));
1319
+ }
1320
+ catch (error) {
1321
+ throw new Error(error.message);
1322
+ }
1323
+ }
1324
+ return result;
1325
+ }
1326
+ /**
1327
+ * Safely parse and validate a Set value
1328
+ */
1329
+ safeParse(value) {
1330
+ try {
1331
+ return { success: true, data: this.parse(value) };
1332
+ }
1333
+ catch (error) {
1334
+ return { success: false, error: error };
1335
+ }
1336
+ }
1337
+ }
1338
+
1339
+ /**
1340
+ * Immutable Map validator
1341
+ */
1342
+ class VldMap extends VldBase {
1343
+ /**
1344
+ * Private constructor to enforce immutability
1345
+ */
1346
+ constructor(keyValidator, valueValidator, errorMessage) {
1347
+ super();
1348
+ this.keyValidator = keyValidator;
1349
+ this.valueValidator = valueValidator;
1350
+ this.errorMessage = errorMessage;
1351
+ }
1352
+ /**
1353
+ * Create a new Map validator
1354
+ */
1355
+ static create(keyValidator, valueValidator) {
1356
+ return new VldMap(keyValidator, valueValidator);
1357
+ }
1358
+ /**
1359
+ * Parse and validate a Map value
1360
+ */
1361
+ parse(value) {
1362
+ if (!(value instanceof Map)) {
1363
+ throw new Error(this.errorMessage || getMessages().invalidMap);
1364
+ }
1365
+ const result = new Map();
1366
+ for (const [key, val] of value) {
1367
+ try {
1368
+ const validKey = this.keyValidator.parse(key);
1369
+ const validValue = this.valueValidator.parse(val);
1370
+ result.set(validKey, validValue);
1371
+ }
1372
+ catch (error) {
1373
+ throw new Error(error.message);
1374
+ }
1375
+ }
1376
+ return result;
1377
+ }
1378
+ /**
1379
+ * Safely parse and validate a Map value
1380
+ */
1381
+ safeParse(value) {
1382
+ try {
1383
+ return { success: true, data: this.parse(value) };
1384
+ }
1385
+ catch (error) {
1386
+ return { success: false, error: error };
1387
+ }
1388
+ }
1389
+ }
1390
+
1391
+ /**
1392
+ * Type guard to check if a value is a plain object
1393
+ */
1394
+ function isPlainObject(obj) {
1395
+ return (obj !== null &&
1396
+ typeof obj === 'object' &&
1397
+ obj.constructor === Object &&
1398
+ Object.prototype.toString.call(obj) === '[object Object]');
1399
+ }
1400
+ /**
1401
+ * Safely deep merge two objects without prototype pollution vulnerability
1402
+ * Optimized for performance with minimal object allocations
1403
+ * BUG-NEW-020 FIX: Use comprehensive dangerous key protection from shared utility
1404
+ * @param target The target object
1405
+ * @param source The source object to merge from
1406
+ * @returns A new merged object
1407
+ */
1408
+ function deepMerge(target, source) {
1409
+ // Create a new object to avoid mutations (only once)
1410
+ const result = { ...target };
1411
+ // Get source keys directly for better performance
1412
+ const sourceKeys = Object.keys(source);
1413
+ for (let i = 0; i < sourceKeys.length; i++) {
1414
+ const key = sourceKeys[i];
1415
+ // Skip dangerous keys that could lead to prototype pollution
1416
+ if (isDangerousKey(key)) {
1417
+ continue;
1418
+ }
1419
+ // Only process own properties (redundant with Object.keys but safe)
1420
+ if (!Object.prototype.hasOwnProperty.call(source, key)) {
1421
+ continue;
1422
+ }
1423
+ const sourceValue = source[key];
1424
+ const targetValue = target[key];
1425
+ // If both values are plain objects, merge them recursively
1426
+ if (isPlainObject(sourceValue) && isPlainObject(targetValue)) {
1427
+ result[key] = deepMerge(targetValue, sourceValue);
1428
+ }
1429
+ else {
1430
+ // Otherwise, use the source value (including undefined)
1431
+ result[key] = sourceValue;
1432
+ }
1433
+ }
1434
+ return result;
1435
+ }
1436
+
1437
+ /**
1438
+ * Immutable intersection validator for combining validators
1439
+ */
1440
+ class VldIntersection extends VldBase {
1441
+ /**
1442
+ * Private constructor to enforce immutability
1443
+ */
1444
+ constructor(validatorA, validatorB) {
1445
+ super();
1446
+ this.validatorA = validatorA;
1447
+ this.validatorB = validatorB;
1448
+ }
1449
+ /**
1450
+ * Create a new intersection validator
1451
+ */
1452
+ static create(validatorA, validatorB) {
1453
+ return new VldIntersection(validatorA, validatorB);
1454
+ }
1455
+ /**
1456
+ * Parse and validate a value against both validators
1457
+ */
1458
+ parse(value) {
1459
+ try {
1460
+ // Both validators must pass
1461
+ const resultA = this.validatorA.parse(value);
1462
+ const resultB = this.validatorB.parse(value);
1463
+ // BUG-NEW-015 FIX: Check type consistency before merging
1464
+ const aIsObject = isPlainObject(resultA);
1465
+ const bIsObject = isPlainObject(resultB);
1466
+ // Both are objects - safe to merge
1467
+ if (aIsObject && bIsObject) {
1468
+ return deepMerge(resultA, resultB);
1469
+ }
1470
+ // Neither are objects - must be identical primitives
1471
+ if (!aIsObject && !bIsObject) {
1472
+ if (resultA === resultB) {
1473
+ return resultA;
1474
+ }
1475
+ throw new Error('Values must be identical for intersection of primitive types');
1476
+ }
1477
+ // One is object, one is primitive - invalid intersection
1478
+ throw new Error('Cannot create intersection of object and primitive types. ' +
1479
+ 'Both validators must produce the same type category.');
1480
+ }
1481
+ catch (error) {
1482
+ throw new Error(getMessages().intersectionError(error.message));
1483
+ }
1484
+ }
1485
+ /**
1486
+ * Safely parse and validate a value against both validators
1487
+ */
1488
+ safeParse(value) {
1489
+ try {
1490
+ return { success: true, data: this.parse(value) };
1491
+ }
1492
+ catch (error) {
1493
+ return { success: false, error: error };
1494
+ }
1495
+ }
1496
+ }
1497
+
1498
+ /**
1499
+ * Validator that accepts any value
1500
+ */
1501
+ class VldAny extends VldBase {
1502
+ /**
1503
+ * Create a new any validator
1504
+ */
1505
+ static create() {
1506
+ return new VldAny();
1507
+ }
1508
+ /**
1509
+ * Parse any value (always succeeds)
1510
+ */
1511
+ parse(value) {
1512
+ return value;
1513
+ }
1514
+ /**
1515
+ * Safely parse any value (always succeeds)
1516
+ */
1517
+ safeParse(value) {
1518
+ return { success: true, data: value };
1519
+ }
1520
+ }
1521
+
1522
+ /**
1523
+ * Validator that only accepts undefined
1524
+ */
1525
+ class VldVoid extends VldBase {
1526
+ /**
1527
+ * Create a new void validator
1528
+ */
1529
+ static create() {
1530
+ return new VldVoid();
1531
+ }
1532
+ /**
1533
+ * Parse void value (must be undefined)
1534
+ */
1535
+ parse(value) {
1536
+ if (value !== undefined) {
1537
+ throw new Error(getMessages().expectedUndefined);
1538
+ }
1539
+ return undefined;
1540
+ }
1541
+ /**
1542
+ * Safely parse void value
1543
+ */
1544
+ safeParse(value) {
1545
+ if (value === undefined) {
1546
+ return { success: true, data: undefined };
1547
+ }
1548
+ return {
1549
+ success: false,
1550
+ error: new Error(getMessages().expectedUndefined)
1551
+ };
1552
+ }
1553
+ }
1554
+
1555
+ /**
1556
+ * Validator that never succeeds
1557
+ */
1558
+ class VldNever extends VldBase {
1559
+ /**
1560
+ * Create a new never validator
1561
+ */
1562
+ static create() {
1563
+ return new VldNever();
1564
+ }
1565
+ /**
1566
+ * Parse never value (always fails)
1567
+ */
1568
+ parse(_value) {
1569
+ throw new Error(getMessages().neverType);
1570
+ }
1571
+ /**
1572
+ * Safely parse never value (always fails)
1573
+ */
1574
+ safeParse(_value) {
1575
+ return {
1576
+ success: false,
1577
+ error: new Error(getMessages().neverType)
1578
+ };
1579
+ }
1580
+ }
1581
+
1582
+ /**
1583
+ * VldNull - Validates that a value is exactly null
1584
+ * Part of Zod 4 API parity implementation
1585
+ */
1586
+ /**
1587
+ * Null validator - validates that a value is exactly null
1588
+ */
1589
+ class VldNull extends VldBase {
1590
+ constructor() {
1591
+ super();
1592
+ }
1593
+ static create() {
1594
+ return new VldNull();
1595
+ }
1596
+ parse(value) {
1597
+ if (value !== null) {
1598
+ throw new Error(`Expected null, received ${typeof value}`);
1599
+ }
1600
+ return null;
1601
+ }
1602
+ safeParse(value) {
1603
+ try {
1604
+ return { success: true, data: this.parse(value) };
1605
+ }
1606
+ catch (error) {
1607
+ return { success: false, error: error };
1608
+ }
1609
+ }
1610
+ }
1611
+
1612
+ /**
1613
+ * VldUndefined - Validates that a value is exactly undefined
1614
+ * Part of Zod 4 API parity implementation
1615
+ */
1616
+ /**
1617
+ * Undefined validator - validates that a value is exactly undefined
1618
+ */
1619
+ class VldUndefined extends VldBase {
1620
+ constructor() {
1621
+ super();
1622
+ }
1623
+ static create() {
1624
+ return new VldUndefined();
1625
+ }
1626
+ parse(value) {
1627
+ if (value !== undefined) {
1628
+ throw new Error(`Expected undefined, received ${typeof value}`);
1629
+ }
1630
+ return undefined;
1631
+ }
1632
+ safeParse(value) {
1633
+ try {
1634
+ return { success: true, data: this.parse(value) };
1635
+ }
1636
+ catch (error) {
1637
+ return { success: false, error: error };
1638
+ }
1639
+ }
1640
+ }
1641
+
1642
+ /**
1643
+ * VldNan - Validates that a value is NaN (Not a Number)
1644
+ * Part of Zod 4 API parity implementation
1645
+ */
1646
+ /**
1647
+ * NaN validator - validates that a value is NaN
1648
+ * Note: Uses Number.isNaN() which is more strict than global isNaN()
1649
+ */
1650
+ class VldNan extends VldBase {
1651
+ constructor() {
1652
+ super();
1653
+ }
1654
+ static create() {
1655
+ return new VldNan();
1656
+ }
1657
+ parse(value) {
1658
+ if (typeof value !== 'number' || !Number.isNaN(value)) {
1659
+ throw new Error(`Expected NaN, received ${typeof value === 'number' ? 'a valid number' : typeof value}`);
1660
+ }
1661
+ return value;
1662
+ }
1663
+ safeParse(value) {
1664
+ try {
1665
+ return { success: true, data: this.parse(value) };
1666
+ }
1667
+ catch (error) {
1668
+ return { success: false, error: error };
1669
+ }
1670
+ }
1671
+ }
1672
+
1673
+ /**
1674
+ * VldLazy - Lazy evaluation for recursive schemas
1675
+ * Part of Zod 4 API parity implementation
1676
+ * Allows schemas that reference themselves (e.g., tree structures, nested categories)
1677
+ */
1678
+ /**
1679
+ * Lazy validator - defers schema evaluation until runtime
1680
+ * Essential for recursive and self-referencing types
1681
+ */
1682
+ class VldLazy extends VldBase {
1683
+ constructor(_schemaGetter) {
1684
+ super();
1685
+ this._schemaGetter = _schemaGetter;
1686
+ this._cachedSchema = null;
1687
+ }
1688
+ static create(schemaGetter) {
1689
+ return new VldLazy(schemaGetter);
1690
+ }
1691
+ /**
1692
+ * Get the actual schema, caching it after first retrieval
1693
+ */
1694
+ _getSchema() {
1695
+ if (!this._cachedSchema) {
1696
+ this._cachedSchema = this._schemaGetter();
1697
+ }
1698
+ return this._cachedSchema;
1699
+ }
1700
+ /**
1701
+ * Get the inner schema (unwrap)
1702
+ */
1703
+ unwrap() {
1704
+ return this._getSchema();
1705
+ }
1706
+ parse(value) {
1707
+ return this._getSchema().parse(value);
1708
+ }
1709
+ safeParse(value) {
1710
+ return this._getSchema().safeParse(value);
1711
+ }
1712
+ }
1713
+
1714
+ /**
1715
+ * VldDiscriminatedUnion - Fast union validation using discriminator field
1716
+ * Part of Zod 4 API parity implementation
1717
+ * Provides O(1) lookup performance by using a discriminator key
1718
+ */
1719
+ /**
1720
+ * Extract literal values from a schema
1721
+ */
1722
+ function extractLiteralValues(schema) {
1723
+ if (schema instanceof VldLiteral) {
1724
+ const value = schema.literal;
1725
+ return [value];
1726
+ }
1727
+ if (schema instanceof VldEnum) {
1728
+ return schema.values;
1729
+ }
1730
+ throw new Error('Discriminator must be a literal or enum schema');
1731
+ }
1732
+ /**
1733
+ * Discriminated union validator - validates union based on discriminator key
1734
+ * Much faster than regular union when you have a discriminator field
1735
+ */
1736
+ class VldDiscriminatedUnion extends VldBase {
1737
+ constructor(_discriminator, _options) {
1738
+ super();
1739
+ this._discriminator = _discriminator;
1740
+ this._options = _options;
1741
+ // Build discriminator map for O(1) lookup
1742
+ this._discriminatorMap = new Map();
1743
+ for (const option of _options) {
1744
+ if (!(option instanceof VldObject)) {
1745
+ throw new Error('All options in a discriminated union must be objects');
1746
+ }
1747
+ const discriminatorSchema = option.config?.shape?.[this._discriminator];
1748
+ if (!discriminatorSchema) {
1749
+ throw new Error(`Missing discriminator key "${this._discriminator}" in one of the options`);
1750
+ }
1751
+ const values = extractLiteralValues(discriminatorSchema);
1752
+ for (const value of values) {
1753
+ if (this._discriminatorMap.has(value)) {
1754
+ throw new Error(`Duplicate discriminator value "${String(value)}" found in discriminated union`);
1755
+ }
1756
+ this._discriminatorMap.set(value, option);
1757
+ }
1758
+ }
1759
+ }
1760
+ static create(discriminator, options) {
1761
+ return new VldDiscriminatedUnion(discriminator, options);
1762
+ }
1763
+ parse(value) {
1764
+ const result = this.safeParse(value);
1765
+ if (!result.success) {
1766
+ throw result.error;
1767
+ }
1768
+ return result.data;
1769
+ }
1770
+ safeParse(value) {
1771
+ // Check if input is an object
1772
+ if (typeof value !== 'object' || value === null) {
1773
+ return {
1774
+ success: false,
1775
+ error: new Error(`Expected object, received ${value === null ? 'null' : typeof value}`)
1776
+ };
1777
+ }
1778
+ // Get discriminator value
1779
+ const discriminatorValue = value[this._discriminator];
1780
+ // Look up matching schema
1781
+ const matchedSchema = this._discriminatorMap.get(discriminatorValue);
1782
+ if (!matchedSchema) {
1783
+ const validValues = Array.from(this._discriminatorMap.keys());
1784
+ return {
1785
+ success: false,
1786
+ error: new Error(`Invalid discriminator value for "${this._discriminator}". ` +
1787
+ `Expected one of: ${JSON.stringify(validValues)}, ` +
1788
+ `received: ${JSON.stringify(discriminatorValue)}`)
1789
+ };
1790
+ }
1791
+ // Validate against matched schema
1792
+ return matchedSchema.safeParse(value);
1793
+ }
1794
+ /**
1795
+ * Get the discriminator key
1796
+ */
1797
+ getDiscriminator() {
1798
+ return this._discriminator;
1799
+ }
1800
+ /**
1801
+ * Get all options
1802
+ */
1803
+ getOptions() {
1804
+ return this._options;
1805
+ }
1806
+ }
1807
+
1808
+ /**
1809
+ * VldXor - Exclusive union validator
1810
+ * Part of Zod 4 API parity implementation
1811
+ * Ensures exactly one schema in the union matches
1812
+ */
1813
+ /**
1814
+ * XOR validator - ensures exactly one option matches
1815
+ * Unlike regular union which allows multiple matches, XOR requires exactly one
1816
+ */
1817
+ class VldXor extends VldBase {
1818
+ constructor(_options) {
1819
+ super();
1820
+ this._options = _options;
1821
+ }
1822
+ static create(options) {
1823
+ if (options.length < 2) {
1824
+ throw new Error('XOR requires at least 2 options');
1825
+ }
1826
+ return new VldXor(options);
1827
+ }
1828
+ parse(value) {
1829
+ const result = this.safeParse(value);
1830
+ if (!result.success) {
1831
+ throw result.error;
1832
+ }
1833
+ return result.data;
1834
+ }
1835
+ safeParse(value) {
1836
+ let matchCount = 0;
1837
+ let lastSuccess = null;
1838
+ for (const option of this._options) {
1839
+ const result = option.safeParse(value);
1840
+ if (result.success) {
1841
+ matchCount++;
1842
+ lastSuccess = result;
1843
+ if (matchCount > 1) {
1844
+ // Early exit if more than one match
1845
+ break;
1846
+ }
1847
+ }
1848
+ }
1849
+ if (matchCount === 0) {
1850
+ return {
1851
+ success: false,
1852
+ error: new Error('No schema matched in XOR union')
1853
+ };
1854
+ }
1855
+ if (matchCount > 1) {
1856
+ return {
1857
+ success: false,
1858
+ error: new Error(`Input matches ${matchCount} schemas in XOR union, but exactly one is required`)
1859
+ };
1860
+ }
1861
+ return lastSuccess;
1862
+ }
1863
+ /**
1864
+ * Get all options
1865
+ */
1866
+ getOptions() {
1867
+ return this._options;
1868
+ }
1869
+ }
1870
+
1871
+ /**
1872
+ * Immutable JSON validator that validates and parses JSON strings
1873
+ * Accepts both strings (which are parsed) and already-parsed objects
1874
+ */
1875
+ class VldJson extends VldBase {
1876
+ constructor(schema) {
1877
+ super();
1878
+ this.schema = schema;
1879
+ }
1880
+ /**
1881
+ * Create a new JSON validator
1882
+ */
1883
+ static create(schema) {
1884
+ return new VldJson(schema);
1885
+ }
1886
+ /**
1887
+ * Parse and validate a JSON value
1888
+ */
1889
+ parse(value) {
1890
+ let parsed;
1891
+ // If it's a string, try to parse it as JSON first
1892
+ if (typeof value === 'string') {
1893
+ try {
1894
+ parsed = JSON.parse(value);
1895
+ }
1896
+ catch (e) {
1897
+ throw new Error(getMessages().invalidJson);
1898
+ }
1899
+ }
1900
+ else {
1901
+ // Not a string - assume it's already parsed
1902
+ parsed = value;
1903
+ }
1904
+ // If we have a schema, validate against it
1905
+ if (this.schema) {
1906
+ return this.schema.parse(parsed);
1907
+ }
1908
+ // No schema - just return the parsed value
1909
+ return parsed;
1910
+ }
1911
+ /**
1912
+ * Safely parse and validate a JSON value
1913
+ */
1914
+ safeParse(value) {
1915
+ try {
1916
+ return { success: true, data: this.parse(value) };
1917
+ }
1918
+ catch (error) {
1919
+ return {
1920
+ success: false,
1921
+ error: error
1922
+ };
1923
+ }
1924
+ }
1925
+ /**
1926
+ * Create a new JSON validator with a schema for validation
1927
+ */
1928
+ withSchema(schema) {
1929
+ return new VldJson(schema);
1930
+ }
1931
+ }
1932
+
1933
+ export { VldSymbol as V, VldStringBool as a, VldArray as b, VldObject as c, VldTuple as d, VldRecord as e, VldSet as f, VldMap as g, VldUnion as h, VldIntersection as i, VldDiscriminatedUnion as j, VldXor as k, VldLiteral as l, VldEnum as m, VldAny as n, VldVoid as o, VldNever as p, VldNull as q, VldUndefined as r, VldNan as s, VldLazy as t, VldJson as u };
1934
+ //# sourceMappingURL=json-nIhyRPOU.js.map